| package manager | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"context" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"net" | 
 | 	"time" | 
 |  | 
 | 	"github.com/pkg/sftp" | 
 | 	"golang.org/x/crypto/ssh" | 
 | ) | 
 |  | 
 | // SSHClient defines a simple interface to an abstract SSH client. Usually this | 
 | // would be PlainSSHClient, but tests can use this interface to dependency-inject | 
 | // fake SSH connections. | 
 | type SSHClient interface { | 
 | 	// Dial returns an SSHConnection to a given address (host:port pair) with | 
 | 	// a timeout for connection. | 
 | 	Dial(ctx context.Context, address string, connectTimeout time.Duration) (SSHConnection, error) | 
 | } | 
 |  | 
 | type SSHConnection interface { | 
 | 	// Execute a given command on a remote host synchronously, passing in stdin as | 
 | 	// input, and returning a captured stdout/stderr. The returned data might be | 
 | 	// valid even when err != nil, which might happen if the remote side returned a | 
 | 	// non-zero exit code. | 
 | 	Execute(ctx context.Context, command string, stdin []byte) (stdout []byte, stderr []byte, err error) | 
 | 	// Upload a given blob to a targetPath on the system and make executable. | 
 | 	Upload(ctx context.Context, targetPath string, data []byte) error | 
 | 	// Close this connection. | 
 | 	Close() error | 
 | } | 
 |  | 
 | // PlainSSHClient implements SSHClient (and SSHConnection) using | 
 | // golang.org/x/crypto/ssh. | 
 | type PlainSSHClient struct { | 
 | 	AuthMethod ssh.AuthMethod | 
 | 	Username   string | 
 | } | 
 |  | 
 | type plainSSHConn struct { | 
 | 	cl *ssh.Client | 
 | } | 
 |  | 
 | func (p *PlainSSHClient) Dial(ctx context.Context, address string, connectTimeout time.Duration) (SSHConnection, error) { | 
 | 	d := net.Dialer{ | 
 | 		Timeout: connectTimeout, | 
 | 	} | 
 | 	conn, err := d.DialContext(ctx, "tcp", address) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	conf := &ssh.ClientConfig{ | 
 | 		User: p.Username, | 
 | 		Auth: []ssh.AuthMethod{ | 
 | 			p.AuthMethod, | 
 | 		}, | 
 | 		// Ignore the host key, since it's likely the first time anything logs into | 
 | 		// this device, and also because there's no way of knowing its fingerprint. | 
 | 		HostKeyCallback: ssh.InsecureIgnoreHostKey(), | 
 | 		// Timeout sets a bound on the time it takes to set up the connection, but | 
 | 		// not on total session time. | 
 | 		Timeout: connectTimeout, | 
 | 	} | 
 | 	conn2, chanC, reqC, err := ssh.NewClientConn(conn, address, conf) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	cl := ssh.NewClient(conn2, chanC, reqC) | 
 | 	return &plainSSHConn{ | 
 | 		cl: cl, | 
 | 	}, nil | 
 | } | 
 |  | 
 | func (p *plainSSHConn) Execute(ctx context.Context, command string, stdin []byte) (stdout []byte, stderr []byte, err error) { | 
 | 	sess, err := p.cl.NewSession() | 
 | 	if err != nil { | 
 | 		return nil, nil, fmt.Errorf("while creating SSH session: %w", err) | 
 | 	} | 
 | 	stdoutBuf := bytes.NewBuffer(nil) | 
 | 	stderrBuf := bytes.NewBuffer(nil) | 
 | 	sess.Stdin = bytes.NewBuffer(stdin) | 
 | 	sess.Stdout = stdoutBuf | 
 | 	sess.Stderr = stderrBuf | 
 | 	defer sess.Close() | 
 |  | 
 | 	if err := sess.Start(command); err != nil { | 
 | 		return nil, nil, err | 
 | 	} | 
 | 	doneC := make(chan error, 1) | 
 | 	go func() { | 
 | 		doneC <- sess.Wait() | 
 | 	}() | 
 | 	select { | 
 | 	case <-ctx.Done(): | 
 | 		return nil, nil, ctx.Err() | 
 | 	case err := <-doneC: | 
 | 		return stdoutBuf.Bytes(), stderrBuf.Bytes(), err | 
 | 	} | 
 | } | 
 |  | 
 | func (p *plainSSHConn) Upload(ctx context.Context, targetPath string, data []byte) error { | 
 | 	sc, err := sftp.NewClient(p.cl) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("while building sftp client: %w", err) | 
 | 	} | 
 | 	defer sc.Close() | 
 |  | 
 | 	acrdr := bytes.NewReader(data) | 
 | 	df, err := sc.Create(targetPath) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("while creating file on the host: %w", err) | 
 | 	} | 
 |  | 
 | 	doneC := make(chan error, 1) | 
 |  | 
 | 	go func() { | 
 | 		_, err := io.Copy(df, acrdr) | 
 | 		df.Close() | 
 | 		doneC <- err | 
 | 	}() | 
 |  | 
 | 	select { | 
 | 	case err := <-doneC: | 
 | 		if err != nil { | 
 | 			return fmt.Errorf("while copying file: %w", err) | 
 | 		} | 
 | 	case <-ctx.Done(): | 
 | 		df.Close() | 
 | 		return ctx.Err() | 
 | 	} | 
 |  | 
 | 	if err := sc.Chmod(targetPath, 0755); err != nil { | 
 | 		return fmt.Errorf("while setting file permissions: %w", err) | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | func (p *plainSSHConn) Close() error { | 
 | 	return p.cl.Close() | 
 | } |