From 709b0a84e39044e5513a1af33df979fe472df5b2 Mon Sep 17 00:00:00 2001 From: Simon Thulbourn Date: Sun, 29 Mar 2015 18:10:06 +0100 Subject: [PATCH] Add SSH client Signed-off-by: Simon Thulbourn --- commands/ssh.go | 24 ++--- drivers/drivers.go | 20 +++- drivers/google/compute_util.go | 14 ++- drivers/virtualbox/virtualbox.go | 12 +-- libmachine/host.go | 51 ++++++--- libmachine/provision/boot2docker.go | 28 ++--- libmachine/provision/provisioner.go | 15 +-- libmachine/provision/ubuntu.go | 41 ++------ libmachine/provision/utils.go | 69 +++---------- ssh/client.go | 154 ++++++++++++++++++++++++++++ ssh/ssh.go | 32 ------ 11 files changed, 276 insertions(+), 184 deletions(-) create mode 100644 ssh/client.go diff --git a/commands/ssh.go b/commands/ssh.go index e2de4c2ca5..aa8f6b6b58 100644 --- a/commands/ssh.go +++ b/commands/ssh.go @@ -1,19 +1,20 @@ package commands import ( + "io" "os" - "os/exec" + "strings" log "github.com/Sirupsen/logrus" "github.com/codegangsta/cli" "github.com/docker/machine/drivers" + "github.com/docker/machine/ssh" ) func cmdSsh(c *cli.Context) { var ( - err error - sshCmd *exec.Cmd + err error ) name := c.Args().First() @@ -59,19 +60,18 @@ func cmdSsh(c *cli.Context) { } } + var output ssh.Output + if len(c.Args()) <= 1 { - sshCmd, err = host.GetSSHCommand() + err = host.CreateSSHShell() } else { - sshCmd, err = host.GetSSHCommand(c.Args()[1:]...) + output, err = host.RunSSHCommand(strings.Join(c.Args()[1:], " ")) + + io.Copy(os.Stderr, output.Stderr) + io.Copy(os.Stdout, output.Stdout) } + if err != nil { log.Fatal(err) } - - sshCmd.Stdin = os.Stdin - sshCmd.Stdout = os.Stdout - sshCmd.Stderr = os.Stderr - if err := sshCmd.Run(); err != nil { - log.Fatal(err) - } } diff --git a/drivers/drivers.go b/drivers/drivers.go index c5f379bd40..2b432624be 100644 --- a/drivers/drivers.go +++ b/drivers/drivers.go @@ -3,7 +3,6 @@ package drivers import ( "errors" "fmt" - "os/exec" "sort" log "github.com/Sirupsen/logrus" @@ -174,21 +173,32 @@ type DriverOptions interface { Bool(key string) bool } -func GetSSHCommandFromDriver(d Driver, args ...string) (*exec.Cmd, error) { +func RunSSHCommandFromDriver(d Driver, args string) (ssh.Output, error) { + var output ssh.Output + host, err := d.GetSSHHostname() if err != nil { - return nil, err + return output, err } port, err := d.GetSSHPort() if err != nil { - return nil, err + return output, err } user := d.GetSSHUsername() keyPath := d.GetSSHKeyPath() - return ssh.GetSSHCommand(host, port, user, keyPath, args...), nil + auth := &ssh.Auth{ + Keys: []string{keyPath}, + } + + client, err := ssh.NewClient(user, host, port, auth) + if err != nil { + return output, err + } + + return client.Run(args) } func MachineInState(d Driver, desiredState state.State) func() bool { diff --git a/drivers/google/compute_util.go b/drivers/google/compute_util.go index a0cfe19bc6..3880e716ed 100644 --- a/drivers/google/compute_util.go +++ b/drivers/google/compute_util.go @@ -234,9 +234,17 @@ func (c *ComputeUtil) deleteInstance() error { func (c *ComputeUtil) executeCommands(commands []string, ip, sshKeyPath string) error { for _, command := range commands { - cmd := ssh.GetSSHCommand(ip, 22, c.userName, sshKeyPath, command) - if err := cmd.Run(); err != nil { - return fmt.Errorf("error executing command: %v %v", command, err) + auth := &ssh.Auth{ + Keys: []string{sshKeyPath}, + } + + client, err := ssh.NewClient(c.userName, ip, 22, auth) + if err != nil { + return err + } + + if _, err := client.Run(command); err != nil { + return err } } return nil diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 153162f617..453afb5d86 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -452,19 +452,17 @@ func (d *Driver) GetIP() (string, error) { if s != state.Running { return "", drivers.ErrHostIsNotRunning } - cmd, err := drivers.GetSSHCommandFromDriver(d, "ip addr show dev eth1") + output, err := drivers.RunSSHCommandFromDriver(d, "ip addr show dev eth1") if err != nil { return "", err } - // reset to nil as if using from Host Stdout is already set when using DEBUG - cmd.Stdout = nil - - b, err := cmd.Output() - if err != nil { + var buf bytes.Buffer + if _, err := buf.ReadFrom(output.Stdout); err != nil { return "", err } - out := string(b) + + out := buf.String() log.Debugf("SSH returned: %s\nEND SSH\n", out) // parse to find: inet 192.168.59.103/24 brd 192.168.59.255 scope global eth1 lines := strings.Split(out, "\n") diff --git a/libmachine/host.go b/libmachine/host.go index b1c5d2dd02..0cd1579931 100644 --- a/libmachine/host.go +++ b/libmachine/host.go @@ -5,7 +5,6 @@ import ( "fmt" "io/ioutil" "os" - "os/exec" "path/filepath" "regexp" @@ -127,23 +126,49 @@ func (h *Host) Create(name string) error { return nil } -func (h *Host) GetSSHCommand(args ...string) (*exec.Cmd, error) { +func (h *Host) RunSSHCommand(command string) (ssh.Output, error) { + var output ssh.Output + addr, err := h.Driver.GetSSHHostname() if err != nil { - return nil, err + return output, err } - user := h.Driver.GetSSHUsername() - port, err := h.Driver.GetSSHPort() if err != nil { - return nil, err + return output, err } - keyPath := h.Driver.GetSSHKeyPath() + auth := &ssh.Auth{ + Keys: []string{h.Driver.GetSSHKeyPath()}, + } - cmd := ssh.GetSSHCommand(addr, port, user, keyPath, args...) - return cmd, nil + client, err := ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth) + + return client.Run(command) +} + +func (h *Host) CreateSSHShell() error { + addr, err := h.Driver.GetSSHHostname() + if err != nil { + return err + } + + port, err := h.Driver.GetSSHPort() + if err != nil { + return err + } + + auth := &ssh.Auth{ + Keys: []string{h.Driver.GetSSHKeyPath()}, + } + + client, err := ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth) + if err != nil { + return err + } + + return client.Shell() } func (h *Host) Start() error { @@ -326,15 +351,11 @@ func sshAvailableFunc(h *Host) func() bool { log.Debugf("Error waiting for TCP waiting for SSH: %s", err) return false } - cmd, err := h.GetSSHCommand("exit 0") - if err != nil { + + if _, err := h.RunSSHCommand("exit 0"); err != nil { log.Debugf("Error getting ssh command 'exit 0' : %s", err) return false } - if err := cmd.Run(); err != nil { - log.Debugf("Error running ssh command 'exit 0' : %s", err) - return false - } return true } } diff --git a/libmachine/provision/boot2docker.go b/libmachine/provision/boot2docker.go index df9caea21d..89c2c4a9a2 100644 --- a/libmachine/provision/boot2docker.go +++ b/libmachine/provision/boot2docker.go @@ -4,7 +4,6 @@ import ( "bytes" "errors" "fmt" - "os/exec" "path" log "github.com/Sirupsen/logrus" @@ -12,6 +11,7 @@ import ( "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/ssh" "github.com/docker/machine/state" "github.com/docker/machine/utils" ) @@ -36,16 +36,13 @@ type Boot2DockerProvisioner struct { func (provisioner *Boot2DockerProvisioner) Service(name string, action pkgaction.ServiceAction) error { var ( - cmd *exec.Cmd err error ) - cmd, err = provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String())) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + + if _, err = provisioner.SSHCommand(fmt.Sprintf("sudo /etc/init.d/%s %s", name, action.String())); err != nil { return err } + return nil } @@ -101,15 +98,13 @@ func (provisioner *Boot2DockerProvisioner) Package(name string, action pkgaction } func (provisioner *Boot2DockerProvisioner) Hostname() (string, error) { - cmd, err := provisioner.SSHCommand(fmt.Sprintf("hostname")) + output, err := provisioner.SSHCommand(fmt.Sprintf("hostname")) if err != nil { return "", err } var so bytes.Buffer - cmd.Stdout = &so - - if err := cmd.Run(); err != nil { + if _, err := so.ReadFrom(output.Stdout); err != nil { return "", err } @@ -117,16 +112,15 @@ func (provisioner *Boot2DockerProvisioner) Hostname() (string, error) { } func (provisioner *Boot2DockerProvisioner) SetHostname(hostname string) error { - cmd, err := provisioner.SSHCommand(fmt.Sprintf( + if _, err := provisioner.SSHCommand(fmt.Sprintf( "sudo hostname %s && echo %q | sudo tee /var/lib/boot2docker/etc/hostname", hostname, hostname, - )) - if err != nil { + )); err != nil { return err } - return cmd.Run() + return nil } func (provisioner *Boot2DockerProvisioner) GetDockerOptionsDir() string { @@ -188,8 +182,8 @@ func (provisioner *Boot2DockerProvisioner) Provision(swarmOptions swarm.SwarmOpt return nil } -func (provisioner *Boot2DockerProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) { - return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...) +func (provisioner *Boot2DockerProvisioner) SSHCommand(args string) (ssh.Output, error) { + return drivers.RunSSHCommandFromDriver(provisioner.Driver, args) } func (provisioner *Boot2DockerProvisioner) GetDriver() drivers.Driver { diff --git a/libmachine/provision/provisioner.go b/libmachine/provision/provisioner.go index 9965ed829b..0a65de47c8 100644 --- a/libmachine/provision/provisioner.go +++ b/libmachine/provision/provisioner.go @@ -3,12 +3,12 @@ package provision import ( "bytes" "fmt" - "os/exec" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/ssh" ) var provisioners = make(map[string]*RegisteredProvisioner) @@ -48,7 +48,7 @@ type Provisioner interface { GetDriver() drivers.Driver // Short-hand for accessing an SSH command from the driver. - SSHCommand(args ...string) (*exec.Cmd, error) + SSHCommand(args string) (ssh.Output, error) // Set the OS Release info depending on how it's represented // internally @@ -68,18 +68,13 @@ func DetectProvisioner(d drivers.Driver) (Provisioner, error) { var ( osReleaseOut bytes.Buffer ) - catOsReleaseCmd, err := drivers.GetSSHCommandFromDriver(d, "cat /etc/os-release") + catOsReleaseOutput, err := drivers.RunSSHCommandFromDriver(d, "cat /etc/os-release") if err != nil { return nil, fmt.Errorf("Error getting SSH command: %s", err) } - // Normally I would just use Output() for this, but d.GetSSHCommand - // defaults to sending the output of the command to stdout in debug - // mode, so that will be broken if we don't set it ourselves. - catOsReleaseCmd.Stdout = &osReleaseOut - - if err := catOsReleaseCmd.Run(); err != nil { - return nil, fmt.Errorf("Error running SSH command to get /etc/os-release: %s", err) + if _, err := osReleaseOut.ReadFrom(catOsReleaseOutput.Stdout); err != nil { + return nil, err } osReleaseInfo, err := NewOsRelease(osReleaseOut.Bytes()) diff --git a/libmachine/provision/ubuntu.go b/libmachine/provision/ubuntu.go index bddc01ecd1..a606fe84d3 100644 --- a/libmachine/provision/ubuntu.go +++ b/libmachine/provision/ubuntu.go @@ -3,13 +3,13 @@ package provision import ( "bytes" "fmt" - "os/exec" log "github.com/Sirupsen/logrus" "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/ssh" "github.com/docker/machine/utils" ) @@ -38,12 +38,7 @@ type UbuntuProvisioner struct { func (provisioner *UbuntuProvisioner) Service(name string, action pkgaction.ServiceAction) error { command := fmt.Sprintf("sudo service %s %s", name, action.String()) - cmd, err := provisioner.SSHCommand(command) - if err != nil { - return err - } - - if err := cmd.Run(); err != nil { + if _, err := provisioner.SSHCommand(command); err != nil { return err } @@ -70,12 +65,7 @@ func (provisioner *UbuntuProvisioner) Package(name string, action pkgaction.Pack command := fmt.Sprintf("DEBIAN_FRONTEND=noninteractive sudo -E apt-get %s -y %s", packageAction, name) - cmd, err := provisioner.SSHCommand(command) - if err != nil { - return err - } - - if err := cmd.Run(); err != nil { + if _, err := provisioner.SSHCommand(command); err != nil { return err } @@ -83,15 +73,10 @@ func (provisioner *UbuntuProvisioner) Package(name string, action pkgaction.Pack } func (provisioner *UbuntuProvisioner) dockerDaemonResponding() bool { - cmd, err := provisioner.SSHCommand("sudo docker version") - if err != nil { + if _, err := provisioner.SSHCommand("sudo docker version"); err != nil { log.Warn("Error getting SSH command to check if the daemon is up: %s", err) return false } - if err := cmd.Run(); err != nil { - log.Debug("Error checking for daemon up: %s", err) - return false - } // The daemon is up if the command worked. Carry on. return true @@ -128,15 +113,13 @@ func (provisioner *UbuntuProvisioner) Provision(swarmOptions swarm.SwarmOptions, } func (provisioner *UbuntuProvisioner) Hostname() (string, error) { - cmd, err := provisioner.SSHCommand("hostname") + output, err := provisioner.SSHCommand("hostname") if err != nil { return "", err } var so bytes.Buffer - cmd.Stdout = &so - - if err := cmd.Run(); err != nil { + if _, err := so.ReadFrom(output.Stdout); err != nil { return "", err } @@ -144,26 +127,24 @@ func (provisioner *UbuntuProvisioner) Hostname() (string, error) { } func (provisioner *UbuntuProvisioner) SetHostname(hostname string) error { - cmd, err := provisioner.SSHCommand(fmt.Sprintf( + if _, err := provisioner.SSHCommand(fmt.Sprintf( "sudo hostname %s && echo %q | sudo tee /etc/hostname && echo \"127.0.0.1 %s\" | sudo tee -a /etc/hosts", hostname, hostname, hostname, - )) - - if err != nil { + )); err != nil { return err } - return cmd.Run() + return nil } func (provisioner *UbuntuProvisioner) GetDockerOptionsDir() string { return "/etc/docker" } -func (provisioner *UbuntuProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) { - return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...) +func (provisioner *UbuntuProvisioner) SSHCommand(args string) (ssh.Output, error) { + return drivers.RunSSHCommandFromDriver(provisioner.Driver, args) } func (provisioner *UbuntuProvisioner) CompatibleWithHost() bool { diff --git a/libmachine/provision/utils.go b/libmachine/provision/utils.go index 236baa31b7..fe884d38b0 100644 --- a/libmachine/provision/utils.go +++ b/libmachine/provision/utils.go @@ -25,18 +25,13 @@ type DockerOptions struct { func installDockerGeneric(p Provisioner) error { // install docker - until cloudinit we use ubuntu everywhere so we // just install it using the docker repos - cmd, err := p.SSHCommand("if ! type docker; then curl -sSL https://get.docker.com | sh -; fi") - if err != nil { - return err - } + if output, err := p.SSHCommand("if ! type docker; then curl -sSL https://get.docker.com | sh -; fi"); err != nil { + var buf bytes.Buffer + if _, err := buf.ReadFrom(output.Stderr); err != nil { + return err + } - // HACK: the script above will output debug to stderr; we save it and - // then check if the command returned an error; if so, we show the debug - var buf bytes.Buffer - cmd.Stderr = &buf - - if err := cmd.Run(); err != nil { - return fmt.Errorf("error installing docker: %s\n%s\n", err, string(buf.Bytes())) + return fmt.Errorf("error installing docker: %s\n", buf.String()) } return nil @@ -99,11 +94,7 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error { dockerDir := p.GetDockerOptionsDir() - cmd, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir)); err != nil { return err } @@ -132,27 +123,15 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error { machineServerKeyPath := path.Join(dockerDir, "server-key.pem") authOptions.ServerKeyRemotePath = machineServerKeyPath - cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(caCert), machineCaCertPath)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(caCert), machineCaCertPath)); err != nil { return err } - cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverKey), machineServerKeyPath)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverKey), machineServerKeyPath)); err != nil { return err } - cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverCert), machineServerCertPath)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverCert), machineServerCertPath)); err != nil { return err } @@ -179,11 +158,7 @@ func ConfigureAuth(p Provisioner, authOptions auth.AuthOptions) error { return err } - cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath)); err != nil { return err } @@ -234,11 +209,7 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error { return err } - cmd, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarm.DockerImage)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err := p.SSHCommand(fmt.Sprintf("sudo docker pull %s", swarm.DockerImage)); err != nil { return err } @@ -248,12 +219,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error { if swarmOptions.Master { log.Debug("launching swarm master") log.Debugf("master args: %s", masterArgs) - cmd, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d -p %s:%s --restart=always --name swarm-agent-master -v %s:%s %s manage %s", - port, port, dockerDir, dockerDir, swarm.DockerImage, masterArgs)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d -p %s:%s --restart=always --name swarm-agent-master -v %s:%s %s manage %s", + port, port, dockerDir, dockerDir, swarm.DockerImage, masterArgs)); err != nil { return err } } @@ -261,12 +228,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error { // start node agent log.Debug("launching swarm node") log.Debugf("node args: %s", nodeArgs) - cmd, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d --restart=always --name swarm-agent -v %s:%s %s join %s", - dockerDir, dockerDir, swarm.DockerImage, nodeArgs)) - if err != nil { - return err - } - if err := cmd.Run(); err != nil { + if _, err = p.SSHCommand(fmt.Sprintf("sudo docker run -d --restart=always --name swarm-agent -v %s:%s %s join %s", + dockerDir, dockerDir, swarm.DockerImage, nodeArgs)); err != nil { return err } diff --git a/ssh/client.go b/ssh/client.go new file mode 100644 index 0000000000..175a0c391e --- /dev/null +++ b/ssh/client.go @@ -0,0 +1,154 @@ +package ssh + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/docker/docker/pkg/term" + "golang.org/x/crypto/ssh" +) + +type Client struct { + Config *ssh.ClientConfig + Hostname string + Port int +} + +func NewClient(user string, host string, port int, auth *Auth) (*Client, error) { + config, err := NewConfig(user, auth) + if err != nil { + return nil, err + } + + return &Client{ + Config: config, + Hostname: host, + Port: port, + }, nil +} + +func NewConfig(user string, auth *Auth) (*ssh.ClientConfig, error) { + var authMethods []ssh.AuthMethod + + for _, k := range auth.Keys { + key, err := ioutil.ReadFile(k) + if err != nil { + return nil, err + } + + privateKey, err := ssh.ParsePrivateKey(key) + if err != nil { + return nil, err + } + + authMethods = append(authMethods, ssh.PublicKeys(privateKey)) + } + + for _, p := range auth.Passwords { + authMethods = append(authMethods, ssh.Password(p)) + } + + return &ssh.ClientConfig{ + User: user, + Auth: authMethods, + }, nil +} + +func (client *Client) Run(command string) (Output, error) { + var output Output + + conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), client.Config) + if err != nil { + return output, err + } + + session, err := conn.NewSession() + if err != nil { + return output, err + } + + defer session.Close() + + var stdout, stderr bytes.Buffer + + session.Stdout = &stdout + session.Stderr = &stderr + + output = Output{ + Stdout: &stdout, + Stderr: &stderr, + } + + return output, session.Run(command) +} + +func (client *Client) Shell() error { + conn, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", client.Hostname, client.Port), client.Config) + if err != nil { + return err + } + + session, err := conn.NewSession() + if err != nil { + return err + } + + defer session.Close() + + session.Stdout = os.Stdout + session.Stderr = os.Stderr + session.Stdin = os.Stdin + + modes := ssh.TerminalModes{ + ssh.ECHO: 1, + } + + var termWidth, termHeight int + + fd := os.Stdin.Fd() + + if term.IsTerminal(fd) { + var oldState *term.State + + oldState, err = term.MakeRaw(fd) + if err != nil { + return err + } + + defer term.RestoreTerminal(fd, oldState) + + winsize, err := term.GetWinsize(fd) + if err != nil { + termWidth = 80 + termHeight = 24 + } else { + termWidth = int(winsize.Width) + termHeight = int(winsize.Height) + } + } + + if err := session.RequestPty("xterm", termHeight, termWidth, modes); err != nil { + return err + } + + if err := session.Shell(); err != nil { + return err + } + + session.Wait() + + return nil +} + +type Auth struct { + Passwords []string + Keys []string +} + +type Output struct { + Stdout io.Reader + Stderr io.Reader +} diff --git a/ssh/ssh.go b/ssh/ssh.go index 9fccc73e54..cd3f6b74f9 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -1,41 +1,9 @@ package ssh import ( - "fmt" "net" - "os" - "os/exec" - "strings" - - log "github.com/Sirupsen/logrus" ) -func GetSSHCommand(host string, port int, user string, sshKey string, args ...string) *exec.Cmd { - defaultSSHArgs := []string{ - "-o", "IdentitiesOnly=yes", - "-o", "StrictHostKeyChecking=no", // don't bother checking in ~/.ssh/known_hosts - "-o", "UserKnownHostsFile=/dev/null", // don't write anything to ~/.ssh/known_hosts - "-o", "ConnectionAttempts=3", // retry 3 times if SSH connection fails - "-o", "ConnectTimeout=10", // timeout after 10 seconds - "-o", "LogLevel=quiet", // suppress "Warning: Permanently added '[localhost]:2022' (ECDSA) to the list of known hosts." - "-p", fmt.Sprintf("%d", port), - "-i", sshKey, - fmt.Sprintf("%s@%s", user, host), - } - - sshArgs := append(defaultSSHArgs, args...) - cmd := exec.Command("ssh", sshArgs...) - cmd.Stderr = os.Stderr - - if os.Getenv("DEBUG") != "" { - cmd.Stdout = os.Stdout - } - - log.Debugf("executing: %v", strings.Join(cmd.Args, " ")) - - return cmd -} - func WaitForTCP(addr string) error { for { conn, err := net.Dial("tcp", addr)