From 8afe967a80ec7fd68b3fe7a33e9bab60ea4bc4ff Mon Sep 17 00:00:00 2001 From: Darren Shepherd Date: Mon, 4 May 2015 16:17:09 -0700 Subject: [PATCH] Fix issue where GetIP failes due to no SSH The main goal of this patch was to make the VirtualBox driver wait for SSH before trying to get the IP of the VM. The generic WaitForSSH method required a Host struct as an arg. This patch moves most of the logic to the driver package so that drivers can call WaitForSSH. The existing functions in host are just wrappers to the real implementation in drivers now. Signed-off-by: Darren Shepherd --- drivers/drivers.go | 29 -------------- drivers/utils.go | 67 ++++++++++++++++++++++++++++++++ drivers/virtualbox/virtualbox.go | 4 ++ libmachine/host.go | 51 +----------------------- ssh/ssh.go | 18 +++++---- 5 files changed, 84 insertions(+), 85 deletions(-) create mode 100644 drivers/utils.go diff --git a/drivers/drivers.go b/drivers/drivers.go index 6b43f501cd..2c86432912 100644 --- a/drivers/drivers.go +++ b/drivers/drivers.go @@ -8,7 +8,6 @@ import ( "github.com/codegangsta/cli" "github.com/docker/machine/log" "github.com/docker/machine/provider" - "github.com/docker/machine/ssh" "github.com/docker/machine/state" ) @@ -173,34 +172,6 @@ type DriverOptions interface { Bool(key string) bool } -func RunSSHCommandFromDriver(d Driver, args string) (ssh.Output, error) { - var output ssh.Output - - host, err := d.GetSSHHostname() - if err != nil { - return output, err - } - - port, err := d.GetSSHPort() - if err != nil { - return output, err - } - - user := d.GetSSHUsername() - keyPath := d.GetSSHKeyPath() - - 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 { return func() bool { currentState, err := d.GetState() diff --git a/drivers/utils.go b/drivers/utils.go new file mode 100644 index 0000000000..793ec3630e --- /dev/null +++ b/drivers/utils.go @@ -0,0 +1,67 @@ +package drivers + +import ( + "fmt" + + "github.com/docker/machine/log" + "github.com/docker/machine/ssh" + "github.com/docker/machine/utils" +) + +func RunSSHCommandFromDriver(d Driver, command string) (ssh.Output, error) { + var output ssh.Output + + addr, err := d.GetSSHHostname() + if err != nil { + return output, err + } + + port, err := d.GetSSHPort() + if err != nil { + return output, err + } + + auth := &ssh.Auth{ + Keys: []string{d.GetSSHKeyPath()}, + } + + client, err := ssh.NewClient(d.GetSSHUsername(), addr, port, auth) + if err != nil { + return output, err + } + + return client.Run(command) +} + +func sshAvailableFunc(d Driver) func() bool { + return func() bool { + log.Debug("Getting to WaitForSSH function...") + hostname, err := d.GetSSHHostname() + if err != nil { + log.Debugf("Error getting IP address waiting for SSH: %s", err) + return false + } + port, err := d.GetSSHPort() + if err != nil { + log.Debugf("Error getting SSH port: %s", err) + return false + } + if err := ssh.WaitForTCP(fmt.Sprintf("%s:%d", hostname, port)); err != nil { + log.Debugf("Error waiting for TCP waiting for SSH: %s", err) + return false + } + + if _, err := RunSSHCommandFromDriver(d, "exit 0"); err != nil { + log.Debugf("Error getting ssh command 'exit 0' : %s", err) + return false + } + return true + } +} + +func WaitForSSH(d Driver) error { + if err := utils.WaitFor(sshAvailableFunc(d)); err != nil { + return fmt.Errorf("Too many retries. Last error: %s", err) + } + return nil +} diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index c1acf6f018..8a8116b0d5 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -393,6 +393,10 @@ func (d *Driver) Start() error { log.Infof("VM not in restartable state") } + if err := drivers.WaitForSSH(d); err != nil { + return err + } + d.IPAddress, err = d.GetIP() return err } diff --git a/libmachine/host.go b/libmachine/host.go index 5f28861ff7..7b2a432bf0 100644 --- a/libmachine/host.go +++ b/libmachine/host.go @@ -136,25 +136,7 @@ func (h *Host) Create(name string) error { } func (h *Host) RunSSHCommand(command string) (ssh.Output, error) { - var output ssh.Output - - addr, err := h.Driver.GetSSHHostname() - if err != nil { - return output, err - } - - port, err := h.Driver.GetSSHPort() - if err != nil { - return output, err - } - - auth := &ssh.Auth{ - Keys: []string{h.Driver.GetSSHKeyPath()}, - } - - client, err := ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth) - - return client.Run(command) + return drivers.RunSSHCommandFromDriver(h.Driver, command) } func (h *Host) CreateSSHShell() error { @@ -352,37 +334,8 @@ func (h *Host) PrintIP() error { return nil } -func sshAvailableFunc(h *Host) func() bool { - return func() bool { - log.Debug("Getting to WaitForSSH function...") - hostname, err := h.Driver.GetSSHHostname() - if err != nil { - log.Debugf("Error getting IP address waiting for SSH: %s", err) - return false - } - port, err := h.Driver.GetSSHPort() - if err != nil { - log.Debugf("Error getting SSH port: %s", err) - return false - } - if err := ssh.WaitForTCP(fmt.Sprintf("%s:%d", hostname, port)); err != nil { - log.Debugf("Error waiting for TCP waiting for SSH: %s", err) - return false - } - - if _, err := h.RunSSHCommand("exit 0"); err != nil { - log.Debugf("Error getting ssh command 'exit 0' : %s", err) - return false - } - return true - } -} - func WaitForSSH(h *Host) error { - if err := utils.WaitFor(sshAvailableFunc(h)); err != nil { - return fmt.Errorf("Too many retries. Last error: %s", err) - } - return nil + return drivers.WaitForSSH(h.Driver) } func getHostState(host Host, hostListItemsChan chan<- HostListItem) { diff --git a/ssh/ssh.go b/ssh/ssh.go index baaf92443a..3abb264a23 100644 --- a/ssh/ssh.go +++ b/ssh/ssh.go @@ -1,18 +1,22 @@ package ssh -import "net" +import ( + "net" + "time" + + "github.com/docker/machine/log" +) func WaitForTCP(addr string) error { for { - conn, err := net.Dial("tcp", addr) + log.Debugf("Testing TCP connection to: %s", addr) + conn, err := net.DialTimeout("tcp", addr, 2*time.Second) + if err != nil { continue } + defer conn.Close() - if _, err = conn.Read(make([]byte, 1)); err != nil { - continue - } - break + return nil } - return nil }