mirror of https://github.com/docker/docs.git
Merge pull request #827 from nathanleclaire/libmachine_provision_merge
Add provisioning based on OS
This commit is contained in:
commit
3392671988
|
@ -1 +1,2 @@
|
|||
docker-machine*
|
||||
*.log
|
||||
|
|
111
commands.go
111
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,33 +40,24 @@ import (
|
|||
type machineConfig struct {
|
||||
machineName string
|
||||
machineDir string
|
||||
caCertPath string
|
||||
caKeyPath string
|
||||
clientCertPath string
|
||||
machineUrl string
|
||||
clientKeyPath string
|
||||
serverCertPath string
|
||||
clientCertPath string
|
||||
caCertPath string
|
||||
caKeyPath string
|
||||
serverKeyPath string
|
||||
machineUrl string
|
||||
swarmMaster bool
|
||||
swarmHost string
|
||||
swarmDiscovery string
|
||||
AuthOptions auth.AuthOptions
|
||||
SwarmOptions swarm.SwarmOptions
|
||||
}
|
||||
|
||||
type hostListItem struct {
|
||||
Name string
|
||||
Active bool
|
||||
DriverName string
|
||||
State state.State
|
||||
URL string
|
||||
SwarmMaster bool
|
||||
SwarmDiscovery string
|
||||
}
|
||||
|
||||
type certPathInfo struct {
|
||||
CaCertPath string
|
||||
CaKeyPath string
|
||||
ClientCertPath string
|
||||
ClientKeyPath string
|
||||
Name string
|
||||
Active bool
|
||||
DriverName string
|
||||
State state.State
|
||||
URL string
|
||||
SwarmOptions swarm.SwarmOptions
|
||||
}
|
||||
|
||||
func sortHostListItemsByName(items []hostListItem) {
|
||||
|
@ -407,17 +399,25 @@ func cmdCreate(c *cli.Context) {
|
|||
}
|
||||
|
||||
hostOptions := &libmachine.HostOptions{
|
||||
DriverOptions: c,
|
||||
AuthOptions: &auth.AuthOptions{
|
||||
CaCertPath: certInfo.CaCertPath,
|
||||
PrivateKeyPath: certInfo.CaKeyPath,
|
||||
ClientCertPath: certInfo.ClientCertPath,
|
||||
ClientKeyPath: certInfo.ClientKeyPath,
|
||||
ServerCertPath: filepath.Join(utils.GetMachineDir(), name, "server.pem"),
|
||||
ServerKeyPath: filepath.Join(utils.GetMachineDir(), name, "server-key.pem"),
|
||||
},
|
||||
EngineOptions: &engine.EngineOptions{},
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: c.GlobalBool("swarm-master"),
|
||||
Discovery: c.GlobalString("swarm-discovery"),
|
||||
Address: c.GlobalString("swarm-addr"),
|
||||
Host: c.GlobalString("swarm-host"),
|
||||
IsSwarm: c.Bool("swarm"),
|
||||
Master: c.Bool("swarm-master"),
|
||||
Discovery: c.String("swarm-discovery"),
|
||||
Address: c.String("swarm-addr"),
|
||||
Host: c.String("swarm-host"),
|
||||
},
|
||||
}
|
||||
|
||||
host, err := mcn.Create(name, driver, hostOptions)
|
||||
host, err := mcn.Create(name, driver, hostOptions, 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 +456,10 @@ func cmdConfig(c *cli.Context) {
|
|||
}
|
||||
|
||||
if c.Bool("swarm") {
|
||||
if !cfg.swarmMaster {
|
||||
if !cfg.SwarmOptions.Master {
|
||||
log.Fatalf("%s is not a swarm master", cfg.machineName)
|
||||
}
|
||||
u, err := url.Parse(cfg.swarmHost)
|
||||
u, err := url.Parse(cfg.SwarmOptions.Host)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -563,13 +563,14 @@ func cmdLs(c *cli.Context) {
|
|||
swarmInfo := make(map[string]string)
|
||||
|
||||
for _, host := range hostList {
|
||||
swarmOptions := host.HostOptions.SwarmOptions
|
||||
if !quiet {
|
||||
if host.SwarmOptions.Master {
|
||||
swarmMasters[host.SwarmOptions.Discovery] = host.Name
|
||||
if swarmOptions.Master {
|
||||
swarmMasters[swarmOptions.Discovery] = host.Name
|
||||
}
|
||||
|
||||
if host.SwarmOptions.Discovery != "" {
|
||||
swarmInfo[host.Name] = host.SwarmOptions.Discovery
|
||||
if swarmOptions.Discovery != "" {
|
||||
swarmInfo[host.Name] = swarmOptions.Discovery
|
||||
}
|
||||
|
||||
go getHostState(*host, defaultStore, hostListItems)
|
||||
|
@ -596,9 +597,9 @@ func cmdLs(c *cli.Context) {
|
|||
|
||||
swarmInfo := ""
|
||||
|
||||
if item.SwarmDiscovery != "" {
|
||||
swarmInfo = swarmMasters[item.SwarmDiscovery]
|
||||
if item.SwarmMaster {
|
||||
if item.SwarmOptions.Discovery != "" {
|
||||
swarmInfo = swarmMasters[item.SwarmOptions.Discovery]
|
||||
if item.SwarmOptions.Master {
|
||||
swarmInfo = fmt.Sprintf("%s (master)", swarmInfo)
|
||||
}
|
||||
}
|
||||
|
@ -674,10 +675,10 @@ func cmdEnv(c *cli.Context) {
|
|||
|
||||
dockerHost := cfg.machineUrl
|
||||
if c.Bool("swarm") {
|
||||
if !cfg.swarmMaster {
|
||||
if !cfg.SwarmOptions.Master {
|
||||
log.Fatalf("%s is not a swarm master", cfg.machineName)
|
||||
}
|
||||
u, err := url.Parse(cfg.swarmHost)
|
||||
u, err := url.Parse(cfg.SwarmOptions.Host)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -1032,13 +1033,12 @@ func getHostState(host libmachine.Host, store libmachine.Store, hostListItems ch
|
|||
}
|
||||
|
||||
hostListItems <- hostListItem{
|
||||
Name: host.Name,
|
||||
Active: isActive,
|
||||
DriverName: host.Driver.DriverName(),
|
||||
State: currentState,
|
||||
URL: url,
|
||||
SwarmMaster: host.SwarmOptions.Master,
|
||||
SwarmDiscovery: host.SwarmOptions.Discovery,
|
||||
Name: host.Name,
|
||||
Active: isActive,
|
||||
DriverName: host.Driver.DriverName(),
|
||||
State: currentState,
|
||||
URL: url,
|
||||
SwarmOptions: *host.HostOptions.SwarmOptions,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1096,16 +1096,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,
|
||||
AuthOptions: *machine.HostOptions.AuthOptions,
|
||||
SwarmOptions: *machine.HostOptions.SwarmOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -1113,7 +1112,7 @@ func getMachineConfig(c *cli.Context) (*machineConfig, error) {
|
|||
// codegangsta/cli will not set the cert paths if the storage-path
|
||||
// is set to something different so we cannot use the paths
|
||||
// in the global options. le sigh.
|
||||
func getCertPathInfo(c *cli.Context) certPathInfo {
|
||||
func getCertPathInfo(c *cli.Context) libmachine.CertPathInfo {
|
||||
// setup cert paths
|
||||
caCertPath := c.GlobalString("tls-ca-cert")
|
||||
caKeyPath := c.GlobalString("tls-ca-key")
|
||||
|
@ -1136,7 +1135,7 @@ func getCertPathInfo(c *cli.Context) certPathInfo {
|
|||
clientKeyPath = filepath.Join(utils.GetMachineCertDir(), "key.pem")
|
||||
}
|
||||
|
||||
return certPathInfo{
|
||||
return libmachine.CertPathInfo{
|
||||
CaCertPath: caCertPath,
|
||||
CaKeyPath: caKeyPath,
|
||||
ClientCertPath: clientCertPath,
|
||||
|
|
183
commands_test.go
183
commands_test.go
|
@ -7,17 +7,16 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
drivers "github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"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/provider"
|
||||
"github.com/docker/machine/state"
|
||||
)
|
||||
|
||||
|
@ -88,7 +87,16 @@ func getDefaultTestHost() (*libmachine.Host, error) {
|
|||
Discovery: "",
|
||||
Address: "",
|
||||
}
|
||||
host, err := libmachine.NewHost(hostTestName, hostTestDriverName, hostTestStorePath, hostTestCaCert, hostTestPrivateKey, engineOptions, swarmOptions)
|
||||
authOptions := &auth.AuthOptions{
|
||||
CaCertPath: hostTestCaCert,
|
||||
PrivateKeyPath: hostTestPrivateKey,
|
||||
}
|
||||
hostOptions := &libmachine.HostOptions{
|
||||
EngineOptions: engineOptions,
|
||||
SwarmOptions: swarmOptions,
|
||||
AuthOptions: authOptions,
|
||||
}
|
||||
host, err := libmachine.NewHost(hostTestName, hostTestDriverName, hostOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -116,113 +124,6 @@ func (d DriverOptionsMock) Int(key string) int {
|
|||
func (d DriverOptionsMock) Bool(key string) bool {
|
||||
return d.Data[key].(bool)
|
||||
}
|
||||
|
||||
type FakeDriver struct {
|
||||
MockState state.State
|
||||
}
|
||||
|
||||
func (d *FakeDriver) DriverName() string {
|
||||
return "fakedriver"
|
||||
}
|
||||
|
||||
func (d *FakeDriver) AuthorizePort(ports []*drivers.Port) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) DeauthorizePort(ports []*drivers.Port) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetURL() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetMachineName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetProviderType() provider.ProviderType {
|
||||
return provider.None
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetIP() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHHostname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHKeyPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHPort() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHUsername() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetState() (state.State, error) {
|
||||
return d.MockState, nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) PreCreateCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Create() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Remove() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Start() error {
|
||||
d.MockState = state.Running
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Stop() error {
|
||||
d.MockState = state.Stopped
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Restart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Kill() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Upgrade() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) StartDocker() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) StopDocker() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetDockerConfigDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
return &exec.Cmd{}, nil
|
||||
}
|
||||
|
||||
func TestGetHostState(t *testing.T) {
|
||||
defer cleanup()
|
||||
|
||||
|
@ -237,40 +138,46 @@ func TestGetHostState(t *testing.T) {
|
|||
{
|
||||
Name: "foo",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
StorePath: store.GetPath(),
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
HostOptions: &libmachine.HostOptions{
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "bar",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
StorePath: store.GetPath(),
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
HostOptions: &libmachine.HostOptions{
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "baz",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
StorePath: store.GetPath(),
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
HostOptions: &libmachine.HostOptions{
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Address: "",
|
||||
Discovery: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -315,7 +222,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
{
|
||||
Name: "foo",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -323,7 +230,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
{
|
||||
Name: "bar",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -335,7 +242,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
// virtualbox... (to test serial actions)
|
||||
// It's actually FakeDriver!
|
||||
DriverName: "virtualbox",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -343,7 +250,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
{
|
||||
Name: "spam",
|
||||
DriverName: "virtualbox",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -351,7 +258,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
{
|
||||
Name: "eggs",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Stopped,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -359,7 +266,7 @@ func TestRunActionForeachMachine(t *testing.T) {
|
|||
{
|
||||
Name: "ham",
|
||||
DriverName: "fakedriver",
|
||||
Driver: &FakeDriver{
|
||||
Driver: &fakedriver.FakeDriver{
|
||||
MockState: state.Running,
|
||||
},
|
||||
StorePath: storePath,
|
||||
|
@ -428,7 +335,6 @@ func TestCmdConfig(t *testing.T) {
|
|||
|
||||
flags := getTestDriverFlags()
|
||||
hostOptions := &libmachine.HostOptions{
|
||||
DriverOptions: flags,
|
||||
EngineOptions: &engine.EngineOptions{},
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
|
@ -436,9 +342,10 @@ func TestCmdConfig(t *testing.T) {
|
|||
Address: "",
|
||||
Host: "",
|
||||
},
|
||||
AuthOptions: &auth.AuthOptions{},
|
||||
}
|
||||
|
||||
host, err := mcn.Create("test-a", "none", hostOptions)
|
||||
host, err := mcn.Create("test-a", "none", hostOptions, flags)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -525,7 +432,6 @@ func TestCmdEnvBash(t *testing.T) {
|
|||
}
|
||||
|
||||
hostOptions := &libmachine.HostOptions{
|
||||
DriverOptions: flags,
|
||||
EngineOptions: &engine.EngineOptions{},
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
|
@ -533,9 +439,10 @@ func TestCmdEnvBash(t *testing.T) {
|
|||
Address: "",
|
||||
Host: "",
|
||||
},
|
||||
AuthOptions: &auth.AuthOptions{},
|
||||
}
|
||||
|
||||
host, err := mcn.Create("test-a", "none", hostOptions)
|
||||
host, err := mcn.Create("test-a", "none", hostOptions, flags)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -620,7 +527,6 @@ func TestCmdEnvFish(t *testing.T) {
|
|||
}
|
||||
|
||||
hostOptions := &libmachine.HostOptions{
|
||||
DriverOptions: flags,
|
||||
EngineOptions: &engine.EngineOptions{},
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
|
@ -628,9 +534,10 @@ func TestCmdEnvFish(t *testing.T) {
|
|||
Address: "",
|
||||
Host: "",
|
||||
},
|
||||
AuthOptions: &auth.AuthOptions{},
|
||||
}
|
||||
|
||||
host, err := mcn.Create("test-a", "none", hostOptions)
|
||||
host, err := mcn.Create("test-a", "none", hostOptions, flags)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -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,115 @@
|
|||
package fakedriver
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/provider"
|
||||
"github.com/docker/machine/state"
|
||||
)
|
||||
|
||||
type FakeDriver struct {
|
||||
MockState state.State
|
||||
}
|
||||
|
||||
func (d *FakeDriver) DriverName() string {
|
||||
return "fakedriver"
|
||||
}
|
||||
|
||||
func (d *FakeDriver) AuthorizePort(ports []*drivers.Port) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) DeauthorizePort(ports []*drivers.Port) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) SetConfigFromFlags(flags drivers.DriverOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetURL() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetMachineName() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetProviderType() provider.ProviderType {
|
||||
return provider.None
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetIP() (string, error) {
|
||||
return "1.2.3.4", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHHostname() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHKeyPath() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHPort() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHUsername() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetState() (state.State, error) {
|
||||
return d.MockState, nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) PreCreateCheck() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Create() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Remove() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Start() error {
|
||||
d.MockState = state.Running
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Stop() error {
|
||||
d.MockState = state.Stopped
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Restart() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Kill() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) Upgrade() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) StartDocker() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) StopDocker() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetDockerConfigDir() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (d *FakeDriver) GetSSHCommand(args ...string) (*exec.Cmd, error) {
|
||||
return &exec.Cmd{}, nil
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package fakedriver
|
|
@ -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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package auth
|
|
@ -0,0 +1,10 @@
|
|||
package libmachine
|
||||
|
||||
type CertPathInfo struct {
|
||||
CaCertPath string
|
||||
CaKeyPath string
|
||||
ClientCertPath string
|
||||
ClientKeyPath string
|
||||
ServerCertPath string
|
||||
ServerKeyPath string
|
||||
}
|
|
@ -8,8 +8,6 @@ import (
|
|||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
|
@ -34,7 +32,7 @@ func (s Filestore) loadHost(name string) (*Host, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
h := validateHost(host)
|
||||
h := ValidateHost(host)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
|
@ -149,22 +147,3 @@ func (s Filestore) RemoveActive() error {
|
|||
func (s Filestore) activePath() string {
|
||||
return filepath.Join(utils.GetMachineDir(), ".active")
|
||||
}
|
||||
|
||||
// validates host config and modifies if needed
|
||||
// this is used for configuration updates
|
||||
func validateHost(host *Host) *Host {
|
||||
if host.EngineOptions == nil {
|
||||
host.EngineOptions = &engine.EngineOptions{}
|
||||
}
|
||||
|
||||
if host.SwarmOptions == nil {
|
||||
host.SwarmOptions = &swarm.SwarmOptions{
|
||||
Address: "",
|
||||
Discovery: host.SwarmDiscovery,
|
||||
Host: host.SwarmHost,
|
||||
Master: host.SwarmMaster,
|
||||
}
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
||||
|
|
|
@ -1,26 +1,20 @@
|
|||
package libmachine
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/provision"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/provider"
|
||||
"github.com/docker/machine/ssh"
|
||||
"github.com/docker/machine/state"
|
||||
"github.com/docker/machine/utils"
|
||||
|
@ -31,74 +25,58 @@ var (
|
|||
validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`)
|
||||
)
|
||||
|
||||
const (
|
||||
swarmDockerImage = "swarm:latest"
|
||||
swarmDiscoveryServiceEndpoint = "https://discovery-stage.hub.docker.com/v1"
|
||||
)
|
||||
|
||||
type Host struct {
|
||||
Name string `json:"-"`
|
||||
DriverName string
|
||||
Driver drivers.Driver
|
||||
Name string `json:"-"`
|
||||
DriverName string
|
||||
Driver drivers.Driver
|
||||
StorePath string
|
||||
HostOptions *HostOptions
|
||||
|
||||
// deprecated options; these are left to assist in config migrations
|
||||
SwarmHost string
|
||||
SwarmMaster bool
|
||||
SwarmDiscovery string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
ServerCertPath string
|
||||
ServerKeyPath string
|
||||
ClientCertPath string
|
||||
StorePath string
|
||||
EngineOptions *engine.EngineOptions
|
||||
SwarmOptions *swarm.SwarmOptions
|
||||
// deprecated options; these are left to assist in config migrations
|
||||
SwarmHost string
|
||||
SwarmMaster bool
|
||||
SwarmDiscovery string
|
||||
ClientKeyPath string
|
||||
}
|
||||
|
||||
type HostOptions struct {
|
||||
Driver string
|
||||
Memory int
|
||||
Disk int
|
||||
DriverOptions drivers.DriverOptions
|
||||
EngineOptions *engine.EngineOptions
|
||||
SwarmOptions *swarm.SwarmOptions
|
||||
AuthOptions *auth.AuthOptions
|
||||
}
|
||||
|
||||
type DockerConfig struct {
|
||||
EngineConfig string
|
||||
EngineConfigPath string
|
||||
type HostMetadata struct {
|
||||
DriverName string
|
||||
HostOptions HostOptions
|
||||
StorePath string
|
||||
CaCertPath string
|
||||
PrivateKeyPath string
|
||||
ServerCertPath string
|
||||
ServerKeyPath string
|
||||
ClientCertPath string
|
||||
}
|
||||
|
||||
type hostConfig struct {
|
||||
DriverName string
|
||||
}
|
||||
|
||||
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, hostOptions *HostOptions) (*Host, error) {
|
||||
authOptions := hostOptions.AuthOptions
|
||||
storePath := filepath.Join(utils.GetMachineDir(), name)
|
||||
driver, err := drivers.NewDriver(driverName, name, storePath, authOptions.CaCertPath, authOptions.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Host{
|
||||
Name: name,
|
||||
DriverName: driverName,
|
||||
Driver: driver,
|
||||
CaCertPath: caCert,
|
||||
PrivateKeyPath: privateKey,
|
||||
EngineOptions: engineOptions,
|
||||
SwarmOptions: swarmOptions,
|
||||
StorePath: StorePath,
|
||||
Name: name,
|
||||
DriverName: driverName,
|
||||
Driver: driver,
|
||||
StorePath: storePath,
|
||||
HostOptions: hostOptions,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -121,373 +99,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 +110,20 @@ func (h *Host) Create(name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// set hostname
|
||||
if err := h.SetHostname(); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Not really a fan of just checking "none" here.
|
||||
if h.Driver.DriverName() != "none" {
|
||||
if err := WaitForSSH(h); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// install docker
|
||||
if err := h.Provision(); err != nil {
|
||||
return err
|
||||
}
|
||||
provisioner, err := provision.DetectProvisioner(h.Driver)
|
||||
if 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")
|
||||
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.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -561,48 +148,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 +264,20 @@ 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)
|
||||
meta := ValidateHostMetadata(&hostMetadata)
|
||||
|
||||
authOptions := meta.HostOptions.AuthOptions
|
||||
|
||||
driver, err := drivers.NewDriver(hostMetadata.DriverName, h.Name, h.StorePath, authOptions.CaCertPath, authOptions.PrivateKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.Driver = driver
|
||||
|
||||
// Second pass: unmarshal driver config into correct driver
|
||||
|
@ -738,6 +288,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.HostOptions.AuthOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Host) SaveConfig() error {
|
||||
data, err := json.Marshal(h)
|
||||
if err != nil {
|
||||
|
|
|
@ -4,11 +4,11 @@ import (
|
|||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"testing"
|
||||
|
||||
_ "github.com/docker/machine/drivers/none"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
)
|
||||
|
@ -58,14 +58,20 @@ func getTestDriverFlags() *DriverOptionsMock {
|
|||
}
|
||||
|
||||
func getDefaultTestHost() (*Host, error) {
|
||||
engineOptions := &engine.EngineOptions{}
|
||||
swarmOptions := &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Host: "",
|
||||
Discovery: "",
|
||||
Address: "",
|
||||
hostOptions := &HostOptions{
|
||||
EngineOptions: &engine.EngineOptions{},
|
||||
SwarmOptions: &swarm.SwarmOptions{
|
||||
Master: false,
|
||||
Host: "",
|
||||
Discovery: "",
|
||||
Address: "",
|
||||
},
|
||||
AuthOptions: &auth.AuthOptions{
|
||||
CaCertPath: hostTestCaCert,
|
||||
PrivateKeyPath: hostTestPrivateKey,
|
||||
},
|
||||
}
|
||||
host, err := NewHost(hostTestName, hostTestDriverName, hostTestStorePath, hostTestCaCert, hostTestPrivateKey, engineOptions, swarmOptions)
|
||||
host, err := NewHost(hostTestName, hostTestDriverName, hostOptions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -90,6 +96,7 @@ func TestLoadHostExists(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
authOptions := host.HostOptions.AuthOptions
|
||||
if host.Name != hostTestName {
|
||||
t.Fatalf("expected name %s; received %s", hostTestName, host.Name)
|
||||
}
|
||||
|
@ -98,12 +105,12 @@ func TestLoadHostExists(t *testing.T) {
|
|||
t.Fatalf("expected driver %s; received %s", hostTestDriverName, host.DriverName)
|
||||
}
|
||||
|
||||
if host.CaCertPath != hostTestCaCert {
|
||||
t.Fatalf("expected ca cert path %s; received %s", hostTestCaCert, host.CaCertPath)
|
||||
if authOptions.CaCertPath != hostTestCaCert {
|
||||
t.Fatalf("expected ca cert path %s; received %s", hostTestCaCert, authOptions.CaCertPath)
|
||||
}
|
||||
|
||||
if host.PrivateKeyPath != hostTestPrivateKey {
|
||||
t.Fatalf("expected key path %s; received %s", hostTestPrivateKey, host.PrivateKeyPath)
|
||||
if authOptions.PrivateKeyPath != hostTestPrivateKey {
|
||||
t.Fatalf("expected key path %s; received %s", hostTestPrivateKey, authOptions.PrivateKeyPath)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,138 +148,7 @@ func TestValidateHostnameInvalid(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestGenerateDockerConfigNonLocal(t *testing.T) {
|
||||
host, err := getDefaultTestHost()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dockerPort := 1234
|
||||
caCertPath := "/test/ca-cert"
|
||||
serverKeyPath := "/test/server-key"
|
||||
serverCertPath := "/test/server-cert"
|
||||
engineConfigPath := "/etc/default/docker"
|
||||
|
||||
dockerCfg, err := host.generateDockerConfig(dockerPort, caCertPath, serverKeyPath, serverCertPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if dockerCfg.EngineConfigPath != engineConfigPath {
|
||||
t.Fatalf("expected engine path %s; received %s", engineConfigPath, dockerCfg.EngineConfigPath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineConfig, fmt.Sprintf("--host=tcp://0.0.0.0:%d", dockerPort)) == -1 {
|
||||
t.Fatalf("--host docker port invalid; expected %d", dockerPort)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineConfig, fmt.Sprintf("--tlscacert=%s", caCertPath)) == -1 {
|
||||
t.Fatalf("--tlscacert option invalid; expected %s", caCertPath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineConfig, fmt.Sprintf("--tlskey=%s", serverKeyPath)) == -1 {
|
||||
t.Fatalf("--tlskey option invalid; expected %s", serverKeyPath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineConfig, fmt.Sprintf("--tlscert=%s", serverCertPath)) == -1 {
|
||||
t.Fatalf("--tlscert option invalid; expected %s", serverCertPath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachinePort(t *testing.T) {
|
||||
dockerPort := 2376
|
||||
bindUrl := fmt.Sprintf("tcp://0.0.0.0:%d", dockerPort)
|
||||
store, err := getTestStore()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
host, err := getDefaultTestHost()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = store.Save(host); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
host, err = store.Get(hostTestName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg, err := host.generateDockerConfig(dockerPort, "", "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
re := regexp.MustCompile("--host=tcp://.*:(.+)")
|
||||
m := re.FindStringSubmatch(cfg.EngineConfig)
|
||||
if len(m) == 0 {
|
||||
t.Errorf("could not find port %d in engine config", dockerPort)
|
||||
}
|
||||
|
||||
b := m[0]
|
||||
u := strings.Split(b, "=")
|
||||
url := u[1]
|
||||
url = strings.Replace(url, "'", "", -1)
|
||||
url = strings.Replace(url, "\\\"", "", -1)
|
||||
if url != bindUrl {
|
||||
t.Errorf("expected url %s; received %s", bindUrl, url)
|
||||
}
|
||||
|
||||
if err := store.Remove(hostTestName, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineCustomPort(t *testing.T) {
|
||||
dockerPort := 3376
|
||||
bindUrl := fmt.Sprintf("tcp://0.0.0.0:%d", dockerPort)
|
||||
store, err := getTestStore()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
host, err := getDefaultTestHost()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if err = store.Save(host); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
host, err = store.Get(hostTestName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg, err := host.generateDockerConfig(dockerPort, "", "", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
re := regexp.MustCompile("--host=tcp://.*:(.+)")
|
||||
m := re.FindStringSubmatch(cfg.EngineConfig)
|
||||
if len(m) == 0 {
|
||||
t.Errorf("could not find port %d in engine config", dockerPort)
|
||||
}
|
||||
|
||||
b := m[0]
|
||||
u := strings.Split(b, "=")
|
||||
url := u[1]
|
||||
url = strings.Replace(url, "'", "", -1)
|
||||
url = strings.Replace(url, "\\\"", "", -1)
|
||||
if url != bindUrl {
|
||||
t.Errorf("expected url %s; received %s", bindUrl, url)
|
||||
}
|
||||
|
||||
if err := store.Remove(hostTestName, true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostConfig(t *testing.T) {
|
||||
func TestHostOptions(t *testing.T) {
|
||||
store, err := getTestStore()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/machine/drivers"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
|
@ -19,11 +19,7 @@ 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, hostOptions *HostOptions, driverConfig drivers.DriverOptions) (*Host, error) {
|
||||
exists, err := m.store.Exists(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -34,22 +30,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, hostOptions)
|
||||
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 +56,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,139 @@
|
|||
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
|
||||
SwarmOptions 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) GetDockerOptionsDir() string {
|
||||
return "/var/lib/boot2docker"
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) GenerateDockerOptions(dockerPort int, authOptions auth.AuthOptions) (*DockerOptions, error) {
|
||||
defaultDaemonOpts := getDefaultDaemonOpts(provisioner.Driver.DriverName(), authOptions)
|
||||
daemonOpts := fmt.Sprintf("-H tcp://0.0.0.0:%d", dockerPort)
|
||||
daemonOptsDir := path.Join(provisioner.GetDockerOptionsDir(), "profile")
|
||||
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
|
||||
daemonCfg := fmt.Sprintf(`EXTRA_ARGS='%s'
|
||||
CACERT=%s
|
||||
SERVERCERT=%s
|
||||
SERVERKEY=%s
|
||||
DOCKER_TLS=no`, opts, authOptions.CaCertRemotePath, authOptions.ServerCertRemotePath, authOptions.ServerKeyRemotePath)
|
||||
return &DockerOptions{
|
||||
EngineOptions: daemonCfg,
|
||||
EngineOptionsPath: daemonOptsDir,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) CompatibleWithHost() bool {
|
||||
return provisioner.OsReleaseInfo.Id == "boot2docker"
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) SetOsReleaseInfo(info *OsRelease) {
|
||||
provisioner.OsReleaseInfo = info
|
||||
}
|
||||
|
||||
func (provisioner *Boot2DockerProvisioner) Provision(swarmOptions swarm.SwarmOptions, authOptions auth.AuthOptions) error {
|
||||
if err := provisioner.SetHostname(provisioner.Driver.GetMachineName()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := installDockerGeneric(provisioner); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ConfigureAuth(provisioner, authOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := configureSwarm(provisioner, swarmOptions); 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,9 @@
|
|||
package pkgaction
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestActionValue(t *testing.T) {
|
||||
if Install.String() != "install" {
|
||||
t.Fatal("Expected %q but got %q", "install", Install.String())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
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 {
|
||||
// Create the files for the daemon to consume configuration settings (return struct of content and path)
|
||||
GenerateDockerOptions(dockerPort int, authOptions auth.AuthOptions) (*DockerOptions, error)
|
||||
|
||||
// Get the directory where the settings files for docker are to be found
|
||||
GetDockerOptionsDir() string
|
||||
|
||||
// Run a package action e.g. install
|
||||
Package(name string, action pkgaction.PackageAction) error
|
||||
|
||||
// Get Hostname
|
||||
Hostname() (string, error)
|
||||
|
||||
// Set hostname
|
||||
SetHostname(hostname string) error
|
||||
|
||||
// Figure out if this is the right provisioner to use based on /etc/os-release info
|
||||
CompatibleWithHost() bool
|
||||
|
||||
// Do the actual provisioning piece:
|
||||
// 1. Set the hostname on the instance.
|
||||
// 2. Install Docker if it is not present.
|
||||
// 3. Configure the daemon to accept connections over TLS.
|
||||
// 4. Copy the needed certificates to the server and local config dir.
|
||||
// 5. Configure / activate swarm if applicable.
|
||||
Provision(swarmOptions swarm.SwarmOptions, authOptions auth.AuthOptions) error
|
||||
|
||||
// Perform action on a named service e.g. stop
|
||||
Service(name string, action pkgaction.ServiceAction) error
|
||||
|
||||
// Get the driver which is contained in the provisioner.
|
||||
GetDriver() drivers.Driver
|
||||
|
||||
// Short-hand for accessing an SSH command from the 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
|
||||
SwarmOptions 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(swarmOptions swarm.SwarmOptions, authOptions 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, authOptions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := configureSwarm(provisioner, swarmOptions); 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) GetDockerOptionsDir() 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) GenerateDockerOptions(dockerPort int, authOptions auth.AuthOptions) (*DockerOptions, error) {
|
||||
defaultDaemonOpts := getDefaultDaemonOpts(provisioner.Driver.DriverName(), authOptions)
|
||||
daemonOpts := fmt.Sprintf("--host=unix:///var/run/docker.sock --host=tcp://0.0.0.0:%d", dockerPort)
|
||||
daemonOptsDir := "/etc/default/docker"
|
||||
opts := fmt.Sprintf("%s %s", defaultDaemonOpts, daemonOpts)
|
||||
daemonCfg := fmt.Sprintf("export DOCKER_OPTS=\\\"%s\\\"", opts)
|
||||
return &DockerOptions{
|
||||
EngineOptions: daemonCfg,
|
||||
EngineOptionsPath: daemonOptsDir,
|
||||
}, 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 DockerOptions struct {
|
||||
EngineOptions string
|
||||
EngineOptionsPath 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, authOptions 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(authOptions.CaCertPath, filepath.Join(machineDir, "ca.pem")); err != nil {
|
||||
log.Fatalf("Error copying ca.pem to machine dir: %s", err)
|
||||
}
|
||||
|
||||
if err := utils.CopyFile(authOptions.ClientCertPath, filepath.Join(machineDir, "cert.pem")); err != nil {
|
||||
log.Fatalf("Error copying cert.pem to machine dir: %s", err)
|
||||
}
|
||||
|
||||
if err := utils.CopyFile(authOptions.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",
|
||||
authOptions.ServerCertPath,
|
||||
authOptions.CaCertPath,
|
||||
authOptions.PrivateKeyPath,
|
||||
org,
|
||||
)
|
||||
|
||||
// TODO: Switch to passing just authOptions to this func
|
||||
// instead of all these individual fields
|
||||
err = utils.GenerateCert(
|
||||
[]string{ip},
|
||||
authOptions.ServerCertPath,
|
||||
authOptions.ServerKeyPath,
|
||||
authOptions.CaCertPath,
|
||||
authOptions.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.GetDockerOptionsDir()
|
||||
|
||||
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(authOptions.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")
|
||||
authOptions.CaCertRemotePath = machineCaCertPath
|
||||
|
||||
serverCert, err := ioutil.ReadFile(authOptions.ServerCertPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
machineServerCertPath := path.Join(dockerDir, "server.pem")
|
||||
authOptions.ServerCertRemotePath = machineServerCertPath
|
||||
|
||||
serverKey, err := ioutil.ReadFile(authOptions.ServerKeyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
machineServerKeyPath := path.Join(dockerDir, "server-key.pem")
|
||||
authOptions.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.GenerateDockerOptions(dockerPort, authOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd, err = p.SSHCommand(fmt.Sprintf("echo \"%s\" | sudo tee -a %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath))
|
||||
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, authOptions auth.AuthOptions) string {
|
||||
return fmt.Sprintf(`--tlsverify --tlscacert=%s --tlskey=%s --tlscert=%s %s`,
|
||||
authOptions.CaCertRemotePath,
|
||||
authOptions.ServerKeyRemotePath,
|
||||
authOptions.ServerCertRemotePath,
|
||||
fmt.Sprintf("--label=provider=%s", driverName),
|
||||
)
|
||||
}
|
||||
|
||||
func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions) error {
|
||||
if !swarmOptions.IsSwarm {
|
||||
return nil
|
||||
}
|
||||
|
||||
basePath := p.GetDockerOptionsDir()
|
||||
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, swarmOptions.Host, swarmOptions.Discovery)
|
||||
nodeArgs := fmt.Sprintf("--addr %s:2376 %s", ip, swarmOptions.Discovery)
|
||||
|
||||
u, err := url.Parse(swarmOptions.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.GetDockerOptionsDir()
|
||||
|
||||
// if master start master agent
|
||||
if swarmOptions.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
|
||||
}
|
|
@ -0,0 +1,114 @@
|
|||
package provision
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/machine/drivers/fakedriver"
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
)
|
||||
|
||||
func TestGenerateDockerOptionsBoot2Docker(t *testing.T) {
|
||||
p := &Boot2DockerProvisioner{
|
||||
Driver: &fakedriver.FakeDriver{},
|
||||
}
|
||||
dockerPort := 1234
|
||||
authOptions := auth.AuthOptions{
|
||||
CaCertRemotePath: "/test/ca-cert",
|
||||
ServerKeyRemotePath: "/test/server-key",
|
||||
ServerCertRemotePath: "/test/server-cert",
|
||||
}
|
||||
engineConfigPath := "/var/lib/boot2docker/profile"
|
||||
|
||||
dockerCfg, err := p.GenerateDockerOptions(dockerPort, authOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if dockerCfg.EngineOptionsPath != engineConfigPath {
|
||||
t.Fatalf("expected engine path %s; received %s", engineConfigPath, dockerCfg.EngineOptionsPath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineOptions, fmt.Sprintf("-H tcp://0.0.0.0:%d", dockerPort)) == -1 {
|
||||
t.Fatalf("-H docker port invalid; expected %d", dockerPort)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineOptions, fmt.Sprintf("CACERT=%s", authOptions.CaCertRemotePath)) == -1 {
|
||||
t.Fatalf("CACERT option invalid; expected %s", authOptions.CaCertRemotePath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineOptions, fmt.Sprintf("SERVERKEY=%s", authOptions.ServerKeyRemotePath)) == -1 {
|
||||
t.Fatalf("SERVERKEY option invalid; expected %s", authOptions.ServerKeyRemotePath)
|
||||
}
|
||||
|
||||
if strings.Index(dockerCfg.EngineOptions, fmt.Sprintf("SERVERCERT=%s", authOptions.ServerCertRemotePath)) == -1 {
|
||||
t.Fatalf("SERVERCERT option invalid; expected %s", authOptions.ServerCertRemotePath)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachinePortBoot2Docker(t *testing.T) {
|
||||
p := &Boot2DockerProvisioner{
|
||||
Driver: &fakedriver.FakeDriver{},
|
||||
}
|
||||
dockerPort := 2376
|
||||
bindUrl := fmt.Sprintf("tcp://0.0.0.0:%d", dockerPort)
|
||||
authOptions := auth.AuthOptions{
|
||||
CaCertRemotePath: "/test/ca-cert",
|
||||
ServerKeyRemotePath: "/test/server-key",
|
||||
ServerCertRemotePath: "/test/server-cert",
|
||||
}
|
||||
|
||||
cfg, err := p.GenerateDockerOptions(dockerPort, authOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
re := regexp.MustCompile("-H tcp://.*:(.+)")
|
||||
m := re.FindStringSubmatch(cfg.EngineOptions)
|
||||
if len(m) == 0 {
|
||||
t.Errorf("could not find port %d in engine config", dockerPort)
|
||||
}
|
||||
|
||||
b := m[0]
|
||||
u := strings.Split(b, " ")
|
||||
url := u[1]
|
||||
url = strings.Replace(url, "'", "", -1)
|
||||
url = strings.Replace(url, "\\\"", "", -1)
|
||||
if url != bindUrl {
|
||||
t.Errorf("expected url %s; received %s", bindUrl, url)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMachineCustomPortBoot2Docker(t *testing.T) {
|
||||
p := &Boot2DockerProvisioner{
|
||||
Driver: &fakedriver.FakeDriver{},
|
||||
}
|
||||
dockerPort := 3376
|
||||
bindUrl := fmt.Sprintf("tcp://0.0.0.0:%d", dockerPort)
|
||||
authOptions := auth.AuthOptions{
|
||||
CaCertRemotePath: "/test/ca-cert",
|
||||
ServerKeyRemotePath: "/test/server-key",
|
||||
ServerCertRemotePath: "/test/server-cert",
|
||||
}
|
||||
|
||||
cfg, err := p.GenerateDockerOptions(dockerPort, authOptions)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
re := regexp.MustCompile("-H tcp://.*:(.+)")
|
||||
m := re.FindStringSubmatch(cfg.EngineOptions)
|
||||
if len(m) == 0 {
|
||||
t.Errorf("could not find port %d in engine config", dockerPort)
|
||||
}
|
||||
|
||||
b := m[0]
|
||||
u := strings.Split(b, " ")
|
||||
url := u[1]
|
||||
url = strings.Replace(url, "'", "", -1)
|
||||
url = strings.Replace(url, "\\\"", "", -1)
|
||||
if url != bindUrl {
|
||||
t.Errorf("expected url %s; received %s", bindUrl, url)
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,115 @@
|
|||
package libmachine
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/swarm"
|
||||
"github.com/docker/machine/utils"
|
||||
)
|
||||
|
||||
// validates host config and modifies if needed
|
||||
// this is used for configuration updates
|
||||
func ValidateHost(host *Host) *Host {
|
||||
certInfo := getCertInfoFromHost(host)
|
||||
|
||||
if host.HostOptions == nil {
|
||||
host.HostOptions = &HostOptions{}
|
||||
}
|
||||
if host.HostOptions.EngineOptions == nil {
|
||||
host.HostOptions.EngineOptions = &engine.EngineOptions{}
|
||||
}
|
||||
|
||||
if host.HostOptions.SwarmOptions == nil {
|
||||
host.HostOptions.SwarmOptions = &swarm.SwarmOptions{
|
||||
Address: "",
|
||||
Discovery: host.SwarmDiscovery,
|
||||
Host: host.SwarmHost,
|
||||
Master: host.SwarmMaster,
|
||||
}
|
||||
}
|
||||
|
||||
host.HostOptions.AuthOptions = &auth.AuthOptions{
|
||||
StorePath: host.StorePath,
|
||||
CaCertPath: certInfo.CaCertPath,
|
||||
CaCertRemotePath: "",
|
||||
ServerCertPath: certInfo.ServerCertPath,
|
||||
ServerKeyPath: certInfo.ServerKeyPath,
|
||||
ClientKeyPath: certInfo.ClientKeyPath,
|
||||
ServerCertRemotePath: "",
|
||||
ServerKeyRemotePath: "",
|
||||
PrivateKeyPath: certInfo.CaKeyPath,
|
||||
ClientCertPath: certInfo.ClientCertPath,
|
||||
}
|
||||
|
||||
return host
|
||||
}
|
||||
|
||||
// validates host metadata and modifies if needed
|
||||
// this is used for configuration updates
|
||||
func ValidateHostMetadata(m *HostMetadata) *HostMetadata {
|
||||
if m.HostOptions.EngineOptions == nil {
|
||||
m.HostOptions.EngineOptions = &engine.EngineOptions{}
|
||||
}
|
||||
|
||||
if m.HostOptions.AuthOptions == nil {
|
||||
m.HostOptions.AuthOptions = &auth.AuthOptions{
|
||||
StorePath: m.StorePath,
|
||||
CaCertPath: m.CaCertPath,
|
||||
CaCertRemotePath: "",
|
||||
ServerCertPath: m.ServerCertPath,
|
||||
ServerKeyPath: m.ServerKeyPath,
|
||||
ClientKeyPath: "",
|
||||
ServerCertRemotePath: "",
|
||||
ServerKeyRemotePath: "",
|
||||
PrivateKeyPath: m.PrivateKeyPath,
|
||||
ClientCertPath: m.ClientCertPath,
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func getCertInfoFromHost(h *Host) CertPathInfo {
|
||||
// setup cert paths
|
||||
caCertPath := h.CaCertPath
|
||||
caKeyPath := h.PrivateKeyPath
|
||||
clientCertPath := h.ClientCertPath
|
||||
clientKeyPath := h.ClientKeyPath
|
||||
serverCertPath := h.ServerCertPath
|
||||
serverKeyPath := h.ServerKeyPath
|
||||
|
||||
if caCertPath == "" {
|
||||
caCertPath = filepath.Join(utils.GetMachineCertDir(), "ca.pem")
|
||||
}
|
||||
|
||||
if caKeyPath == "" {
|
||||
caKeyPath = filepath.Join(utils.GetMachineCertDir(), "ca-key.pem")
|
||||
}
|
||||
|
||||
if clientCertPath == "" {
|
||||
clientCertPath = filepath.Join(utils.GetMachineCertDir(), "cert.pem")
|
||||
}
|
||||
|
||||
if clientKeyPath == "" {
|
||||
clientKeyPath = filepath.Join(utils.GetMachineCertDir(), "key.pem")
|
||||
}
|
||||
|
||||
if serverCertPath == "" {
|
||||
serverCertPath = filepath.Join(utils.GetMachineCertDir(), "server.pem")
|
||||
}
|
||||
|
||||
if serverKeyPath == "" {
|
||||
serverKeyPath = filepath.Join(utils.GetMachineCertDir(), "server-key.pem")
|
||||
}
|
||||
|
||||
return CertPathInfo{
|
||||
CaCertPath: caCertPath,
|
||||
CaKeyPath: caKeyPath,
|
||||
ClientCertPath: clientCertPath,
|
||||
ClientKeyPath: clientKeyPath,
|
||||
ServerCertPath: serverCertPath,
|
||||
ServerKeyPath: serverKeyPath,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package libmachine
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Tests a function which "prefills" certificate information for a host
|
||||
// due to a schema migration from "flat" to a "nested" structure.
|
||||
func TestGetCertInfoFromHost(t *testing.T) {
|
||||
os.Setenv("MACHINE_STORAGE_PATH", "/tmp/migration")
|
||||
host := &Host{
|
||||
CaCertPath: "",
|
||||
PrivateKeyPath: "",
|
||||
ClientCertPath: "",
|
||||
ClientKeyPath: "",
|
||||
ServerCertPath: "",
|
||||
ServerKeyPath: "",
|
||||
}
|
||||
expectedCertInfo := CertPathInfo{
|
||||
CaCertPath: "/tmp/migration/certs/ca.pem",
|
||||
CaKeyPath: "/tmp/migration/certs/ca-key.pem",
|
||||
ClientCertPath: "/tmp/migration/certs/cert.pem",
|
||||
ClientKeyPath: "/tmp/migration/certs/key.pem",
|
||||
ServerCertPath: "/tmp/migration/certs/server.pem",
|
||||
ServerKeyPath: "/tmp/migration/certs/server-key.pem",
|
||||
}
|
||||
certInfo := getCertInfoFromHost(host)
|
||||
if !reflect.DeepEqual(expectedCertInfo, certInfo) {
|
||||
t.Log("\n\n\n", expectedCertInfo, "\n\n\n", certInfo)
|
||||
t.Fatal("Expected these structs to be equal, they were different")
|
||||
}
|
||||
}
|
|
@ -12,6 +12,19 @@ else
|
|||
ARCH="386"
|
||||
fi
|
||||
MACHINE_BIN_NAME=docker-machine_$PLATFORM-$ARCH
|
||||
BATS_LOG=${MACHINE_ROOT}/bats.log
|
||||
|
||||
touch ${BATS_LOG}
|
||||
rm ${BATS_LOG}
|
||||
|
||||
teardown() {
|
||||
echo "$BATS_TEST_NAME
|
||||
----------
|
||||
$output
|
||||
----------
|
||||
|
||||
" >> ${BATS_LOG}
|
||||
}
|
||||
|
||||
build_machine() {
|
||||
pushd $MACHINE_ROOT >/dev/null
|
||||
|
|
|
@ -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