Implement majority of provisioning changes

Signed-off-by: Simon Thulborn <simon+github@thulborn.com>
Signed-off-by: Nathan LeClaire <nathan.leclaire@gmail.com>
This commit is contained in:
Nathan LeClaire and Simon Thulborn 2015-02-16 23:16:36 +00:00 committed by Nathan LeClaire
parent 8d5a59b43e
commit 49feb33457
16 changed files with 1096 additions and 580 deletions

View File

@ -30,6 +30,7 @@ import (
_ "github.com/docker/machine/drivers/vmwarevcloudair"
_ "github.com/docker/machine/drivers/vmwarevsphere"
"github.com/docker/machine/libmachine"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/engine"
"github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/state"
@ -39,26 +40,24 @@ import (
type machineConfig struct {
machineName string
machineDir string
caCertPath string
caKeyPath string
clientCertPath string
machineUrl string
clientKeyPath string
serverCertPath string
clientCertPath string
caCertPath string
caKeyPath string
serverKeyPath string
machineUrl string
swarmMaster bool
swarmHost string
swarmDiscovery string
AuthConfig auth.AuthOptions
SwarmConfig swarm.SwarmOptions
}
type hostListItem struct {
Name string
Active bool
DriverName string
State state.State
URL string
SwarmMaster bool
SwarmDiscovery string
Name string
Active bool
DriverName string
State state.State
URL string
SwarmConfig swarm.SwarmOptions
}
type certPathInfo struct {
@ -406,10 +405,18 @@ func cmdCreate(c *cli.Context) {
log.Fatal(err)
}
hostOptions := &libmachine.HostOptions{
DriverOptions: c,
EngineOptions: &engine.EngineOptions{},
SwarmOptions: &swarm.SwarmOptions{
hostConfig := &libmachine.HostOptions{
AuthConfig: &auth.AuthOptions{
CaCertPath: c.GlobalString("tls-ca-cert"),
PrivateKeyPath: c.GlobalString("tls-ca-key"),
ClientCertPath: c.GlobalString("tls-client-cert"),
ClientKeyPath: filepath.Join(utils.GetMachineCertDir(), "key.pem"),
ServerCertPath: filepath.Join(utils.GetMachineDir(), name, "server.pem"),
ServerKeyPath: filepath.Join(utils.GetMachineDir(), name, "server-key.pem"),
},
EngineConfig: &engine.EngineOptions{},
SwarmConfig: &swarm.SwarmOptions{
IsSwarm: c.Bool("swarm"),
Master: c.GlobalBool("swarm-master"),
Discovery: c.GlobalString("swarm-discovery"),
Address: c.GlobalString("swarm-addr"),
@ -417,7 +424,7 @@ func cmdCreate(c *cli.Context) {
},
}
host, err := mcn.Create(name, driver, hostOptions)
host, err := mcn.Create(name, driver, hostConfig, c)
if err != nil {
log.Errorf("Error creating machine: %s", err)
log.Warn("You will want to check the provider to make sure the machine and associated resources were properly removed.")
@ -456,10 +463,10 @@ func cmdConfig(c *cli.Context) {
}
if c.Bool("swarm") {
if !cfg.swarmMaster {
if !cfg.SwarmConfig.Master {
log.Fatalf("%s is not a swarm master", cfg.machineName)
}
u, err := url.Parse(cfg.swarmHost)
u, err := url.Parse(cfg.SwarmConfig.Host)
if err != nil {
log.Fatal(err)
}
@ -563,13 +570,14 @@ func cmdLs(c *cli.Context) {
swarmInfo := make(map[string]string)
for _, host := range hostList {
swarmConfig := host.HostConfig.SwarmConfig
if !quiet {
if host.SwarmOptions.Master {
swarmMasters[host.SwarmOptions.Discovery] = host.Name
if swarmConfig.Master {
swarmMasters[swarmConfig.Discovery] = host.Name
}
if host.SwarmOptions.Discovery != "" {
swarmInfo[host.Name] = host.SwarmOptions.Discovery
if swarmConfig.Discovery != "" {
swarmInfo[host.Name] = swarmConfig.Discovery
}
go getHostState(*host, defaultStore, hostListItems)
@ -596,9 +604,9 @@ func cmdLs(c *cli.Context) {
swarmInfo := ""
if item.SwarmDiscovery != "" {
swarmInfo = swarmMasters[item.SwarmDiscovery]
if item.SwarmMaster {
if item.SwarmConfig.Discovery != "" {
swarmInfo = swarmMasters[item.SwarmConfig.Discovery]
if item.SwarmConfig.Master {
swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
}
}
@ -674,10 +682,10 @@ func cmdEnv(c *cli.Context) {
dockerHost := cfg.machineUrl
if c.Bool("swarm") {
if !cfg.swarmMaster {
if !cfg.SwarmConfig.Master {
log.Fatalf("%s is not a swarm master", cfg.machineName)
}
u, err := url.Parse(cfg.swarmHost)
u, err := url.Parse(cfg.SwarmConfig.Host)
if err != nil {
log.Fatal(err)
}
@ -1032,13 +1040,12 @@ func getHostState(host libmachine.Host, store libmachine.Store, hostListItems ch
}
hostListItems <- hostListItem{
Name: host.Name,
Active: isActive,
DriverName: host.Driver.DriverName(),
State: currentState,
URL: url,
SwarmMaster: host.SwarmOptions.Master,
SwarmDiscovery: host.SwarmOptions.Discovery,
Name: host.Name,
Active: isActive,
DriverName: host.Driver.DriverName(),
State: currentState,
URL: url,
SwarmConfig: *host.HostConfig.SwarmConfig,
}
}
@ -1096,16 +1103,15 @@ func getMachineConfig(c *cli.Context) (*machineConfig, error) {
return &machineConfig{
machineName: name,
machineDir: machineDir,
caCertPath: caCert,
caKeyPath: caKey,
clientCertPath: clientCert,
clientKeyPath: clientKey,
serverCertPath: serverCert,
serverKeyPath: serverKey,
machineUrl: machineUrl,
swarmMaster: machine.SwarmOptions.Master,
swarmHost: machine.SwarmOptions.Host,
swarmDiscovery: machine.SwarmOptions.Discovery,
clientKeyPath: clientKey,
clientCertPath: clientCert,
serverCertPath: serverCert,
caKeyPath: caKey,
caCertPath: caCert,
serverKeyPath: serverKey,
AuthConfig: *machine.HostConfig.AuthConfig,
SwarmConfig: *machine.HostConfig.SwarmConfig,
}, nil
}

View File

@ -158,6 +158,7 @@ func (e *EC2) awsApiCall(v url.Values) (*http.Response, error) {
fmt.Printf("client encountered error while doing the request: %s", err.Error())
return resp, fmt.Errorf("client encountered error while doing the request: %s", err)
}
if resp.StatusCode != http.StatusOK {
return resp, newAwsApiResponseError(*resp)
}

View File

@ -0,0 +1,14 @@
package auth
type AuthOptions struct {
StorePath string
CaCertPath string
CaCertRemotePath string
ServerCertPath string
ServerKeyPath string
ClientKeyPath string
ServerCertRemotePath string
ServerKeyRemotePath string
PrivateKeyPath string
ClientCertPath string
}

View File

@ -1,26 +1,20 @@
package libmachine
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/url"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
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/swarm"
"github.com/docker/machine/provider"
"github.com/docker/machine/ssh"
"github.com/docker/machine/state"
"github.com/docker/machine/utils"
@ -31,74 +25,53 @@ var (
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
)
const (
swarmDockerImage = "swarm:latest"
swarmDiscoveryServiceEndpoint = "https://discovery-stage.hub.docker.com/v1"
)
type Host struct {
Name string `json:"-"`
DriverName string
Driver drivers.Driver
Name string `json:"-"`
DriverName string
Driver drivers.Driver
StorePath string
EngineOptions *engine.EngineOptions
SwarmOptions *swarm.SwarmOptions
HostConfig 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
StorePath string
EngineOptions *engine.EngineOptions
SwarmOptions *swarm.SwarmOptions
// deprecated options; these are left to assist in config migrations
SwarmHost string
SwarmMaster bool
SwarmDiscovery string
}
type HostOptions struct {
Driver string
Memory int
Disk int
DriverOptions drivers.DriverOptions
EngineOptions *engine.EngineOptions
SwarmOptions *swarm.SwarmOptions
Driver string
Memory int
Disk int
EngineConfig *engine.EngineOptions
SwarmConfig *swarm.SwarmOptions
AuthConfig *auth.AuthOptions
}
type DockerConfig struct {
EngineConfig string
EngineConfigPath string
}
type hostConfig struct {
type HostMetadata struct {
DriverName string
HostConfig HostOptions
}
func waitForDocker(addr string) error {
for {
conn, err := net.DialTimeout("tcp", addr, time.Second*5)
if err != nil {
time.Sleep(time.Second * 5)
continue
}
conn.Close()
break
}
return nil
}
func NewHost(name, driverName, StorePath, caCert, privateKey string, engineOptions *engine.EngineOptions, swarmOptions *swarm.SwarmOptions) (*Host, error) {
driver, err := drivers.NewDriver(driverName, name, StorePath, caCert, privateKey)
func NewHost(name, driverName string, hostConfig HostOptions) (*Host, error) {
authConfig := hostConfig.AuthConfig
storePath := filepath.Join(utils.GetMachineDir(), name)
driver, err := drivers.NewDriver(driverName, name, storePath, authConfig.CaCertPath, authConfig.PrivateKeyPath)
if err != nil {
return nil, err
}
return &Host{
Name: name,
DriverName: driverName,
Driver: driver,
CaCertPath: caCert,
PrivateKeyPath: privateKey,
EngineOptions: engineOptions,
SwarmOptions: swarmOptions,
StorePath: StorePath,
Name: name,
DriverName: driverName,
Driver: driver,
StorePath: storePath,
HostConfig: hostConfig,
}, nil
}
@ -121,373 +94,7 @@ func ValidateHostName(name string) (string, error) {
return name, nil
}
func (h *Host) GetDockerConfigDir() (string, error) {
// TODO: this will be refactored in https://github.com/docker/machine/issues/699
switch h.Driver.GetProviderType() {
case provider.Local:
return "/var/lib/boot2docker", nil
case provider.Remote:
return "/etc/docker", nil
case provider.None:
return "", nil
default:
return "", ErrUnknownProviderType
}
}
func (h *Host) ConfigureSwarm(discovery string, master bool, host string, addr string) error {
d := h.Driver
if d.DriverName() == "none" {
return nil
}
if addr == "" {
ip, err := d.GetIP()
if err != nil {
return err
}
// TODO: remove hardcoded port
addr = fmt.Sprintf("%s:2376", ip)
}
basePath, err := h.GetDockerConfigDir()
if err != nil {
return err
}
tlsCaCert := path.Join(basePath, "ca.pem")
tlsCert := path.Join(basePath, "server.pem")
tlsKey := path.Join(basePath, "server-key.pem")
masterArgs := fmt.Sprintf("--tlsverify --tlscacert=%s --tlscert=%s --tlskey=%s -H %s %s",
tlsCaCert, tlsCert, tlsKey, host, discovery)
nodeArgs := fmt.Sprintf("--addr %s %s", addr, discovery)
u, err := url.Parse(host)
if err != nil {
return err
}
parts := strings.Split(u.Host, ":")
port := parts[1]
if err := waitForDocker(addr); err != nil {
return err
}
cmd, err := h.GetSSHCommand(fmt.Sprintf("sudo docker pull %s", swarmDockerImage))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
dockerDir, err := h.GetDockerConfigDir()
if err != nil {
return err
}
// if master start master agent
if master {
log.Debug("launching swarm master")
log.Debugf("master args: %s", masterArgs)
cmd, err = h.GetSSHCommand(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, swarmDockerImage, masterArgs))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
}
// start node agent
log.Debug("launching swarm node")
log.Debugf("node args: %s", nodeArgs)
cmd, err = h.GetSSHCommand(fmt.Sprintf("sudo docker run -d --restart=always --name swarm-agent -v %s:%s %s join %s",
dockerDir, dockerDir, swarmDockerImage, nodeArgs))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func (h *Host) StartDocker() error {
log.Debug("Starting Docker...")
var (
cmd *exec.Cmd
err error
)
switch h.Driver.GetProviderType() {
case provider.Local:
cmd, err = h.GetSSHCommand("sudo /etc/init.d/docker start")
case provider.Remote:
cmd, err = h.GetSSHCommand("sudo service docker start")
default:
return ErrUnknownProviderType
}
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func (h *Host) StopDocker() error {
log.Debug("Stopping Docker...")
var (
cmd *exec.Cmd
err error
)
switch h.Driver.GetProviderType() {
case provider.Local:
cmd, err = h.GetSSHCommand("if [ -e /var/run/docker.pid ] && [ -d /proc/$(cat /var/run/docker.pid) ]; then sudo /etc/init.d/docker stop ; exit 0; fi")
case provider.Remote:
cmd, err = h.GetSSHCommand("sudo service docker stop")
default:
return ErrUnknownProviderType
}
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func (h *Host) ConfigureAuth() error {
d := h.Driver
if d.DriverName() == "none" {
return nil
}
// copy certs to client dir for docker client
machineDir := filepath.Join(utils.GetMachineDir(), h.Name)
if err := utils.CopyFile(h.CaCertPath, filepath.Join(machineDir, "ca.pem")); err != nil {
log.Fatalf("Error copying ca.pem to machine dir: %s", err)
}
clientCertPath := filepath.Join(utils.GetMachineCertDir(), "cert.pem")
if err := utils.CopyFile(clientCertPath, filepath.Join(machineDir, "cert.pem")); err != nil {
log.Fatalf("Error copying cert.pem to machine dir: %s", err)
}
clientKeyPath := filepath.Join(utils.GetMachineCertDir(), "key.pem")
if err := utils.CopyFile(clientKeyPath, filepath.Join(machineDir, "key.pem")); err != nil {
log.Fatalf("Error copying key.pem to machine dir: %s", err)
}
var (
ip = ""
ipErr error
maxRetries = 4
)
for i := 0; i < maxRetries; i++ {
ip, ipErr = h.Driver.GetIP()
if ip != "" {
break
}
log.Debugf("waiting for ip: %s", ipErr)
time.Sleep(5 * time.Second)
}
if ipErr != nil {
return ipErr
}
if ip == "" {
return fmt.Errorf("unable to get machine IP")
}
serverCertPath := filepath.Join(h.StorePath, "server.pem")
serverKeyPath := filepath.Join(h.StorePath, "server-key.pem")
org := h.Name
bits := 2048
log.Debugf("generating server cert: %s ca-key=%s private-key=%s org=%s",
serverCertPath,
h.CaCertPath,
h.PrivateKeyPath,
org,
)
if err := utils.GenerateCert([]string{ip}, serverCertPath, serverKeyPath, h.CaCertPath, h.PrivateKeyPath, org, bits); err != nil {
return fmt.Errorf("error generating server cert: %s", err)
}
if err := h.StopDocker(); err != nil {
return err
}
dockerDir, err := h.GetDockerConfigDir()
if err != nil {
return err
}
cmd, err := h.GetSSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
// upload certs and configure TLS auth
caCert, err := ioutil.ReadFile(h.CaCertPath)
if err != nil {
return err
}
// due to windows clients, we cannot use filepath.Join as the paths
// will be mucked on the linux hosts
machineCaCertPath := path.Join(dockerDir, "ca.pem")
serverCert, err := ioutil.ReadFile(serverCertPath)
if err != nil {
return err
}
machineServerCertPath := path.Join(dockerDir, "server.pem")
serverKey, err := ioutil.ReadFile(serverKeyPath)
if err != nil {
return err
}
machineServerKeyPath := path.Join(dockerDir, "server-key.pem")
cmd, err = h.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(caCert), machineCaCertPath))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = h.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverKey), machineServerKeyPath))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
cmd, err = h.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee %s", string(serverCert), machineServerCertPath))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
dockerUrl, err := h.Driver.GetURL()
if err != nil {
return err
}
u, err := url.Parse(dockerUrl)
if err != nil {
return err
}
dockerPort := 2376
parts := strings.Split(u.Host, ":")
if len(parts) == 2 {
dPort, err := strconv.Atoi(parts[1])
if err != nil {
return err
}
dockerPort = dPort
}
cfg, err := h.generateDockerConfig(dockerPort, machineCaCertPath, machineServerKeyPath, machineServerCertPath)
if err != nil {
return err
}
cmd, err = h.GetSSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", cfg.EngineConfig, cfg.EngineConfigPath))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
if err := h.StartDocker(); err != nil {
return err
}
return nil
}
func (h *Host) generateDockerConfig(dockerPort int, caCertPath string, serverKeyPath string, serverCertPath string) (*DockerConfig, error) {
d := h.Driver
var (
daemonOpts string
daemonOptsCfg string
daemonCfg string
swarmLabels = []string{}
)
swarmLabels = append(swarmLabels, fmt.Sprintf("--label=provider=%s", h.Driver.DriverName()))
defaultDaemonOpts := fmt.Sprintf(`--tlsverify --tlscacert=%s --tlskey=%s --tlscert=%s %s`,
caCertPath,
serverKeyPath,
serverCertPath,
strings.Join(swarmLabels, " "),
)
dockerDir, err := h.GetDockerConfigDir()
if err != nil {
return nil, err
}
switch d.DriverName() {
case "virtualbox", "vmwarefusion", "vmwarevsphere", "hyper-v":
daemonOpts = fmt.Sprintf("-H tcp://0.0.0.0:%d", dockerPort)
daemonOptsCfg = path.Join(dockerDir, "profile")
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
daemonCfg = fmt.Sprintf(`EXTRA_ARGS='%s'
CACERT=%s
SERVERCERT=%s
SERVERKEY=%s
DOCKER_TLS=no`, opts, caCertPath, serverKeyPath, serverCertPath)
default:
daemonOpts = fmt.Sprintf("--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:%d", dockerPort)
daemonOptsCfg = "/etc/default/docker"
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
daemonCfg = fmt.Sprintf("export DOCKER_OPTS=\\\"%s\\\"", opts)
}
return &DockerConfig{
EngineConfig: daemonCfg,
EngineConfigPath: daemonOptsCfg,
}, nil
}
func (h *Host) Create(name string) error {
name, err := ValidateHostName(name)
if err != nil {
return err
}
// create the instance
if err := h.Driver.Create(); err != nil {
return err
@ -498,45 +105,17 @@ func (h *Host) Create(name string) error {
return err
}
// set hostname
if err := h.SetHostname(); err != nil {
return err
}
// install docker
if err := h.Provision(); err != nil {
return err
}
return nil
}
func (h *Host) Provision() error {
// "local" providers use b2d; no provisioning necessary
switch h.Driver.DriverName() {
case "none", "virtualbox", "vmwarefusion", "vmwarevsphere":
return nil
}
if err := WaitForSSH(h); err != nil {
return err
}
// install docker - until cloudinit we use ubuntu everywhere so we
// just install it using the docker repos
cmd, err := h.GetSSHCommand("if [ ! -e /usr/bin/docker ]; then curl -sSL https://get.docker.com | sh -; fi")
provisioner, err := provision.DetectProvisioner(h.Driver)
if 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()))
if err := provisioner.Provision(*h.HostConfig.SwarmConfig, *h.HostConfig.AuthConfig); err != nil {
return err
}
return nil
@ -561,48 +140,6 @@ func (h *Host) GetSSHCommand(args ...string) (*exec.Cmd, error) {
return cmd, nil
}
func (h *Host) SetHostname() error {
var (
cmd *exec.Cmd
err error
)
log.Debugf("setting hostname for provider type %s: %s",
h.Driver.GetProviderType(),
h.Name,
)
switch h.Driver.GetProviderType() {
case provider.None:
return nil
case provider.Local:
cmd, err = h.GetSSHCommand(fmt.Sprintf(
"sudo hostname %s && echo \"%s\" | sudo tee /var/lib/boot2docker/etc/hostname",
h.Name,
h.Name,
))
case provider.Remote:
cmd, err = h.GetSSHCommand(fmt.Sprintf(
"echo \"127.0.0.1 %s\" | sudo tee -a /etc/hosts && sudo hostname %s && echo \"%s\" | sudo tee /etc/hostname",
h.Name,
h.Name,
h.Name,
))
default:
return ErrUnknownProviderType
}
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
func (h *Host) MachineInState(desiredState state.State) func() bool {
return func() bool {
currentState, err := h.Driver.GetState()
@ -719,15 +256,18 @@ func (h *Host) LoadConfig() error {
}
// First pass: find the driver name and load the driver
var config hostConfig
if err := json.Unmarshal(data, &config); err != nil {
var hostMetadata HostMetadata
if err := json.Unmarshal(data, &hostMetadata); err != nil {
return err
}
driver, err := drivers.NewDriver(config.DriverName, h.Name, h.StorePath, h.CaCertPath, h.PrivateKeyPath)
authConfig := hostMetadata.HostConfig.AuthConfig
driver, err := drivers.NewDriver(hostMetadata.DriverName, h.Name, h.StorePath, authConfig.CaCertPath, authConfig.PrivateKeyPath)
if err != nil {
return err
}
h.Driver = driver
// Second pass: unmarshal driver config into correct driver
@ -738,6 +278,19 @@ func (h *Host) LoadConfig() error {
return nil
}
func (h *Host) ConfigureAuth() error {
provisioner, err := provision.DetectProvisioner(h.Driver)
if err != nil {
return err
}
if err := provision.ConfigureAuth(provisioner, *h.HostConfig.AuthConfig); err != nil {
return err
}
return nil
}
func (h *Host) SaveConfig() error {
data, err := json.Marshal(h)
if err != nil {

View File

@ -5,7 +5,7 @@ import (
"os"
"path/filepath"
log "github.com/Sirupsen/logrus"
"github.com/docker/machine/drivers"
"github.com/docker/machine/utils"
)
@ -19,10 +19,16 @@ func New(store Store) (*Machine, error) {
}, nil
}
func (m *Machine) Create(name string, driverName string, options *HostOptions) (*Host, error) {
driverOptions := options.DriverOptions
engineOptions := options.EngineOptions
swarmOptions := options.SwarmOptions
func (m *Machine) Create(name string, driverName string, options *HostOptions, driverConfig drivers.DriverOptions) (*Host, error) {
engineConfig := options.EngineConfig
swarmConfig := options.SwarmConfig
authConfig := options.AuthConfig
hostConfig := HostOptions{
AuthConfig: authConfig,
EngineConfig: engineConfig,
SwarmConfig: swarmConfig,
}
exists, err := m.store.Exists(name)
if err != nil {
@ -34,22 +40,12 @@ func (m *Machine) Create(name string, driverName string, options *HostOptions) (
hostPath := filepath.Join(utils.GetMachineDir(), name)
caCert, err := m.store.GetCACertificatePath()
if err != nil {
return nil, err
}
privateKey, err := m.store.GetPrivateKeyPath()
if err != nil {
return nil, err
}
host, err := NewHost(name, driverName, hostPath, caCert, privateKey, engineOptions, swarmOptions)
host, err := NewHost(name, driverName, hostConfig)
if err != nil {
return host, err
}
if driverOptions != nil {
if err := host.Driver.SetConfigFromFlags(driverOptions); err != nil {
if driverConfig != nil {
if err := host.Driver.SetConfigFromFlags(driverConfig); err != nil {
return host, err
}
}
@ -70,22 +66,6 @@ func (m *Machine) Create(name string, driverName string, options *HostOptions) (
return host, err
}
if err := host.ConfigureAuth(); err != nil {
return host, err
}
if swarmOptions.Host != "" {
log.Info("Configuring Swarm...")
discovery := swarmOptions.Discovery
master := swarmOptions.Master
swarmHost := swarmOptions.Host
addr := swarmOptions.Address
if err := host.ConfigureSwarm(discovery, master, swarmHost, addr); err != nil {
log.Errorf("Error configuring Swarm: %s", err)
}
}
if err := m.store.SetActive(host); err != nil {
return nil, err
}

View File

@ -0,0 +1,141 @@
package provision
import (
"bytes"
"fmt"
"os/exec"
"path"
"github.com/docker/machine/drivers"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/provision/pkgaction"
"github.com/docker/machine/libmachine/swarm"
)
func init() {
Register("boot2docker", &RegisteredProvisioner{
New: NewBoot2DockerProvisioner,
})
}
func NewBoot2DockerProvisioner(d drivers.Driver) Provisioner {
return &Boot2DockerProvisioner{
Driver: d,
}
}
type Boot2DockerProvisioner struct {
OsReleaseInfo *OsRelease
Driver drivers.Driver
SwarmConfig swarm.SwarmOptions
}
func (provisioner *Boot2DockerProvisioner) Service(name string, action pkgaction.ServiceAction) error {
var (
cmd *exec.Cmd
err error
)
if name == "docker" && action == pkgaction.Stop {
cmd, err = provisioner.SSHCommand("if [ -e /var/run/docker.pid ] && [ -d /proc/$(cat /var/run/docker.pid) ]; then sudo /etc/init.d/docker stop ; exit 0; fi")
} else {
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 {
return err
}
return nil
}
func (provisioner *Boot2DockerProvisioner) Package(name string, action pkgaction.PackageAction) error {
return nil
}
func (provisioner *Boot2DockerProvisioner) Hostname() (string, error) {
cmd, err := provisioner.SSHCommand(fmt.Sprintf("hostname"))
if err != nil {
return "", err
}
var so bytes.Buffer
cmd.Stdout = &so
if err := cmd.Run(); err != nil {
return "", err
}
return so.String(), nil
}
func (provisioner *Boot2DockerProvisioner) SetHostname(hostname string) error {
cmd, err := provisioner.SSHCommand(fmt.Sprintf(
"sudo hostname %s && echo %q | sudo tee /var/lib/boot2docker/etc/hostname",
hostname,
hostname,
))
if err != nil {
return err
}
return cmd.Run()
}
func (provisioner *Boot2DockerProvisioner) GetDockerConfigDir() string {
return "/var/lib/boot2docker"
}
func (provisioner *Boot2DockerProvisioner) GenerateDockerConfig(dockerPort int, authConfig auth.AuthOptions) (*DockerConfig, error) {
defaultDaemonOpts := getDefaultDaemonOpts(provisioner.Driver.DriverName(), authConfig)
daemonOpts := fmt.Sprintf("-H tcp://0.0.0.0:%d", dockerPort)
daemonOptsCfg := path.Join(provisioner.GetDockerConfigDir(), "profile")
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
daemonCfg := fmt.Sprintf(`EXTRA_ARGS='%s'
CACERT=%s
SERVERCERT=%s
SERVERKEY=%s
DOCKER_TLS=no`, opts, authConfig.CaCertRemotePath, authConfig.ServerKeyRemotePath, authConfig.ServerCertRemotePath)
return &DockerConfig{
EngineConfig: daemonCfg,
EngineConfigPath: daemonOptsCfg,
}, nil
}
func (provisioner *Boot2DockerProvisioner) CompatibleWithHost() bool {
return provisioner.OsReleaseInfo.Id == "boot2docker"
}
func (provisioner *Boot2DockerProvisioner) SetOsReleaseInfo(info *OsRelease) {
provisioner.OsReleaseInfo = info
}
func (provisioner *Boot2DockerProvisioner) Provision(swarmConfig swarm.SwarmOptions, authConfig auth.AuthOptions) error {
fmt.Println("before set hostname")
if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
return err
}
fmt.Println("after set hostname")
if err := installDockerGeneric(provisioner); err != nil {
return err
}
if err := ConfigureAuth(provisioner, authConfig); err != nil {
return err
}
if err := configureSwarm(provisioner, swarmConfig); err != nil {
return err
}
return nil
}
func (provisioner *Boot2DockerProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) {
return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...)
}
func (provisioner *Boot2DockerProvisioner) GetDriver() drivers.Driver {
return provisioner.Driver
}

View File

@ -0,0 +1,11 @@
package provision
import (
"errors"
)
var (
ErrDetectionFailed = errors.New("OS type not recognized")
ErrSSHCommandFailed = errors.New("SSH command failure")
ErrNotImplemented = errors.New("Runtime not implemented")
)

View File

@ -0,0 +1,83 @@
package provision
import (
"bufio"
"bytes"
"fmt"
"reflect"
"strings"
log "github.com/Sirupsen/logrus"
)
// The /etc/os-release file contains operating system identification data
// See http://www.freedesktop.org/software/systemd/man/os-release.html for more details
// Values in this struct must always be string
// or the reflection will not work properly.
type OsRelease struct {
AnsiColor string `osr:"ANSI_COLOR"`
Name string `osr:"NAME"`
Version string `osr:"VERSION"`
Id string `osr:"ID"`
IdLike string `osr:"ID_LIKE"`
PrettyName string `osr:"PRETTY_NAME"`
VersionId string `osr:"VERSION_ID"`
HomeUrl string `osr:"HOME_URL"`
SupportUrl string `osr:"SUPPORT_URL"`
BugReportUrl string `osr:"BUG_REPORT_URL"`
}
func stripQuotes(val string) string {
if val[0] == '"' {
return val[1 : len(val)-1]
}
return val
}
func (osr *OsRelease) setIfPossible(key, val string) error {
v := reflect.ValueOf(osr).Elem()
for i := 0; i < v.NumField(); i++ {
fieldValue := v.Field(i)
fieldType := v.Type().Field(i)
originalName := fieldType.Tag.Get("osr")
if key == originalName && fieldValue.Kind() == reflect.String {
fieldValue.SetString(val)
return nil
}
}
return fmt.Errorf("Couldn't set key %s, no corresponding struct field found", key)
}
func parseLine(osrLine string) (string, string, error) {
vals := strings.Split(osrLine, "=")
if len(vals) != 2 {
return "", "", fmt.Errorf("Expected %s to split by '=' char into two strings, instead got %d strings", osrLine, len(vals))
}
key := vals[0]
val := stripQuotes(vals[1])
return key, val, nil
}
func (osr *OsRelease) ParseOsRelease(osReleaseContents []byte) error {
r := bytes.NewReader(osReleaseContents)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
key, val, err := parseLine(scanner.Text())
if err != nil {
return err
}
if err := osr.setIfPossible(key, val); err != nil {
log.Debug(err)
}
}
return nil
}
func NewOsRelease(contents []byte) (*OsRelease, error) {
osr := &OsRelease{}
if err := osr.ParseOsRelease(contents); err != nil {
return nil, err
}
return osr, nil
}

View File

@ -0,0 +1,138 @@
package provision
import (
"reflect"
"testing"
)
func TestParseOsRelease(t *testing.T) {
// These example osr files stolen shamelessly from
// https://github.com/docker/docker/blob/master/pkg/parsers/operatingsystem/operatingsystem_test.go
// cheers @tiborvass
var (
ubuntuTrusty = []byte(`NAME="Ubuntu"
VERSION="14.04, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
gentoo = []byte(`NAME=Gentoo
ID=gentoo
PRETTY_NAME="Gentoo/Linux"
ANSI_COLOR="1;32"
HOME_URL="http://www.gentoo.org/"
SUPPORT_URL="http://www.gentoo.org/main/en/support.xml"
BUG_REPORT_URL="https://bugs.gentoo.org/"
`)
noPrettyName = []byte(`NAME="Ubuntu"
VERSION="14.04, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"`)
)
osr, err := NewOsRelease(ubuntuTrusty)
if err != nil {
t.Fatal("Unexpected error parsing os release: %s", err)
}
expectedOsr := OsRelease{
AnsiColor: "",
Name: "Ubuntu",
Version: "14.04, Trusty Tahr",
Id: "ubuntu",
IdLike: "debian",
PrettyName: "Ubuntu 14.04 LTS",
VersionId: "14.04",
HomeUrl: "http://www.ubuntu.com/",
SupportUrl: "http://help.ubuntu.com/",
BugReportUrl: "http://bugs.launchpad.net/ubuntu/",
}
if !reflect.DeepEqual(*osr, expectedOsr) {
t.Fatal("Error with ubuntu osr parsing: structs do not match")
}
osr, err = NewOsRelease(gentoo)
if err != nil {
t.Fatal("Unexpected error parsing os release: %s", err)
}
expectedOsr = OsRelease{
AnsiColor: "1;32",
Name: "Gentoo",
Version: "",
Id: "gentoo",
IdLike: "",
PrettyName: "Gentoo/Linux",
VersionId: "",
HomeUrl: "http://www.gentoo.org/",
SupportUrl: "http://www.gentoo.org/main/en/support.xml",
BugReportUrl: "https://bugs.gentoo.org/",
}
if !reflect.DeepEqual(*osr, expectedOsr) {
t.Fatal("Error with gentoo osr parsing: structs do not match")
}
osr, err = NewOsRelease(noPrettyName)
if err != nil {
t.Fatal("Unexpected error parsing os release: %s", err)
}
expectedOsr = OsRelease{
AnsiColor: "",
Name: "Ubuntu",
Version: "14.04, Trusty Tahr",
Id: "ubuntu",
IdLike: "debian",
PrettyName: "",
VersionId: "14.04",
HomeUrl: "http://www.ubuntu.com/",
SupportUrl: "http://help.ubuntu.com/",
BugReportUrl: "http://bugs.launchpad.net/ubuntu/",
}
if !reflect.DeepEqual(*osr, expectedOsr) {
t.Fatal("Error with noPrettyName osr parsing: structs do not match")
}
}
func TestParseLine(t *testing.T) {
var (
withQuotes = "ID=\"ubuntu\""
withoutQuotes = "ID=gentoo"
wtf = "LOTS=OF=EQUALS"
)
key, val, err := parseLine(withQuotes)
if key != "ID" {
t.Fatalf("Expected ID, got %s", key)
}
if val != "ubuntu" {
t.Fatalf("Expected ubuntu, got %s", val)
}
if err != nil {
t.Fatalf("Got error on parseLine with quotes: %s", err)
}
key, val, err = parseLine(withoutQuotes)
if key != "ID" {
t.Fatalf("Expected ID, got %s", key)
}
if val != "gentoo" {
t.Fatalf("Expected gentoo, got %s", val)
}
if err != nil {
t.Fatalf("Got error on parseLine without quotes: %s", err)
}
key, val, err = parseLine(wtf)
if err == nil {
t.Fatal("Expected to get an error on parseLine, got nil")
}
}

View File

@ -0,0 +1,43 @@
package pkgaction
type ServiceAction int
const (
Restart ServiceAction = iota
Start
Stop
)
var serviceActions = []string{
"restart",
"start",
"stop",
}
func (s ServiceAction) String() string {
if int(s) >= 0 && int(s) < len(serviceActions) {
return serviceActions[s]
}
return ""
}
type PackageAction int
const (
Install PackageAction = iota
Remove
)
var packageActions = []string{
"install",
"remove",
}
func (s PackageAction) String() string {
if int(s) >= 0 && int(s) < len(packageActions) {
return packageActions[s]
}
return ""
}

View File

@ -0,0 +1,90 @@
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"
)
var provisioners = make(map[string]*RegisteredProvisioner)
// Distribution specific actions
type Provisioner interface {
GenerateDockerConfig(dockerPort int, authConfig auth.AuthOptions) (*DockerConfig, error)
GetDockerConfigDir() string
// Run a package action
Package(name string, action pkgaction.PackageAction) error
// Hostname
Hostname() (string, error)
// Set hostname
SetHostname(hostname string) error
// Detection function
CompatibleWithHost() bool
Provision(swarmConfig swarm.SwarmOptions, authConfig auth.AuthOptions) error
// Perform action on a named service
Service(name string, action pkgaction.ServiceAction) error
GetDriver() drivers.Driver
SSHCommand(args ...string) (*exec.Cmd, error)
// Set the OS Release info depending on how it's represented
// internally
SetOsReleaseInfo(info *OsRelease)
}
// Detection
type RegisteredProvisioner struct {
New func(d drivers.Driver) Provisioner
}
func Register(name string, p *RegisteredProvisioner) {
provisioners[name] = p
}
func DetectProvisioner(d drivers.Driver) (Provisioner, error) {
var (
osReleaseOut bytes.Buffer
)
catOsReleaseCmd, err := drivers.GetSSHCommandFromDriver(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)
}
osReleaseInfo, err := NewOsRelease(osReleaseOut.Bytes())
if err != nil {
return nil, fmt.Errorf("Error parsing /etc/os-release file: %s", err)
}
for _, p := range provisioners {
provisioner := p.New(d)
provisioner.SetOsReleaseInfo(osReleaseInfo)
if provisioner.CompatibleWithHost() {
return provisioner, nil
}
}
return nil, ErrDetectionFailed
}

View File

@ -0,0 +1,162 @@
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"
)
func init() {
Register("Ubuntu", &RegisteredProvisioner{
New: NewUbuntuProvisioner,
})
}
func NewUbuntuProvisioner(d drivers.Driver) Provisioner {
return &UbuntuProvisioner{
packages: []string{
"curl",
},
Driver: d,
}
}
type UbuntuProvisioner struct {
packages []string
OsReleaseInfo *OsRelease
Driver drivers.Driver
SwarmConfig swarm.SwarmOptions
}
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 {
return err
}
return nil
}
func (provisioner *UbuntuProvisioner) Package(name string, action pkgaction.PackageAction) error {
var packageAction string
switch action {
case pkgaction.Install:
packageAction = "install"
case pkgaction.Remove:
packageAction = "remove"
}
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 {
return err
}
return nil
}
func (provisioner *UbuntuProvisioner) Provision(swarmConfig swarm.SwarmOptions, authConfig auth.AuthOptions) error {
if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
return err
}
for _, pkg := range provisioner.packages {
if err := provisioner.Package(pkg, pkgaction.Install); err != nil {
return err
}
}
if err := installDockerGeneric(provisioner); err != nil {
return err
}
if err := ConfigureAuth(provisioner, authConfig); err != nil {
return err
}
if err := configureSwarm(provisioner, swarmConfig); err != nil {
return err
}
return nil
}
func (provisioner *UbuntuProvisioner) Hostname() (string, error) {
cmd, err := provisioner.SSHCommand("hostname")
if err != nil {
return "", err
}
var so bytes.Buffer
cmd.Stdout = &so
if err := cmd.Run(); err != nil {
return "", err
}
return so.String(), nil
}
func (provisioner *UbuntuProvisioner) SetHostname(hostname string) error {
cmd, 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 {
return err
}
return cmd.Run()
}
func (provisioner *UbuntuProvisioner) GetDockerConfigDir() string {
return "/etc/docker"
}
func (provisioner *UbuntuProvisioner) SSHCommand(args ...string) (*exec.Cmd, error) {
return drivers.GetSSHCommandFromDriver(provisioner.Driver, args...)
}
func (provisioner *UbuntuProvisioner) CompatibleWithHost() bool {
return provisioner.OsReleaseInfo.Id == "ubuntu"
}
func (provisioner *UbuntuProvisioner) SetOsReleaseInfo(info *OsRelease) {
provisioner.OsReleaseInfo = info
}
func (provisioner *UbuntuProvisioner) GenerateDockerConfig(dockerPort int, authConfig auth.AuthOptions) (*DockerConfig, error) {
defaultDaemonOpts := getDefaultDaemonOpts(provisioner.Driver.DriverName(), authConfig)
daemonOpts := fmt.Sprintf("--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:%d", dockerPort)
daemonOptsCfg := "/etc/default/docker"
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
daemonCfg := fmt.Sprintf("export DOCKER_OPTS=\\\"%s\\\"", opts)
return &DockerConfig{
EngineConfig: daemonCfg,
EngineConfigPath: daemonOptsCfg,
}, nil
}
func (provisioner *UbuntuProvisioner) GetDriver() drivers.Driver {
return provisioner.Driver
}

View File

@ -0,0 +1,274 @@
package provision
import (
"bytes"
"fmt"
"io/ioutil"
"net/url"
"path"
"path/filepath"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/docker/machine/libmachine/auth"
"github.com/docker/machine/libmachine/provision/pkgaction"
"github.com/docker/machine/libmachine/swarm"
"github.com/docker/machine/utils"
)
type DockerConfig struct {
EngineConfig string
EngineConfigPath string
}
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
}
// 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 nil
}
func ConfigureAuth(p Provisioner, authConfig auth.AuthOptions) error {
var (
err error
)
machineName := p.GetDriver().GetMachineName()
org := machineName
bits := 2048
ip, err := p.GetDriver().GetIP()
if err != nil {
return err
}
// copy certs to client dir for docker client
machineDir := filepath.Join(utils.GetMachineDir(), machineName)
if err := utils.CopyFile(authConfig.CaCertPath, filepath.Join(machineDir, "ca.pem")); err != nil {
log.Fatalf("Error copying ca.pem to machine dir: %s", err)
}
if err := utils.CopyFile(authConfig.ClientCertPath, filepath.Join(machineDir, "cert.pem")); err != nil {
log.Fatalf("Error copying cert.pem to machine dir: %s", err)
}
if err := utils.CopyFile(authConfig.ClientKeyPath, filepath.Join(machineDir, "key.pem")); err != nil {
log.Fatalf("Error copying key.pem to machine dir: %s", err)
}
log.Debugf("generating server cert: %s ca-key=%s private-key=%s org=%s",
authConfig.ServerCertPath,
authConfig.CaCertPath,
authConfig.PrivateKeyPath,
org,
)
// TODO: Switch to passing just authConfig to this func
// instead of all these individual fields
err = utils.GenerateCert(
[]string{ip},
authConfig.ServerCertPath,
authConfig.ServerKeyPath,
authConfig.CaCertPath,
authConfig.PrivateKeyPath,
org,
bits,
)
if err != nil {
return fmt.Errorf("error generating server cert: %s", err)
}
if err := p.Service("docker", pkgaction.Stop); err != nil {
return err
}
dockerDir := p.GetDockerConfigDir()
cmd, err := p.SSHCommand(fmt.Sprintf("sudo mkdir -p %s", dockerDir))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
// upload certs and configure TLS auth
caCert, err := ioutil.ReadFile(authConfig.CaCertPath)
if err != nil {
return err
}
// due to windows clients, we cannot use filepath.Join as the paths
// will be mucked on the linux hosts
machineCaCertPath := path.Join(dockerDir, "ca.pem")
authConfig.CaCertRemotePath = machineCaCertPath
serverCert, err := ioutil.ReadFile(authConfig.ServerCertPath)
if err != nil {
return err
}
machineServerCertPath := path.Join(dockerDir, "server.pem")
authConfig.ServerCertRemotePath = machineServerCertPath
serverKey, err := ioutil.ReadFile(authConfig.ServerKeyPath)
if err != nil {
return err
}
machineServerKeyPath := path.Join(dockerDir, "server-key.pem")
authConfig.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 {
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 {
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 {
return err
}
dockerUrl, err := p.GetDriver().GetURL()
if err != nil {
return err
}
u, err := url.Parse(dockerUrl)
if err != nil {
return err
}
dockerPort := 2376
parts := strings.Split(u.Host, ":")
if len(parts) == 2 {
dPort, err := strconv.Atoi(parts[1])
if err != nil {
return err
}
dockerPort = dPort
}
dkrcfg, err := p.GenerateDockerConfig(dockerPort, authConfig)
if err != nil {
return err
}
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineConfig, dkrcfg.EngineConfigPath))
if err != nil {
return err
}
if err := cmd.Run(); err != nil {
return err
}
if err := p.Service("docker", pkgaction.Start); err != nil {
return err
}
return nil
}
func getDefaultDaemonOpts(driverName string, authConfig auth.AuthOptions) string {
return fmt.Sprintf(`--tlsverify --tlscacert=%s --tlskey=%s --tlscert=%s %s`,
authConfig.CaCertRemotePath,
authConfig.ServerKeyRemotePath,
authConfig.ServerCertRemotePath,
fmt.Sprintf("--label=provider=%s", driverName),
)
}
func configureSwarm(p Provisioner, swarmConfig swarm.SwarmOptions) error {
if !swarmConfig.IsSwarm {
return nil
}
basePath := p.GetDockerConfigDir()
ip, err := p.GetDriver().GetIP()
if err != nil {
return err
}
tlsCaCert := path.Join(basePath, "ca.pem")
tlsCert := path.Join(basePath, "server.pem")
tlsKey := path.Join(basePath, "server-key.pem")
masterArgs := fmt.Sprintf("--tlsverify --tlscacert=%s --tlscert=%s --tlskey=%s -H %s %s",
tlsCaCert, tlsCert, tlsKey, swarmConfig.Host, swarmConfig.Discovery)
nodeArgs := fmt.Sprintf("--addr %s:2376 %s", ip, swarmConfig.Discovery)
u, err := url.Parse(swarmConfig.Host)
if err != nil {
return err
}
parts := strings.Split(u.Host, ":")
port := parts[1]
// TODO: Do not hardcode daemon port, ask the driver
if err := utils.WaitForDocker(ip, 2376); err != nil {
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 {
return err
}
dockerDir := p.GetDockerConfigDir()
// if master start master agent
if swarmConfig.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 {
return err
}
}
// 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 {
return err
}
return nil
}

View File

@ -1,6 +1,12 @@
package swarm
const (
DockerImage = "swarm:latest"
DiscoveryServiceEndpoint = "https://discovery-stage.hub.docker.com/v1"
)
type SwarmOptions struct {
IsSwarm bool
Address string
Discovery string
Master bool

View File

@ -0,0 +1 @@
package swarm

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"path/filepath"
"runtime"
@ -95,6 +96,18 @@ func WaitFor(f func() bool) error {
return WaitForSpecific(f, 60, 3*time.Second)
}
func WaitForDocker(ip string, daemonPort int) error {
return WaitFor(func() bool {
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, daemonPort))
if err != nil {
fmt.Println("Got an error it was", err)
return false
}
conn.Close()
return true
})
}
func DumpVal(vals ...interface{}) {
for _, val := range vals {
prettyJSON, err := json.MarshalIndent(val, "", " ")