mirror of https://github.com/docker/docs.git
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:
parent
8d5a59b43e
commit
49feb33457
78
commands.go
78
commands.go
|
@ -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,16 +40,15 @@ 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 {
|
||||
|
@ -57,8 +57,7 @@ type hostListItem struct {
|
|||
DriverName string
|
||||
State state.State
|
||||
URL string
|
||||
SwarmMaster bool
|
||||
SwarmDiscovery 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)
|
||||
}
|
||||
|
@ -1037,8 +1045,7 @@ func getHostState(host libmachine.Host, store libmachine.Store, hostListItems ch
|
|||
DriverName: host.Driver.DriverName(),
|
||||
State: currentState,
|
||||
URL: url,
|
||||
SwarmMaster: host.SwarmOptions.Master,
|
||||
SwarmDiscovery: host.SwarmOptions.Discovery,
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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,62 +25,44 @@ 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
|
||||
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
|
||||
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
|
||||
}
|
||||
|
@ -94,11 +70,8 @@ func NewHost(name, driverName, StorePath, caCert, privateKey string, engineOptio
|
|||
Name: name,
|
||||
DriverName: driverName,
|
||||
Driver: driver,
|
||||
CaCertPath: caCert,
|
||||
PrivateKeyPath: privateKey,
|
||||
EngineOptions: engineOptions,
|
||||
SwarmOptions: swarmOptions,
|
||||
StorePath: StorePath,
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
)
|
|
@ -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
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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 ""
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
package swarm
|
|
@ -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, "", " ")
|
||||
|
|
Loading…
Reference in New Issue