mirror of https://github.com/docker/docs.git
369 lines
7.7 KiB
Go
369 lines
7.7 KiB
Go
package libmachine
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
"github.com/docker/machine/drivers"
|
|
"github.com/docker/machine/libmachine/auth"
|
|
"github.com/docker/machine/libmachine/engine"
|
|
"github.com/docker/machine/libmachine/provision"
|
|
"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"
|
|
)
|
|
|
|
var (
|
|
validHostNameChars = `[a-zA-Z0-9\-\.]`
|
|
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
|
|
)
|
|
|
|
type Host struct {
|
|
Name string `json:"-"`
|
|
DriverName string
|
|
Driver drivers.Driver
|
|
StorePath string
|
|
HostOptions *HostOptions
|
|
|
|
// deprecated options; these are left to assist in config migrations
|
|
SwarmHost string
|
|
SwarmMaster bool
|
|
SwarmDiscovery string
|
|
CaCertPath string
|
|
PrivateKeyPath string
|
|
ServerCertPath string
|
|
ServerKeyPath string
|
|
ClientCertPath string
|
|
ClientKeyPath string
|
|
}
|
|
|
|
type HostOptions struct {
|
|
Driver string
|
|
Memory int
|
|
Disk int
|
|
EngineOptions *engine.EngineOptions
|
|
SwarmOptions *swarm.SwarmOptions
|
|
AuthOptions *auth.AuthOptions
|
|
}
|
|
|
|
type HostMetadata struct {
|
|
DriverName string
|
|
HostOptions HostOptions
|
|
StorePath string
|
|
CaCertPath string
|
|
PrivateKeyPath string
|
|
ServerCertPath string
|
|
ServerKeyPath string
|
|
ClientCertPath string
|
|
}
|
|
|
|
func NewHost(name, driverName string, hostOptions *HostOptions) (*Host, error) {
|
|
authOptions := hostOptions.AuthOptions
|
|
storePath := filepath.Join(utils.GetMachineDir(), name)
|
|
driver, err := drivers.NewDriver(driverName, name, storePath, authOptions.CaCertPath, authOptions.PrivateKeyPath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Host{
|
|
Name: name,
|
|
DriverName: driverName,
|
|
Driver: driver,
|
|
StorePath: storePath,
|
|
HostOptions: hostOptions,
|
|
}, nil
|
|
}
|
|
|
|
func LoadHost(name string, StorePath string) (*Host, error) {
|
|
if _, err := os.Stat(StorePath); os.IsNotExist(err) {
|
|
return nil, fmt.Errorf("Host %q does not exist", name)
|
|
}
|
|
|
|
host := &Host{Name: name, StorePath: StorePath}
|
|
if err := host.LoadConfig(); err != nil {
|
|
return nil, err
|
|
}
|
|
return host, nil
|
|
}
|
|
|
|
func ValidateHostName(name string) bool {
|
|
return validHostNamePattern.MatchString(name)
|
|
}
|
|
|
|
func (h *Host) Create(name string) error {
|
|
// create the instance
|
|
if err := h.Driver.Create(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// save to store
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
// TODO: Not really a fan of just checking "none" here.
|
|
if h.Driver.DriverName() != "none" {
|
|
if err := WaitForSSH(h); err != nil {
|
|
return err
|
|
}
|
|
|
|
provisioner, err := provision.DetectProvisioner(h.Driver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
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 {
|
|
if err := h.Driver.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Running))
|
|
}
|
|
|
|
func (h *Host) Stop() error {
|
|
if err := h.Driver.Stop(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped))
|
|
}
|
|
|
|
func (h *Host) Kill() error {
|
|
if err := h.Driver.Stop(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped))
|
|
}
|
|
|
|
func (h *Host) Restart() error {
|
|
if drivers.MachineInState(h.Driver, state.Running)() {
|
|
if err := h.Stop(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := h.Start(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *Host) Upgrade() error {
|
|
provisioner, err := provision.DetectProvisioner(h.Driver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := provisioner.Package("docker", pkgaction.Upgrade); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := provisioner.Service("docker", pkgaction.Restart); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *Host) Remove(force bool) error {
|
|
if err := h.Driver.Remove(); err != nil {
|
|
if !force {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := h.SaveConfig(); err != nil {
|
|
return err
|
|
}
|
|
|
|
return h.removeStorePath()
|
|
}
|
|
|
|
func (h *Host) removeStorePath() error {
|
|
file, err := os.Stat(h.StorePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !file.IsDir() {
|
|
return fmt.Errorf("%q is not a directory", h.StorePath)
|
|
}
|
|
return os.RemoveAll(h.StorePath)
|
|
}
|
|
|
|
func (h *Host) GetURL() (string, error) {
|
|
return h.Driver.GetURL()
|
|
}
|
|
|
|
func (h *Host) LoadConfig() error {
|
|
data, err := ioutil.ReadFile(filepath.Join(h.StorePath, "config.json"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// First pass: find the driver name and load the driver
|
|
var hostMetadata HostMetadata
|
|
if err := json.Unmarshal(data, &hostMetadata); err != nil {
|
|
return err
|
|
}
|
|
|
|
meta := FillNestedHostMetadata(&hostMetadata)
|
|
|
|
authOptions := meta.HostOptions.AuthOptions
|
|
|
|
driver, err := drivers.NewDriver(hostMetadata.DriverName, h.Name, h.StorePath, authOptions.CaCertPath, authOptions.PrivateKeyPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
h.Driver = driver
|
|
|
|
// Second pass: unmarshal driver config into correct driver
|
|
if err := json.Unmarshal(data, &h); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *Host) ConfigureAuth() error {
|
|
provisioner, err := provision.DetectProvisioner(h.Driver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := provision.ConfigureAuth(provisioner, *h.HostOptions.AuthOptions); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *Host) SaveConfig() error {
|
|
data, err := json.Marshal(h)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := ioutil.WriteFile(filepath.Join(h.StorePath, "config.json"), data, 0600); err != nil {
|
|
return err
|
|
}
|
|
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
|
|
}
|