risor icon indicating copy to clipboard operation
risor copied to clipboard

New module: SSH client

Open runsisi opened this issue 2 years ago • 8 comments

i think ssh support is a must for devops, it is possible for risor?

thank you for your amazing work :)

runsisi avatar Nov 23 '23 23:11 runsisi

Hi @runsisi, probably so. I would guess you're most interested in having Risor be an SSH client, right?

Looks like we should just be able to wrap golang.org/x/crypto/ssh.

Any chance you want to take a stab at the implementation? I could provide guidance as needed :-)

myzie avatar Dec 07 '23 14:12 myzie

I can look into this, if no one else is, @runsisi do you have any specific use cases in mind?

luisdavim avatar Jan 18 '24 18:01 luisdavim

hi @myzie i'm so sorry for delayed response, really busy these days :(

hi @luisdavim , my use case is simple enough:

  1. connect to remote node
  2. scp local file/directory to remote node (for directory copy then something like https://github.com/golang/go/issues/62484 ?)
  3. run command on remote node and get stdout/stderr output (maybe in real time)
  4. with context support, e.g., cancel/timeout, would be better :)

something like the code snippet:

scp

import "github.com/bramvdbogaerde/go-scp"

func ScpFile(ctx context.Context, sshc *ssh.Client, r io.Reader, p string, perm os.FileMode) error {
	scpc, err := scp.NewClientBySSH(sshc)
	if err != nil {
		return errors.New(err)
	}
	defer scpc.Close()

	if err := scpc.CopyFile(ctx, r, p, "0"+strconv.FormatUint(uint64(perm), 8)); err != nil {
		return errors.New(err)
	}
	return nil
}

func ScpContent(ctx context.Context, sshc *ssh.Client, r io.Reader, p string, perm os.FileMode, len int) error {
	scpc, err := scp.NewClientBySSH(sshc)
	if err != nil {
		return errors.New(err)
	}
	defer scpc.Close()

	if err := scpc.Copy(ctx, r, p, "0"+strconv.FormatUint(uint64(perm), 8), int64(len)); err != nil {
		return errors.New(err)
	}
	return nil
}

run command

func SshRunCommand(sshc *ssh.Client, cmdline string, out io.Writer) (string, string, error) {
	sess, err := sshc.NewSession()
	if err != nil {
		return "", "", errors.New(err)
	}
	defer sess.Close()

	if out == nil {
		out = io.Discard
	}

	sob := &bytes.Buffer{}
	seb := &bytes.Buffer{}

	var sow io.Writer = sob
	var sew io.Writer = seb

	if out != nil {
		sw := &singleWriter{
			w: out,
		}

		sow = io.MultiWriter(sob, sw)
		sew = io.MultiWriter(seb, sw)
	}

	sess.Stdout = sow
	sess.Stderr = sew

	if err := sess.Start(cmdline); err != nil {
		return "", "", errors.New(err)
	}
	if err := sess.Wait(); err != nil {
		return "", "", errors.New(err)
	}

	return strings.TrimSpace(sob.String()), strings.TrimSpace(sob.String()), nil
}

runsisi avatar Jan 19 '24 00:01 runsisi

Hi! I am using Risor for myself and would like to work on this issue.

roopeshsn avatar Feb 19 '24 02:02 roopeshsn

Hi @roopeshsn, to my knowledge no one has started on this yet. Go ahead and take a stab at it and I'm sure a couple of us will help with reviews.

It seems like the main two cases are 1) an scp like command and 2) running a command remotely via SSH.

myzie avatar Feb 19 '24 04:02 myzie

Hi @roopeshsn, to my knowledge no one has started on this yet. Go ahead and take a stab at it and I'm sure a couple of us will help with reviews.

It seems like the main two cases are 1) an scp like command and 2) running a command remotely via SSH.

Thanks! I'll take a look at the code and reach back for queries.

roopeshsn avatar Feb 19 '24 04:02 roopeshsn

Hi @myzie! For SCP, there is a package called go-scp. It depends on crypto/ssh for copying files. From the discussions, what I understood was that in addition to implementing SCP, we need the crypto/ssh package to be implemented in Risor for other tasks, right?

roopeshsn avatar Mar 05 '24 12:03 roopeshsn

It's ok to depend on another library. When we do that, it needs to be a Risor module that has its own go.mod file. For example this would make it like the K8s module.

https://github.com/risor-io/risor/blob/main/modules/kubernetes/go.mod

myzie avatar Mar 05 '24 12:03 myzie