diff --git a/commands/commands.go b/commands/commands.go index 06736be40c..c912aebcd0 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -17,6 +17,7 @@ import ( _ "github.com/docker/machine/drivers/azure" _ "github.com/docker/machine/drivers/digitalocean" _ "github.com/docker/machine/drivers/exoscale" + _ "github.com/docker/machine/drivers/generic" _ "github.com/docker/machine/drivers/google" _ "github.com/docker/machine/drivers/hyperv" _ "github.com/docker/machine/drivers/none" diff --git a/drivers/generic/generic.go b/drivers/generic/generic.go new file mode 100644 index 0000000000..5b2a39fbdd --- /dev/null +++ b/drivers/generic/generic.go @@ -0,0 +1,265 @@ +package generic + +import ( + "fmt" + "net" + "os" + "os/exec" + "path/filepath" + + log "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/docker/machine/drivers" + "github.com/docker/machine/ssh" + "github.com/docker/machine/state" + "github.com/docker/machine/utils" +) + +const ( + dockerConfigDir = "/etc/docker" +) + +type Driver struct { + MachineName string + IPAddress string + User string + SSHKey string + SSHPort int + CaCertPath string + PrivateKeyPath string + DriverKeyPath string + storePath string +} + +func init() { + drivers.Register("generic", &drivers.RegisteredDriver{ + New: NewDriver, + GetCreateFlags: GetCreateFlags, + }) +} + +// GetCreateFlags registers the flags this driver adds to +// "docker hosts create" +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + Name: "generic-ip-address", + Usage: "IP Address of machine", + Value: "", + }, + cli.StringFlag{ + Name: "generic-user", + Usage: "User to use for machine", + Value: "root", + }, + cli.StringFlag{ + Name: "generic-ssh-key", + Usage: "SSH private key path", + Value: "", + }, + cli.IntFlag{ + Name: "generic-ssh-port", + Usage: "SSH port", + Value: 22, + }, + } +} + +func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { + return &Driver{MachineName: machineName, storePath: storePath, CaCertPath: caCert, PrivateKeyPath: privateKey}, nil +} + +func (d *Driver) DriverName() string { + return "generic" +} + +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + d.IPAddress = flags.String("generic-ip-address") + d.User = flags.String("generic-user") + d.SSHKey = flags.String("generic-ssh-key") + d.SSHPort = flags.Int("generic-ssh-port") + + if d.IPAddress == "" { + return fmt.Errorf("generic driver requires the --generic-ip-address option") + } + + if d.SSHKey == "" { + return fmt.Errorf("generic driver requires the --generic-ssh-key option") + } + + return nil +} + +func (d *Driver) Create() error { + log.Infof("Copying SSH key...") + + if err := utils.CopyFile(d.SSHKey, d.sshKeyPath()); err != nil { + return fmt.Errorf("unable to copy ssh key: %s", err) + } + + if err := os.Chmod(d.sshKeyPath(), 0600); err != nil { + return err + } + + log.Infof("Waiting for SSH...") + + if err := ssh.WaitForTCP(fmt.Sprintf("%s:%d", d.IPAddress, 22)); err != nil { + return err + } + + log.Info("Configuring machine...") + + log.Debugf("Setting hostname: %s", d.MachineName) + cmd, err := d.GetSSHCommand(fmt.Sprintf( + "echo \"127.0.0.1 %s\" | sudo tee -a /etc/hosts && sudo hostname %s && echo \"%s\" | sudo tee /etc/hostname", + d.MachineName, + d.MachineName, + d.MachineName, + )) + + if err != nil { + return err + } + if err := cmd.Run(); err != nil { + return err + } + + log.Debug("Installing Docker") + + cmd, err = d.GetSSHCommand("if [ ! -e /usr/bin/docker ]; then curl get.docker.io | sudo sh -; fi") + if err != nil { + return err + + } + if err := cmd.Run(); err != nil { + return err + + } + + return nil +} + +func (d *Driver) GetURL() (string, error) { + ip, err := d.GetIP() + if err != nil { + return "", err + } + return fmt.Sprintf("tcp://%s:2376", ip), nil +} + +func (d *Driver) GetIP() (string, error) { + if d.IPAddress == "" { + return "", fmt.Errorf("IP address is not set") + } + return d.IPAddress, nil +} + +func (d *Driver) GetState() (state.State, error) { + addr := fmt.Sprintf("%s:%d", d.IPAddress, d.SSHPort) + _, err := net.Dial("tcp", addr) + var st state.State + if err != nil { + st = state.Stopped + } else { + st = state.Running + } + return st, nil +} + +func (d *Driver) Start() error { + return fmt.Errorf("generic driver does not support start") +} + +func (d *Driver) Stop() error { + return fmt.Errorf("generic driver does not support stop") +} + +func (d *Driver) Remove() error { + return nil +} + +func (d *Driver) Restart() error { + log.Debug("Restarting...") + + cmd, err := d.GetSSHCommand("sudo shutdown -r now") + if err != nil { + return err + } + if err := cmd.Run(); err != nil { + return err + } + + return nil +} + +func (d *Driver) Kill() error { + log.Debug("Killing...") + + cmd, err := d.GetSSHCommand("sudo shutdown -P now") + if err != nil { + return err + } + if err := cmd.Run(); err != nil { + return err + } + + return nil +} + +func (d *Driver) StartDocker() error { + log.Debug("Starting Docker...") + + cmd, err := d.GetSSHCommand("sudo service docker start") + if err != nil { + return err + } + if err := cmd.Run(); err != nil { + return err + } + + return nil +} + +func (d *Driver) StopDocker() error { + log.Debug("Stopping Docker...") + + cmd, err := d.GetSSHCommand("sudo service docker stop") + if err != nil { + return err + } + if err := cmd.Run(); err != nil { + return err + } + + return nil +} + +func (d *Driver) GetDockerConfigDir() string { + return dockerConfigDir +} + +func (d *Driver) Upgrade() error { + sshCmd, err := d.GetSSHCommand("apt-get update && apt-get install lxc-docker") + if err != nil { + return err + } + sshCmd.Stdin = os.Stdin + sshCmd.Stdout = os.Stdout + sshCmd.Stderr = os.Stderr + if err := sshCmd.Run(); err != nil { + return fmt.Errorf("%s", err) + } + return nil +} + +func (d *Driver) GetSSHCommand(args ...string) (*exec.Cmd, error) { + return ssh.GetSSHCommand(d.IPAddress, d.SSHPort, d.User, d.sshKeyPath(), args...), nil +} + +func (d *Driver) sshKeyPath() string { + return filepath.Join(d.storePath, "id_rsa") +} + +func (d *Driver) publicSSHKeyPath() string { + return d.sshKeyPath() + ".pub" +}