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
98
commands.go
98
commands.go
|
@ -30,6 +30,7 @@ import (
|
||||||
_ "github.com/docker/machine/drivers/vmwarevcloudair"
|
_ "github.com/docker/machine/drivers/vmwarevcloudair"
|
||||||
_ "github.com/docker/machine/drivers/vmwarevsphere"
|
_ "github.com/docker/machine/drivers/vmwarevsphere"
|
||||||
"github.com/docker/machine/libmachine"
|
"github.com/docker/machine/libmachine"
|
||||||
|
"github.com/docker/machine/libmachine/auth"
|
||||||
"github.com/docker/machine/libmachine/engine"
|
"github.com/docker/machine/libmachine/engine"
|
||||||
"github.com/docker/machine/libmachine/swarm"
|
"github.com/docker/machine/libmachine/swarm"
|
||||||
"github.com/docker/machine/state"
|
"github.com/docker/machine/state"
|
||||||
|
@ -39,26 +40,24 @@ import (
|
||||||
type machineConfig struct {
|
type machineConfig struct {
|
||||||
machineName string
|
machineName string
|
||||||
machineDir string
|
machineDir string
|
||||||
caCertPath string
|
machineUrl string
|
||||||
caKeyPath string
|
|
||||||
clientCertPath string
|
|
||||||
clientKeyPath string
|
clientKeyPath string
|
||||||
serverCertPath string
|
serverCertPath string
|
||||||
|
clientCertPath string
|
||||||
|
caCertPath string
|
||||||
|
caKeyPath string
|
||||||
serverKeyPath string
|
serverKeyPath string
|
||||||
machineUrl string
|
AuthConfig auth.AuthOptions
|
||||||
swarmMaster bool
|
SwarmConfig swarm.SwarmOptions
|
||||||
swarmHost string
|
|
||||||
swarmDiscovery string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type hostListItem struct {
|
type hostListItem struct {
|
||||||
Name string
|
Name string
|
||||||
Active bool
|
Active bool
|
||||||
DriverName string
|
DriverName string
|
||||||
State state.State
|
State state.State
|
||||||
URL string
|
URL string
|
||||||
SwarmMaster bool
|
SwarmConfig swarm.SwarmOptions
|
||||||
SwarmDiscovery string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type certPathInfo struct {
|
type certPathInfo struct {
|
||||||
|
@ -406,10 +405,18 @@ func cmdCreate(c *cli.Context) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
hostOptions := &libmachine.HostOptions{
|
hostConfig := &libmachine.HostOptions{
|
||||||
DriverOptions: c,
|
AuthConfig: &auth.AuthOptions{
|
||||||
EngineOptions: &engine.EngineOptions{},
|
CaCertPath: c.GlobalString("tls-ca-cert"),
|
||||||
SwarmOptions: &swarm.SwarmOptions{
|
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"),
|
Master: c.GlobalBool("swarm-master"),
|
||||||
Discovery: c.GlobalString("swarm-discovery"),
|
Discovery: c.GlobalString("swarm-discovery"),
|
||||||
Address: c.GlobalString("swarm-addr"),
|
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 {
|
if err != nil {
|
||||||
log.Errorf("Error creating machine: %s", err)
|
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.")
|
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 c.Bool("swarm") {
|
||||||
if !cfg.swarmMaster {
|
if !cfg.SwarmConfig.Master {
|
||||||
log.Fatalf("%s is not a swarm master", cfg.machineName)
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -563,13 +570,14 @@ func cmdLs(c *cli.Context) {
|
||||||
swarmInfo := make(map[string]string)
|
swarmInfo := make(map[string]string)
|
||||||
|
|
||||||
for _, host := range hostList {
|
for _, host := range hostList {
|
||||||
|
swarmConfig := host.HostConfig.SwarmConfig
|
||||||
if !quiet {
|
if !quiet {
|
||||||
if host.SwarmOptions.Master {
|
if swarmConfig.Master {
|
||||||
swarmMasters[host.SwarmOptions.Discovery] = host.Name
|
swarmMasters[swarmConfig.Discovery] = host.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if host.SwarmOptions.Discovery != "" {
|
if swarmConfig.Discovery != "" {
|
||||||
swarmInfo[host.Name] = host.SwarmOptions.Discovery
|
swarmInfo[host.Name] = swarmConfig.Discovery
|
||||||
}
|
}
|
||||||
|
|
||||||
go getHostState(*host, defaultStore, hostListItems)
|
go getHostState(*host, defaultStore, hostListItems)
|
||||||
|
@ -596,9 +604,9 @@ func cmdLs(c *cli.Context) {
|
||||||
|
|
||||||
swarmInfo := ""
|
swarmInfo := ""
|
||||||
|
|
||||||
if item.SwarmDiscovery != "" {
|
if item.SwarmConfig.Discovery != "" {
|
||||||
swarmInfo = swarmMasters[item.SwarmDiscovery]
|
swarmInfo = swarmMasters[item.SwarmConfig.Discovery]
|
||||||
if item.SwarmMaster {
|
if item.SwarmConfig.Master {
|
||||||
swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
|
swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -674,10 +682,10 @@ func cmdEnv(c *cli.Context) {
|
||||||
|
|
||||||
dockerHost := cfg.machineUrl
|
dockerHost := cfg.machineUrl
|
||||||
if c.Bool("swarm") {
|
if c.Bool("swarm") {
|
||||||
if !cfg.swarmMaster {
|
if !cfg.SwarmConfig.Master {
|
||||||
log.Fatalf("%s is not a swarm master", cfg.machineName)
|
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 {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -1032,13 +1040,12 @@ func getHostState(host libmachine.Host, store libmachine.Store, hostListItems ch
|
||||||
}
|
}
|
||||||
|
|
||||||
hostListItems <- hostListItem{
|
hostListItems <- hostListItem{
|
||||||
Name: host.Name,
|
Name: host.Name,
|
||||||
Active: isActive,
|
Active: isActive,
|
||||||
DriverName: host.Driver.DriverName(),
|
DriverName: host.Driver.DriverName(),
|
||||||
State: currentState,
|
State: currentState,
|
||||||
URL: url,
|
URL: url,
|
||||||
SwarmMaster: host.SwarmOptions.Master,
|
SwarmConfig: *host.HostConfig.SwarmConfig,
|
||||||
SwarmDiscovery: host.SwarmOptions.Discovery,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1096,16 +1103,15 @@ func getMachineConfig(c *cli.Context) (*machineConfig, error) {
|
||||||
return &machineConfig{
|
return &machineConfig{
|
||||||
machineName: name,
|
machineName: name,
|
||||||
machineDir: machineDir,
|
machineDir: machineDir,
|
||||||
caCertPath: caCert,
|
|
||||||
caKeyPath: caKey,
|
|
||||||
clientCertPath: clientCert,
|
|
||||||
clientKeyPath: clientKey,
|
|
||||||
serverCertPath: serverCert,
|
|
||||||
serverKeyPath: serverKey,
|
|
||||||
machineUrl: machineUrl,
|
machineUrl: machineUrl,
|
||||||
swarmMaster: machine.SwarmOptions.Master,
|
clientKeyPath: clientKey,
|
||||||
swarmHost: machine.SwarmOptions.Host,
|
clientCertPath: clientCert,
|
||||||
swarmDiscovery: machine.SwarmOptions.Discovery,
|
serverCertPath: serverCert,
|
||||||
|
caKeyPath: caKey,
|
||||||
|
caCertPath: caCert,
|
||||||
|
serverKeyPath: serverKey,
|
||||||
|
AuthConfig: *machine.HostConfig.AuthConfig,
|
||||||
|
SwarmConfig: *machine.HostConfig.SwarmConfig,
|
||||||
}, nil
|
}, 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())
|
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)
|
return resp, fmt.Errorf("client encountered error while doing the request: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return resp, newAwsApiResponseError(*resp)
|
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
|
package libmachine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/machine/drivers"
|
"github.com/docker/machine/drivers"
|
||||||
|
"github.com/docker/machine/libmachine/auth"
|
||||||
"github.com/docker/machine/libmachine/engine"
|
"github.com/docker/machine/libmachine/engine"
|
||||||
|
"github.com/docker/machine/libmachine/provision"
|
||||||
"github.com/docker/machine/libmachine/swarm"
|
"github.com/docker/machine/libmachine/swarm"
|
||||||
"github.com/docker/machine/provider"
|
|
||||||
"github.com/docker/machine/ssh"
|
"github.com/docker/machine/ssh"
|
||||||
"github.com/docker/machine/state"
|
"github.com/docker/machine/state"
|
||||||
"github.com/docker/machine/utils"
|
"github.com/docker/machine/utils"
|
||||||
|
@ -31,74 +25,53 @@ var (
|
||||||
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
|
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
swarmDockerImage = "swarm:latest"
|
|
||||||
swarmDiscoveryServiceEndpoint = "https://discovery-stage.hub.docker.com/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Host struct {
|
type Host struct {
|
||||||
Name string `json:"-"`
|
Name string `json:"-"`
|
||||||
DriverName string
|
DriverName string
|
||||||
Driver drivers.Driver
|
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
|
CaCertPath string
|
||||||
PrivateKeyPath string
|
PrivateKeyPath string
|
||||||
ServerCertPath string
|
ServerCertPath string
|
||||||
ServerKeyPath string
|
ServerKeyPath string
|
||||||
ClientCertPath 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 {
|
type HostOptions struct {
|
||||||
Driver string
|
Driver string
|
||||||
Memory int
|
Memory int
|
||||||
Disk int
|
Disk int
|
||||||
DriverOptions drivers.DriverOptions
|
EngineConfig *engine.EngineOptions
|
||||||
EngineOptions *engine.EngineOptions
|
SwarmConfig *swarm.SwarmOptions
|
||||||
SwarmOptions *swarm.SwarmOptions
|
AuthConfig *auth.AuthOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
type DockerConfig struct {
|
type HostMetadata struct {
|
||||||
EngineConfig string
|
|
||||||
EngineConfigPath string
|
|
||||||
}
|
|
||||||
|
|
||||||
type hostConfig struct {
|
|
||||||
DriverName string
|
DriverName string
|
||||||
|
HostConfig HostOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func waitForDocker(addr string) error {
|
func NewHost(name, driverName string, hostConfig HostOptions) (*Host, error) {
|
||||||
for {
|
authConfig := hostConfig.AuthConfig
|
||||||
conn, err := net.DialTimeout("tcp", addr, time.Second*5)
|
storePath := filepath.Join(utils.GetMachineDir(), name)
|
||||||
if err != nil {
|
driver, err := drivers.NewDriver(driverName, name, storePath, authConfig.CaCertPath, authConfig.PrivateKeyPath)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &Host{
|
return &Host{
|
||||||
Name: name,
|
Name: name,
|
||||||
DriverName: driverName,
|
DriverName: driverName,
|
||||||
Driver: driver,
|
Driver: driver,
|
||||||
CaCertPath: caCert,
|
StorePath: storePath,
|
||||||
PrivateKeyPath: privateKey,
|
HostConfig: hostConfig,
|
||||||
EngineOptions: engineOptions,
|
|
||||||
SwarmOptions: swarmOptions,
|
|
||||||
StorePath: StorePath,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,373 +94,7 @@ func ValidateHostName(name string) (string, error) {
|
||||||
return name, nil
|
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 {
|
func (h *Host) Create(name string) error {
|
||||||
name, err := ValidateHostName(name)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the instance
|
// create the instance
|
||||||
if err := h.Driver.Create(); err != nil {
|
if err := h.Driver.Create(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -498,45 +105,17 @@ func (h *Host) Create(name string) error {
|
||||||
return err
|
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 {
|
if err := WaitForSSH(h); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// install docker - until cloudinit we use ubuntu everywhere so we
|
provisioner, err := provision.DetectProvisioner(h.Driver)
|
||||||
// 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")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: the script above will output debug to stderr; we save it and
|
if err := provisioner.Provision(*h.HostConfig.SwarmConfig, *h.HostConfig.AuthConfig); err != nil {
|
||||||
// then check if the command returned an error; if so, we show the debug
|
return err
|
||||||
|
|
||||||
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
|
return nil
|
||||||
|
@ -561,48 +140,6 @@ func (h *Host) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||||
return cmd, nil
|
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 {
|
func (h *Host) MachineInState(desiredState state.State) func() bool {
|
||||||
return func() bool {
|
return func() bool {
|
||||||
currentState, err := h.Driver.GetState()
|
currentState, err := h.Driver.GetState()
|
||||||
|
@ -719,15 +256,18 @@ func (h *Host) LoadConfig() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// First pass: find the driver name and load the driver
|
// First pass: find the driver name and load the driver
|
||||||
var config hostConfig
|
var hostMetadata HostMetadata
|
||||||
if err := json.Unmarshal(data, &config); err != nil {
|
if err := json.Unmarshal(data, &hostMetadata); err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Driver = driver
|
h.Driver = driver
|
||||||
|
|
||||||
// Second pass: unmarshal driver config into correct driver
|
// Second pass: unmarshal driver config into correct driver
|
||||||
|
@ -738,6 +278,19 @@ func (h *Host) LoadConfig() error {
|
||||||
return nil
|
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 {
|
func (h *Host) SaveConfig() error {
|
||||||
data, err := json.Marshal(h)
|
data, err := json.Marshal(h)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
log "github.com/Sirupsen/logrus"
|
"github.com/docker/machine/drivers"
|
||||||
"github.com/docker/machine/utils"
|
"github.com/docker/machine/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,10 +19,16 @@ func New(store Store) (*Machine, error) {
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Machine) Create(name string, driverName string, options *HostOptions) (*Host, error) {
|
func (m *Machine) Create(name string, driverName string, options *HostOptions, driverConfig drivers.DriverOptions) (*Host, error) {
|
||||||
driverOptions := options.DriverOptions
|
engineConfig := options.EngineConfig
|
||||||
engineOptions := options.EngineOptions
|
swarmConfig := options.SwarmConfig
|
||||||
swarmOptions := options.SwarmOptions
|
authConfig := options.AuthConfig
|
||||||
|
|
||||||
|
hostConfig := HostOptions{
|
||||||
|
AuthConfig: authConfig,
|
||||||
|
EngineConfig: engineConfig,
|
||||||
|
SwarmConfig: swarmConfig,
|
||||||
|
}
|
||||||
|
|
||||||
exists, err := m.store.Exists(name)
|
exists, err := m.store.Exists(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -34,22 +40,12 @@ func (m *Machine) Create(name string, driverName string, options *HostOptions) (
|
||||||
|
|
||||||
hostPath := filepath.Join(utils.GetMachineDir(), name)
|
hostPath := filepath.Join(utils.GetMachineDir(), name)
|
||||||
|
|
||||||
caCert, err := m.store.GetCACertificatePath()
|
host, err := NewHost(name, driverName, hostConfig)
|
||||||
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)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return host, err
|
return host, err
|
||||||
}
|
}
|
||||||
if driverOptions != nil {
|
if driverConfig != nil {
|
||||||
if err := host.Driver.SetConfigFromFlags(driverOptions); err != nil {
|
if err := host.Driver.SetConfigFromFlags(driverConfig); err != nil {
|
||||||
return host, err
|
return host, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,22 +66,6 @@ func (m *Machine) Create(name string, driverName string, options *HostOptions) (
|
||||||
return host, err
|
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 {
|
if err := m.store.SetActive(host); err != nil {
|
||||||
return nil, err
|
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
|
package swarm
|
||||||
|
|
||||||
|
const (
|
||||||
|
DockerImage = "swarm:latest"
|
||||||
|
DiscoveryServiceEndpoint = "https://discovery-stage.hub.docker.com/v1"
|
||||||
|
)
|
||||||
|
|
||||||
type SwarmOptions struct {
|
type SwarmOptions struct {
|
||||||
|
IsSwarm bool
|
||||||
Address string
|
Address string
|
||||||
Discovery string
|
Discovery string
|
||||||
Master bool
|
Master bool
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
package swarm
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
@ -95,6 +96,18 @@ func WaitFor(f func() bool) error {
|
||||||
return WaitForSpecific(f, 60, 3*time.Second)
|
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{}) {
|
func DumpVal(vals ...interface{}) {
|
||||||
for _, val := range vals {
|
for _, val := range vals {
|
||||||
prettyJSON, err := json.MarshalIndent(val, "", " ")
|
prettyJSON, err := json.MarshalIndent(val, "", " ")
|
||||||
|
|
Loading…
Reference in New Issue