diff --git a/.travis.yml b/.travis.yml index 170ebc1dc5..b19fd0d7b1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,10 @@ language: go sudo: false go: -- 1.4.2 - 1.5 install: - export GOPATH=${TRAVIS_BUILD_DIR}/Godeps/_workspace:$GOPATH - export PATH=${TRAVIS_BUILD_DIR}/Godeps/_workspace/bin:$PATH -- go get -t -v ./... - go get golang.org/x/tools/cmd/cover script: - script/validate-dco diff --git a/Makefile b/Makefile index 4f4d249c8f..2492e2bf0b 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,6 @@ default: build -remote: build-remote - test: script/test @@ -18,9 +16,12 @@ validate: validate-dco validate-gofmt test build: clean script/build -build-remote: clean +remote: clean script/build-remote +rmi: + docker rmi docker-machine-build + clean: rm -f docker-machine_* rm -rf Godeps/_workspace/pkg diff --git a/commands/active.go b/commands/active.go index 94af14e588..c69cae2305 100644 --- a/commands/active.go +++ b/commands/active.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/codegangsta/cli" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) func cmdActive(c *cli.Context) { @@ -12,22 +12,8 @@ func cmdActive(c *cli.Context) { log.Fatal("Error: Too many arguments given.") } - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - host, err := provider.GetActive() + store := getStore(c) + host, err := getActiveHost(store) if err != nil { log.Fatalf("Error getting active host: %s", err) } diff --git a/commands/active_test.go b/commands/active_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/active_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/commands.go b/commands/commands.go index cbb59fa42b..2e46fd397b 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -6,13 +6,10 @@ import ( "os" "path/filepath" "runtime" - "sort" "strings" "github.com/codegangsta/cli" - "github.com/skarademir/naturalsort" - - "github.com/docker/machine/drivers" + "github.com/docker/machine/commands/mcndirs" _ "github.com/docker/machine/drivers/amazonec2" _ "github.com/docker/machine/drivers/azure" _ "github.com/docker/machine/drivers/digitalocean" @@ -28,12 +25,11 @@ import ( _ "github.com/docker/machine/drivers/vmwarefusion" _ "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/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/cert" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/persist" ) var ( @@ -42,34 +38,6 @@ var ( ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument.") ) -type machineConfig struct { - machineName string - machineDir string - machineUrl string - clientKeyPath string - serverCertPath string - clientCertPath string - caCertPath string - caKeyPath string - serverKeyPath string - AuthOptions auth.AuthOptions - SwarmOptions swarm.SwarmOptions -} - -func sortHostListItemsByName(items []libmachine.HostListItem) { - m := make(map[string]libmachine.HostListItem, len(items)) - s := make([]string, len(items)) - for i, v := range items { - name := strings.ToLower(v.Name) - m[name] = v - s[i] = name - } - sort.Sort(naturalsort.NaturalSort(s)) - for i, v := range s { - items[i] = m[v] - } -} - func confirmInput(msg string) bool { fmt.Printf("%s (y/n): ", msg) var resp string @@ -88,69 +56,46 @@ func confirmInput(msg string) bool { return false } -func newProvider(store libmachine.Store) (*libmachine.Provider, error) { - return libmachine.New(store) -} - -func getDefaultStore(rootPath, caCertPath, privateKeyPath string) (libmachine.Store, error) { - return libmachine.NewFilestore( - rootPath, - caCertPath, - privateKeyPath, - ), nil +func getMachineDir(rootPath string) string { + return filepath.Join(rootPath, "machines") } -func setupCertificates(caCertPath, caKeyPath, clientCertPath, clientKeyPath string) error { - org := utils.GetUsername() - bits := 2048 - - if _, err := os.Stat(utils.GetMachineCertDir()); err != nil { - if os.IsNotExist(err) { - if err := os.MkdirAll(utils.GetMachineCertDir(), 0700); err != nil { - log.Fatalf("Error creating machine config dir: %s", err) - } - } else { - log.Fatal(err) - } +func getStore(c *cli.Context) persist.Store { + certInfo := getCertPathInfoFromContext(c) + return &persist.Filestore{ + Path: c.GlobalString("storage-path"), + CaCertPath: certInfo.CaCertPath, + CaPrivateKeyPath: certInfo.CaPrivateKeyPath, } +} - if _, err := os.Stat(caCertPath); os.IsNotExist(err) { - log.Infof("Creating CA: %s", caCertPath) - - // check if the key path exists; if so, error - if _, err := os.Stat(caKeyPath); err == nil { - log.Fatalf("The CA key already exists. Please remove it or specify a different key/cert.") - } - - if err := utils.GenerateCACertificate(caCertPath, caKeyPath, org, bits); err != nil { - log.Infof("Error generating CA certificate: %s", err) - } +func getFirstArgHost(c *cli.Context) *host.Host { + store := getStore(c) + hostName := c.Args().First() + h, err := store.Load(hostName) + if err != nil { + // I guess I feel OK with bailing here since if we can't get + // the host reliably we're definitely not going to be able to + // do anything else interesting, but also this premature exit + // feels wrong to me. Let's revisit it later. + log.Fatalf("Error trying to get host %q: %s", hostName, err) } + return h +} - if _, err := os.Stat(clientCertPath); os.IsNotExist(err) { - log.Infof("Creating client certificate: %s", clientCertPath) - - if _, err := os.Stat(utils.GetMachineCertDir()); err != nil { - if os.IsNotExist(err) { - if err := os.Mkdir(utils.GetMachineCertDir(), 0700); err != nil { - log.Fatalf("Error creating machine client cert dir: %s", err) - } - } else { - log.Fatal(err) - } - } - - // check if the key path exists; if so, error - if _, err := os.Stat(clientKeyPath); err == nil { - log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.") - } +func getHostsFromContext(c *cli.Context) ([]*host.Host, error) { + store := getStore(c) + hosts := []*host.Host{} - if err := utils.GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caKeyPath, org, bits); err != nil { - log.Fatalf("Error generating client certificate: %s", err) + for _, hostName := range c.Args() { + h, err := store.Load(hostName) + if err != nil { + return nil, fmt.Errorf("Could not load host %q: %s", hostName, err) } + hosts = append(hosts, h) } - return nil + return hosts, nil } var sharedCreateFlags = []cli.Flag{ @@ -406,9 +351,21 @@ var Commands = []cli.Command{ }, } +func printIP(h *host.Host) func() error { + return func() error { + ip, err := h.Driver.GetIP() + if err != nil { + return fmt.Errorf("Error getting IP address: %s", err) + } + fmt.Println(ip) + return nil + } +} + // machineCommand maps the command name to the corresponding machine command. // We run commands concurrently and communicate back an error if there was one. -func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- error) { +func machineCommand(actionName string, host *host.Host, errorChan chan<- error) { + // TODO: These actions should have their own type. commands := map[string](func() error){ "configureAuth": host.ConfigureAuth, "start": host.Start, @@ -416,7 +373,7 @@ func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- e "restart": host.Restart, "kill": host.Kill, "upgrade": host.Upgrade, - "ip": host.PrintIP, + "ip": printIP(host), } log.Debugf("command=%s machine=%s", actionName, host.Name) @@ -430,10 +387,10 @@ func machineCommand(actionName string, host *libmachine.Host, errorChan chan<- e } // runActionForeachMachine will run the command across multiple machines -func runActionForeachMachine(actionName string, machines []*libmachine.Host) { +func runActionForeachMachine(actionName string, machines []*host.Host) { var ( numConcurrentActions = 0 - serialMachines = []*libmachine.Host{} + serialMachines = []*host.Host{} errorChan = make(chan error) ) @@ -458,7 +415,7 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) { serialChan := make(chan error) go machineCommand(actionName, machine, serialChan) if err := <-serialChan; err != nil { - log.Error(err) + log.Errorln(err) } close(serialChan) } @@ -468,7 +425,7 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) { // rate limit us. for i := 0; i < numConcurrentActions; i++ { if err := <-errorChan; err != nil { - log.Error(err) + log.Errorln(err) } } @@ -476,185 +433,59 @@ func runActionForeachMachine(actionName string, machines []*libmachine.Host) { } func runActionWithContext(actionName string, c *cli.Context) error { - machines, err := getHosts(c) + store := getStore(c) + + hosts, err := getHostsFromContext(c) if err != nil { return err } - if len(machines) == 0 { + if len(hosts) == 0 { log.Fatal(ErrNoMachineSpecified) } - runActionForeachMachine(actionName, machines) + runActionForeachMachine(actionName, hosts) - return nil -} - -func getHosts(c *cli.Context) ([]*libmachine.Host, error) { - machines := []*libmachine.Host{} - for _, n := range c.Args() { - machine, err := loadMachine(n, c) - if err != nil { - return nil, err + for _, h := range hosts { + if err := store.Save(h); err != nil { + return fmt.Errorf("Error saving host to store: %s", err) } - - machines = append(machines, machine) } - return machines, nil -} - -func loadMachine(name string, c *cli.Context) (*libmachine.Host, error) { - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - host, err := provider.Get(name) - if err != nil { - return nil, err - } - - return host, nil -} - -func getHost(c *cli.Context) *libmachine.Host { - name := c.Args().First() - - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - c.GlobalString("tls-ca-cert"), - c.GlobalString("tls-ca-key"), - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - host, err := provider.Get(name) - if err != nil { - log.Fatalf("unable to load host: %v", err) - } - return host -} - -func getDefaultProvider(c *cli.Context) *libmachine.Provider { - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - return provider -} - -func getMachineConfig(c *cli.Context) (*machineConfig, error) { - name := c.Args().First() - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - m, err := provider.Get(name) - if err != nil { - return nil, err - } - - machineDir := filepath.Join(utils.GetMachineDir(), m.Name) - caCert := filepath.Join(machineDir, "ca.pem") - caKey := filepath.Join(utils.GetMachineCertDir(), "ca-key.pem") - clientCert := filepath.Join(machineDir, "cert.pem") - clientKey := filepath.Join(machineDir, "key.pem") - serverCert := filepath.Join(machineDir, "server.pem") - serverKey := filepath.Join(machineDir, "server-key.pem") - machineUrl, err := m.GetURL() - if err != nil { - if err == drivers.ErrHostIsNotRunning { - machineUrl = "" - } else { - return nil, fmt.Errorf("Unexpected error getting machine url: %s", err) - } - } - return &machineConfig{ - machineName: name, - machineDir: machineDir, - machineUrl: machineUrl, - clientKeyPath: clientKey, - clientCertPath: clientCert, - serverCertPath: serverCert, - caKeyPath: caKey, - caCertPath: caCert, - serverKeyPath: serverKey, - AuthOptions: *m.HostOptions.AuthOptions, - SwarmOptions: *m.HostOptions.SwarmOptions, - }, nil + return nil } -// getCertPaths returns the cert paths -// 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) libmachine.CertPathInfo { - // setup cert paths +// Returns the cert paths. +// 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 getCertPathInfoFromContext(c *cli.Context) cert.CertPathInfo { caCertPath := c.GlobalString("tls-ca-cert") caKeyPath := c.GlobalString("tls-ca-key") clientCertPath := c.GlobalString("tls-client-cert") clientKeyPath := c.GlobalString("tls-client-key") if caCertPath == "" { - caCertPath = filepath.Join(utils.GetMachineCertDir(), "ca.pem") + caCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca.pem") } if caKeyPath == "" { - caKeyPath = filepath.Join(utils.GetMachineCertDir(), "ca-key.pem") + caKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "ca-key.pem") } if clientCertPath == "" { - clientCertPath = filepath.Join(utils.GetMachineCertDir(), "cert.pem") + clientCertPath = filepath.Join(mcndirs.GetMachineCertDir(), "cert.pem") } if clientKeyPath == "" { - clientKeyPath = filepath.Join(utils.GetMachineCertDir(), "key.pem") + clientKeyPath = filepath.Join(mcndirs.GetMachineCertDir(), "key.pem") } - return libmachine.CertPathInfo{ - CaCertPath: caCertPath, - CaKeyPath: caKeyPath, - ClientCertPath: clientCertPath, - ClientKeyPath: clientKeyPath, + return cert.CertPathInfo{ + CaCertPath: caCertPath, + CaPrivateKeyPath: caKeyPath, + ClientCertPath: clientCertPath, + ClientKeyPath: clientKeyPath, } } diff --git a/commands/commands_test.go b/commands/commands_test.go index 1cd184a343..26083e9491 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -1,112 +1,27 @@ package commands import ( - "fmt" - "io/ioutil" - "os" + "strings" "testing" "github.com/docker/machine/drivers/fakedriver" - _ "github.com/docker/machine/drivers/none" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/hosttest" + "github.com/docker/machine/libmachine/persisttest" + "github.com/docker/machine/libmachine/state" + "github.com/stretchr/testify/assert" ) -const ( - hostTestName = "test-host" - hostTestDriverName = "none" - hostTestCaCert = "test-cert" - hostTestPrivateKey = "test-key" -) - -var ( - hostTestStorePath string - TestStoreDir string -) - -func init() { - tmpDir, err := ioutil.TempDir("", "machine-test-") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - TestStoreDir = tmpDir -} - -func clearHosts() error { - return os.RemoveAll(TestStoreDir) -} - -func getTestStore() (libmachine.Store, error) { - tmpDir, err := ioutil.TempDir("", "machine-test-") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - hostTestStorePath = tmpDir - - os.Setenv("MACHINE_STORAGE_PATH", tmpDir) - - return libmachine.NewFilestore(tmpDir, hostTestCaCert, hostTestPrivateKey), nil -} - -func cleanup() { - os.RemoveAll(hostTestStorePath) -} - -func getTestDriverFlags() *DriverOptionsMock { - name := hostTestName - flags := &DriverOptionsMock{ - Data: map[string]interface{}{ - "name": name, - "url": "unix:///var/run/docker.sock", - "swarm": false, - "swarm-host": "", - "swarm-master": false, - "swarm-discovery": "", - }, - } - return flags -} - -type DriverOptionsMock struct { - Data map[string]interface{} -} - -func (d DriverOptionsMock) String(key string) string { - return d.Data[key].(string) -} - -func (d DriverOptionsMock) StringSlice(key string) []string { - return d.Data[key].([]string) -} - -func (d DriverOptionsMock) Int(key string) int { - return d.Data[key].(int) -} - -func (d DriverOptionsMock) Bool(key string) bool { - return d.Data[key].(bool) -} - func TestRunActionForeachMachine(t *testing.T) { - storePath, err := ioutil.TempDir("", ".docker") - if err != nil { - t.Fatal("Error creating tmp dir:", err) - } - // Assume a bunch of machines in randomly started or // stopped states. - machines := []*libmachine.Host{ + machines := []*host.Host{ { Name: "foo", DriverName: "fakedriver", Driver: &fakedriver.FakeDriver{ MockState: state.Running, }, - StorePath: storePath, }, { Name: "bar", @@ -114,7 +29,6 @@ func TestRunActionForeachMachine(t *testing.T) { Driver: &fakedriver.FakeDriver{ MockState: state.Stopped, }, - StorePath: storePath, }, { Name: "baz", @@ -126,7 +40,6 @@ func TestRunActionForeachMachine(t *testing.T) { Driver: &fakedriver.FakeDriver{ MockState: state.Stopped, }, - StorePath: storePath, }, { Name: "spam", @@ -134,7 +47,6 @@ func TestRunActionForeachMachine(t *testing.T) { Driver: &fakedriver.FakeDriver{ MockState: state.Running, }, - StorePath: storePath, }, { Name: "eggs", @@ -142,7 +54,6 @@ func TestRunActionForeachMachine(t *testing.T) { Driver: &fakedriver.FakeDriver{ MockState: state.Stopped, }, - StorePath: storePath, }, { Name: "ham", @@ -150,7 +61,6 @@ func TestRunActionForeachMachine(t *testing.T) { Driver: &fakedriver.FakeDriver{ MockState: state.Running, }, - StorePath: storePath, }, } @@ -191,3 +101,29 @@ func TestRunActionForeachMachine(t *testing.T) { } } } + +func TestPrintIPEmptyGivenLocalEngine(t *testing.T) { + defer persisttest.Cleanup() + host, _ := hosttest.GetDefaultTestHost() + + out, w := captureStdout() + + assert.Nil(t, printIP(host)()) + w.Close() + + assert.Equal(t, "", strings.TrimSpace(<-out)) +} + +func TestPrintIPPrintsGivenRemoteEngine(t *testing.T) { + defer cleanup() + host, _ := hosttest.GetDefaultTestHost() + host.Driver = &fakedriver.FakeDriver{} + + out, w := captureStdout() + + assert.Nil(t, printIP(host)()) + + w.Close() + + assert.Equal(t, "1.2.3.4", strings.TrimSpace(<-out)) +} diff --git a/commands/config.go b/commands/config.go index a3d0e71e38..c6b5d3d49f 100644 --- a/commands/config.go +++ b/commands/config.go @@ -6,74 +6,116 @@ import ( "strings" "github.com/codegangsta/cli" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/cert" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/state" ) func cmdConfig(c *cli.Context) { if len(c.Args()) != 1 { log.Fatal(ErrExpectedOneMachine) } - cfg, err := getMachineConfig(c) + + h := getFirstArgHost(c) + + dockerHost, authOptions, err := runConnectionBoilerplate(h, c) if err != nil { - log.Fatal(err) + log.Fatalf("Error running connection boilerplate: %s", err) } - dockerHost, err := getHost(c).Driver.GetURL() + log.Debug(dockerHost) + + fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s", + authOptions.CaCertPath, authOptions.ClientCertPath, authOptions.ClientKeyPath, dockerHost) +} + +func runConnectionBoilerplate(h *host.Host, c *cli.Context) (string, *auth.AuthOptions, error) { + hostState, err := h.Driver.GetState() if err != nil { - log.Fatal(err) + // TODO: This is a common operation and should have a commonly + // defined error. + return "", &auth.AuthOptions{}, fmt.Errorf("Error trying to get host state: %s", err) + } + if hostState != state.Running { + return "", &auth.AuthOptions{}, fmt.Errorf("%s is not running. Please start it in order to use the connection settings.", h.Name) + } + + dockerHost, err := h.Driver.GetURL() + if err != nil { + return "", &auth.AuthOptions{}, fmt.Errorf("Error getting driver URL: %s", err) } if c.Bool("swarm") { - if !cfg.SwarmOptions.Master { - log.Fatalf("%s is not a swarm master", cfg.machineName) - } - u, err := url.Parse(cfg.SwarmOptions.Host) + var err error + dockerHost, err = parseSwarm(dockerHost, h) if err != nil { - log.Fatal(err) + return "", &auth.AuthOptions{}, fmt.Errorf("Error parsing swarm: %s", err) } - parts := strings.Split(u.Host, ":") - swarmPort := parts[1] + } - // get IP of machine to replace in case swarm host is 0.0.0.0 - mUrl, err := url.Parse(dockerHost) - if err != nil { - log.Fatal(err) - } - mParts := strings.Split(mUrl.Host, ":") - machineIp := mParts[0] + u, err := url.Parse(dockerHost) + if err != nil { + return "", &auth.AuthOptions{}, fmt.Errorf("Error parsing URL: %s", err) + } + + authOptions := h.HostOptions.AuthOptions - dockerHost = fmt.Sprintf("tcp://%s:%s\n", machineIp, swarmPort) + if err := checkCert(u.Host, authOptions, c); err != nil { + return "", &auth.AuthOptions{}, fmt.Errorf("Error checking and/or regenerating the certs: %s", err) } - log.Debug(dockerHost) + return dockerHost, authOptions, nil +} - u, err := url.Parse(cfg.machineUrl) +func checkCert(hostUrl string, authOptions *auth.AuthOptions, c *cli.Context) error { + valid, err := cert.ValidateCertificate( + hostUrl, + authOptions.CaCertPath, + authOptions.ServerCertPath, + authOptions.ServerKeyPath, + ) if err != nil { - log.Fatal(err) + return fmt.Errorf("Error attempting to validate the certficate: %s", err) } - if u.Scheme != "unix" { - // validate cert and regenerate if needed - valid, err := utils.ValidateCertificate( - u.Host, - cfg.caCertPath, - cfg.serverCertPath, - cfg.serverKeyPath, - ) - if err != nil { - log.Fatal(err) + if !valid { + log.Errorf("Invalid certs detected; regenerating for %s", hostUrl) + + if err := runActionWithContext("configureAuth", c); err != nil { + return fmt.Errorf("Error attempting to regenerate the certs: %s", err) } + } + + return nil +} - if !valid { - log.Debugf("invalid certs detected; regenerating for %s", u.Host) +// TODO: This could use a unit test. +func parseSwarm(hostUrl string, h *host.Host) (string, error) { + swarmOptions := h.HostOptions.SwarmOptions - if err := runActionWithContext("configureAuth", c); err != nil { - log.Fatal(err) - } - } + if !swarmOptions.Master { + return "", fmt.Errorf("Error: %s is not a swarm master. The --swarm flag is intended for use with swarm masters.", h.Name) } - fmt.Printf("--tlsverify --tlscacert=%q --tlscert=%q --tlskey=%q -H=%s\n", - cfg.caCertPath, cfg.clientCertPath, cfg.clientKeyPath, dockerHost) + u, err := url.Parse(swarmOptions.Host) + if err != nil { + return "", fmt.Errorf("There was an error parsing the url: %s", err) + } + parts := strings.Split(u.Host, ":") + swarmPort := parts[1] + + // get IP of machine to replace in case swarm host is 0.0.0.0 + mUrl, err := url.Parse(hostUrl) + if err != nil { + return "", fmt.Errorf("There was an error parsing the url: %s", err) + } + + mParts := strings.Split(mUrl.Host, ":") + machineIp := mParts[0] + + hostUrl = fmt.Sprintf("tcp://%s:%s", machineIp, swarmPort) + + return hostUrl, nil } diff --git a/commands/config_test.go b/commands/config_test.go deleted file mode 100644 index b24b5269ad..0000000000 --- a/commands/config_test.go +++ /dev/null @@ -1,104 +0,0 @@ -package commands - -import ( - "bytes" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/codegangsta/cli" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/libmachine/auth" - "github.com/docker/machine/libmachine/engine" - "github.com/docker/machine/libmachine/swarm" -) - -func TestCmdConfig(t *testing.T) { - defer cleanup() - - stdout := os.Stdout - r, w, _ := os.Pipe() - - os.Stdout = w - - defer func() { - os.Stdout = stdout - }() - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - provider, err := libmachine.New(store) - if err != nil { - t.Fatal(err) - } - - flags := getTestDriverFlags() - hostOptions := &libmachine.HostOptions{ - EngineOptions: &engine.EngineOptions{}, - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Discovery: "", - Address: "", - Host: "", - }, - AuthOptions: &auth.AuthOptions{}, - } - - host, err := provider.Create("test-a", "none", hostOptions, flags) - if err != nil { - t.Fatal(err) - } - - outStr := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - outStr <- testOutput.String() - }() - - set := flag.NewFlagSet("config", 0) - set.Parse([]string{"test-a"}) - globalSet := flag.NewFlagSet("test", 0) - globalSet.String("storage-path", store.GetPath(), "") - - c := cli.NewContext(nil, set, globalSet) - - cmdConfig(c) - - w.Close() - - out := <-outStr - - if !strings.Contains(out, "--tlsverify") { - t.Fatalf("Expect --tlsverify") - } - - testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name) - - tlscacert := fmt.Sprintf("--tlscacert=\"%s/ca.pem\"", testMachineDir) - if !strings.Contains(out, tlscacert) { - t.Fatalf("Expected to find %s in %s", tlscacert, out) - } - - tlscert := fmt.Sprintf("--tlscert=\"%s/cert.pem\"", testMachineDir) - if !strings.Contains(out, tlscert) { - t.Fatalf("Expected to find %s in %s", tlscert, out) - } - - tlskey := fmt.Sprintf("--tlskey=\"%s/key.pem\"", testMachineDir) - if !strings.Contains(out, tlskey) { - t.Fatalf("Expected to find %s in %s", tlskey, out) - } - - if !strings.Contains(out, "-H=unix:///var/run/docker.sock") { - t.Fatalf("Expect docker host URL") - } -} diff --git a/commands/create.go b/commands/create.go index 5d9416b939..d6f0e54875 100644 --- a/commands/create.go +++ b/commands/create.go @@ -1,31 +1,51 @@ package commands import ( + "errors" "fmt" + "os" "path/filepath" "regexp" - "github.com/docker/machine/log" - "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" + "github.com/docker/machine/commands/mcndirs" + "github.com/docker/machine/drivers/driverfactory" "github.com/docker/machine/libmachine" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnerror" + "github.com/docker/machine/libmachine/persist" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/utils" +) + +var ( + ErrDriverNotRecognized = errors.New("Driver not recognized.") ) func cmdCreate(c *cli.Context) { var ( - err error + driver drivers.Driver ) - driver := c.String("driver") + + driverName := c.String("driver") name := c.Args().First() + certInfo := getCertPathInfoFromContext(c) + + storePath := c.GlobalString("storage-path") + store := &persist.Filestore{ + Path: storePath, + CaCertPath: certInfo.CaCertPath, + CaPrivateKeyPath: certInfo.CaPrivateKeyPath, + } // TODO: Not really a fan of "none" as the default driver... - if driver != "none" { - c.App.Commands, err = trimDriverFlags(driver, c.App.Commands) + if driverName != "none" { + var err error + + c.App.Commands, err = trimDriverFlags(driverName, c.App.Commands) if err != nil { log.Fatal(err) } @@ -40,38 +60,16 @@ func cmdCreate(c *cli.Context) { log.Fatalf("Error parsing swarm discovery: %s", err) } - certInfo := getCertPathInfo(c) - - if err := setupCertificates( - certInfo.CaCertPath, - certInfo.CaKeyPath, - certInfo.ClientCertPath, - certInfo.ClientKeyPath); err != nil { - log.Fatalf("Error generating certificates: %s", err) - } - - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - hostOptions := &libmachine.HostOptions{ + hostOptions := &host.HostOptions{ 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"), + CertDir: mcndirs.GetMachineCertDir(), + CaCertPath: certInfo.CaCertPath, + CaPrivateKeyPath: certInfo.CaPrivateKeyPath, + ClientCertPath: certInfo.ClientCertPath, + ClientKeyPath: certInfo.ClientKeyPath, + ServerCertPath: filepath.Join(mcndirs.GetMachineDir(), name, "server.pem"), + ServerKeyPath: filepath.Join(mcndirs.GetMachineDir(), name, "server-key.pem"), + StorePath: filepath.Join(mcndirs.GetMachineDir(), name), }, EngineOptions: &engine.EngineOptions{ ArbitraryFlags: c.StringSlice("engine-opt"), @@ -95,13 +93,37 @@ func cmdCreate(c *cli.Context) { }, } - _, err = provider.Create(name, driver, hostOptions, c) + driver, err := driverfactory.NewDriver(driverName, name, storePath) + if err != nil { + log.Fatalf("Error trying to get driver: %s", err) + } + + h, err := store.NewHost(driver) + if err != nil { + log.Fatalf("Error getting new host: %s", err) + } + + h.HostOptions = hostOptions + + exists, err := store.Exists(h.Name) if err != nil { - log.Errorf("Error creating machine: %s", err) - log.Fatal("You will want to check the provider to make sure the machine and associated resources were properly removed.") + log.Fatalf("Error checking if host exists: %s", err) + } + if exists { + log.Fatal(mcnerror.ErrHostAlreadyExists{h.Name}) + } + + // TODO: This should be moved out of the driver and done in the + // commands module. + if err := h.Driver.SetConfigFromFlags(c); err != nil { + log.Fatalf("Error setting machine configuration from flags provided: %s", err) + } + + if err := libmachine.Create(store, h); err != nil { + log.Fatal(err) } - info := fmt.Sprintf("%s env %s", c.App.Name, name) + info := fmt.Sprintf("%s env %s", os.Args[0], name) log.Infof("To see how to connect Docker to this machine, run: %s", info) } diff --git a/commands/env.go b/commands/env.go index 66ae77be7e..e15dbe1369 100644 --- a/commands/env.go +++ b/commands/env.go @@ -3,15 +3,11 @@ package commands import ( "errors" "fmt" - "net/url" "os" - "strings" "text/template" - "github.com/docker/machine/log" - "github.com/codegangsta/cli" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/log" ) const ( @@ -37,6 +33,14 @@ func cmdEnv(c *cli.Context) { if len(c.Args()) != 1 && !c.Bool("unset") { log.Fatal(improperEnvArgsError) } + + h := getFirstArgHost(c) + + dockerHost, authOptions, err := runConnectionBoilerplate(h, c) + if err != nil { + log.Fatalf("Error running connection boilerplate: %s", err) + } + userShell := c.String("shell") if userShell == "" { shell, err := detectShell() @@ -93,70 +97,12 @@ func cmdEnv(c *cli.Context) { return } - cfg, err := getMachineConfig(c) - if err != nil { - log.Fatal(err) - } - - if cfg.machineUrl == "" { - log.Fatalf("%s is not running. Please start this with %s start %s", cfg.machineName, c.App.Name, cfg.machineName) - } - - dockerHost := cfg.machineUrl - if c.Bool("swarm") { - if !cfg.SwarmOptions.Master { - log.Fatalf("%s is not a swarm master", cfg.machineName) - } - u, err := url.Parse(cfg.SwarmOptions.Host) - if err != nil { - log.Fatal(err) - } - parts := strings.Split(u.Host, ":") - swarmPort := parts[1] - - // get IP of machine to replace in case swarm host is 0.0.0.0 - mUrl, err := url.Parse(cfg.machineUrl) - if err != nil { - log.Fatal(err) - } - mParts := strings.Split(mUrl.Host, ":") - machineIp := mParts[0] - - dockerHost = fmt.Sprintf("tcp://%s:%s", machineIp, swarmPort) - } - - u, err := url.Parse(cfg.machineUrl) - if err != nil { - log.Fatal(err) - } - - if u.Scheme != "unix" { - // validate cert and regenerate if needed - valid, err := utils.ValidateCertificate( - u.Host, - cfg.caCertPath, - cfg.serverCertPath, - cfg.serverKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - if !valid { - log.Debugf("invalid certs detected; regenerating for %s", u.Host) - - if err := runActionWithContext("configureAuth", c); err != nil { - log.Fatal(err) - } - } - } - shellCfg = ShellConfig{ - DockerCertPath: cfg.machineDir, + DockerCertPath: authOptions.CertDir, DockerHost: dockerHost, DockerTLSVerify: "1", UsageHint: usageHint, - MachineName: cfg.machineName, + MachineName: h.Name, } switch userShell { diff --git a/commands/env_test.go b/commands/env_test.go deleted file mode 100644 index 0749579ef8..0000000000 --- a/commands/env_test.go +++ /dev/null @@ -1,316 +0,0 @@ -package commands - -import ( - "bytes" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/codegangsta/cli" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/libmachine/auth" - "github.com/docker/machine/libmachine/engine" - "github.com/docker/machine/libmachine/swarm" -) - -func TestCmdEnvBash(t *testing.T) { - stdout := os.Stdout - shell := os.Getenv("SHELL") - r, w, _ := os.Pipe() - - os.Stdout = w - os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir) - os.Setenv("SHELL", "/bin/bash") - - defer func() { - os.Setenv("MACHINE_STORAGE_PATH", "") - os.Setenv("SHELL", shell) - os.Stdout = stdout - }() - - if err := clearHosts(); err != nil { - t.Fatal(err) - } - - flags := getTestDriverFlags() - - store, sErr := getTestStore() - if sErr != nil { - t.Fatal(sErr) - } - - provider, err := libmachine.New(store) - if err != nil { - t.Fatal(err) - } - - hostOptions := &libmachine.HostOptions{ - EngineOptions: &engine.EngineOptions{}, - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Discovery: "", - Address: "", - Host: "", - }, - AuthOptions: &auth.AuthOptions{}, - } - - host, err := provider.Create("test-a", "none", hostOptions, flags) - if err != nil { - t.Fatal(err) - } - - host, err = provider.Get("test-a") - if err != nil { - t.Fatalf("error loading host: %v", err) - } - - outStr := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - outStr <- testOutput.String() - }() - - set := flag.NewFlagSet("config", 0) - set.Parse([]string{"test-a"}) - c := cli.NewContext(nil, set, set) - c.App = &cli.App{ - Name: "docker-machine-test", - } - cmdEnv(c) - - w.Close() - - out := <-outStr - - // parse the output into a map of envvar:value for easier testing below - envvars := make(map[string]string) - for _, e := range strings.Split(strings.TrimSpace(out), "\n") { - if !strings.HasPrefix(e, "export ") { - continue - } - kv := strings.SplitN(e, "=", 2) - key, value := kv[0], kv[1] - envvars[strings.Replace(key, "export ", "", 1)] = value - } - - testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name) - - expected := map[string]string{ - "DOCKER_TLS_VERIFY": "\"1\"", - "DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir), - "DOCKER_HOST": "\"unix:///var/run/docker.sock\"", - "DOCKER_MACHINE_NAME": `"test-a"`, - } - - for k, v := range envvars { - if v != expected[k] { - t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v) - } - } -} - -func TestCmdEnvFish(t *testing.T) { - stdout := os.Stdout - shell := os.Getenv("SHELL") - r, w, _ := os.Pipe() - - os.Stdout = w - os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir) - os.Setenv("SHELL", "/bin/fish") - - defer func() { - os.Setenv("MACHINE_STORAGE_PATH", "") - os.Setenv("SHELL", shell) - os.Stdout = stdout - }() - - if err := clearHosts(); err != nil { - t.Fatal(err) - } - - flags := getTestDriverFlags() - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - provider, err := libmachine.New(store) - if err != nil { - t.Fatal(err) - } - - hostOptions := &libmachine.HostOptions{ - EngineOptions: &engine.EngineOptions{}, - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Discovery: "", - Address: "", - Host: "", - }, - AuthOptions: &auth.AuthOptions{}, - } - - host, err := provider.Create("test-a", "none", hostOptions, flags) - if err != nil { - t.Fatal(err) - } - - host, err = provider.Get("test-a") - if err != nil { - t.Fatalf("error loading host: %v", err) - } - - outStr := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - outStr <- testOutput.String() - }() - - set := flag.NewFlagSet("config", 0) - set.Parse([]string{"test-a"}) - c := cli.NewContext(nil, set, set) - c.App = &cli.App{ - Name: "docker-machine-test", - } - cmdEnv(c) - - w.Close() - - out := <-outStr - - // parse the output into a map of envvar:value for easier testing below - envvars := make(map[string]string) - for _, e := range strings.Split(strings.TrimSuffix(out, ";\n"), ";\n") { - if !strings.HasPrefix(e, "set -x ") { - continue - } - kv := strings.SplitN(strings.Replace(e, "set -x ", "", 1), " ", 2) - key, value := kv[0], kv[1] - envvars[key] = value - } - - testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name) - - expected := map[string]string{ - "DOCKER_TLS_VERIFY": "\"1\"", - "DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir), - "DOCKER_HOST": "\"unix:///var/run/docker.sock\"", - "DOCKER_MACHINE_NAME": `"test-a"`, - } - - for k, v := range envvars { - if v != expected[k] { - t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v) - } - } -} - -func TestCmdEnvPowerShell(t *testing.T) { - stdout := os.Stdout - shell := os.Getenv("SHELL") - r, w, _ := os.Pipe() - - os.Stdout = w - os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir) - os.Setenv("SHELL", "") - - defer func() { - os.Setenv("MACHINE_STORAGE_PATH", "") - os.Setenv("SHELL", shell) - os.Stdout = stdout - }() - - if err := clearHosts(); err != nil { - t.Fatal(err) - } - - flags := getTestDriverFlags() - - store, sErr := getTestStore() - if sErr != nil { - t.Fatal(sErr) - } - - provider, err := libmachine.New(store) - if err != nil { - t.Fatal(err) - } - - hostOptions := &libmachine.HostOptions{ - EngineOptions: &engine.EngineOptions{}, - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Discovery: "", - Address: "", - Host: "", - }, - AuthOptions: &auth.AuthOptions{}, - } - - host, err := provider.Create("test-a", "none", hostOptions, flags) - if err != nil { - t.Fatal(err) - } - - host, err = provider.Get("test-a") - if err != nil { - t.Fatalf("error loading host: %v", err) - } - - outStr := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - outStr <- testOutput.String() - }() - - set := flag.NewFlagSet("config", 0) - set.Parse([]string{"test-a"}) - set.String("shell", "powershell", "") - c := cli.NewContext(nil, set, set) - c.App = &cli.App{ - Name: "docker-machine-test", - } - cmdEnv(c) - - w.Close() - - out := <-outStr - - // parse the output into a map of envvar:value for easier testing below - envvars := make(map[string]string) - for _, e := range strings.Split(strings.TrimSpace(out), "\n") { - if !strings.HasPrefix(e, "$Env") { - continue - } - kv := strings.SplitN(e, " = ", 2) - key, value := kv[0], kv[1] - envvars[strings.Replace(key, "$Env:", "", 1)] = value - } - - testMachineDir := filepath.Join(store.GetPath(), "machines", host.Name) - - expected := map[string]string{ - "DOCKER_TLS_VERIFY": "\"1\"", - "DOCKER_CERT_PATH": fmt.Sprintf("\"%s\"", testMachineDir), - "DOCKER_HOST": "\"unix:///var/run/docker.sock\"", - "DOCKER_MACHINE_NAME": `"test-a"`, - } - - for k, v := range envvars { - if v != expected[k] { - t.Fatalf("Expected %s == <%s>, but was <%s>", k, expected[k], v) - } - } -} diff --git a/commands/inspect.go b/commands/inspect.go index 0759f136a7..0ce219507a 100644 --- a/commands/inspect.go +++ b/commands/inspect.go @@ -6,7 +6,7 @@ import ( "os" "text/template" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) @@ -31,7 +31,7 @@ func cmdInspect(c *cli.Context) { log.Fatalf("Template parsing error: %v\n", err) } - jsonHost, err := json.Marshal(getHost(c)) + jsonHost, err := json.Marshal(getFirstArgHost(c)) if err != nil { log.Fatal(err) } @@ -45,7 +45,7 @@ func cmdInspect(c *cli.Context) { } os.Stdout.Write([]byte{'\n'}) } else { - prettyJSON, err := json.MarshalIndent(getHost(c), "", " ") + prettyJSON, err := json.MarshalIndent(getFirstArgHost(c), "", " ") if err != nil { log.Fatal(err) } diff --git a/commands/inspect_test.go b/commands/inspect_test.go deleted file mode 100644 index 779095d5ca..0000000000 --- a/commands/inspect_test.go +++ /dev/null @@ -1,117 +0,0 @@ -package commands - -import ( - "bytes" - "encoding/json" - "flag" - "io" - "os" - "strings" - "testing" - - "github.com/codegangsta/cli" - "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/stretchr/testify/assert" -) - -func TestCmdInspectFormat(t *testing.T) { - actual, host := runInspectCommand(t, []string{"test-a"}) - expected, _ := json.MarshalIndent(host, "", " ") - assert.Equal(t, string(expected), actual) - - actual, _ = runInspectCommand(t, []string{"--format", "{{.DriverName}}", "test-a"}) - assert.Equal(t, "none", actual) - - actual, _ = runInspectCommand(t, []string{"--format", "{{json .DriverName}}", "test-a"}) - assert.Equal(t, "\"none\"", actual) - - actual, _ = runInspectCommand(t, []string{"--format", "{{prettyjson .Driver}}", "test-a"}) - type ExpectedDriver struct { - CaCertPath string - IPAddress string - MachineName string - PrivateKeyPath string - SSHPort int - SSHUser string - SwarmDiscovery string - SwarmHost string - SwarmMaster bool - URL string - } - expected, err := json.MarshalIndent(&ExpectedDriver{MachineName: "test-a", URL: "unix:///var/run/docker.sock"}, "", " ") - assert.NoError(t, err) - assert.Equal(t, string(expected), actual) -} - -func runInspectCommand(t *testing.T, args []string) (string, *libmachine.Host) { - stdout := os.Stdout - stderr := os.Stderr - shell := os.Getenv("SHELL") - r, w, _ := os.Pipe() - - os.Stdout = w - os.Stderr = w - os.Setenv("MACHINE_STORAGE_PATH", TestStoreDir) - os.Setenv("SHELL", "/bin/bash") - - defer func() { - os.Setenv("MACHINE_STORAGE_PATH", "") - os.Setenv("SHELL", shell) - os.Stdout = stdout - os.Stderr = stderr - }() - - if err := clearHosts(); err != nil { - t.Fatal(err) - } - - store, sErr := getTestStore() - if sErr != nil { - t.Fatal(sErr) - } - - provider, err := libmachine.New(store) - if err != nil { - t.Fatal(err) - } - - hostOptions := &libmachine.HostOptions{ - EngineOptions: &engine.EngineOptions{}, - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Discovery: "", - Address: "", - Host: "", - }, - AuthOptions: &auth.AuthOptions{}, - } - - flags := getTestDriverFlags() - _, err = provider.Create("test-a", "none", hostOptions, flags) - if err != nil { - t.Fatal(err) - } - - outStr := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - outStr <- testOutput.String() - }() - - set := flag.NewFlagSet("inspect", 0) - set.String("format", "", "") - set.Parse(args) - c := cli.NewContext(nil, set, set) - cmdInspect(c) - - w.Close() - - out := <-outStr - - return strings.TrimSpace(out), getHost(c) -} diff --git a/commands/ip.go b/commands/ip.go index 5a8f68aece..d60194dc0e 100644 --- a/commands/ip.go +++ b/commands/ip.go @@ -2,7 +2,7 @@ package commands import ( "github.com/codegangsta/cli" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) func cmdIp(c *cli.Context) { diff --git a/commands/ip_test.go b/commands/ip_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/ip_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/kill.go b/commands/kill.go index 607b3475b8..43c13f523a 100644 --- a/commands/kill.go +++ b/commands/kill.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) diff --git a/commands/kill_test.go b/commands/kill_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/kill_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/ls.go b/commands/ls.go index 0567b54a17..372559c017 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -5,12 +5,23 @@ import ( "fmt" "os" "regexp" + "sort" "strings" "text/tabwriter" + "time" "github.com/codegangsta/cli" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/persist" + "github.com/docker/machine/libmachine/state" + "github.com/docker/machine/libmachine/swarm" + "github.com/skarademir/naturalsort" +) + +var ( + stateTimeoutDuration = 10 * time.Second ) // FilterOptions - @@ -21,6 +32,15 @@ type FilterOptions struct { Name []string } +type HostListItem struct { + Name string + Active bool + DriverName string + State state.State + URL string + SwarmOptions *swarm.SwarmOptions +} + func cmdLs(c *cli.Context) { quiet := c.Bool("quiet") filters, err := parseFilters(c.StringSlice("filter")) @@ -28,8 +48,8 @@ func cmdLs(c *cli.Context) { log.Fatal(err) } - provider := getDefaultProvider(c) - hostList, err := provider.List() + store := getStore(c) + hostList, err := store.List() if err != nil { log.Fatal(err) } @@ -61,7 +81,7 @@ func cmdLs(c *cli.Context) { } } - items := libmachine.GetHostListItems(hostList) + items := getHostListItems(hostList) sortHostListItemsByName(items) @@ -111,7 +131,7 @@ func parseFilters(filters []string) (FilterOptions, error) { return options, nil } -func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine.Host { +func filterHosts(hosts []*host.Host, filters FilterOptions) []*host.Host { if len(filters.SwarmName) == 0 && len(filters.DriverName) == 0 && len(filters.State) == 0 && @@ -119,7 +139,7 @@ func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine. return hosts } - filteredHosts := []*libmachine.Host{} + filteredHosts := []*host.Host{} swarmMasters := getSwarmMasters(hosts) for _, h := range hosts { @@ -130,7 +150,7 @@ func filterHosts(hosts []*libmachine.Host, filters FilterOptions) []*libmachine. return filteredHosts } -func getSwarmMasters(hosts []*libmachine.Host) map[string]string { +func getSwarmMasters(hosts []*host.Host) map[string]string { swarmMasters := make(map[string]string) for _, h := range hosts { swarmOptions := h.HostOptions.SwarmOptions @@ -141,7 +161,7 @@ func getSwarmMasters(hosts []*libmachine.Host) map[string]string { return swarmMasters } -func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[string]string) bool { +func filterHost(host *host.Host, filters FilterOptions, swarmMasters map[string]string) bool { swarmMatches := matchesSwarmName(host, filters.SwarmName, swarmMasters) driverMatches := matchesDriverName(host, filters.DriverName) stateMatches := matchesState(host, filters.State) @@ -150,7 +170,7 @@ func filterHost(host *libmachine.Host, filters FilterOptions, swarmMasters map[s return swarmMatches && driverMatches && stateMatches && nameMatches } -func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters map[string]string) bool { +func matchesSwarmName(host *host.Host, swarmNames []string, swarmMasters map[string]string) bool { if len(swarmNames) == 0 { return true } @@ -164,7 +184,7 @@ func matchesSwarmName(host *libmachine.Host, swarmNames []string, swarmMasters m return false } -func matchesDriverName(host *libmachine.Host, driverNames []string) bool { +func matchesDriverName(host *host.Host, driverNames []string) bool { if len(driverNames) == 0 { return true } @@ -176,7 +196,7 @@ func matchesDriverName(host *libmachine.Host, driverNames []string) bool { return false } -func matchesState(host *libmachine.Host, states []string) bool { +func matchesState(host *host.Host, states []string) bool { if len(states) == 0 { return true } @@ -192,7 +212,7 @@ func matchesState(host *libmachine.Host, states []string) bool { return false } -func matchesName(host *libmachine.Host, names []string) bool { +func matchesName(host *host.Host, names []string) bool { if len(names) == 0 { return true } @@ -207,3 +227,139 @@ func matchesName(host *libmachine.Host, names []string) bool { } return false } + +func getActiveHost(store persist.Store) (*host.Host, error) { + hosts, err := store.List() + if err != nil { + return nil, err + } + + hostListItems := getHostListItems(hosts) + + for _, item := range hostListItems { + if item.Active { + h, err := store.Load(item.Name) + if err != nil { + return nil, err + } + return h, nil + } + } + + return nil, errors.New("Active host not found") +} + +func attemptGetHostState(h *host.Host, stateQueryChan chan<- HostListItem) { + currentState, err := h.Driver.GetState() + if err != nil { + log.Errorf("error getting state for host %s: %s", h.Name, err) + } + + url, err := h.GetURL() + if err != nil { + if err == drivers.ErrHostIsNotRunning { + url = "" + } else { + log.Errorf("error getting URL for host %s: %s", h.Name, err) + } + } + + active, err := isActive(h) + if err != nil { + log.Errorf("error determining if host is active for host %s: %s", + h.Name, err) + } + + stateQueryChan <- HostListItem{ + Name: h.Name, + Active: active, + DriverName: h.Driver.DriverName(), + State: currentState, + URL: url, + SwarmOptions: h.HostOptions.SwarmOptions, + } +} + +func getHostState(h *host.Host, hostListItemsChan chan<- HostListItem) { + // This channel is used to communicate the properties we are querying + // about the host in the case of a successful read. + stateQueryChan := make(chan HostListItem) + + go attemptGetHostState(h, stateQueryChan) + + select { + // If we get back useful information, great. Forward it straight to + // the original parent channel. + case hli := <-stateQueryChan: + hostListItemsChan <- hli + + // Otherwise, give up after a predetermined duration. + case <-time.After(stateTimeoutDuration): + hostListItemsChan <- HostListItem{ + Name: h.Name, + DriverName: h.Driver.DriverName(), + State: state.Timeout, + } + } +} + +func getHostListItems(hostList []*host.Host) []HostListItem { + hostListItems := []HostListItem{} + hostListItemsChan := make(chan HostListItem) + + for _, h := range hostList { + go getHostState(h, hostListItemsChan) + } + + for range hostList { + hostListItems = append(hostListItems, <-hostListItemsChan) + } + + close(hostListItemsChan) + return hostListItems +} + +// IsActive provides a single function for determining if a host is active +// based on both the url and if the host is stopped. +func isActive(h *host.Host) (bool, error) { + currentState, err := h.Driver.GetState() + + if err != nil { + log.Errorf("error getting state for host %s: %s", h.Name, err) + return false, err + } + + url, err := h.GetURL() + + if err != nil { + if err == drivers.ErrHostIsNotRunning { + url = "" + } else { + log.Errorf("error getting URL for host %s: %s", h.Name, err) + return false, err + } + } + + dockerHost := os.Getenv("DOCKER_HOST") + + notStopped := currentState != state.Stopped + correctURL := url == dockerHost + + isActive := notStopped && correctURL + + return isActive, nil +} + +func sortHostListItemsByName(items []HostListItem) { + m := make(map[string]HostListItem, len(items)) + s := make([]string, len(items)) + for i, v := range items { + name := strings.ToLower(v.Name) + m[name] = v + s[i] = name + } + sort.Sort(naturalsort.NaturalSort(s)) + for i, v := range s { + items[i] = m[v] + } +} diff --git a/commands/ls_test.go b/commands/ls_test.go index becdad3497..35e27f2f5e 100644 --- a/commands/ls_test.go +++ b/commands/ls_test.go @@ -1,15 +1,32 @@ package commands import ( + "bytes" + "io" + "os" "testing" "github.com/docker/machine/drivers/fakedriver" - "github.com/docker/machine/libmachine" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/state" "github.com/stretchr/testify/assert" ) +var ( + hostTestStorePath string + stdout *os.File +) + +func init() { + stdout = os.Stdout +} + +func cleanup() { + os.Stdout = stdout + os.RemoveAll(hostTestStorePath) +} + func TestParseFiltersErrorsGivenInvalidFilter(t *testing.T) { _, err := parseFilters([]string{"foo=bar"}) assert.EqualError(t, err, "Unsupported filter key 'foo'") @@ -52,11 +69,11 @@ func TestParseFiltersValueWithEqual(t *testing.T) { func TestFilterHostsReturnsSameGivenNoFilters(t *testing.T) { opts := FilterOptions{} - hosts := []*libmachine.Host{ + hosts := []*host.Host{ { Name: "testhost", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, }, } actual := filterHosts(hosts, opts) @@ -67,7 +84,7 @@ func TestFilterHostsReturnsEmptyGivenEmptyHosts(t *testing.T) { opts := FilterOptions{ SwarmName: []string{"foo"}, } - hosts := []*libmachine.Host{} + hosts := []*host.Host{} assert.Empty(t, filterHosts(hosts, opts)) } @@ -75,11 +92,11 @@ func TestFilterHostsReturnsEmptyGivenNonMatchingFilters(t *testing.T) { opts := FilterOptions{ SwarmName: []string{"foo"}, } - hosts := []*libmachine.Host{ + hosts := []*host.Host{ { Name: "testhost", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, }, } assert.Empty(t, filterHosts(hosts, opts)) @@ -90,28 +107,28 @@ func TestFilterHostsBySwarmName(t *testing.T) { SwarmName: []string{"master"}, } master := - &libmachine.Host{ + &host.Host{ Name: "master", - HostOptions: &libmachine.HostOptions{ + HostOptions: &host.HostOptions{ SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "foo"}, }, } node1 := - &libmachine.Host{ + &host.Host{ Name: "node1", - HostOptions: &libmachine.HostOptions{ + HostOptions: &host.HostOptions{ SwarmOptions: &swarm.SwarmOptions{Master: false, Discovery: "foo"}, }, } othermaster := - &libmachine.Host{ + &host.Host{ Name: "othermaster", - HostOptions: &libmachine.HostOptions{ + HostOptions: &host.HostOptions{ SwarmOptions: &swarm.SwarmOptions{Master: true, Discovery: "bar"}, }, } - hosts := []*libmachine.Host{master, node1, othermaster} - expected := []*libmachine.Host{master, node1} + hosts := []*host.Host{master, node1, othermaster} + expected := []*host.Host{master, node1} assert.EqualValues(t, filterHosts(hosts, opts), expected) } @@ -121,25 +138,25 @@ func TestFilterHostsByDriverName(t *testing.T) { DriverName: []string{"fakedriver"}, } node1 := - &libmachine.Host{ + &host.Host{ Name: "node1", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } node2 := - &libmachine.Host{ + &host.Host{ Name: "node2", DriverName: "virtualbox", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } node3 := - &libmachine.Host{ + &host.Host{ Name: "node3", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } - hosts := []*libmachine.Host{node1, node2, node3} - expected := []*libmachine.Host{node1, node3} + hosts := []*host.Host{node1, node2, node3} + expected := []*host.Host{node1, node3} assert.EqualValues(t, filterHosts(hosts, opts), expected) } @@ -149,28 +166,28 @@ func TestFilterHostsByState(t *testing.T) { State: []string{"Paused", "Saved", "Stopped"}, } node1 := - &libmachine.Host{ + &host.Host{ Name: "node1", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused}, } node2 := - &libmachine.Host{ + &host.Host{ Name: "node2", DriverName: "virtualbox", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Stopped}, } node3 := - &libmachine.Host{ + &host.Host{ Name: "node3", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Running}, } - hosts := []*libmachine.Host{node1, node2, node3} - expected := []*libmachine.Host{node1, node2} + hosts := []*host.Host{node1, node2, node3} + expected := []*host.Host{node1, node2} assert.EqualValues(t, filterHosts(hosts, opts), expected) } @@ -180,35 +197,35 @@ func TestFilterHostsByName(t *testing.T) { Name: []string{"fire", "ice", "earth", "a.?r"}, } node1 := - &libmachine.Host{ + &host.Host{ Name: "fire", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "fire"}, } node2 := - &libmachine.Host{ + &host.Host{ Name: "ice", DriverName: "adriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "ice"}, } node3 := - &libmachine.Host{ + &host.Host{ Name: "air", DriverName: "nodriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "air"}, } node4 := - &libmachine.Host{ + &host.Host{ Name: "water", DriverName: "falsedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused, MockName: "water"}, } - hosts := []*libmachine.Host{node1, node2, node3, node4} - expected := []*libmachine.Host{node1, node2, node3} + hosts := []*host.Host{node1, node2, node3, node4} + expected := []*host.Host{node1, node2, node3} assert.EqualValues(t, filterHosts(hosts, opts), expected) } @@ -219,25 +236,25 @@ func TestFilterHostsMultiFlags(t *testing.T) { DriverName: []string{"fakedriver", "virtualbox"}, } node1 := - &libmachine.Host{ + &host.Host{ Name: "node1", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } node2 := - &libmachine.Host{ + &host.Host{ Name: "node2", DriverName: "virtualbox", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } node3 := - &libmachine.Host{ + &host.Host{ Name: "node3", DriverName: "softlayer", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, } - hosts := []*libmachine.Host{node1, node2, node3} - expected := []*libmachine.Host{node1, node2} + hosts := []*host.Host{node1, node2, node3} + expected := []*host.Host{node1, node2} assert.EqualValues(t, filterHosts(hosts, opts), expected) } @@ -248,28 +265,114 @@ func TestFilterHostsDifferentFlagsProduceAND(t *testing.T) { State: []string{"Running"}, } node1 := - &libmachine.Host{ + &host.Host{ Name: "node1", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Paused}, } node2 := - &libmachine.Host{ + &host.Host{ Name: "node2", DriverName: "virtualbox", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Stopped}, } node3 := - &libmachine.Host{ + &host.Host{ Name: "node3", DriverName: "fakedriver", - HostOptions: &libmachine.HostOptions{}, + HostOptions: &host.HostOptions{}, Driver: &fakedriver.FakeDriver{MockState: state.Running}, } - hosts := []*libmachine.Host{node1, node2, node3} - expected := []*libmachine.Host{} + hosts := []*host.Host{node1, node2, node3} + expected := []*host.Host{} assert.EqualValues(t, filterHosts(hosts, opts), expected) } +func captureStdout() (chan string, *os.File) { + r, w, _ := os.Pipe() + os.Stdout = w + + out := make(chan string) + + go func() { + var testOutput bytes.Buffer + io.Copy(&testOutput, r) + out <- testOutput.String() + }() + + return out, w +} + +func TestGetHostListItems(t *testing.T) { + defer cleanup() + + hostListItemsChan := make(chan HostListItem) + + hosts := []*host.Host{ + { + Name: "foo", + DriverName: "fakedriver", + Driver: &fakedriver.FakeDriver{ + MockState: state.Running, + }, + HostOptions: &host.HostOptions{ + SwarmOptions: &swarm.SwarmOptions{ + Master: false, + Address: "", + Discovery: "", + }, + }, + }, + { + Name: "bar", + DriverName: "fakedriver", + Driver: &fakedriver.FakeDriver{ + MockState: state.Stopped, + }, + HostOptions: &host.HostOptions{ + SwarmOptions: &swarm.SwarmOptions{ + Master: false, + Address: "", + Discovery: "", + }, + }, + }, + { + Name: "baz", + DriverName: "fakedriver", + Driver: &fakedriver.FakeDriver{ + MockState: state.Running, + }, + HostOptions: &host.HostOptions{ + SwarmOptions: &swarm.SwarmOptions{ + Master: false, + Address: "", + Discovery: "", + }, + }, + }, + } + + expected := map[string]state.State{ + "foo": state.Running, + "bar": state.Stopped, + "baz": state.Running, + } + + items := []HostListItem{} + for _, host := range hosts { + go getHostState(host, hostListItemsChan) + } + + for i := 0; i < len(hosts); i++ { + items = append(items, <-hostListItemsChan) + } + + for _, item := range items { + if expected[item.Name] != item.State { + t.Fatal("Expected state did not match for item", item) + } + } +} diff --git a/commands/mcndirs/utils.go b/commands/mcndirs/utils.go new file mode 100644 index 0000000000..6ed6b9ee01 --- /dev/null +++ b/commands/mcndirs/utils.go @@ -0,0 +1,32 @@ +package mcndirs + +import ( + "os" + "path/filepath" + + "github.com/docker/machine/libmachine/mcnutils" +) + +func GetBaseDir() string { + baseDir := os.Getenv("MACHINE_STORAGE_PATH") + if baseDir == "" { + baseDir = filepath.Join(mcnutils.GetHomeDir(), ".docker", "machine") + } + return baseDir +} + +func GetDockerDir() string { + return filepath.Join(mcnutils.GetHomeDir(), ".docker") +} + +func GetMachineDir() string { + return filepath.Join(GetBaseDir(), "machines") +} + +func GetMachineCertDir() string { + return filepath.Join(GetBaseDir(), "certs") +} + +func GetMachineCacheDir() string { + return filepath.Join(GetBaseDir(), "cache") +} diff --git a/utils/utils_test.go b/commands/mcndirs/utils_test.go similarity index 59% rename from utils/utils_test.go rename to commands/mcndirs/utils_test.go index 1b9f4c6cdb..63fd6bada5 100644 --- a/utils/utils_test.go +++ b/commands/mcndirs/utils_test.go @@ -1,18 +1,17 @@ -package utils +package mcndirs import ( - "io/ioutil" "os" "path" - "path/filepath" - "runtime" "strings" "testing" + + "github.com/docker/machine/libmachine/mcnutils" ) func TestGetBaseDir(t *testing.T) { // reset any override env var - homeDir := GetHomeDir() + homeDir := mcnutils.GetHomeDir() baseDir := GetBaseDir() if strings.Index(baseDir, homeDir) != 0 { @@ -32,7 +31,7 @@ func TestGetCustomBaseDir(t *testing.T) { } func TestGetDockerDir(t *testing.T) { - homeDir := GetHomeDir() + homeDir := mcnutils.GetHomeDir() baseDir := GetBaseDir() if strings.Index(baseDir, homeDir) != 0 { @@ -77,63 +76,3 @@ func TestGetMachineCertDir(t *testing.T) { } os.Setenv("MACHINE_STORAGE_PATH", "") } - -func TestCopyFile(t *testing.T) { - testStr := "test-machine" - - srcFile, err := ioutil.TempFile("", "machine-test-") - if err != nil { - t.Fatal(err) - } - srcFi, err := srcFile.Stat() - if err != nil { - t.Fatal(err) - } - - srcFile.Write([]byte(testStr)) - srcFile.Close() - - srcFilePath := filepath.Join(os.TempDir(), srcFi.Name()) - - destFile, err := ioutil.TempFile("", "machine-copy-test-") - if err != nil { - t.Fatal(err) - } - - destFi, err := destFile.Stat() - if err != nil { - t.Fatal(err) - } - - destFile.Close() - - destFilePath := filepath.Join(os.TempDir(), destFi.Name()) - - if err := CopyFile(srcFilePath, destFilePath); err != nil { - t.Fatal(err) - } - - data, err := ioutil.ReadFile(destFilePath) - if err != nil { - t.Fatal(err) - } - - if string(data) != testStr { - t.Fatalf("expected data \"%s\"; received \"%\"", testStr, string(data)) - } -} - -func TestGetUsername(t *testing.T) { - currentUser := "unknown" - switch runtime.GOOS { - case "darwin", "linux": - currentUser = os.Getenv("USER") - case "windows": - currentUser = os.Getenv("USERNAME") - } - - username := GetUsername() - if username != currentUser { - t.Fatalf("expected username %s; received %s", currentUser, username) - } -} diff --git a/commands/regeneratecerts.go b/commands/regeneratecerts.go index 930178fa04..99887aceec 100644 --- a/commands/regeneratecerts.go +++ b/commands/regeneratecerts.go @@ -2,7 +2,7 @@ package commands import ( "github.com/codegangsta/cli" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) func cmdRegenerateCerts(c *cli.Context) { diff --git a/commands/regeneratecerts_test.go b/commands/regeneratecerts_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/regeneratecerts_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/restart.go b/commands/restart.go index 6e04f75655..fd6967bc15 100644 --- a/commands/restart.go +++ b/commands/restart.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) diff --git a/commands/restart_test.go b/commands/restart_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/restart_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/rm.go b/commands/rm.go index 5af17f5cb2..6bffbed5ac 100644 --- a/commands/rm.go +++ b/commands/rm.go @@ -2,7 +2,7 @@ package commands import ( "github.com/codegangsta/cli" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) func cmdRm(c *cli.Context) { @@ -15,27 +15,14 @@ func cmdRm(c *cli.Context) { isError := false - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } + store := getStore(c) - for _, host := range c.Args() { - if err := provider.Remove(host, force); err != nil { - log.Errorf("Error removing machine %s: %s", host, err) + for _, hostName := range c.Args() { + if err := store.Remove(hostName, force); err != nil { + log.Errorf("Error removing machine %s: %s", hostName, err) isError = true } else { - log.Infof("Successfully removed %s", host) + log.Infof("Successfully removed %s", hostName) } } if isError { diff --git a/commands/rm_test.go b/commands/rm_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/rm_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/scp.go b/commands/scp.go index 09dea5ebc3..4b6a8cc0a2 100644 --- a/commands/scp.go +++ b/commands/scp.go @@ -8,8 +8,9 @@ import ( "strings" "github.com/codegangsta/cli" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/persist" ) var ( @@ -26,7 +27,7 @@ var ( } ) -func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmachine.Host, string, []string, error) { +func getInfoForScpArg(hostAndPath string, store persist.Store) (*host.Host, string, []string, error) { // TODO: What to do about colon in filepath? splitInfo := strings.Split(hostAndPath, ":") @@ -38,7 +39,7 @@ func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmac // Remote path. e.g. "machinename:/usr/bin/cmatrix" if len(splitInfo) == 2 { path := splitInfo[1] - host, err := provider.Get(splitInfo[0]) + host, err := store.Load(splitInfo[0]) if err != nil { return nil, "", nil, fmt.Errorf("Error loading host: %s", err) } @@ -52,7 +53,7 @@ func getInfoForScpArg(hostAndPath string, provider libmachine.Provider) (*libmac return nil, "", nil, ErrMalformedInput } -func generateLocationArg(host *libmachine.Host, path string) (string, error) { +func generateLocationArg(host *host.Host, path string) (string, error) { locationPrefix := "" if host != nil { ip, err := host.Driver.GetIP() @@ -64,18 +65,18 @@ func generateLocationArg(host *libmachine.Host, path string) (string, error) { return locationPrefix + path, nil } -func getScpCmd(src, dest string, sshArgs []string, provider libmachine.Provider) (*exec.Cmd, error) { +func getScpCmd(src, dest string, sshArgs []string, store persist.Store) (*exec.Cmd, error) { cmdPath, err := exec.LookPath("scp") if err != nil { return nil, errors.New("Error: You must have a copy of the scp binary locally to use the scp feature.") } - srcHost, srcPath, srcOpts, err := getInfoForScpArg(src, provider) + srcHost, srcPath, srcOpts, err := getInfoForScpArg(src, store) if err != nil { return nil, err } - destHost, destPath, destOpts, err := getInfoForScpArg(dest, provider) + destHost, destPath, destOpts, err := getInfoForScpArg(dest, store) if err != nil { return nil, err } @@ -129,8 +130,8 @@ func cmdScp(c *cli.Context) { src := args[0] dest := args[1] - provider := getDefaultProvider(c) - cmd, err := getScpCmd(src, dest, sshArgs, *provider) + store := getStore(c) + cmd, err := getScpCmd(src, dest, sshArgs, store) if err != nil { log.Fatal(err) diff --git a/commands/scp_test.go b/commands/scp_test.go index e77f262bf9..c110a1c412 100644 --- a/commands/scp_test.go +++ b/commands/scp_test.go @@ -7,9 +7,9 @@ import ( "reflect" "testing" - "github.com/docker/machine/drivers" - "github.com/docker/machine/libmachine" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/state" ) type ScpFakeDriver struct { @@ -18,14 +18,6 @@ type ScpFakeDriver struct { type ScpFakeStore struct{} -func (d ScpFakeDriver) AuthorizePort(ports []*drivers.Port) error { - return nil -} - -func (d ScpFakeDriver) DeauthorizePort(ports []*drivers.Port) error { - return nil -} - func (d ScpFakeDriver) DriverName() string { return "fake" } @@ -114,33 +106,25 @@ func (d ScpFakeDriver) GetSSHKeyPath() string { return "/fake/keypath/id_rsa" } +func (d ScpFakeDriver) ResolveStorePath(file string) string { + return "/tmp/store/machines/fake" +} + func (s ScpFakeStore) Exists(name string) (bool, error) { return true, nil } -func (s ScpFakeStore) GetActive() (*libmachine.Host, error) { +func (s ScpFakeStore) GetActive() (*host.Host, error) { return nil, nil } -func (s ScpFakeStore) GetPath() string { - return "" -} - -func (s ScpFakeStore) GetCACertificatePath() (string, error) { - return "", nil -} - -func (s ScpFakeStore) GetPrivateKeyPath() (string, error) { - return "", nil -} - -func (s ScpFakeStore) List() ([]*libmachine.Host, error) { +func (s ScpFakeStore) List() ([]*host.Host, error) { return nil, nil } -func (s ScpFakeStore) Get(name string) (*libmachine.Host, error) { +func (s ScpFakeStore) Load(name string) (*host.Host, error) { if name == "myfunhost" { - return &libmachine.Host{ + return &host.Host{ Name: "myfunhost", Driver: ScpFakeDriver{}, }, nil @@ -152,15 +136,19 @@ func (s ScpFakeStore) Remove(name string, force bool) error { return nil } -func (s ScpFakeStore) Save(host *libmachine.Host) error { +func (s ScpFakeStore) Save(host *host.Host) error { return nil } +func (s ScpFakeStore) NewHost(driver drivers.Driver) (*host.Host, error) { + return nil, nil +} + func TestGetInfoForScpArg(t *testing.T) { - provider, _ := libmachine.New(ScpFakeStore{}) + store := ScpFakeStore{} expectedPath := "/tmp/foo" - host, path, opts, err := getInfoForScpArg("/tmp/foo", *provider) + host, path, opts, err := getInfoForScpArg("/tmp/foo", store) if err != nil { t.Fatalf("Unexpected error in local getInfoForScpArg call: %s", err) } @@ -174,7 +162,7 @@ func TestGetInfoForScpArg(t *testing.T) { t.Fatal("opts should be nil") } - host, path, opts, err = getInfoForScpArg("myfunhost:/home/docker/foo", *provider) + host, path, opts, err = getInfoForScpArg("myfunhost:/home/docker/foo", store) if err != nil { t.Fatalf("Unexpected error in machine-based getInfoForScpArg call: %s", err) } @@ -194,14 +182,14 @@ func TestGetInfoForScpArg(t *testing.T) { t.Fatalf("Expected path to be /home/docker/foo, got %s", path) } - host, path, opts, err = getInfoForScpArg("foo:bar:widget", *provider) + host, path, opts, err = getInfoForScpArg("foo:bar:widget", store) if err != ErrMalformedInput { t.Fatalf("Didn't get back an error when we were expecting it for malformed args") } } func TestGenerateLocationArg(t *testing.T) { - host := libmachine.Host{ + host := host.Host{ Driver: ScpFakeDriver{}, } @@ -224,8 +212,6 @@ func TestGenerateLocationArg(t *testing.T) { } func TestGetScpCmd(t *testing.T) { - provider, _ := libmachine.New(ScpFakeStore{}) - // TODO: This is a little "integration-ey". Perhaps // make an ScpDispatcher (name?) interface so that the reliant // methods can be mocked. @@ -238,8 +224,9 @@ func TestGetScpCmd(t *testing.T) { "root@12.34.56.78:/home/docker/foo", ) expectedCmd := exec.Command("/usr/bin/scp", expectedArgs...) + store := ScpFakeStore{} - cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", append(baseSSHArgs, "-3"), *provider) + cmd, err := getScpCmd("/tmp/foo", "myfunhost:/home/docker/foo", append(baseSSHArgs, "-3"), store) if err != nil { t.Fatalf("Unexpected err getting scp command: %s", err) } diff --git a/commands/ssh.go b/commands/ssh.go index 00f45a6a4e..57c8b9e007 100644 --- a/commands/ssh.go +++ b/commands/ssh.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/docker/machine/log" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/state" "github.com/codegangsta/cli" ) @@ -19,22 +19,8 @@ func cmdSsh(c *cli.Context) { log.Fatal("Error: Please specify a machine name.") } - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - provider, err := newProvider(defaultStore) - if err != nil { - log.Fatal(err) - } - - host, err := provider.Get(name) + store := getStore(c) + host, err := store.Load(name) if err != nil { log.Fatal(err) } diff --git a/commands/ssh_test.go b/commands/ssh_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/ssh_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/start.go b/commands/start.go index 2cd7054ed7..6e34e2baeb 100644 --- a/commands/start.go +++ b/commands/start.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) diff --git a/commands/start_test.go b/commands/start_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/start_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/status.go b/commands/status.go index 1247d31235..f2ca12b3de 100644 --- a/commands/status.go +++ b/commands/status.go @@ -1,13 +1,13 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) func cmdStatus(c *cli.Context) { - host := getHost(c) + host := getFirstArgHost(c) currentState, err := host.Driver.GetState() if err != nil { log.Errorf("error getting state for host %s: %s", host.Name, err) diff --git a/commands/status_test.go b/commands/status_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/status_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/stop.go b/commands/stop.go index 7a47818a85..af8181aa49 100644 --- a/commands/stop.go +++ b/commands/stop.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) diff --git a/commands/stop_test.go b/commands/stop_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/stop_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/upgrade.go b/commands/upgrade.go index 6e22c00cea..580786d7bd 100644 --- a/commands/upgrade.go +++ b/commands/upgrade.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) diff --git a/commands/upgrade_test.go b/commands/upgrade_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/upgrade_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/commands/url.go b/commands/url.go index a714c1c486..2d28195890 100644 --- a/commands/url.go +++ b/commands/url.go @@ -3,13 +3,13 @@ package commands import ( "fmt" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" ) func cmdUrl(c *cli.Context) { - url, err := getHost(c).GetURL() + url, err := getFirstArgHost(c).GetURL() if err != nil { log.Fatal(err) } diff --git a/commands/url_test.go b/commands/url_test.go deleted file mode 100644 index cdff10da75..0000000000 --- a/commands/url_test.go +++ /dev/null @@ -1 +0,0 @@ -package commands diff --git a/drivers/amazonec2/amazonec2.go b/drivers/amazonec2/amazonec2.go index 2a1af5e6de..5ab4999abe 100644 --- a/drivers/amazonec2/amazonec2.go +++ b/drivers/amazonec2/amazonec2.go @@ -12,21 +12,26 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" "github.com/docker/machine/drivers/amazonec2/amz" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) const ( driverName = "amazonec2" + ipRange = "0.0.0.0/0" + machineSecurityGroupName = "docker-machine" + defaultAmiId = "ami-615cb725" defaultRegion = "us-east-1" defaultInstanceType = "t2.micro" defaultRootSize = 16 - ipRange = "0.0.0.0/0" - machineSecurityGroupName = "docker-machine" + defaultZone = "a" + defaultSecurityGroup = machineSecurityGroupName + defaultSSHUser = "ubuntu" + defaultSpotPrice = "0.50" ) var ( @@ -65,7 +70,6 @@ type Driver struct { func init() { drivers.Register(driverName, &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -75,19 +79,16 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "amazonec2-access-key", Usage: "AWS Access Key", - Value: "", EnvVar: "AWS_ACCESS_KEY_ID", }, cli.StringFlag{ Name: "amazonec2-secret-key", Usage: "AWS Secret Key", - Value: "", EnvVar: "AWS_SECRET_ACCESS_KEY", }, cli.StringFlag{ Name: "amazonec2-session-token", Usage: "AWS Session Token", - Value: "", EnvVar: "AWS_SESSION_TOKEN", }, cli.StringFlag{ @@ -104,25 +105,23 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "amazonec2-vpc-id", Usage: "AWS VPC id", - Value: "", EnvVar: "AWS_VPC_ID", }, cli.StringFlag{ Name: "amazonec2-zone", Usage: "AWS zone for instance (i.e. a,b,c,d,e)", - Value: "a", + Value: defaultZone, EnvVar: "AWS_ZONE", }, cli.StringFlag{ Name: "amazonec2-subnet-id", Usage: "AWS VPC subnet id", - Value: "", EnvVar: "AWS_SUBNET_ID", }, cli.StringFlag{ Name: "amazonec2-security-group", Usage: "AWS VPC security group", - Value: "docker-machine", + Value: defaultSecurityGroup, EnvVar: "AWS_SECURITY_GROUP", }, cli.StringFlag{ @@ -145,7 +144,7 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "amazonec2-ssh-user", Usage: "set the name of the ssh user", - Value: "ubuntu", + Value: defaultSSHUser, EnvVar: "AWS_SSH_USER", }, cli.BoolFlag{ @@ -155,7 +154,7 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "amazonec2-spot-price", Usage: "AWS spot instance bid price (in dollar)", - Value: "0.50", + Value: defaultSpotPrice, }, cli.BoolFlag{ Name: "amazonec2-private-address-only", @@ -172,13 +171,23 @@ func GetCreateFlags() []cli.Flag { } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { +func NewDriver(hostName, storePath string) drivers.Driver { id := generateId() - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) return &Driver{ - Id: id, - BaseDriver: inner, - }, nil + Id: id, + AMI: defaultAmiId, + Region: defaultRegion, + InstanceType: defaultInstanceType, + RootSize: defaultRootSize, + Zone: defaultZone, + SecurityGroupName: defaultSecurityGroup, + SpotPrice: defaultSpotPrice, + BaseDriver: &drivers.BaseDriver{ + SSHUser: defaultSSHUser, + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { @@ -372,7 +381,7 @@ func (d *Driver) Create() error { d.InstanceId = instance.InstanceId log.Debug("waiting for ip address to become available") - if err := utils.WaitFor(d.instanceIpAvailable); err != nil { + if err := mcnutils.WaitFor(d.instanceIpAvailable); err != nil { return err } @@ -536,7 +545,7 @@ func (d *Driver) instanceIsRunning() bool { } func (d *Driver) waitForInstance() error { - if err := utils.WaitFor(d.instanceIsRunning); err != nil { + if err := mcnutils.WaitFor(d.instanceIsRunning); err != nil { return err } @@ -622,7 +631,7 @@ func (d *Driver) configureSecurityGroup(groupName string) error { securityGroup = group // wait until created (dat eventual consistency) log.Debugf("waiting for group (%s) to become available", group.GroupId) - if err := utils.WaitFor(d.securityGroupAvailableFunc(group.GroupId)); err != nil { + if err := mcnutils.WaitFor(d.securityGroupAvailableFunc(group.GroupId)); err != nil { return err } } diff --git a/drivers/amazonec2/amazonec2_test.go b/drivers/amazonec2/amazonec2_test.go index 35d1fbb34c..7b615676c5 100644 --- a/drivers/amazonec2/amazonec2_test.go +++ b/drivers/amazonec2/amazonec2_test.go @@ -98,10 +98,7 @@ func getTestDriver() (*Driver, error) { } defer cleanup() - d, err := NewDriver(machineTestName, storePath, machineTestCaCert, machineTestPrivateKey) - if err != nil { - return nil, err - } + d := NewDriver(machineTestName, storePath) d.SetConfigFromFlags(getDefaultTestDriverFlags()) drv := d.(*Driver) return drv, nil diff --git a/drivers/amazonec2/amz/auth_test.go b/drivers/amazonec2/amz/auth_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/auth_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/block_device_mapping_test.go b/drivers/amazonec2/amz/block_device_mapping_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/block_device_mapping_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/describe_instances_test.go b/drivers/amazonec2/amz/describe_instances_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/describe_instances_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/describe_keypairs_test.go b/drivers/amazonec2/amz/describe_keypairs_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/describe_keypairs_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/describe_security_groups_test.go b/drivers/amazonec2/amz/describe_security_groups_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/describe_security_groups_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/describe_subnets_test.go b/drivers/amazonec2/amz/describe_subnets_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/describe_subnets_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/ec2.go b/drivers/amazonec2/amz/ec2.go index 44a342bb16..262b8dce64 100644 --- a/drivers/amazonec2/amz/ec2.go +++ b/drivers/amazonec2/amz/ec2.go @@ -9,8 +9,8 @@ import ( "net/url" "strconv" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" awsauth "github.com/smartystreets/go-aws-auth" ) @@ -151,7 +151,7 @@ func NewEC2(auth Auth, region string) *EC2 { func (e *EC2) awsApiCall(v url.Values) (*http.Response, error) { v.Set("Version", "2014-06-15") log.Debug("Making AWS API call with values:") - utils.DumpVal(v) + mcnutils.DumpVal(v) client := &http.Client{} finalEndpoint := fmt.Sprintf("%s?%s", e.Endpoint, v.Encode()) req, err := http.NewRequest("GET", finalEndpoint, nil) diff --git a/drivers/amazonec2/amz/ec2_test.go b/drivers/amazonec2/amz/ec2_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/ec2_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/error_codes_test.go b/drivers/amazonec2/amz/error_codes_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/error_codes_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/error_test.go b/drivers/amazonec2/amz/error_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/error_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/ip_permission_test.go b/drivers/amazonec2/amz/ip_permission_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/ip_permission_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/keypair_test.go b/drivers/amazonec2/amz/keypair_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/keypair_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/security_group_test.go b/drivers/amazonec2/amz/security_group_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/security_group_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/amazonec2/amz/tags_test.go b/drivers/amazonec2/amz/tags_test.go deleted file mode 100644 index c4228e299f..0000000000 --- a/drivers/amazonec2/amz/tags_test.go +++ /dev/null @@ -1 +0,0 @@ -package amz diff --git a/drivers/azure/azure.go b/drivers/azure/azure.go index f92fd1bcf6..c41569eafa 100644 --- a/drivers/azure/azure.go +++ b/drivers/azure/azure.go @@ -11,11 +11,11 @@ import ( "github.com/MSOpenTech/azure-sdk-for-go/clients/vmClient" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) type Driver struct { @@ -31,9 +31,17 @@ type Driver struct { DockerSwarmMasterPort int } +const ( + defaultDockerPort = 2376 + defaultSwarmMasterPort = 3376 + defaultLocation = "West US" + defaultSize = "Small" + defaultSSHPort = 22 + defaultSSHUsername = "ubuntu" +) + func init() { drivers.Register("azure", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -45,12 +53,12 @@ func GetCreateFlags() []cli.Flag { cli.IntFlag{ Name: "azure-docker-port", Usage: "Azure Docker port", - Value: 2376, + Value: defaultDockerPort, }, cli.IntFlag{ Name: "azure-docker-swarm-master-port", Usage: "Azure Docker Swarm master port", - Value: 3376, + Value: defaultSwarmMasterPort, }, cli.StringFlag{ EnvVar: "AZURE_IMAGE", @@ -61,7 +69,7 @@ func GetCreateFlags() []cli.Flag { EnvVar: "AZURE_LOCATION", Name: "azure-location", Usage: "Azure location", - Value: "West US", + Value: defaultLocation, }, cli.StringFlag{ Name: "azure-password", @@ -76,12 +84,12 @@ func GetCreateFlags() []cli.Flag { EnvVar: "AZURE_SIZE", Name: "azure-size", Usage: "Azure size", - Value: "Small", + Value: defaultSize, }, cli.IntFlag{ Name: "azure-ssh-port", Usage: "Azure SSH port", - Value: 22, + Value: defaultSSHPort, }, cli.StringFlag{ @@ -97,15 +105,25 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "azure-username", Usage: "Azure username", - Value: "ubuntu", + Value: defaultSSHUsername, }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - d := &Driver{BaseDriver: inner} - return d, nil +func NewDriver(hostName, storePath string) drivers.Driver { + d := &Driver{ + DockerPort: defaultDockerPort, + DockerSwarmMasterPort: defaultSwarmMasterPort, + Location: defaultLocation, + Size: defaultSize, + BaseDriver: &drivers.BaseDriver{ + SSHPort: defaultSSHPort, + SSHUser: defaultSSHUsername, + MachineName: hostName, + StorePath: storePath, + }, + } + return d } func (d *Driver) GetSSHHostname() (string, error) { @@ -368,7 +386,7 @@ func (d *Driver) Kill() error { } func generateVMName() string { - randomID := utils.TruncateID(utils.GenerateRandomID()) + randomID := mcnutils.TruncateID(mcnutils.GenerateRandomID()) return fmt.Sprintf("docker-host-%s", randomID) } diff --git a/drivers/azure/azure_test.go b/drivers/azure/azure_test.go deleted file mode 100644 index 6512f735e0..0000000000 --- a/drivers/azure/azure_test.go +++ /dev/null @@ -1 +0,0 @@ -package azure diff --git a/drivers/digitalocean/digitalocean.go b/drivers/digitalocean/digitalocean.go index fff953a0d9..bc70a2ab1e 100644 --- a/drivers/digitalocean/digitalocean.go +++ b/drivers/digitalocean/digitalocean.go @@ -8,10 +8,10 @@ import ( "code.google.com/p/goauth2/oauth" "github.com/codegangsta/cli" "github.com/digitalocean/godo" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) type Driver struct { @@ -28,9 +28,14 @@ type Driver struct { PrivateNetworking bool } +const ( + defaultImage = "ubuntu-14-04-x64" + defaultRegion = "nyc3" + defaultSize = "512mb" +) + func init() { drivers.Register("digitalocean", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -54,19 +59,19 @@ func GetCreateFlags() []cli.Flag { EnvVar: "DIGITALOCEAN_IMAGE", Name: "digitalocean-image", Usage: "Digital Ocean Image", - Value: "ubuntu-14-04-x64", + Value: defaultImage, }, cli.StringFlag{ EnvVar: "DIGITALOCEAN_REGION", Name: "digitalocean-region", Usage: "Digital Ocean region", - Value: "nyc3", + Value: defaultRegion, }, cli.StringFlag{ EnvVar: "DIGITALOCEAN_SIZE", Name: "digitalocean-size", Usage: "Digital Ocean size", - Value: "512mb", + Value: defaultSize, }, cli.BoolFlag{ EnvVar: "DIGITALOCEAN_IPV6", @@ -86,9 +91,16 @@ func GetCreateFlags() []cli.Flag { } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) *Driver { + return &Driver{ + Image: defaultImage, + Size: defaultSize, + Region: defaultRegion, + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { diff --git a/drivers/digitalocean/digitalocean_test.go b/drivers/digitalocean/digitalocean_test.go deleted file mode 100644 index 53c59d2fe1..0000000000 --- a/drivers/digitalocean/digitalocean_test.go +++ /dev/null @@ -1 +0,0 @@ -package digitalocean diff --git a/drivers/driverfactory/factory.go b/drivers/driverfactory/factory.go new file mode 100644 index 0000000000..778e00ee4c --- /dev/null +++ b/drivers/driverfactory/factory.go @@ -0,0 +1,65 @@ +package driverfactory + +import ( + "fmt" + + "github.com/docker/machine/drivers/amazonec2" + "github.com/docker/machine/drivers/azure" + "github.com/docker/machine/drivers/digitalocean" + "github.com/docker/machine/drivers/exoscale" + "github.com/docker/machine/drivers/generic" + "github.com/docker/machine/drivers/google" + "github.com/docker/machine/drivers/hyperv" + "github.com/docker/machine/drivers/none" + "github.com/docker/machine/drivers/openstack" + "github.com/docker/machine/drivers/rackspace" + "github.com/docker/machine/drivers/softlayer" + "github.com/docker/machine/drivers/virtualbox" + "github.com/docker/machine/drivers/vmwarefusion" + "github.com/docker/machine/drivers/vmwarevcloudair" + "github.com/docker/machine/drivers/vmwarevsphere" + "github.com/docker/machine/libmachine/drivers" +) + +func NewDriver(driverName, hostName, storePath string) (drivers.Driver, error) { + var ( + driver drivers.Driver + ) + + switch driverName { + case "virtualbox": + driver = virtualbox.NewDriver(hostName, storePath) + case "digitalocean": + driver = digitalocean.NewDriver(hostName, storePath) + case "amazonec2": + driver = amazonec2.NewDriver(hostName, storePath) + case "azure": + driver = azure.NewDriver(hostName, storePath) + case "exoscale": + driver = exoscale.NewDriver(hostName, storePath) + case "generic": + driver = generic.NewDriver(hostName, storePath) + case "google": + driver = google.NewDriver(hostName, storePath) + case "hyperv": + driver = hyperv.NewDriver(hostName, storePath) + case "openstack": + driver = openstack.NewDriver(hostName, storePath) + case "rackspace": + driver = rackspace.NewDriver(hostName, storePath) + case "softlayer": + driver = softlayer.NewDriver(hostName, storePath) + case "vmwarefusion": + driver = vmwarefusion.NewDriver(hostName, storePath) + case "vmwarevcloudair": + driver = vmwarevcloudair.NewDriver(hostName, storePath) + case "vmwarevsphere": + driver = vmwarevsphere.NewDriver(hostName, storePath) + case "none": + driver = none.NewDriver(hostName, storePath) + default: + return nil, fmt.Errorf("Driver %q not recognized", driverName) + } + + return driver, nil +} diff --git a/drivers/exoscale/exoscale.go b/drivers/exoscale/exoscale.go index a666fc05b0..743bff66f8 100644 --- a/drivers/exoscale/exoscale.go +++ b/drivers/exoscale/exoscale.go @@ -9,10 +9,10 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/state" "github.com/pyr/egoscale/src/egoscale" ) @@ -31,9 +31,15 @@ type Driver struct { Id string } +const ( + defaultInstanceProfile = "small" + defaultDiskSize = 50 + defaultImage = "ubuntu-14.04" + defaultAvailabilityZone = "ch-gva-2" +) + func init() { drivers.Register("exoscale", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -60,19 +66,19 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ EnvVar: "EXOSCALE_INSTANCE_PROFILE", Name: "exoscale-instance-profile", - Value: "small", + Value: defaultInstanceProfile, Usage: "exoscale instance profile (small, medium, large, ...)", }, cli.IntFlag{ EnvVar: "EXOSCALE_DISK_SIZE", Name: "exoscale-disk-size", - Value: 50, + Value: defaultDiskSize, Usage: "exoscale disk size (10, 50, 100, 200, 400)", }, cli.StringFlag{ EnvVar: "EXSOCALE_IMAGE", Name: "exoscale-image", - Value: "ubuntu-14.04", + Value: defaultImage, Usage: "exoscale image template", }, cli.StringSliceFlag{ @@ -84,15 +90,23 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ EnvVar: "EXOSCALE_AVAILABILITY_ZONE", Name: "exoscale-availability-zone", - Value: "ch-gva-2", + Value: defaultAvailabilityZone, Usage: "exoscale availibility zone", }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + InstanceProfile: defaultInstanceProfile, + DiskSize: defaultDiskSize, + Image: defaultImage, + AvailabilityZone: defaultAvailabilityZone, + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -426,7 +440,7 @@ func (d *Driver) jobIsDone(client *egoscale.Client, jobid string) (bool, error) func (d *Driver) waitForJob(client *egoscale.Client, jobid string) error { log.Infof("Waiting for job to complete...") - return utils.WaitForSpecificOrError(func() (bool, error) { + return mcnutils.WaitForSpecificOrError(func() (bool, error) { return d.jobIsDone(client, jobid) }, 60, 2*time.Second) } diff --git a/drivers/exoscale/exoscale_test.go b/drivers/exoscale/exoscale_test.go deleted file mode 100644 index b6e101732a..0000000000 --- a/drivers/exoscale/exoscale_test.go +++ /dev/null @@ -1 +0,0 @@ -package exoscale diff --git a/drivers/fakedriver/fakedriver.go b/drivers/fakedriver/fakedriver.go index bccf7db534..eb1bf07a6a 100644 --- a/drivers/fakedriver/fakedriver.go +++ b/drivers/fakedriver/fakedriver.go @@ -1,8 +1,8 @@ package fakedriver import ( - "github.com/docker/machine/drivers" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/state" ) type FakeDriver struct { diff --git a/drivers/fakedriver/fakedriver_test.go b/drivers/fakedriver/fakedriver_test.go deleted file mode 100644 index 0fd2ad07cc..0000000000 --- a/drivers/fakedriver/fakedriver_test.go +++ /dev/null @@ -1 +0,0 @@ -package fakedriver diff --git a/drivers/generic/generic.go b/drivers/generic/generic.go index b79618ca80..bf8d31768a 100644 --- a/drivers/generic/generic.go +++ b/drivers/generic/generic.go @@ -8,10 +8,10 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/state" ) type Driver struct { @@ -20,12 +20,17 @@ type Driver struct { } const ( + defaultSSHUser = "root" + defaultSSHPort = 22 defaultTimeout = 1 * time.Second ) +var ( + defaultSSHKey = filepath.Join(mcnutils.GetHomeDir(), ".ssh", "id_rsa") +) + func init() { drivers.Register("generic", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -37,29 +42,35 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "generic-ip-address", Usage: "IP Address of machine", - Value: "", }, cli.StringFlag{ Name: "generic-ssh-user", Usage: "SSH user", - Value: "root", + Value: defaultSSHUser, }, cli.StringFlag{ Name: "generic-ssh-key", Usage: "SSH private key path", - Value: filepath.Join(utils.GetHomeDir(), ".ssh", "id_rsa"), + Value: defaultSSHKey, }, cli.IntFlag{ Name: "generic-ssh-port", Usage: "SSH port", - Value: 22, + Value: defaultSSHPort, }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + SSHKey: defaultSSHKey, + BaseDriver: &drivers.BaseDriver{ + SSHUser: defaultSSHUser, + SSHPort: defaultSSHPort, + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) DriverName() string { @@ -98,7 +109,7 @@ func (d *Driver) PreCreateCheck() error { func (d *Driver) Create() error { log.Infof("Importing SSH key...") - if err := utils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil { + if err := mcnutils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil { return fmt.Errorf("unable to copy ssh key: %s", err) } diff --git a/drivers/generic/generic_test.go b/drivers/generic/generic_test.go deleted file mode 100644 index 0d16b43f98..0000000000 --- a/drivers/generic/generic_test.go +++ /dev/null @@ -1 +0,0 @@ -package generic diff --git a/drivers/google/auth_util.go b/drivers/google/auth_util.go index b8d5bd0869..4b3e8e7df9 100644 --- a/drivers/google/auth_util.go +++ b/drivers/google/auth_util.go @@ -12,7 +12,7 @@ import ( "time" "code.google.com/p/goauth2/oauth" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" raw "google.golang.org/api/compute/v1" ) diff --git a/drivers/google/auth_util_test.go b/drivers/google/auth_util_test.go deleted file mode 100644 index 71664db3c8..0000000000 --- a/drivers/google/auth_util_test.go +++ /dev/null @@ -1 +0,0 @@ -package google diff --git a/drivers/google/compute_util.go b/drivers/google/compute_util.go index c700ace964..c160c2ce02 100644 --- a/drivers/google/compute_util.go +++ b/drivers/google/compute_util.go @@ -8,8 +8,8 @@ import ( "strings" "time" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/ssh" raw "google.golang.org/api/compute/v1" ) diff --git a/drivers/google/compute_util_test.go b/drivers/google/compute_util_test.go deleted file mode 100644 index 71664db3c8..0000000000 --- a/drivers/google/compute_util_test.go +++ /dev/null @@ -1 +0,0 @@ -package google diff --git a/drivers/google/google.go b/drivers/google/google.go index 14d4b58d37..3f7054ee3d 100644 --- a/drivers/google/google.go +++ b/drivers/google/google.go @@ -5,10 +5,10 @@ import ( "strings" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) // Driver is a struct compatible with the docker.hosts.drivers.Driver interface. @@ -26,9 +26,17 @@ type Driver struct { Tags []string } +const ( + defaultZone = "us-central1-a" + defaultUser = "docker-user" + defaultMachineType = "f1-micro" + defaultScopes = "https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write" + defaultDiskType = "pd-standard" + defaultDiskSize = 10 +) + func init() { drivers.Register("google", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -40,19 +48,19 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "google-zone", Usage: "GCE Zone", - Value: "us-central1-a", + Value: defaultZone, EnvVar: "GOOGLE_ZONE", }, cli.StringFlag{ Name: "google-machine-type", Usage: "GCE Machine Type", - Value: "f1-micro", + Value: defaultMachineType, EnvVar: "GOOGLE_MACHINE_TYPE", }, cli.StringFlag{ Name: "google-username", Usage: "GCE User Name", - Value: "docker-user", + Value: defaultUser, EnvVar: "GOOGLE_USERNAME", }, cli.StringFlag{ @@ -68,19 +76,19 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "google-scopes", Usage: "GCE Scopes (comma-separated if multiple scopes)", - Value: "https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write", + Value: defaultScopes, EnvVar: "GOOGLE_SCOPES", }, cli.IntFlag{ Name: "google-disk-size", Usage: "GCE Instance Disk Size (in GB)", - Value: 10, + Value: defaultDiskSize, EnvVar: "GOOGLE_DISK_SIZE", }, cli.StringFlag{ Name: "google-disk-type", Usage: "GCE Instance Disk type", - Value: "pd-standard", + Value: defaultDiskType, EnvVar: "GOOGLE_DISK_TYPE", }, cli.StringFlag{ @@ -102,9 +110,19 @@ func GetCreateFlags() []cli.Flag { } // NewDriver creates a Driver with the specified storePath. -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(machineName string, storePath string) *Driver { + return &Driver{ + Zone: defaultZone, + DiskType: defaultDiskType, + DiskSize: defaultDiskSize, + MachineType: defaultMachineType, + Scopes: defaultScopes, + BaseDriver: &drivers.BaseDriver{ + SSHUser: defaultUser, + MachineName: machineName, + StorePath: storePath, + }, + } } // GetSSHHostname returns hostname for use with ssh diff --git a/drivers/google/google_test.go b/drivers/google/google_test.go deleted file mode 100644 index 71664db3c8..0000000000 --- a/drivers/google/google_test.go +++ /dev/null @@ -1 +0,0 @@ -package google diff --git a/drivers/hyperv/hyperv.go b/drivers/hyperv/hyperv.go index e1b3bec79a..d121f61d53 100644 --- a/drivers/hyperv/hyperv.go +++ b/drivers/hyperv/hyperv.go @@ -1,2 +1,472 @@ -// this is empty to allow builds on non-windows platforms package hyperv + +import ( + "archive/tar" + "bytes" + "fmt" + "io/ioutil" + "os" + "time" + + "github.com/codegangsta/cli" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" +) + +type Driver struct { + *drivers.BaseDriver + boot2DockerURL string + boot2DockerLoc string + vSwitch string + diskImage string + DiskSize int + MemSize int +} + +const ( + defaultDiskSize = 20000 + defaultMemory = 1024 +) + +func init() { + drivers.Register("hyper-v", &drivers.RegisteredDriver{ + GetCreateFlags: GetCreateFlags, + }) +} + +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + DiskSize: defaultDiskSize, + MemSize: defaultMemory, + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } +} + +// GetCreateFlags registers the flags this driver adds to +// "docker hosts create" +func GetCreateFlags() []cli.Flag { + return []cli.Flag{ + cli.StringFlag{ + Name: "hyper-v-boot2docker-url", + Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.", + }, + cli.StringFlag{ + Name: "hyper-v-boot2docker-location", + Usage: "Hyper-V local boot2docker iso. Overrides URL.", + }, + cli.StringFlag{ + Name: "hyper-v-virtual-switch", + Usage: "Hyper-V virtual switch name. Defaults to first found.", + }, + cli.IntFlag{ + Name: "hyper-v-disk-size", + Usage: "Hyper-V disk size for host in MB.", + Value: defaultDiskSize, + }, + cli.IntFlag{ + Name: "hyper-v-memory", + Usage: "Hyper-V memory size for host in MB.", + Value: defaultMemory, + }, + } +} + +func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { + d.boot2DockerURL = flags.String("hyper-v-boot2docker-url") + d.boot2DockerLoc = flags.String("hyper-v-boot2docker-location") + d.vSwitch = flags.String("hyper-v-virtual-switch") + d.DiskSize = flags.Int("hyper-v-disk-size") + d.MemSize = flags.Int("hyper-v-memory") + d.SwarmMaster = flags.Bool("swarm-master") + d.SwarmHost = flags.String("swarm-host") + d.SwarmDiscovery = flags.String("swarm-discovery") + d.SSHUser = "docker" + d.SSHPort = 22 + return nil +} + +func (d *Driver) GetSSHHostname() (string, error) { + return d.GetIP() +} + +func (d *Driver) GetSSHUsername() string { + if d.SSHUser == "" { + d.SSHUser = "docker" + } + + return d.SSHUser +} + +func (d *Driver) DriverName() string { + return "hyper-v" +} + +func (d *Driver) PreCreateCheck() error { + return nil +} + +func (d *Driver) GetURL() (string, error) { + ip, err := d.GetIP() + if err != nil { + return "", err + } + if ip == "" { + return "", nil + } + return fmt.Sprintf("tcp://%s:2376", ip), nil +} + +func (d *Driver) GetState() (state.State, error) { + + command := []string{ + "(", + "Get-VM", + "-Name", d.MachineName, + ").state"} + stdout, err := execute(command) + if err != nil { + return state.None, fmt.Errorf("Failed to find the VM status") + } + resp := parseStdout(stdout) + + if len(resp) < 1 { + return state.None, nil + } + switch resp[0] { + case "Running": + return state.Running, nil + case "Off": + return state.Stopped, nil + } + return state.None, nil +} + +func (d *Driver) Create() error { + err := hypervAvailable() + if err != nil { + return err + } + + d.setMachineNameIfNotSet() + + b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath) + if err := b2dutils.CopyIsoToMachineDir(d.boot2DockerURL, d.MachineName); err != nil { + return err + } + + log.Infof("Creating SSH key...") + + if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { + return err + } + + log.Infof("Creating VM...") + + virtualSwitch, err := d.chooseVirtualSwitch() + if err != nil { + return err + } + + err = d.generateDiskImage() + if err != nil { + return err + } + + command := []string{ + "New-VM", + "-Name", d.MachineName, + "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")), + "-MemoryStartupBytes", fmt.Sprintf("%dMB", d.MemSize)} + _, err = execute(command) + if err != nil { + return err + } + + command = []string{ + "Set-VMDvdDrive", + "-VMName", d.MachineName, + "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))} + _, err = execute(command) + if err != nil { + return err + } + + command = []string{ + "Add-VMHardDiskDrive", + "-VMName", d.MachineName, + "-Path", fmt.Sprintf("'%s'", d.diskImage)} + _, err = execute(command) + if err != nil { + return err + } + + command = []string{ + "Connect-VMNetworkAdapter", + "-VMName", d.MachineName, + "-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)} + _, err = execute(command) + if err != nil { + return err + } + + log.Infof("Starting VM...") + if err := d.Start(); err != nil { + return err + } + + return nil +} + +func (d *Driver) chooseVirtualSwitch() (string, error) { + if d.vSwitch != "" { + return d.vSwitch, nil + } + command := []string{ + "@(Get-VMSwitch).Name"} + stdout, err := execute(command) + if err != nil { + return "", err + } + switches := parseStdout(stdout) + if len(switches) > 0 { + log.Infof("Using switch %s", switches[0]) + return switches[0], nil + } + return "", fmt.Errorf("no vswitch found") +} + +func (d *Driver) wait() error { + log.Infof("Waiting for host to start...") + for { + ip, _ := d.GetIP() + if ip != "" { + break + } + time.Sleep(1 * time.Second) + } + return nil +} + +func (d *Driver) Start() error { + command := []string{ + "Start-VM", + "-Name", d.MachineName} + _, err := execute(command) + if err != nil { + return err + } + + if err := d.wait(); err != nil { + return err + } + + d.IPAddress, err = d.GetIP() + return err +} + +func (d *Driver) Stop() error { + command := []string{ + "Stop-VM", + "-Name", d.MachineName} + _, err := execute(command) + if err != nil { + return err + } + for { + s, err := d.GetState() + if err != nil { + return err + } + if s == state.Running { + time.Sleep(1 * time.Second) + } else { + break + } + } + d.IPAddress = "" + return nil +} + +func (d *Driver) Remove() error { + s, err := d.GetState() + if err != nil { + return err + } + if s == state.Running { + if err := d.Kill(); err != nil { + return err + } + } + command := []string{ + "Remove-VM", + "-Name", d.MachineName, + "-Force"} + _, err = execute(command) + return err +} + +func (d *Driver) Restart() error { + err := d.Stop() + if err != nil { + return err + } + + return d.Start() +} + +func (d *Driver) Kill() error { + command := []string{ + "Stop-VM", + "-Name", d.MachineName, + "-TurnOff"} + _, err := execute(command) + if err != nil { + return err + } + for { + s, err := d.GetState() + if err != nil { + return err + } + if s == state.Running { + time.Sleep(1 * time.Second) + } else { + break + } + } + d.IPAddress = "" + return nil +} + +func (d *Driver) setMachineNameIfNotSet() { + if d.MachineName == "" { + d.MachineName = fmt.Sprintf("docker-machine-unknown") + } +} + +func (d *Driver) GetIP() (string, error) { + command := []string{ + "((", + "Get-VM", + "-Name", d.MachineName, + ").networkadapters[0]).ipaddresses[0]"} + stdout, err := execute(command) + if err != nil { + return "", err + } + resp := parseStdout(stdout) + if len(resp) < 1 { + return "", fmt.Errorf("IP not found") + } + return resp[0], nil +} + +func (d *Driver) publicSSHKeyPath() string { + return d.GetSSHKeyPath() + ".pub" +} + +func (d *Driver) generateDiskImage() error { + // Create a small fixed vhd, put the tar in, + // convert to dynamic, then resize + + d.diskImage = d.ResolveStorePath("disk.vhd") + fixed := d.ResolveStorePath("fixed.vhd") + log.Infof("Creating VHD") + command := []string{ + "New-VHD", + "-Path", fmt.Sprintf("'%s'", fixed), + "-SizeBytes", "10MB", + "-Fixed"} + _, err := execute(command) + if err != nil { + return err + } + + tarBuf, err := d.generateTar() + if err != nil { + return err + } + + file, err := os.OpenFile(fixed, os.O_WRONLY, 0644) + if err != nil { + return err + } + defer file.Close() + file.Seek(0, os.SEEK_SET) + _, err = file.Write(tarBuf.Bytes()) + if err != nil { + return err + } + file.Close() + + command = []string{ + "Convert-VHD", + "-Path", fmt.Sprintf("'%s'", fixed), + "-DestinationPath", fmt.Sprintf("'%s'", d.diskImage), + "-VHDType", "Dynamic"} + _, err = execute(command) + if err != nil { + return err + } + command = []string{ + "Resize-VHD", + "-Path", fmt.Sprintf("'%s'", d.diskImage), + "-SizeBytes", fmt.Sprintf("%dMB", d.DiskSize)} + _, err = execute(command) + if err != nil { + return err + } + + return err +} + +// Make a boot2docker VM disk image. +// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount +func (d *Driver) generateTar() (*bytes.Buffer, error) { + magicString := "boot2docker, please format-me" + + buf := new(bytes.Buffer) + tw := tar.NewWriter(buf) + + // magicString first so the automount script knows to format the disk + file := &tar.Header{Name: magicString, Size: int64(len(magicString))} + if err := tw.WriteHeader(file); err != nil { + return nil, err + } + if _, err := tw.Write([]byte(magicString)); err != nil { + return nil, err + } + // .ssh/key.pub => authorized_keys + file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700} + if err := tw.WriteHeader(file); err != nil { + return nil, err + } + pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath()) + if err != nil { + return nil, err + } + file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644} + if err := tw.WriteHeader(file); err != nil { + return nil, err + } + if _, err := tw.Write([]byte(pubKey)); err != nil { + return nil, err + } + file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644} + if err := tw.WriteHeader(file); err != nil { + return nil, err + } + if _, err := tw.Write([]byte(pubKey)); err != nil { + return nil, err + } + if err := tw.Close(); err != nil { + return nil, err + } + return buf, nil +} diff --git a/drivers/hyperv/hyperv_test.go b/drivers/hyperv/hyperv_test.go deleted file mode 100644 index 5683645238..0000000000 --- a/drivers/hyperv/hyperv_test.go +++ /dev/null @@ -1 +0,0 @@ -package hyperv diff --git a/drivers/hyperv/hyperv_windows.go b/drivers/hyperv/hyperv_windows.go deleted file mode 100644 index 849cbb8ab2..0000000000 --- a/drivers/hyperv/hyperv_windows.go +++ /dev/null @@ -1,509 +0,0 @@ -package hyperv - -import ( - "archive/tar" - "bytes" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "time" - - "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" -) - -type Driver struct { - *drivers.BaseDriver - boot2DockerURL string - boot2DockerLoc string - vSwitch string - diskImage string - diskSize int - memSize int -} - -func init() { - drivers.Register("hyper-v", &drivers.RegisteredDriver{ - New: NewDriver, - GetCreateFlags: GetCreateFlags, - }) -} - -// GetCreateFlags registers the flags this driver adds to -// "docker hosts create" -func GetCreateFlags() []cli.Flag { - return []cli.Flag{ - cli.StringFlag{ - Name: "hyper-v-boot2docker-url", - Usage: "Hyper-V URL of the boot2docker image. Defaults to the latest available version.", - }, - cli.StringFlag{ - Name: "hyper-v-boot2docker-location", - Usage: "Hyper-V local boot2docker iso. Overrides URL.", - }, - cli.StringFlag{ - Name: "hyper-v-virtual-switch", - Usage: "Hyper-V virtual switch name. Defaults to first found.", - }, - cli.IntFlag{ - Name: "hyper-v-disk-size", - Usage: "Hyper-V disk size for host in MB.", - Value: 20000, - }, - cli.IntFlag{ - Name: "hyper-v-memory", - Usage: "Hyper-V memory size for host in MB.", - Value: 1024, - }, - } -} - -func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { - d.boot2DockerURL = flags.String("hyper-v-boot2docker-url") - d.boot2DockerLoc = flags.String("hyper-v-boot2docker-location") - d.vSwitch = flags.String("hyper-v-virtual-switch") - d.diskSize = flags.Int("hyper-v-disk-size") - d.memSize = flags.Int("hyper-v-memory") - d.SwarmMaster = flags.Bool("swarm-master") - d.SwarmHost = flags.String("swarm-host") - d.SwarmDiscovery = flags.String("swarm-discovery") - d.SSHUser = "docker" - d.SSHPort = 22 - return nil -} - -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil -} - -func (d *Driver) GetSSHHostname() (string, error) { - return d.GetIP() -} - -func (d *Driver) GetSSHUsername() string { - if d.SSHUser == "" { - d.SSHUser = "docker" - } - - return d.SSHUser -} - -func (d *Driver) DriverName() string { - return "hyper-v" -} - -func (d *Driver) PreCreateCheck() error { - return nil -} - -func (d *Driver) GetURL() (string, error) { - ip, err := d.GetIP() - if err != nil { - return "", err - } - if ip == "" { - return "", nil - } - return fmt.Sprintf("tcp://%s:2376", ip), nil -} - -func (d *Driver) GetState() (state.State, error) { - - command := []string{ - "(", - "Get-VM", - "-Name", d.MachineName, - ").state"} - stdout, err := execute(command) - if err != nil { - return state.None, fmt.Errorf("Failed to find the VM status") - } - resp := parseStdout(stdout) - - if len(resp) < 1 { - return state.None, nil - } - switch resp[0] { - case "Running": - return state.Running, nil - case "Off": - return state.Stopped, nil - } - return state.None, nil -} - -func (d *Driver) Create() error { - err := hypervAvailable() - if err != nil { - return err - } - - d.setMachineNameIfNotSet() - - var isoURL string - - b2dutils := utils.NewB2dUtils("", "") - - if d.boot2DockerLoc == "" { - if d.boot2DockerURL != "" { - isoURL = d.boot2DockerURL - log.Infof("Downloading boot2docker.iso from %s...", isoURL) - if err := b2dutils.DownloadISO(d.ResolveStorePath("."), "boot2docker.iso", isoURL); err != nil { - return err - } - } else { - // todo: check latest release URL, download if it's new - // until then always use "latest" - isoURL, err = b2dutils.GetLatestBoot2DockerReleaseURL() - if err != nil { - log.Warnf("Unable to check for the latest release: %s", err) - - } - // todo: use real constant for .docker - rootPath := filepath.Join(utils.GetDockerDir()) - imgPath := filepath.Join(rootPath, "images") - commonIsoPath := filepath.Join(imgPath, "boot2docker.iso") - if _, err := os.Stat(commonIsoPath); os.IsNotExist(err) { - log.Infof("Downloading boot2docker.iso to %s...", commonIsoPath) - // just in case boot2docker.iso has been manually deleted - if _, err := os.Stat(imgPath); os.IsNotExist(err) { - if err := os.Mkdir(imgPath, 0700); err != nil { - return err - - } - - } - if err := b2dutils.DownloadISO(imgPath, "boot2docker.iso", isoURL); err != nil { - return err - - } - - } - isoDest := d.ResolveStorePath("boot2docker.iso") - if err := utils.CopyFile(commonIsoPath, isoDest); err != nil { - return err - - } - } - } else { - if err := utils.CopyFile(d.boot2DockerLoc, d.ResolveStorePath("boot2docker.iso")); err != nil { - return err - } - } - - log.Infof("Creating SSH key...") - - if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil { - return err - } - - log.Infof("Creating VM...") - - virtualSwitch, err := d.chooseVirtualSwitch() - if err != nil { - return err - } - - err = d.generateDiskImage() - if err != nil { - return err - } - - command := []string{ - "New-VM", - "-Name", d.MachineName, - "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")), - "-MemoryStartupBytes", fmt.Sprintf("%dMB", d.memSize)} - _, err = execute(command) - if err != nil { - return err - } - - command = []string{ - "Set-VMDvdDrive", - "-VMName", d.MachineName, - "-Path", fmt.Sprintf("'%s'", d.ResolveStorePath("boot2docker.iso"))} - _, err = execute(command) - if err != nil { - return err - } - - command = []string{ - "Add-VMHardDiskDrive", - "-VMName", d.MachineName, - "-Path", fmt.Sprintf("'%s'", d.diskImage)} - _, err = execute(command) - if err != nil { - return err - } - - command = []string{ - "Connect-VMNetworkAdapter", - "-VMName", d.MachineName, - "-SwitchName", fmt.Sprintf("'%s'", virtualSwitch)} - _, err = execute(command) - if err != nil { - return err - } - - log.Infof("Starting VM...") - if err := d.Start(); err != nil { - return err - } - - return nil -} - -func (d *Driver) chooseVirtualSwitch() (string, error) { - if d.vSwitch != "" { - return d.vSwitch, nil - } - command := []string{ - "@(Get-VMSwitch).Name"} - stdout, err := execute(command) - if err != nil { - return "", err - } - switches := parseStdout(stdout) - if len(switches) > 0 { - log.Infof("Using switch %s", switches[0]) - return switches[0], nil - } - return "", fmt.Errorf("no vswitch found") -} - -func (d *Driver) wait() error { - log.Infof("Waiting for host to start...") - for { - ip, _ := d.GetIP() - if ip != "" { - break - } - time.Sleep(1 * time.Second) - } - return nil -} - -func (d *Driver) Start() error { - command := []string{ - "Start-VM", - "-Name", d.MachineName} - _, err := execute(command) - if err != nil { - return err - } - - if err := d.wait(); err != nil { - return err - } - - d.IPAddress, err = d.GetIP() - return err -} - -func (d *Driver) Stop() error { - command := []string{ - "Stop-VM", - "-Name", d.MachineName} - _, err := execute(command) - if err != nil { - return err - } - for { - s, err := d.GetState() - if err != nil { - return err - } - if s == state.Running { - time.Sleep(1 * time.Second) - } else { - break - } - } - d.IPAddress = "" - return nil -} - -func (d *Driver) Remove() error { - s, err := d.GetState() - if err != nil { - return err - } - if s == state.Running { - if err := d.Kill(); err != nil { - return err - } - } - command := []string{ - "Remove-VM", - "-Name", d.MachineName, - "-Force"} - _, err = execute(command) - return err -} - -func (d *Driver) Restart() error { - err := d.Stop() - if err != nil { - return err - } - - return d.Start() -} - -func (d *Driver) Kill() error { - command := []string{ - "Stop-VM", - "-Name", d.MachineName, - "-TurnOff"} - _, err := execute(command) - if err != nil { - return err - } - for { - s, err := d.GetState() - if err != nil { - return err - } - if s == state.Running { - time.Sleep(1 * time.Second) - } else { - break - } - } - d.IPAddress = "" - return nil -} - -func (d *Driver) setMachineNameIfNotSet() { - if d.MachineName == "" { - d.MachineName = fmt.Sprintf("docker-machine-unknown") - } -} - -func (d *Driver) GetIP() (string, error) { - command := []string{ - "((", - "Get-VM", - "-Name", d.MachineName, - ").networkadapters[0]).ipaddresses[0]"} - stdout, err := execute(command) - if err != nil { - return "", err - } - resp := parseStdout(stdout) - if len(resp) < 1 { - return "", fmt.Errorf("IP not found") - } - return resp[0], nil -} - -func (d *Driver) publicSSHKeyPath() string { - return d.GetSSHKeyPath() + ".pub" -} - -func (d *Driver) generateDiskImage() error { - // Create a small fixed vhd, put the tar in, - // convert to dynamic, then resize - - d.diskImage = d.ResolveStorePath("disk.vhd") - fixed := d.ResolveStorePath("fixed.vhd") - log.Infof("Creating VHD") - command := []string{ - "New-VHD", - "-Path", fmt.Sprintf("'%s'", fixed), - "-SizeBytes", "10MB", - "-Fixed"} - _, err := execute(command) - if err != nil { - return err - } - - tarBuf, err := d.generateTar() - if err != nil { - return err - } - - file, err := os.OpenFile(fixed, os.O_WRONLY, 0644) - if err != nil { - return err - } - defer file.Close() - file.Seek(0, os.SEEK_SET) - _, err = file.Write(tarBuf.Bytes()) - if err != nil { - return err - } - file.Close() - - command = []string{ - "Convert-VHD", - "-Path", fmt.Sprintf("'%s'", fixed), - "-DestinationPath", fmt.Sprintf("'%s'", d.diskImage), - "-VHDType", "Dynamic"} - _, err = execute(command) - if err != nil { - return err - } - command = []string{ - "Resize-VHD", - "-Path", fmt.Sprintf("'%s'", d.diskImage), - "-SizeBytes", fmt.Sprintf("%dMB", d.diskSize)} - _, err = execute(command) - if err != nil { - return err - } - - return err -} - -// Make a boot2docker VM disk image. -// See https://github.com/boot2docker/boot2docker/blob/master/rootfs/rootfs/etc/rc.d/automount -func (d *Driver) generateTar() (*bytes.Buffer, error) { - magicString := "boot2docker, please format-me" - - buf := new(bytes.Buffer) - tw := tar.NewWriter(buf) - - // magicString first so the automount script knows to format the disk - file := &tar.Header{Name: magicString, Size: int64(len(magicString))} - if err := tw.WriteHeader(file); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(magicString)); err != nil { - return nil, err - } - // .ssh/key.pub => authorized_keys - file = &tar.Header{Name: ".ssh", Typeflag: tar.TypeDir, Mode: 0700} - if err := tw.WriteHeader(file); err != nil { - return nil, err - } - pubKey, err := ioutil.ReadFile(d.publicSSHKeyPath()) - if err != nil { - return nil, err - } - file = &tar.Header{Name: ".ssh/authorized_keys", Size: int64(len(pubKey)), Mode: 0644} - if err := tw.WriteHeader(file); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(pubKey)); err != nil { - return nil, err - } - file = &tar.Header{Name: ".ssh/authorized_keys2", Size: int64(len(pubKey)), Mode: 0644} - if err := tw.WriteHeader(file); err != nil { - return nil, err - } - if _, err := tw.Write([]byte(pubKey)); err != nil { - return nil, err - } - if err := tw.Close(); err != nil { - return nil, err - } - return buf, nil -} diff --git a/drivers/hyperv/powershell_windows.go b/drivers/hyperv/powershell.go similarity index 96% rename from drivers/hyperv/powershell_windows.go rename to drivers/hyperv/powershell.go index 19d012494b..eb204ac13f 100644 --- a/drivers/hyperv/powershell_windows.go +++ b/drivers/hyperv/powershell.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) var powershell string diff --git a/drivers/none/none.go b/drivers/none/none.go index 94dd7e7efc..17fa8f904a 100644 --- a/drivers/none/none.go +++ b/drivers/none/none.go @@ -5,8 +5,8 @@ import ( neturl "net/url" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/state" ) // Driver is the driver used when no driver is selected. It is used to @@ -19,7 +19,6 @@ type Driver struct { func init() { drivers.Register("none", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -34,9 +33,13 @@ func GetCreateFlags() []cli.Flag { } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{inner, ""}, nil +func NewDriver(hostName, storePath string) *Driver { + return &Driver{ + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) Create() error { diff --git a/drivers/none/none_test.go b/drivers/none/none_test.go deleted file mode 100644 index 5467f722f3..0000000000 --- a/drivers/none/none_test.go +++ /dev/null @@ -1 +0,0 @@ -package none diff --git a/drivers/openstack/client.go b/drivers/openstack/client.go index 64905bad85..a4838e8866 100644 --- a/drivers/openstack/client.go +++ b/drivers/openstack/client.go @@ -6,9 +6,9 @@ import ( "net/http" "time" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" - "github.com/docker/machine/version" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/version" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/openstack" "github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs" @@ -136,7 +136,7 @@ func (c *GenericClient) DeleteInstance(d *Driver) error { } func (c *GenericClient) WaitForInstanceStatus(d *Driver, status string) error { - return utils.WaitForSpecificOrError(func() (bool, error) { + return mcnutils.WaitForSpecificOrError(func() (bool, error) { current, err := servers.Get(c.Compute, d.MachineId).Extract() if err != nil { return true, err @@ -437,7 +437,7 @@ func (c *GenericClient) Authenticate(d *Driver) error { return err } - provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.Version)) + provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.ApiVersion)) if d.Insecure { // Configure custom TLS settings. diff --git a/drivers/openstack/client_test.go b/drivers/openstack/client_test.go deleted file mode 100644 index a0d778caa2..0000000000 --- a/drivers/openstack/client_test.go +++ /dev/null @@ -1 +0,0 @@ -package openstack diff --git a/drivers/openstack/openstack.go b/drivers/openstack/openstack.go index 7e25efaacb..e93ecfd3a3 100644 --- a/drivers/openstack/openstack.go +++ b/drivers/openstack/openstack.go @@ -7,11 +7,11 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) type Driver struct { @@ -42,9 +42,14 @@ type Driver struct { client Client } +const ( + defaultSSHUser = "root" + defaultSSHPort = 22 + defaultActiveTimeout = 200 +) + func init() { drivers.Register("openstack", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -158,35 +163,36 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "openstack-ssh-user", Usage: "OpenStack SSH user", - Value: "root", + Value: defaultSSHUser, }, cli.IntFlag{ Name: "openstack-ssh-port", Usage: "OpenStack SSH port", - Value: 22, + Value: defaultSSHPort, }, cli.IntFlag{ Name: "openstack-active-timeout", Usage: "OpenStack active timeout", - Value: 200, + Value: defaultActiveTimeout, }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - log.WithFields(log.Fields{ - "machineName": machineName, - "storePath": storePath, - "caCert": caCert, - "privateKey": privateKey, - }).Debug("Instantiating OpenStack driver...") - - return NewDerivedDriver(machineName, storePath, &GenericClient{}, caCert, privateKey) +func NewDriver(hostName, storePath string) drivers.Driver { + return NewDerivedDriver(hostName, storePath) } -func NewDerivedDriver(machineName string, storePath string, client Client, caCert string, privateKey string) (*Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner, client: client}, nil +func NewDerivedDriver(hostName, storePath string) *Driver { + return &Driver{ + client: &GenericClient{}, + ActiveTimeout: defaultActiveTimeout, + BaseDriver: &drivers.BaseDriver{ + SSHUser: defaultSSHUser, + SSHPort: defaultSSHPort, + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -310,7 +316,7 @@ func (d *Driver) PreCreateCheck() error { } func (d *Driver) Create() error { - d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, utils.GenerateRandomID()) + d.KeyPairName = fmt.Sprintf("%s-%s", d.MachineName, mcnutils.GenerateRandomID()) if err := d.resolveIds(); err != nil { return err diff --git a/drivers/openstack/openstack_test.go b/drivers/openstack/openstack_test.go deleted file mode 100644 index a0d778caa2..0000000000 --- a/drivers/openstack/openstack_test.go +++ /dev/null @@ -1 +0,0 @@ -package openstack diff --git a/drivers/rackspace/client.go b/drivers/rackspace/client.go index c506a4578c..93d1c5a3c4 100644 --- a/drivers/rackspace/client.go +++ b/drivers/rackspace/client.go @@ -4,8 +4,8 @@ import ( "fmt" "github.com/docker/machine/drivers/openstack" - "github.com/docker/machine/log" - "github.com/docker/machine/version" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/version" "github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud/rackspace" ) @@ -17,7 +17,6 @@ func unsupportedOpErr(operation string) error { // Client is a Rackspace specialization of the generic OpenStack driver. type Client struct { openstack.GenericClient - driver *Driver } @@ -42,7 +41,7 @@ func (c *Client) Authenticate(d *openstack.Driver) error { return err } - provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.Version)) + provider.UserAgent.Prepend(fmt.Sprintf("docker-machine/v%s", version.ApiVersion)) err = rackspace.Authenticate(provider, opts) if err != nil { diff --git a/drivers/rackspace/client_test.go b/drivers/rackspace/client_test.go deleted file mode 100644 index 5d17b32caa..0000000000 --- a/drivers/rackspace/client_test.go +++ /dev/null @@ -1 +0,0 @@ -package rackspace diff --git a/drivers/rackspace/rackspace.go b/drivers/rackspace/rackspace.go index cbb7cedaf5..31517ec0e3 100644 --- a/drivers/rackspace/rackspace.go +++ b/drivers/rackspace/rackspace.go @@ -4,9 +4,9 @@ import ( "fmt" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" "github.com/docker/machine/drivers/openstack" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" ) // Driver is a machine driver for Rackspace. It's a specialization of the generic OpenStack one. @@ -16,9 +16,16 @@ type Driver struct { APIKey string } +const ( + defaultEndpointType = "publicURL" + defaultFlavorId = "general1-1" + defaultSSHUser = "root" + defaultSSHPort = 22 + defaultDockerInstall = "true" +) + func init() { drivers.Register("rackspace", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -49,7 +56,7 @@ func GetCreateFlags() []cli.Flag { EnvVar: "OS_ENDPOINT_TYPE", Name: "rackspace-endpoint-type", Usage: "Rackspace endpoint type (adminURL, internalURL or the default publicURL)", - Value: "publicURL", + Value: defaultEndpointType, }, cli.StringFlag{ Name: "rackspace-image-id", @@ -58,45 +65,38 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "rackspace-flavor-id", Usage: "Rackspace flavor ID. Default: General Purpose 1GB", - Value: "general1-1", + Value: defaultFlavorId, EnvVar: "OS_FLAVOR_ID", }, cli.StringFlag{ Name: "rackspace-ssh-user", Usage: "SSH user for the newly booted machine. Set to root by default", - Value: "root", + Value: defaultSSHUser, }, cli.IntFlag{ Name: "rackspace-ssh-port", Usage: "SSH port for the newly booted machine. Set to 22 by default", - Value: 22, + Value: defaultSSHPort, }, cli.StringFlag{ Name: "rackspace-docker-install", Usage: "Set if docker have to be installed on the machine", - Value: "true", + Value: defaultDockerInstall, }, } } // NewDriver instantiates a Rackspace driver. -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { +func NewDriver(machineName, storePath string) drivers.Driver { log.WithFields(log.Fields{ "machineName": machineName, - "storePath": storePath, - "caCert": caCert, - "privateKey": privateKey, }).Debug("Instantiating Rackspace driver.") - client := &Client{} - inner, err := openstack.NewDerivedDriver(machineName, storePath, client, caCert, privateKey) - if err != nil { - return nil, err - } + inner := openstack.NewDerivedDriver(machineName, storePath) - driver := &Driver{Driver: inner} - client.driver = driver - return driver, nil + return &Driver{ + Driver: inner, + } } // DriverName is the user-visible name of this driver. diff --git a/drivers/rackspace/rackspace_test.go b/drivers/rackspace/rackspace_test.go deleted file mode 100644 index 5d17b32caa..0000000000 --- a/drivers/rackspace/rackspace_test.go +++ /dev/null @@ -1 +0,0 @@ -package rackspace diff --git a/drivers/softlayer/driver.go b/drivers/softlayer/driver.go index 077b3911d6..a396e46cd5 100644 --- a/drivers/softlayer/driver.go +++ b/drivers/softlayer/driver.go @@ -8,10 +8,10 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) const ( @@ -41,16 +41,42 @@ type deviceConfig struct { PrivateVLAN int } +const ( + defaultMemory = 1024 + defaultDiskSize = 0 + defaultRegion = "dal01" + defaultCpus = 1 + defaultImage = "UBUNTU_LATEST" + defaultPublicVLANIP = 0 + defaultPrivateVLANIP = 0 +) + func init() { drivers.Register("softlayer", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + Client: &Client{ + Endpoint: ApiEndpoint, + }, + deviceConfig: &deviceConfig{ + HourlyBilling: true, + DiskSize: defaultDiskSize, + Image: defaultImage, + Memory: defaultMemory, + Cpu: defaultCpus, + Region: defaultRegion, + PrivateVLAN: defaultPrivateVLANIP, + PublicVLAN: defaultPublicVLANIP, + }, + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -67,49 +93,45 @@ func GetCreateFlags() []cli.Flag { EnvVar: "SOFTLAYER_MEMORY", Name: "softlayer-memory", Usage: "Memory in MB for machine", - Value: 1024, + Value: defaultMemory, }, cli.IntFlag{ EnvVar: "SOFTLAYER_DISK_SIZE", Name: "softlayer-disk-size", Usage: "Disk size for machine, a value of 0 uses the default size on softlayer", - Value: 0, + Value: defaultDiskSize, }, cli.StringFlag{ EnvVar: "SOFTLAYER_USER", Name: "softlayer-user", Usage: "softlayer user account name", - Value: "", }, cli.StringFlag{ EnvVar: "SOFTLAYER_API_KEY", Name: "softlayer-api-key", Usage: "softlayer user API key", - Value: "", }, cli.StringFlag{ EnvVar: "SOFTLAYER_REGION", Name: "softlayer-region", Usage: "softlayer region for machine", - Value: "dal01", + Value: defaultRegion, }, cli.IntFlag{ EnvVar: "SOFTLAYER_CPU", Name: "softlayer-cpu", Usage: "number of CPU's for the machine", - Value: 1, + Value: defaultCpus, }, cli.StringFlag{ EnvVar: "SOFTLAYER_HOSTNAME", Name: "softlayer-hostname", Usage: "hostname for the machine - defaults to machine name", - Value: "", }, cli.StringFlag{ EnvVar: "SOFTLAYER_DOMAIN", Name: "softlayer-domain", Usage: "domain name for machine", - Value: "", }, cli.StringFlag{ EnvVar: "SOFTLAYER_API_ENDPOINT", @@ -136,19 +158,17 @@ func GetCreateFlags() []cli.Flag { EnvVar: "SOFTLAYER_IMAGE", Name: "softlayer-image", Usage: "OS image for machine", - Value: "UBUNTU_LATEST", + Value: defaultImage, }, cli.IntFlag{ EnvVar: "SOFTLAYER_PUBLIC_VLAN_ID", Name: "softlayer-public-vlan-id", Usage: "", - Value: 0, }, cli.IntFlag{ EnvVar: "SOFTLAYER_PRIVATE_VLAN_ID", Name: "softlayer-private-vlan-id", Usage: "", - Value: 0, }, } } diff --git a/drivers/softlayer/driver_test.go b/drivers/softlayer/driver_test.go index 79038f324c..44688163e8 100644 --- a/drivers/softlayer/driver_test.go +++ b/drivers/softlayer/driver_test.go @@ -80,10 +80,7 @@ func getTestDriver() (*Driver, error) { } defer cleanup() - d, err := NewDriver(machineTestName, storePath, machineTestCaCert, machineTestPrivateKey) - if err != nil { - return nil, err - } + d := NewDriver(machineTestName, storePath) d.SetConfigFromFlags(getDefaultTestDriverFlags()) drv := d.(*Driver) return drv, nil diff --git a/drivers/softlayer/softlayer_test.go b/drivers/softlayer/softlayer_test.go deleted file mode 100644 index 22853c9ea9..0000000000 --- a/drivers/softlayer/softlayer_test.go +++ /dev/null @@ -1 +0,0 @@ -package softlayer diff --git a/drivers/virtualbox/network_test.go b/drivers/virtualbox/network_test.go deleted file mode 100644 index 50e40b533b..0000000000 --- a/drivers/virtualbox/network_test.go +++ /dev/null @@ -1 +0,0 @@ -package virtualbox diff --git a/drivers/virtualbox/vbm.go b/drivers/virtualbox/vbm.go index a6f9434adf..13e9065e20 100644 --- a/drivers/virtualbox/vbm.go +++ b/drivers/virtualbox/vbm.go @@ -11,7 +11,7 @@ import ( "runtime" "strings" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) var ( @@ -68,15 +68,17 @@ func vbmOut(args ...string) (string, error) { func vbmOutErr(args ...string) (string, string, error) { cmd := exec.Command(vboxManageCmd, args...) - log.Debugf("executing: %v %v", vboxManageCmd, strings.Join(args, " ")) + log.Debugf("COMMAND: %v %v", vboxManageCmd, strings.Join(args, " ")) var stdout bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() stderrStr := stderr.String() - log.Debugf("STDOUT: %v", stdout.String()) - log.Debugf("STDERR: %v", stderrStr) + if len(args) > 0 { + log.Debugf("STDOUT:\n{\n%v}", stdout.String()) + log.Debugf("STDERR:\n{\n%v}", stderrStr) + } if err != nil { if ee, ok := err.(*exec.Error); ok && ee == exec.ErrNotFound { err = ErrVBMNotFound diff --git a/drivers/virtualbox/vbm_test.go b/drivers/virtualbox/vbm_test.go deleted file mode 100644 index 50e40b533b..0000000000 --- a/drivers/virtualbox/vbm_test.go +++ /dev/null @@ -1 +0,0 @@ -package virtualbox diff --git a/drivers/virtualbox/virtualbox.go b/drivers/virtualbox/virtualbox.go index 9052b0872b..0c24474ed4 100644 --- a/drivers/virtualbox/virtualbox.go +++ b/drivers/virtualbox/virtualbox.go @@ -19,18 +19,24 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) const ( - isoFilename = "boot2docker.iso" - defaultHostOnlyCIDR = "192.168.99.1/24" - defaultNictype = "82540EM" - defaultNicpromisc = "deny" + isoFilename = "boot2docker.iso" + defaultCPU = 1 + defaultMemory = 1024 + defaultBoot2DockerURL = "" + defaultBoot2DockerImportVM = "" + defaultHostOnlyCIDR = "192.168.99.1/24" + defaultHostOnlyNictype = "82540EM" + defaultHostOnlyPromiscMode = "deny" + defaultNoShare = false + defaultDiskSize = 20000 ) var ( @@ -52,11 +58,25 @@ type Driver struct { func init() { drivers.Register("virtualbox", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } +func NewDriver(hostName, storePath string) *Driver { + return &Driver{ + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + Memory: defaultMemory, + CPU: defaultCPU, + DiskSize: defaultDiskSize, + HostOnlyCIDR: defaultHostOnlyCIDR, + HostOnlyNicType: defaultHostOnlyNictype, + HostOnlyPromiscMode: defaultHostOnlyPromiscMode, + } +} + // RegisterCreateFlags registers the flags this driver adds to // "docker hosts create" func GetCreateFlags() []cli.Flag { @@ -65,30 +85,30 @@ func GetCreateFlags() []cli.Flag { EnvVar: "VIRTUALBOX_MEMORY_SIZE", Name: "virtualbox-memory", Usage: "Size of memory for host in MB", - Value: 1024, + Value: defaultMemory, }, cli.IntFlag{ EnvVar: "VIRTUALBOX_CPU_COUNT", Name: "virtualbox-cpu-count", Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)", - Value: 1, + Value: defaultCPU, }, cli.IntFlag{ EnvVar: "VIRTUALBOX_DISK_SIZE", Name: "virtualbox-disk-size", Usage: "Size of disk for host in MB", - Value: 20000, + Value: defaultDiskSize, }, cli.StringFlag{ EnvVar: "VIRTUALBOX_BOOT2DOCKER_URL", Name: "virtualbox-boot2docker-url", Usage: "The URL of the boot2docker image. Defaults to the latest available version", - Value: "", + Value: defaultBoot2DockerURL, }, cli.StringFlag{ Name: "virtualbox-import-boot2docker-vm", Usage: "The name of a Boot2Docker VM to import", - Value: "", + Value: defaultBoot2DockerImportVM, }, cli.StringFlag{ Name: "virtualbox-hostonly-cidr", @@ -99,13 +119,13 @@ func GetCreateFlags() []cli.Flag { cli.StringFlag{ Name: "virtualbox-hostonly-nictype", Usage: "Specify the Host Only Network Adapter Type", - Value: defaultNictype, + Value: defaultHostOnlyNictype, EnvVar: "VIRTUALBOX_HOSTONLY_NIC_TYPE", }, cli.StringFlag{ Name: "virtualbox-hostonly-nicpromisc", Usage: "Specify the Host Only Network Adapter Promiscuous Mode", - Value: defaultNicpromisc, + Value: defaultHostOnlyPromiscMode, EnvVar: "VIRTUALBOX_HOSTONLY_NIC_PROMISC", }, cli.BoolFlag{ @@ -115,11 +135,6 @@ func GetCreateFlags() []cli.Flag { } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil -} - func (d *Driver) GetSSHHostname() (string, error) { return "localhost", nil } @@ -179,7 +194,7 @@ func (d *Driver) Create() error { return err } - b2dutils := utils.NewB2dUtils("", "") + b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath) if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err } @@ -216,8 +231,8 @@ func (d *Driver) Create() error { d.Memory = vmInfo.Memory log.Debugf("Importing SSH key...") - keyPath := filepath.Join(utils.GetHomeDir(), ".ssh", "id_boot2docker") - if err := utils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { + keyPath := filepath.Join(mcnutils.GetHomeDir(), ".ssh", "id_boot2docker") + if err := mcnutils.CopyFile(keyPath, d.GetSSHKeyPath()); err != nil { return err } } else { @@ -416,7 +431,7 @@ func (d *Driver) Start() error { } // Bail if we don't get an IP from DHCP after a given number of seconds. - if err := utils.WaitForSpecific(d.hostOnlyIpAvailable, 5, 4*time.Second); err != nil { + if err := mcnutils.WaitForSpecific(d.hostOnlyIpAvailable, 5, 4*time.Second); err != nil { return err } @@ -510,12 +525,6 @@ func (d *Driver) GetState() (state.State, error) { return state.None, nil } -func (d *Driver) setMachineNameIfNotSet() { - if d.MachineName == "" { - d.MachineName = fmt.Sprintf("docker-machine-unknown") - } -} - func (d *Driver) GetIP() (string, error) { // DHCP is used to get the IP, so virtualbox hosts don't have IPs unless // they are running diff --git a/drivers/vmwarefusion/fusion_darwin.go b/drivers/vmwarefusion/fusion.go similarity index 94% rename from drivers/vmwarefusion/fusion_darwin.go rename to drivers/vmwarefusion/fusion.go index e76feabffb..ea165058b0 100644 --- a/drivers/vmwarefusion/fusion_darwin.go +++ b/drivers/vmwarefusion/fusion.go @@ -18,11 +18,11 @@ import ( "time" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" cryptossh "golang.org/x/crypto/ssh" ) @@ -48,9 +48,16 @@ type Driver struct { ConfigDriveURL string } +const ( + defaultSSHUser = B2DUser + defaultSSHPass = B2DPass + defaultDiskSize = 20000 + defaultCpus = 1 + defaultMemory = 1024 +) + func init() { drivers.Register("vmwarefusion", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -73,38 +80,47 @@ func GetCreateFlags() []cli.Flag { EnvVar: "FUSION_CPU_COUNT", Name: "vmwarefusion-cpu-count", Usage: "number of CPUs for the machine (-1 to use the number of CPUs available)", - Value: 1, + Value: defaultCpus, }, cli.IntFlag{ EnvVar: "FUSION_MEMORY_SIZE", Name: "vmwarefusion-memory-size", Usage: "Fusion size of memory for host VM (in MB)", - Value: 1024, + Value: defaultMemory, }, cli.IntFlag{ EnvVar: "FUSION_DISK_SIZE", Name: "vmwarefusion-disk-size", Usage: "Fusion size of disk for host VM (in MB)", - Value: 20000, + Value: defaultDiskSize, }, cli.StringFlag{ EnvVar: "FUSION_SSH_USER", Name: "vmwarefusion-ssh-user", Usage: "SSH user", - Value: B2DUser, + Value: defaultSSHUser, }, cli.StringFlag{ EnvVar: "FUSION_SSH_PASSWORD", Name: "vmwarefusion-ssh-password", Usage: "SSH password", - Value: B2DPass, + Value: defaultSSHPass, }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + CPUS: defaultCpus, + Memory: defaultMemory, + DiskSize: defaultDiskSize, + SSHPassword: defaultSSHPass, + BaseDriver: &drivers.BaseDriver{ + SSHUser: defaultSSHUser, + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -129,7 +145,6 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.DiskSize = flags.Int("vmwarefusion-disk-size") d.Boot2DockerURL = flags.String("vmwarefusion-boot2docker-url") d.ConfigDriveURL = flags.String("vmwarefusion-configdrive-url") - d.ConfigDriveISO = d.ResolveStorePath(isoConfigDrive) d.ISO = d.ResolveStorePath(isoFilename) d.SwarmMaster = flags.Bool("swarm-master") d.SwarmHost = flags.String("swarm-host") @@ -191,8 +206,7 @@ func (d *Driver) PreCreateCheck() error { } func (d *Driver) Create() error { - - b2dutils := utils.NewB2dUtils("", "") + b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath) if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err } diff --git a/drivers/vmwarefusion/fusion_test.go b/drivers/vmwarefusion/fusion_test.go deleted file mode 100644 index 5eacfbb54e..0000000000 --- a/drivers/vmwarefusion/fusion_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarefusion diff --git a/drivers/vmwarefusion/vmrun_darwin.go b/drivers/vmwarefusion/vmrun.go similarity index 97% rename from drivers/vmwarefusion/vmrun_darwin.go rename to drivers/vmwarefusion/vmrun.go index 33c77efe45..67ad7b90a1 100644 --- a/drivers/vmwarefusion/vmrun_darwin.go +++ b/drivers/vmwarefusion/vmrun.go @@ -8,11 +8,12 @@ import ( "bytes" "errors" "fmt" - "github.com/docker/machine/log" "os" "os/exec" "path/filepath" "strings" + + "github.com/docker/machine/libmachine/log" ) var ( diff --git a/drivers/vmwarefusion/vmrun_test.go b/drivers/vmwarefusion/vmrun_test.go deleted file mode 100644 index 5eacfbb54e..0000000000 --- a/drivers/vmwarefusion/vmrun_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarefusion diff --git a/drivers/vmwarefusion/vmwarefusion.go b/drivers/vmwarefusion/vmwarefusion.go deleted file mode 100644 index a7b86c9580..0000000000 --- a/drivers/vmwarefusion/vmwarefusion.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package vmwarefusion is empty to allow builds on non-darwin platforms -package vmwarefusion diff --git a/drivers/vmwarefusion/vmx_darwin.go b/drivers/vmwarefusion/vmx.go similarity index 100% rename from drivers/vmwarefusion/vmx_darwin.go rename to drivers/vmwarefusion/vmx.go diff --git a/drivers/vmwarefusion/vmx_test.go b/drivers/vmwarefusion/vmx_test.go deleted file mode 100644 index 5eacfbb54e..0000000000 --- a/drivers/vmwarefusion/vmx_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarefusion diff --git a/drivers/vmwarevcloudair/vcloudair.go b/drivers/vmwarevcloudair/vcloudair.go index 3749246476..2443de6c74 100644 --- a/drivers/vmwarevcloudair/vcloudair.go +++ b/drivers/vmwarevcloudair/vcloudair.go @@ -12,11 +12,11 @@ import ( "github.com/vmware/govcloudair" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) type Driver struct { @@ -37,9 +37,17 @@ type Driver struct { VAppID string } +const ( + defaultCatalog = "Public Catalog" + defaultCatalogItem = "Ubuntu Server 12.04 LTS (amd64 20150127)" + defaultCpus = 1 + defaultMemory = 2048 + defaultSSHPort = 22 + defaultDockerPort = 2376 +) + func init() { drivers.Register("vmwarevcloudair", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -87,13 +95,13 @@ func GetCreateFlags() []cli.Flag { EnvVar: "VCLOUDAIR_CATALOG", Name: "vmwarevcloudair-catalog", Usage: "vCloud Air Catalog (default is Public Catalog)", - Value: "Public Catalog", + Value: defaultCatalog, }, cli.StringFlag{ EnvVar: "VCLOUDAIR_CATALOGITEM", Name: "vmwarevcloudair-catalogitem", Usage: "vCloud Air Catalog Item (default is Ubuntu Precise)", - Value: "Ubuntu Server 12.04 LTS (amd64 20150127)", + Value: defaultCatalogItem, }, // BoolTFlag is true by default. @@ -107,32 +115,42 @@ func GetCreateFlags() []cli.Flag { EnvVar: "VCLOUDAIR_CPU_COUNT", Name: "vmwarevcloudair-cpu-count", Usage: "vCloud Air VM Cpu Count (default 1)", - Value: 1, + Value: defaultCpus, }, cli.IntFlag{ EnvVar: "VCLOUDAIR_MEMORY_SIZE", Name: "vmwarevcloudair-memory-size", Usage: "vCloud Air VM Memory Size in MB (default 2048)", - Value: 2048, + Value: defaultMemory, }, cli.IntFlag{ EnvVar: "VCLOUDAIR_SSH_PORT", Name: "vmwarevcloudair-ssh-port", Usage: "vCloud Air SSH port", - Value: 22, + Value: defaultSSHPort, }, cli.IntFlag{ EnvVar: "VCLOUDAIR_DOCKER_PORT", Name: "vmwarevcloudair-docker-port", Usage: "vCloud Air Docker port", - Value: 2376, + Value: defaultDockerPort, }, } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + Catalog: defaultCatalog, + CatalogItem: defaultCatalogItem, + CPUCount: defaultCpus, + MemorySize: defaultMemory, + DockerPort: defaultDockerPort, + BaseDriver: &drivers.BaseDriver{ + SSHPort: defaultSSHPort, + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -645,7 +663,7 @@ func (d *Driver) Kill() error { // Helpers func generateVMName() string { - randomID := utils.TruncateID(utils.GenerateRandomID()) + randomID := mcnutils.TruncateID(mcnutils.GenerateRandomID()) return fmt.Sprintf("docker-host-%s", randomID) } diff --git a/drivers/vmwarevcloudair/vcloudair_test.go b/drivers/vmwarevcloudair/vcloudair_test.go deleted file mode 100644 index 99039e4f38..0000000000 --- a/drivers/vmwarevcloudair/vcloudair_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarevcloudair diff --git a/drivers/vmwarevsphere/errors/config_error_test.go b/drivers/vmwarevsphere/errors/config_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/config_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/datastore_error_test.go b/drivers/vmwarevsphere/errors/datastore_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/datastore_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/errors_test.go b/drivers/vmwarevsphere/errors/errors_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/errors_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/govc_error_test.go b/drivers/vmwarevsphere/errors/govc_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/govc_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/guest_error_test.go b/drivers/vmwarevsphere/errors/guest_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/guest_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/login_error_test.go b/drivers/vmwarevsphere/errors/login_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/login_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/state_error_test.go b/drivers/vmwarevsphere/errors/state_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/state_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/errors/vm_error_test.go b/drivers/vmwarevsphere/errors/vm_error_test.go deleted file mode 100644 index 04b32187ba..0000000000 --- a/drivers/vmwarevsphere/errors/vm_error_test.go +++ /dev/null @@ -1 +0,0 @@ -package errors diff --git a/drivers/vmwarevsphere/govc.go b/drivers/vmwarevsphere/govc.go index c8612e8fec..6848e7793a 100644 --- a/drivers/vmwarevsphere/govc.go +++ b/drivers/vmwarevsphere/govc.go @@ -9,7 +9,7 @@ import ( "os/exec" "strings" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) var ( diff --git a/drivers/vmwarevsphere/govc_test.go b/drivers/vmwarevsphere/govc_test.go deleted file mode 100644 index 6c68b2fc86..0000000000 --- a/drivers/vmwarevsphere/govc_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarevsphere diff --git a/drivers/vmwarevsphere/vc_conn.go b/drivers/vmwarevsphere/vc_conn.go index 78f9bd310f..a18c7c27bc 100644 --- a/drivers/vmwarevsphere/vc_conn.go +++ b/drivers/vmwarevsphere/vc_conn.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/docker/machine/drivers/vmwarevsphere/errors" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) type VcConn struct { diff --git a/drivers/vmwarevsphere/vc_conn_test.go b/drivers/vmwarevsphere/vc_conn_test.go deleted file mode 100644 index 6c68b2fc86..0000000000 --- a/drivers/vmwarevsphere/vc_conn_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarevsphere diff --git a/drivers/vmwarevsphere/vsphere.go b/drivers/vmwarevsphere/vsphere.go index 829cd5e24e..153feaced8 100644 --- a/drivers/vmwarevsphere/vsphere.go +++ b/drivers/vmwarevsphere/vsphere.go @@ -9,27 +9,25 @@ import ( "fmt" "io/ioutil" "os" - "path" "path/filepath" "strings" "time" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" "github.com/codegangsta/cli" - "github.com/docker/machine/drivers" "github.com/docker/machine/drivers/vmwarevsphere/errors" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" ) const ( - isoFilename = "boot2docker.iso" - B2DISOName = isoFilename - DefaultCPUNumber = 2 - B2DUser = "docker" - B2DPass = "tcuser" + isoFilename = "boot2docker.iso" + B2DISOName = isoFilename + B2DUser = "docker" + B2DPass = "tcuser" ) type Driver struct { @@ -49,9 +47,14 @@ type Driver struct { ISO string } +const ( + defaultCpus = 2 + defaultMemory = 2048 + defaultDiskSize = 20000 +) + func init() { drivers.Register("vmwarevsphere", &drivers.RegisteredDriver{ - New: NewDriver, GetCreateFlags: GetCreateFlags, }) } @@ -64,19 +67,19 @@ func GetCreateFlags() []cli.Flag { EnvVar: "VSPHERE_CPU_COUNT", Name: "vmwarevsphere-cpu-count", Usage: "vSphere CPU number for docker VM", - Value: 2, + Value: defaultCpus, }, cli.IntFlag{ EnvVar: "VSPHERE_MEMORY_SIZE", Name: "vmwarevsphere-memory-size", Usage: "vSphere size of memory for docker VM (in MB)", - Value: 2048, + Value: defaultMemory, }, cli.IntFlag{ EnvVar: "VSPHERE_DISK_SIZE", Name: "vmwarevsphere-disk-size", Usage: "vSphere size of disk for docker VM (in MB)", - Value: 20000, + Value: defaultDiskSize, }, cli.StringFlag{ EnvVar: "VSPHERE_BOOT2DOCKER_URL", @@ -126,9 +129,16 @@ func GetCreateFlags() []cli.Flag { } } -func NewDriver(machineName string, storePath string, caCert string, privateKey string) (drivers.Driver, error) { - inner := drivers.NewBaseDriver(machineName, storePath, caCert, privateKey) - return &Driver{BaseDriver: inner}, nil +func NewDriver(hostName, storePath string) drivers.Driver { + return &Driver{ + CPU: defaultCpus, + Memory: defaultMemory, + DiskSize: defaultDiskSize, + BaseDriver: &drivers.BaseDriver{ + MachineName: hostName, + StorePath: storePath, + }, + } } func (d *Driver) GetSSHHostname() (string, error) { @@ -166,10 +176,7 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error { d.SwarmHost = flags.String("swarm-host") d.SwarmDiscovery = flags.String("swarm-discovery") - imgPath := utils.GetMachineCacheDir() - commonIsoPath := filepath.Join(imgPath, isoFilename) - - d.ISO = path.Join(commonIsoPath) + d.ISO = filepath.Join(d.StorePath, isoFilename) return nil } @@ -225,7 +232,7 @@ func (d *Driver) Create() error { return err } - b2dutils := utils.NewB2dUtils("", "") + b2dutils := mcnutils.NewB2dUtils("", "", d.StorePath) if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil { return err } diff --git a/drivers/vmwarevsphere/vsphere_test.go b/drivers/vmwarevsphere/vsphere_test.go deleted file mode 100644 index 6c68b2fc86..0000000000 --- a/drivers/vmwarevsphere/vsphere_test.go +++ /dev/null @@ -1 +0,0 @@ -package vmwarevsphere diff --git a/libmachine/auth/auth.go b/libmachine/auth/auth.go index ef0bf583ed..0bc45294aa 100644 --- a/libmachine/auth/auth.go +++ b/libmachine/auth/auth.go @@ -1,14 +1,18 @@ package auth type AuthOptions struct { - StorePath string + CertDir string CaCertPath string + CaPrivateKeyPath string CaCertRemotePath string ServerCertPath string ServerKeyPath string ClientKeyPath string ServerCertRemotePath string ServerKeyRemotePath string - PrivateKeyPath string ClientCertPath string + + // StorePath is left in for historical reasons, but not really meant to + // be used directly. + StorePath string } diff --git a/libmachine/auth/auth_test.go b/libmachine/auth/auth_test.go deleted file mode 100644 index 8832b06d18..0000000000 --- a/libmachine/auth/auth_test.go +++ /dev/null @@ -1 +0,0 @@ -package auth diff --git a/libmachine/cert/bootstrap.go b/libmachine/cert/bootstrap.go new file mode 100644 index 0000000000..c78989fb12 --- /dev/null +++ b/libmachine/cert/bootstrap.go @@ -0,0 +1,72 @@ +package cert + +import ( + "os" + + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" +) + +func BootstrapCertificates(authOptions *auth.AuthOptions) error { + certDir := authOptions.CertDir + caCertPath := authOptions.CaCertPath + caPrivateKeyPath := authOptions.CaPrivateKeyPath + clientCertPath := authOptions.ClientCertPath + clientKeyPath := authOptions.ClientKeyPath + + // TODO: I'm not super happy about this use of "org", the user should + // have to specify it explicitly instead of implicitly basing it on + // $USER. + org := mcnutils.GetUsername() + + bits := 2048 + + if _, err := os.Stat(certDir); err != nil { + if os.IsNotExist(err) { + if err := os.MkdirAll(certDir, 0700); err != nil { + log.Fatalf("Error creating machine certificate dir: %s", err) + } + } else { + log.Fatal(err) + } + } + + if _, err := os.Stat(caCertPath); os.IsNotExist(err) { + log.Infof("Creating CA: %s", caCertPath) + + // check if the key path exists; if so, error + if _, err := os.Stat(caPrivateKeyPath); err == nil { + log.Fatalf("The CA key already exists. Please remove it or specify a different key/cert.") + } + + if err := GenerateCACertificate(caCertPath, caPrivateKeyPath, org, bits); err != nil { + log.Infof("Error generating CA certificate: %s", err) + } + } + + if _, err := os.Stat(clientCertPath); os.IsNotExist(err) { + log.Infof("Creating client certificate: %s", clientCertPath) + + if _, err := os.Stat(certDir); err != nil { + if os.IsNotExist(err) { + if err := os.Mkdir(certDir, 0700); err != nil { + log.Fatalf("Error creating machine client cert dir: %s", err) + } + } else { + log.Fatal(err) + } + } + + // check if the key path exists; if so, error + if _, err := os.Stat(clientKeyPath); err == nil { + log.Fatalf("The client key already exists. Please remove it or specify a different key/cert.") + } + + if err := GenerateCert([]string{""}, clientCertPath, clientKeyPath, caCertPath, caPrivateKeyPath, org, bits); err != nil { + log.Fatalf("Error generating client certificate: %s", err) + } + } + + return nil +} diff --git a/utils/certs.go b/libmachine/cert/cert.go similarity index 93% rename from utils/certs.go rename to libmachine/cert/cert.go index 87f9baaedb..f1ac890702 100644 --- a/utils/certs.go +++ b/libmachine/cert/cert.go @@ -1,4 +1,4 @@ -package utils +package cert import ( "crypto/rand" @@ -7,6 +7,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "fmt" "io/ioutil" "math/big" "net" @@ -14,6 +15,14 @@ import ( "time" ) +type ErrValidatingCert struct { + wrappedErr error +} + +func (e ErrValidatingCert) Error() string { + return fmt.Sprintf("There was an error validating the cert: %s", e.wrappedErr) +} + func getTLSConfig(caCert, cert, key []byte, allowInsecure bool) (*tls.Config, error) { // TLS config var tlsConfig tls.Config @@ -168,22 +177,22 @@ func GenerateCert(hosts []string, certFile, keyFile, caFile, caKeyFile, org stri func ValidateCertificate(addr, caCertPath, serverCertPath, serverKeyPath string) (bool, error) { caCert, err := ioutil.ReadFile(caCertPath) if err != nil { - return false, err + return false, ErrValidatingCert{err} } serverCert, err := ioutil.ReadFile(serverCertPath) if err != nil { - return false, err + return false, ErrValidatingCert{err} } serverKey, err := ioutil.ReadFile(serverKeyPath) if err != nil { - return false, err + return false, ErrValidatingCert{err} } tlsConfig, err := getTLSConfig(caCert, serverCert, serverKey, false) if err != nil { - return false, err + return false, ErrValidatingCert{err} } dialer := &net.Dialer{ diff --git a/libmachine/cert/cert_path_info.go b/libmachine/cert/cert_path_info.go new file mode 100644 index 0000000000..d5abc5d71c --- /dev/null +++ b/libmachine/cert/cert_path_info.go @@ -0,0 +1,10 @@ +package cert + +type CertPathInfo struct { + CaCertPath string + CaPrivateKeyPath string + ClientCertPath string + ClientKeyPath string + ServerCertPath string + ServerKeyPath string +} diff --git a/utils/certs_test.go b/libmachine/cert/cert_test.go similarity index 99% rename from utils/certs_test.go rename to libmachine/cert/cert_test.go index 1201e63d28..69df4deed6 100644 --- a/utils/certs_test.go +++ b/libmachine/cert/cert_test.go @@ -1,4 +1,4 @@ -package utils +package cert import ( "io/ioutil" diff --git a/libmachine/certs.go b/libmachine/certs.go deleted file mode 100644 index 746e5e341c..0000000000 --- a/libmachine/certs.go +++ /dev/null @@ -1,10 +0,0 @@ -package libmachine - -type CertPathInfo struct { - CaCertPath string - CaKeyPath string - ClientCertPath string - ClientKeyPath string - ServerCertPath string - ServerKeyPath string -} diff --git a/drivers/base.go b/libmachine/drivers/base.go similarity index 56% rename from drivers/base.go rename to libmachine/drivers/base.go index dcfd1b82fb..3d7039a381 100644 --- a/drivers/base.go +++ b/libmachine/drivers/base.go @@ -5,46 +5,19 @@ import "path/filepath" // BaseDriver - Embed this struct into drivers to provide the common set // of fields and functions. type BaseDriver struct { - storePath string IPAddress string SSHUser string SSHPort int MachineName string - CaCertPath string - PrivateKeyPath string SwarmMaster bool SwarmHost string SwarmDiscovery string -} - -// NewBaseDriver - Get an instance of a BaseDriver -func NewBaseDriver(machineName string, storePath string, caCert string, privateKey string) *BaseDriver { - return &BaseDriver{ - MachineName: machineName, - storePath: storePath, - CaCertPath: caCert, - PrivateKeyPath: privateKey, - } + StorePath string } // GetSSHKeyPath - func (d *BaseDriver) GetSSHKeyPath() string { - return d.ResolveStorePath("id_rsa") -} - -// ResolveStorePath - -func (d *BaseDriver) ResolveStorePath(path string) string { - return filepath.Join(d.storePath, path) -} - -// AuthorizePort - -func (d *BaseDriver) AuthorizePort(ports []*Port) error { - return nil -} - -// DeauthorizePort - -func (d *BaseDriver) DeauthorizePort(ports []*Port) error { - return nil + return filepath.Join(d.StorePath, "machines", d.MachineName, "id_rsa") } // DriverName - This must be implemented in every driver @@ -57,6 +30,11 @@ func (d *BaseDriver) GetMachineName() string { return d.MachineName } +// ResolveStorePath - +func (d *BaseDriver) ResolveStorePath(file string) string { + return filepath.Join(d.StorePath, "machines", d.MachineName, file) +} + // GetSSHPort - func (d *BaseDriver) GetSSHPort() (int, error) { if d.SSHPort == 0 { diff --git a/libmachine/drivers/driver_options_mock.go b/libmachine/drivers/driver_options_mock.go new file mode 100644 index 0000000000..9e0488c5ab --- /dev/null +++ b/libmachine/drivers/driver_options_mock.go @@ -0,0 +1,21 @@ +package drivers + +type DriverOptionsMock struct { + Data map[string]interface{} +} + +func (d DriverOptionsMock) String(key string) string { + return d.Data[key].(string) +} + +func (d DriverOptionsMock) StringSlice(key string) []string { + return d.Data[key].([]string) +} + +func (d DriverOptionsMock) Int(key string) int { + return d.Data[key].(int) +} + +func (d DriverOptionsMock) Bool(key string) bool { + return d.Data[key].(bool) +} diff --git a/drivers/drivers.go b/libmachine/drivers/drivers.go similarity index 75% rename from drivers/drivers.go rename to libmachine/drivers/drivers.go index a6109a092c..2902a3e778 100644 --- a/drivers/drivers.go +++ b/libmachine/drivers/drivers.go @@ -6,28 +6,17 @@ import ( "sort" "github.com/codegangsta/cli" - "github.com/docker/machine/log" - "github.com/docker/machine/state" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/state" ) -type Port struct { - Protocol string - Port int -} - // Driver defines how a host is created and controlled. Different types of // driver represent different ways hosts can be created (e.g. different // hypervisors, different cloud providers) type Driver interface { - // AuthorizePort authorizes a port for machine access - AuthorizePort(ports []*Port) error - // Create a host using the driver's config Create() error - // DeauthorizePort removes a port for machine access - DeauthorizePort(ports []*Port) error - // DriverName returns the name of the driver as it is registered DriverName() string @@ -81,18 +70,15 @@ type Driver interface { Stop() error } -// RegisteredDriver is used to register a driver with the Register function. -// It has two attributes: -// - New: a function that returns a new driver given a path to store host -// configuration in -// - RegisterCreateFlags: a function that takes the FlagSet for -// "docker hosts create" and returns an object to pass to SetConfigFromFlags +// RegisteredDriver is a wrapper struct used to "register" drivers so that +// information about them can be displayed in the CLI. type RegisteredDriver struct { - New func(machineName string, storePath string, caCert string, privateKey string) (Driver, error) + // GetCreateFlags gets the FlagSet for "docker-machine create" for + // display to the user. GetCreateFlags func() []cli.Flag } -var ErrHostIsNotRunning = errors.New("host is not running") +var ErrHostIsNotRunning = errors.New("Host is not running") var ( drivers map[string]*RegisteredDriver @@ -112,15 +98,6 @@ func Register(name string, registeredDriver *RegisteredDriver) error { return nil } -// NewDriver creates a new driver of type "name" -func NewDriver(name string, machineName string, storePath string, caCert string, privateKey string) (Driver, error) { - driver, exists := drivers[name] - if !exists { - return nil, fmt.Errorf("hosts: Unknown driver %q", name) - } - return driver.New(machineName, storePath, caCert, privateKey) -} - // GetCreateFlags runs GetCreateFlags for all of the drivers and // returns their return values indexed by the driver name func GetCreateFlags() []cli.Flag { diff --git a/drivers/drivers_test.go b/libmachine/drivers/drivers_test.go similarity index 82% rename from drivers/drivers_test.go rename to libmachine/drivers/drivers_test.go index 031eb3d189..fd20a00e11 100644 --- a/drivers/drivers_test.go +++ b/libmachine/drivers/drivers_test.go @@ -8,9 +8,6 @@ import ( func TestGetCreateFlags(t *testing.T) { Register("foo", &RegisteredDriver{ - New: func(machineName string, storePath string, caCert string, privateKey string) (Driver, error) { - return nil, nil - }, GetCreateFlags: func() []cli.Flag { return []cli.Flag{ cli.StringFlag{ @@ -35,9 +32,6 @@ func TestGetCreateFlags(t *testing.T) { }, }) Register("bar", &RegisteredDriver{ - New: func(machineName string, storePath string, caCert string, privateKey string) (Driver, error) { - return nil, nil - }, GetCreateFlags: func() []cli.Flag { return []cli.Flag{ cli.StringFlag{ diff --git a/drivers/flag_sort.go b/libmachine/drivers/flag_sort.go similarity index 100% rename from drivers/flag_sort.go rename to libmachine/drivers/flag_sort.go diff --git a/drivers/utils.go b/libmachine/drivers/utils.go similarity index 90% rename from drivers/utils.go rename to libmachine/drivers/utils.go index 4c73076f61..40174fb67d 100644 --- a/drivers/utils.go +++ b/libmachine/drivers/utils.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/ssh" ) const ( @@ -84,7 +84,7 @@ func sshAvailableFunc(d Driver) func() bool { } func WaitForSSH(d Driver) error { - if err := utils.WaitFor(sshAvailableFunc(d)); err != nil { + if err := mcnutils.WaitFor(sshAvailableFunc(d)); err != nil { return fmt.Errorf("Too many retries. Last error: %s", err) } return nil diff --git a/libmachine/engine/engine.go b/libmachine/engine/engine.go index 39049106dc..5a6c6f34d8 100644 --- a/libmachine/engine/engine.go +++ b/libmachine/engine/engine.go @@ -11,9 +11,6 @@ type EngineOptions struct { LogLevel string StorageDriver string SelinuxEnabled bool - TlsCaCert string - TlsCert string - TlsKey string TlsVerify bool RegistryMirror []string InstallURL string diff --git a/libmachine/engine/engine_test.go b/libmachine/engine/engine_test.go deleted file mode 100644 index 00a22ef62d..0000000000 --- a/libmachine/engine/engine_test.go +++ /dev/null @@ -1 +0,0 @@ -package engine diff --git a/libmachine/examples/vbox_create.go b/libmachine/examples/vbox_create.go new file mode 100644 index 0000000000..2da9d7aa3c --- /dev/null +++ b/libmachine/examples/vbox_create.go @@ -0,0 +1,54 @@ +package main + +// Sample Virtualbox create independent of Machine CLI. +import ( + "fmt" + "os" + + "github.com/docker/machine/drivers/virtualbox" + "github.com/docker/machine/libmachine" + "github.com/docker/machine/libmachine/log" +) + +func main() { + libmachine.SetDebug(true) + + log.SetOutWriter(os.Stdout) + log.SetErrWriter(os.Stderr) + + // returns the familiar store at $HOME/.docker/machine + store := libmachine.GetDefaultStore() + + // over-ride this for now (don't want to muck with my default store) + store.Path = "/tmp/automatic" + + hostName := "myfunhost" + + // Set some options on the provider... + driver := virtualbox.NewDriver(hostName, "/tmp/automatic") + driver.CPU = 2 + driver.Memory = 2048 + + h, err := store.NewHost(driver) + if err != nil { + log.Fatal(err) + } + + h.HostOptions.EngineOptions.StorageDriver = "overlay" + + if err := libmachine.Create(store, h); err != nil { + log.Fatal(err) + } + + out, err := h.RunSSHCommand("df -h") + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Results of your disk space query:\n%s\n", out) + + fmt.Println("Powering down machine now...") + if err := h.Stop(); err != nil { + log.Fatal(err) + } +} diff --git a/libmachine/filestore.go b/libmachine/filestore.go deleted file mode 100644 index 6d96032ac2..0000000000 --- a/libmachine/filestore.go +++ /dev/null @@ -1,141 +0,0 @@ -package libmachine - -import ( - "encoding/json" - "errors" - "io/ioutil" - "os" - "path/filepath" - "strings" - - "github.com/docker/machine/log" - "github.com/docker/machine/utils" -) - -type Filestore struct { - path string - caCertPath string - privateKeyPath string -} - -func NewFilestore(rootPath string, caCert string, privateKey string) *Filestore { - return &Filestore{ - path: rootPath, - caCertPath: caCert, - privateKeyPath: privateKey, - } -} - -func (s Filestore) loadHost(name string) (*Host, error) { - hostPath := filepath.Join(utils.GetMachineDir(), name) - if _, err := os.Stat(hostPath); os.IsNotExist(err) { - return nil, ErrHostDoesNotExist{ - Name: name, - } - } - - host := &Host{ - Name: name, - StorePath: hostPath, - } - if err := host.LoadConfig(); err != nil { - return nil, err - } - - return host, nil -} - -func (s Filestore) GetPath() string { - return s.path -} - -func (s Filestore) GetCACertificatePath() (string, error) { - return s.caCertPath, nil -} - -func (s Filestore) GetPrivateKeyPath() (string, error) { - return s.privateKeyPath, nil -} - -func (s Filestore) Save(host *Host) error { - data, err := json.Marshal(host) - if err != nil { - return err - } - - hostPath := filepath.Join(utils.GetMachineDir(), host.Name) - - if err := os.MkdirAll(hostPath, 0700); err != nil { - return err - } - - if err := ioutil.WriteFile(filepath.Join(hostPath, "config.json"), data, 0600); err != nil { - return err - } - - return nil -} - -func (s Filestore) Remove(name string, force bool) error { - hostPath := filepath.Join(utils.GetMachineDir(), name) - return os.RemoveAll(hostPath) -} - -func (s Filestore) List() ([]*Host, error) { - dir, err := ioutil.ReadDir(utils.GetMachineDir()) - if err != nil && !os.IsNotExist(err) { - return nil, err - } - - hosts := []*Host{} - - for _, file := range dir { - // don't load hidden dirs; used for configs - if file.IsDir() && !strings.HasPrefix(file.Name(), ".") { - host, err := s.Get(file.Name()) - if err != nil { - log.Errorf("error loading host %q: %s", file.Name(), err) - continue - } - hosts = append(hosts, host) - } - } - return hosts, nil -} - -func (s Filestore) Exists(name string) (bool, error) { - _, err := os.Stat(filepath.Join(utils.GetMachineDir(), name)) - - if os.IsNotExist(err) { - return false, nil - } else if err == nil { - return true, nil - } - - return false, err -} - -func (s Filestore) Get(name string) (*Host, error) { - return s.loadHost(name) -} - -func (s Filestore) GetActive() (*Host, error) { - hosts, err := s.List() - if err != nil { - return nil, err - } - - hostListItems := GetHostListItems(hosts) - - for _, item := range hostListItems { - if item.Active { - host, err := s.Get(item.Name) - if err != nil { - return nil, err - } - return host, nil - } - } - - return nil, errors.New("Active host not found") -} diff --git a/libmachine/filestore_test.go b/libmachine/filestore_test.go deleted file mode 100644 index 2bd9815010..0000000000 --- a/libmachine/filestore_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package libmachine - -import ( - "os" - "path/filepath" - "testing" - - _ "github.com/docker/machine/drivers/none" - "github.com/docker/machine/utils" -) - -type DriverOptionsMock struct { - Data map[string]interface{} -} - -func (d DriverOptionsMock) String(key string) string { - return d.Data[key].(string) -} - -func (d DriverOptionsMock) StringSlice(key string) []string { - return d.Data[key].([]string) -} - -func (d DriverOptionsMock) Int(key string) int { - return d.Data[key].(int) -} - -func (d DriverOptionsMock) Bool(key string) bool { - return d.Data[key].(bool) -} - -func TestStoreSave(t *testing.T) { - defer cleanup() - - 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) - } - - path := filepath.Join(utils.GetMachineDir(), host.Name) - if _, err := os.Stat(path); os.IsNotExist(err) { - t.Fatalf("Host path doesn't exist: %s", path) - } -} - -func TestStoreRemove(t *testing.T) { - defer cleanup() - - 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) - } - path := filepath.Join(utils.GetMachineDir(), host.Name) - if _, err := os.Stat(path); os.IsNotExist(err) { - t.Fatalf("Host path doesn't exist: %s", path) - } - err = store.Remove(host.Name, false) - if err != nil { - t.Fatal(err) - } - if _, err := os.Stat(path); err == nil { - t.Fatalf("Host path still exists after remove: %s", path) - } -} - -func TestStoreList(t *testing.T) { - defer cleanup() - - 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) - } - - hosts, err := store.List() - if len(hosts) != 1 { - t.Fatalf("List returned %d items", len(hosts)) - } - if hosts[0].Name != host.Name { - t.Fatalf("hosts[0] name is incorrect, got: %s", hosts[0].Name) - } -} - -func TestStoreExists(t *testing.T) { - defer cleanup() - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - host, err := getDefaultTestHost() - if err != nil { - t.Fatal(err) - } - - exists, err := store.Exists(host.Name) - if exists { - t.Fatal("Exists returned true when it should have been false") - } - - if err := store.Save(host); err != nil { - t.Fatal(err) - } - - exists, err = store.Exists(host.Name) - if err != nil { - t.Fatal(err) - } - - if !exists { - t.Fatal("Exists returned false when it should have been true") - } - if err := store.Remove(host.Name, true); err != nil { - t.Fatal(err) - } - - exists, err = store.Exists(host.Name) - if err != nil { - t.Fatal(err) - } - - if exists { - t.Fatal("Exists returned true when it should have been false") - } -} - -func TestStoreLoad(t *testing.T) { - defer cleanup() - - expectedURL := "unix:///foo/baz" - flags := getTestDriverFlags() - flags.Data["url"] = expectedURL - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - host, err := getDefaultTestHost() - if err != nil { - t.Fatal(err) - } - - if err := host.Driver.SetConfigFromFlags(flags); err != nil { - t.Fatal(err) - } - - if err := store.Save(host); err != nil { - t.Fatal(err) - } - - host, err = store.Get(host.Name) - if host.Name != host.Name { - t.Fatal("Host name is incorrect") - } - actualURL, err := host.GetURL() - if err != nil { - t.Fatal(err) - } - - if actualURL != expectedURL { - t.Fatalf("GetURL is not %q, got %q", expectedURL, actualURL) - } -} - -func TestStoreGetSetActive(t *testing.T) { - defer cleanup() - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - // No host set - host, err := store.GetActive() - if err == nil { - t.Fatal("Expected an error because there is no active host set") - } - - if host != nil { - t.Fatalf("GetActive: Active host should not exist") - } - - host, err = getDefaultTestHost() - if err != nil { - t.Fatal(err) - } - - // Set normal host - if err := store.Save(host); err != nil { - t.Fatal(err) - } - - url, err := host.GetURL() - if err != nil { - t.Fatal(err) - } - - os.Setenv("DOCKER_HOST", url) - - host, err = store.GetActive() - if err != nil { - t.Fatal(err) - } - if host.Name != hostTestName { - t.Fatalf("Active host is not '%s', got %s", hostTestName, host.Name) - } -} diff --git a/libmachine/host.go b/libmachine/host.go deleted file mode 100644 index 455d9edb20..0000000000 --- a/libmachine/host.go +++ /dev/null @@ -1,462 +0,0 @@ -package libmachine - -import ( - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "os" - "path/filepath" - "regexp" - "time" - - "github.com/docker/machine/drivers" - "github.com/docker/machine/libmachine/auth" - "github.com/docker/machine/libmachine/engine" - "github.com/docker/machine/libmachine/provision" - "github.com/docker/machine/libmachine/provision/pkgaction" - "github.com/docker/machine/libmachine/provision/serviceaction" - "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" - "github.com/docker/machine/version" -) - -var ( - validHostNameChars = `^[a-zA-Z0-9][a-zA-Z0-9\-\.]*$` - validHostNamePattern = regexp.MustCompile(validHostNameChars) - errMachineMustBeRunningForUpgrade = errors.New("Error: machine must be running to upgrade.") - stateTimeoutDuration = time.Second * 3 -) - -type Host struct { - ConfigVersion int - Driver drivers.Driver - DriverName string - HostOptions *HostOptions - Name string `json:"-"` - StorePath string -} - -type HostOptions struct { - Driver string - Memory int - Disk int - EngineOptions *engine.EngineOptions - SwarmOptions *swarm.SwarmOptions - AuthOptions *auth.AuthOptions -} - -type HostMetadata struct { - ConfigVersion int - DriverName string - HostOptions HostOptions -} - -type HostListItem struct { - Name string - Active bool - DriverName string - State state.State - URL string - SwarmOptions swarm.SwarmOptions -} - -type ErrSavingConfig struct { - wrappedErr error -} - -func (e ErrSavingConfig) Error() string { - return fmt.Sprintf("Error saving config: %s", e.wrappedErr) -} - -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, - ConfigVersion: version.ConfigVersion, - DriverName: driverName, - Driver: driver, - StorePath: storePath, - HostOptions: hostOptions, - }, nil -} - -func LoadHost(name string, StorePath string) (*Host, error) { - if _, err := os.Stat(StorePath); os.IsNotExist(err) { - return nil, fmt.Errorf("Host %q does not exist", name) - } - - host := &Host{Name: name, StorePath: StorePath} - if err := host.LoadConfig(); err != nil { - return nil, err - } - - return host, nil -} - -func ValidateHostName(name string) bool { - return validHostNamePattern.MatchString(name) -} - -func (h *Host) Create(name string) error { - // create the instance - if err := h.Driver.Create(); err != nil { - return err - } - - // save to store - if err := h.SaveConfig(); err != nil { - return err - } - - // TODO: Not really a fan of just checking "none" here. - if h.Driver.DriverName() != "none" { - if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil { - return err - } - - if err := WaitForSSH(h); err != nil { - return err - } - - provisioner, err := provision.DetectProvisioner(h.Driver) - if err != nil { - return err - } - - if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil { - return err - } - } - - return nil -} - -func (h *Host) RunSSHCommand(command string) (string, error) { - return drivers.RunSSHCommandFromDriver(h.Driver, command) -} - -func (h *Host) CreateSSHClient() (ssh.Client, error) { - addr, err := h.Driver.GetSSHHostname() - if err != nil { - return ssh.ExternalClient{}, err - } - - port, err := h.Driver.GetSSHPort() - if err != nil { - return ssh.ExternalClient{}, err - } - - auth := &ssh.Auth{ - Keys: []string{h.Driver.GetSSHKeyPath()}, - } - - return ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth) -} - -func (h *Host) CreateSSHShell() error { - client, err := h.CreateSSHClient() - if err != nil { - return err - } - - return client.Shell() -} - -func (h *Host) runActionForState(action func() error, desiredState state.State) error { - if drivers.MachineInState(h.Driver, desiredState)() { - log.Debug("Machine already in state %s, returning", desiredState) - return nil - } - - if err := action(); err != nil { - return err - } - - if err := h.SaveConfig(); err != nil { - return err - } - - return utils.WaitFor(drivers.MachineInState(h.Driver, desiredState)) -} - -func (h *Host) Start() error { - return h.runActionForState(h.Driver.Start, state.Running) -} - -func (h *Host) Stop() error { - return h.runActionForState(h.Driver.Stop, state.Stopped) -} - -func (h *Host) Kill() error { - return h.runActionForState(h.Driver.Kill, state.Stopped) -} - -func (h *Host) Restart() error { - if drivers.MachineInState(h.Driver, state.Running)() { - if err := h.Stop(); err != nil { - return err - } - - if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped)); err != nil { - return err - } - } - - if err := h.Start(); err != nil { - return err - } - - if err := utils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil { - return err - } - - if err := h.SaveConfig(); err != nil { - return err - } - - return nil -} - -func (h *Host) Upgrade() error { - machineState, err := h.Driver.GetState() - if err != nil { - return err - } - - if machineState != state.Running { - log.Fatal(errMachineMustBeRunningForUpgrade) - } - - provisioner, err := provision.DetectProvisioner(h.Driver) - if err != nil { - return err - } - - if err := provisioner.Package("docker", pkgaction.Upgrade); err != nil { - return err - } - - if err := provisioner.Service("docker", serviceaction.Restart); err != nil { - return err - } - return nil -} - -func (h *Host) Remove(force bool) error { - if err := h.Driver.Remove(); err != nil { - if !force { - return err - } - } - - if err := h.SaveConfig(); err != nil { - return err - } - - return h.removeStorePath() -} - -func (h *Host) removeStorePath() error { - file, err := os.Stat(h.StorePath) - if err != nil { - return err - } - if !file.IsDir() { - return fmt.Errorf("%q is not a directory", h.StorePath) - } - return os.RemoveAll(h.StorePath) -} - -func (h *Host) GetURL() (string, error) { - return h.Driver.GetURL() -} - -// IsActive provides a single method for determining if a host is active based -// on both the url and if the host is stopped. -func (h *Host) IsActive() (bool, error) { - currentState, err := h.Driver.GetState() - - if err != nil { - log.Errorf("error getting state for host %s: %s", h.Name, err) - return false, err - } - - url, err := h.GetURL() - - if err != nil { - if err == drivers.ErrHostIsNotRunning { - url = "" - } else { - log.Errorf("error getting URL for host %s: %s", h.Name, err) - return false, err - } - } - - dockerHost := os.Getenv("DOCKER_HOST") - - notStopped := currentState != state.Stopped - correctURL := url == dockerHost - - isActive := notStopped && correctURL - - return isActive, nil -} - -func (h *Host) LoadConfig() error { - data, err := ioutil.ReadFile(filepath.Join(h.StorePath, "config.json")) - if err != nil { - return err - } - - // Remember the machine name and store path so we don't have to pass it - // through each struct in the migration. - name := h.Name - storePath := h.StorePath - - // If we end up performing a migration, we should save afterwards so we don't have to do it again on subsequent invocations. - migrationPerformed := h.ConfigVersion != version.ConfigVersion - - migratedHost, err := MigrateHost(h, data) - if err != nil { - return fmt.Errorf("Error getting migrated host: %s", err) - } - - *h = *migratedHost - - h.Name = name - h.StorePath = storePath - - if migrationPerformed { - if err := h.SaveConfig(); err != nil { - return fmt.Errorf("Error saving config after migration was performed: %s", err) - } - } - - return nil -} - -func (h *Host) ConfigureAuth() error { - if err := h.LoadConfig(); err != nil { - return err - } - - provisioner, err := provision.DetectProvisioner(h.Driver) - if err != nil { - return err - } - - // TODO: This is kind of a hack (or is it? I'm not really sure until - // we have more clearly defined outlook on what the responsibilities - // and modularity of the provisioners should be). - // - // Call provision to re-provision the certs properly. - if err := provisioner.Provision(swarm.SwarmOptions{}, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil { - return err - } - - return nil -} - -func (h *Host) SaveConfig() error { - data, err := json.Marshal(h) - if err != nil { - return err - } - - if err := ioutil.WriteFile(filepath.Join(h.StorePath, "config.json"), data, 0600); err != nil { - return err - } - return nil -} - -func (h *Host) PrintIP() error { - if ip, err := h.Driver.GetIP(); err != nil { - return err - } else { - fmt.Println(ip) - } - return nil -} - -func WaitForSSH(h *Host) error { - return drivers.WaitForSSH(h.Driver) -} - -func attemptGetHostState(host Host, stateQueryChan chan<- HostListItem) { - currentState, err := host.Driver.GetState() - if err != nil { - log.Errorf("error getting state for host %s: %s", host.Name, err) - } - - url, err := host.GetURL() - if err != nil { - if err == drivers.ErrHostIsNotRunning { - url = "" - } else { - log.Errorf("error getting URL for host %s: %s", host.Name, err) - } - } - - isActive, err := host.IsActive() - if err != nil { - log.Errorf("error determining if host is active for host %s: %s", - host.Name, err) - } - - stateQueryChan <- HostListItem{ - Name: host.Name, - Active: isActive, - DriverName: host.Driver.DriverName(), - State: currentState, - URL: url, - SwarmOptions: *host.HostOptions.SwarmOptions, - } -} - -func getHostState(host Host, hostListItemsChan chan<- HostListItem) { - // This channel is used to communicate the properties we are querying - // about the host in the case of a successful read. - stateQueryChan := make(chan HostListItem) - - go attemptGetHostState(host, stateQueryChan) - - select { - // If we get back useful information, great. Forward it straight to - // the original parent channel. - case hli := <-stateQueryChan: - hostListItemsChan <- hli - - // Otherwise, give up after a predetermined duration. - case <-time.After(stateTimeoutDuration): - hostListItemsChan <- HostListItem{ - Name: host.Name, - DriverName: host.Driver.DriverName(), - State: state.Timeout, - } - } -} - -func GetHostListItems(hostList []*Host) []HostListItem { - hostListItems := []HostListItem{} - hostListItemsChan := make(chan HostListItem) - - for _, host := range hostList { - go getHostState(*host, hostListItemsChan) - } - - for range hostList { - hostListItems = append(hostListItems, <-hostListItemsChan) - } - - close(hostListItemsChan) - return hostListItems -} diff --git a/libmachine/host/host.go b/libmachine/host/host.go new file mode 100644 index 0000000000..c55e45b190 --- /dev/null +++ b/libmachine/host/host.go @@ -0,0 +1,181 @@ +package host + +import ( + "errors" + "fmt" + "regexp" + "strings" + + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/provision" + "github.com/docker/machine/libmachine/provision/pkgaction" + "github.com/docker/machine/libmachine/provision/serviceaction" + "github.com/docker/machine/libmachine/ssh" + "github.com/docker/machine/libmachine/state" + "github.com/docker/machine/libmachine/swarm" +) + +var ( + validHostNameChars = `[a-zA-Z0-9\-\.]` + validHostNamePattern = regexp.MustCompile(`^` + validHostNameChars + `+$`) + errMachineMustBeRunningForUpgrade = errors.New("Error: machine must be running to upgrade.") +) + +type Host struct { + ConfigVersion int + Driver drivers.Driver + DriverName string + HostOptions *HostOptions + Name string +} + +type HostOptions struct { + Driver string + Memory int + Disk int + EngineOptions *engine.EngineOptions + SwarmOptions *swarm.SwarmOptions + AuthOptions *auth.AuthOptions +} + +type HostMetadata struct { + ConfigVersion int + DriverName string + HostOptions HostOptions +} + +func ValidateHostName(name string) bool { + return validHostNamePattern.MatchString(name) +} + +func (h *Host) RunSSHCommand(command string) (string, error) { + return drivers.RunSSHCommandFromDriver(h.Driver, command) +} + +func (h *Host) CreateSSHClient() (ssh.Client, error) { + addr, err := h.Driver.GetSSHHostname() + if err != nil { + return ssh.ExternalClient{}, err + } + + port, err := h.Driver.GetSSHPort() + if err != nil { + return ssh.ExternalClient{}, err + } + + auth := &ssh.Auth{ + Keys: []string{h.Driver.GetSSHKeyPath()}, + } + + return ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth) +} + +func (h *Host) CreateSSHShell() error { + client, err := h.CreateSSHClient() + if err != nil { + return err + } + + return client.Shell() +} + +func (h *Host) runActionForState(action func() error, desiredState state.State) error { + if drivers.MachineInState(h.Driver, desiredState)() { + return fmt.Errorf("Machine %q is already %s.", h.Name, strings.ToLower(desiredState.String())) + } + + if err := action(); err != nil { + return err + } + + return mcnutils.WaitFor(drivers.MachineInState(h.Driver, desiredState)) +} + +func (h *Host) Start() error { + return h.runActionForState(h.Driver.Start, state.Running) +} + +func (h *Host) Stop() error { + return h.runActionForState(h.Driver.Stop, state.Stopped) +} + +func (h *Host) Kill() error { + return h.runActionForState(h.Driver.Kill, state.Stopped) +} + +func (h *Host) Restart() error { + if drivers.MachineInState(h.Driver, state.Running)() { + if err := h.Stop(); err != nil { + return err + } + + if err := mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Stopped)); err != nil { + return err + } + } + + if err := h.Start(); err != nil { + return err + } + + if err := mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil { + return err + } + + return nil +} + +func (h *Host) Upgrade() error { + machineState, err := h.Driver.GetState() + if err != nil { + return err + } + + if machineState != state.Running { + log.Fatal(errMachineMustBeRunningForUpgrade) + } + + provisioner, err := provision.DetectProvisioner(h.Driver) + if err != nil { + return err + } + + if err := provisioner.Package("docker", pkgaction.Upgrade); err != nil { + return err + } + + if err := provisioner.Service("docker", serviceaction.Restart); err != nil { + return err + } + return nil +} + +func (h *Host) GetURL() (string, error) { + return h.Driver.GetURL() +} + +func (h *Host) ConfigureAuth() error { + provisioner, err := provision.DetectProvisioner(h.Driver) + if err != nil { + return err + } + + // TODO: This is kind of a hack (or is it? I'm not really sure until + // we have more clearly defined outlook on what the responsibilities + // and modularity of the provisioners should be). + // + // Call provision to re-provision the certs properly. + if err := provisioner.Provision(swarm.SwarmOptions{}, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil { + return err + } + + return nil +} + +func WaitForSSH(h *Host) error { + return drivers.WaitForSSH(h.Driver) +} diff --git a/libmachine/host/host_test.go b/libmachine/host/host_test.go new file mode 100644 index 0000000000..746a4ca0e2 --- /dev/null +++ b/libmachine/host/host_test.go @@ -0,0 +1,37 @@ +package host + +import ( + "testing" + + _ "github.com/docker/machine/drivers/none" +) + +func TestValidateHostnameValid(t *testing.T) { + hosts := []string{ + "zomg", + "test-ing", + "some.h0st", + } + + for _, v := range hosts { + isValid := ValidateHostName(v) + if !isValid { + t.Fatalf("Thought a valid hostname was invalid: %s", v) + } + } +} + +func TestValidateHostnameInvalid(t *testing.T) { + hosts := []string{ + "zom_g", + "test$ing", + "some😄host", + } + + for _, v := range hosts { + isValid := ValidateHostName(v) + if isValid { + t.Fatalf("Thought an invalid hostname was valid: %s", v) + } + } +} diff --git a/libmachine/host_v0.go b/libmachine/host/host_v0.go similarity index 87% rename from libmachine/host_v0.go rename to libmachine/host/host_v0.go index 8c1d63839d..4fa503bbd1 100644 --- a/libmachine/host_v0.go +++ b/libmachine/host/host_v0.go @@ -1,6 +1,6 @@ -package libmachine +package host -import "github.com/docker/machine/drivers" +import "github.com/docker/machine/libmachine/drivers" type HostV0 struct { Name string `json:"-"` @@ -25,6 +25,7 @@ type HostMetadataV0 struct { HostOptions HostOptions DriverName string + ConfigVersion int StorePath string CaCertPath string PrivateKeyPath string diff --git a/libmachine/host/migrate.go b/libmachine/host/migrate.go new file mode 100644 index 0000000000..4f04d9cc06 --- /dev/null +++ b/libmachine/host/migrate.go @@ -0,0 +1,78 @@ +package host + +import ( + "encoding/json" + "fmt" + "path/filepath" + + "github.com/docker/machine/drivers/driverfactory" + "github.com/docker/machine/libmachine/version" +) + +func getMigratedHostMetadata(data []byte) (*HostMetadata, error) { + // HostMetadata is for a "first pass" so we can then load the driver + var ( + hostMetadata *HostMetadataV0 + ) + + if err := json.Unmarshal(data, &hostMetadata); err != nil { + return &HostMetadata{}, err + } + + migratedHostMetadata := MigrateHostMetadataV0ToHostMetadataV1(hostMetadata) + + return migratedHostMetadata, nil +} + +func MigrateHost(h *Host, data []byte) (*Host, bool, error) { + var ( + migrationPerformed = false + hostV1 *HostV1 + ) + + migratedHostMetadata, err := getMigratedHostMetadata(data) + if err != nil { + return &Host{}, false, err + } + + globalStorePath := filepath.Dir(filepath.Dir(migratedHostMetadata.HostOptions.AuthOptions.StorePath)) + + driver, err := driverfactory.NewDriver(migratedHostMetadata.DriverName, h.Name, globalStorePath) + if err != nil { + return &Host{}, false, err + } + + if migratedHostMetadata.ConfigVersion == version.ConfigVersion { + h.Driver = driver + if err := json.Unmarshal(data, &h); err != nil { + return &Host{}, migrationPerformed, fmt.Errorf("Error unmarshalling most recent host version: %s", err) + } + } else { + migrationPerformed = true + for h.ConfigVersion = migratedHostMetadata.ConfigVersion; h.ConfigVersion < version.ConfigVersion; h.ConfigVersion++ { + switch h.ConfigVersion { + case 0: + hostV0 := &HostV0{ + Driver: driver, + } + if err := json.Unmarshal(data, &hostV0); err != nil { + return &Host{}, migrationPerformed, fmt.Errorf("Error unmarshalling host config version 0: %s", err) + } + hostV1 = MigrateHostV0ToHostV1(hostV0) + case 1: + if hostV1 == nil { + hostV1 = &HostV1{ + Driver: driver, + } + if err := json.Unmarshal(data, &hostV1); err != nil { + return &Host{}, migrationPerformed, fmt.Errorf("Error unmarshalling host config version 1: %s", err) + } + } + h = MigrateHostV1ToHostV2(hostV1) + } + } + + } + + return h, migrationPerformed, nil +} diff --git a/libmachine/host/migrate_v0_v1.go b/libmachine/host/migrate_v0_v1.go new file mode 100644 index 0000000000..87adfe5050 --- /dev/null +++ b/libmachine/host/migrate_v0_v1.go @@ -0,0 +1,69 @@ +package host + +import ( + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/swarm" +) + +// In the 0.0.1 => 0.0.2 transition, the JSON representation of +// machines changed from a "flat" to a more "nested" structure +// for various options and configuration settings. To preserve +// compatibility with existing machines, these migration functions +// have been introduced. They preserve backwards compat at the expense +// of some duplicated information. + +// validates host config and modifies if needed +// this is used for configuration updates +func MigrateHostV0ToHostV1(hostV0 *HostV0) *HostV1 { + hostV1 := &HostV1{ + Driver: hostV0.Driver, + } + + hostV1.HostOptions = &HostOptionsV1{} + hostV1.HostOptions.EngineOptions = &engine.EngineOptions{} + hostV1.HostOptions.SwarmOptions = &swarm.SwarmOptions{ + Address: "", + Discovery: hostV0.SwarmDiscovery, + Host: hostV0.SwarmHost, + Master: hostV0.SwarmMaster, + } + hostV1.HostOptions.AuthOptions = &AuthOptionsV1{ + StorePath: hostV0.StorePath, + CaCertPath: hostV0.CaCertPath, + CaCertRemotePath: "", + ServerCertPath: hostV0.ServerCertPath, + ServerKeyPath: hostV0.ServerKeyPath, + ClientKeyPath: hostV0.ClientKeyPath, + ServerCertRemotePath: "", + ServerKeyRemotePath: "", + PrivateKeyPath: hostV0.PrivateKeyPath, + ClientCertPath: hostV0.ClientCertPath, + } + + return hostV1 +} + +// fills nested host metadata and modifies if needed +// this is used for configuration updates +func MigrateHostMetadataV0ToHostMetadataV1(m *HostMetadataV0) *HostMetadata { + hostMetadata := &HostMetadata{} + hostMetadata.DriverName = m.DriverName + hostMetadata.HostOptions.EngineOptions = &engine.EngineOptions{} + hostMetadata.HostOptions.AuthOptions = &auth.AuthOptions{ + StorePath: m.StorePath, + CaCertPath: m.CaCertPath, + CaCertRemotePath: "", + ServerCertPath: m.ServerCertPath, + ServerKeyPath: m.ServerKeyPath, + ClientKeyPath: "", + ServerCertRemotePath: "", + ServerKeyRemotePath: "", + CaPrivateKeyPath: m.PrivateKeyPath, + ClientCertPath: m.ClientCertPath, + } + + hostMetadata.ConfigVersion = m.ConfigVersion + + return hostMetadata +} diff --git a/libmachine/migrate_v0_v1_test.go b/libmachine/host/migrate_v0_v1_test.go similarity index 71% rename from libmachine/migrate_v0_v1_test.go rename to libmachine/host/migrate_v0_v1_test.go index 9b6cacb15b..199fb0eb57 100644 --- a/libmachine/migrate_v0_v1_test.go +++ b/libmachine/host/migrate_v0_v1_test.go @@ -1,4 +1,4 @@ -package libmachine +package host import ( "os" @@ -17,20 +17,20 @@ func TestMigrateHostV0ToV1(t *testing.T) { SwarmDiscovery: "token://foobar", SwarmHost: "1.2.3.4:2376", SwarmMaster: true, - CaCertPath: "", - PrivateKeyPath: "", - ClientCertPath: "", - ClientKeyPath: "", - ServerCertPath: "", - ServerKeyPath: "", + CaCertPath: "/tmp/migration/certs/ca.pem", + PrivateKeyPath: "/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", } - hostOptions := &HostOptions{ + hostOptions := &HostOptionsV1{ SwarmOptions: &swarm.SwarmOptions{ Master: true, Discovery: "token://foobar", Host: "1.2.3.4:2376", }, - AuthOptions: &auth.AuthOptions{ + AuthOptions: &AuthOptionsV1{ CaCertPath: "/tmp/migration/certs/ca.pem", PrivateKeyPath: "/tmp/migration/certs/ca-key.pem", ClientCertPath: "/tmp/migration/certs/cert.pem", @@ -41,7 +41,7 @@ func TestMigrateHostV0ToV1(t *testing.T) { EngineOptions: &engine.EngineOptions{}, } - expectedHost := &Host{ + expectedHost := &HostV1{ HostOptions: hostOptions, } @@ -84,30 +84,3 @@ func TestMigrateHostMetadataV0ToV1(t *testing.T) { t.Fatal("Expected these structs to be equal, they were different") } } - -// 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 := &HostV0{ - 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") - } -} diff --git a/libmachine/host/migrate_v1_v2.go b/libmachine/host/migrate_v1_v2.go new file mode 100644 index 0000000000..19473fde31 --- /dev/null +++ b/libmachine/host/migrate_v1_v2.go @@ -0,0 +1,78 @@ +package host + +import ( + "path/filepath" + + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/swarm" +) + +type AuthOptionsV1 struct { + StorePath string + CaCertPath string + CaCertRemotePath string + ServerCertPath string + ServerKeyPath string + ClientKeyPath string + ServerCertRemotePath string + ServerKeyRemotePath string + PrivateKeyPath string + ClientCertPath string +} + +type HostOptionsV1 struct { + Driver string + Memory int + Disk int + EngineOptions *engine.EngineOptions + SwarmOptions *swarm.SwarmOptions + AuthOptions *AuthOptionsV1 +} + +type HostV1 struct { + ConfigVersion int + Driver drivers.Driver + DriverName string + HostOptions *HostOptionsV1 + Name string `json:"-"` + StorePath string +} + +func MigrateHostV1ToHostV2(hostV1 *HostV1) *Host { + // Changed: Put StorePath directly in AuthOptions (for provisioning), + // and AuthOptions.PrivateKeyPath => AuthOptions.CaPrivateKeyPath + // Also, CertDir has been added. + + globalStorePath := filepath.Dir(filepath.Dir(hostV1.StorePath)) + + h := &Host{ + ConfigVersion: hostV1.ConfigVersion, + Driver: hostV1.Driver, + Name: hostV1.Driver.GetMachineName(), + DriverName: hostV1.DriverName, + HostOptions: &HostOptions{ + Driver: hostV1.HostOptions.Driver, + Memory: hostV1.HostOptions.Memory, + Disk: hostV1.HostOptions.Disk, + EngineOptions: hostV1.HostOptions.EngineOptions, + SwarmOptions: hostV1.HostOptions.SwarmOptions, + AuthOptions: &auth.AuthOptions{ + CertDir: filepath.Join(globalStorePath, "certs"), + CaCertPath: hostV1.HostOptions.AuthOptions.CaCertPath, + CaPrivateKeyPath: hostV1.HostOptions.AuthOptions.PrivateKeyPath, + CaCertRemotePath: hostV1.HostOptions.AuthOptions.CaCertRemotePath, + ServerCertPath: hostV1.HostOptions.AuthOptions.ServerCertPath, + ServerKeyPath: hostV1.HostOptions.AuthOptions.ServerKeyPath, + ClientKeyPath: hostV1.HostOptions.AuthOptions.ClientKeyPath, + ServerCertRemotePath: hostV1.HostOptions.AuthOptions.ServerCertRemotePath, + ServerKeyRemotePath: hostV1.HostOptions.AuthOptions.ServerKeyRemotePath, + ClientCertPath: hostV1.HostOptions.AuthOptions.ClientCertPath, + StorePath: globalStorePath, + }, + }, + } + + return h +} diff --git a/libmachine/host/migrate_v1_v2_test.go b/libmachine/host/migrate_v1_v2_test.go new file mode 100644 index 0000000000..f7f99f4fc4 --- /dev/null +++ b/libmachine/host/migrate_v1_v2_test.go @@ -0,0 +1,108 @@ +package host + +import ( + "path/filepath" + "testing" +) + +var ( + v1conf = []byte(`{ + "ConfigVersion": 1, + "Driver": { + "IPAddress": "192.168.99.100", + "SSHUser": "docker", + "SSHPort": 64477, + "MachineName": "foobar", + "CaCertPath": "/Users/catbug/.docker/machine/certs/ca.pem", + "PrivateKeyPath": "/Users/catbug/.docker/machine/certs/ca-key.pem", + "SwarmMaster": false, + "SwarmHost": "tcp://0.0.0.0:3376", + "SwarmDiscovery": "", + "CPU": 1, + "Memory": 1024, + "DiskSize": 20000, + "Boot2DockerURL": "", + "Boot2DockerImportVM": "", + "HostOnlyCIDR": "192.168.99.1/24" + }, + "DriverName": "virtualbox", + "HostOptions": { + "Driver": "", + "Memory": 0, + "Disk": 0, + "EngineOptions": { + "ArbitraryFlags": [], + "Dns": null, + "GraphDir": "", + "Env": [], + "Ipv6": false, + "InsecureRegistry": [], + "Labels": [], + "LogLevel": "", + "StorageDriver": "", + "SelinuxEnabled": false, + "TlsCaCert": "", + "TlsCert": "", + "TlsKey": "", + "TlsVerify": true, + "RegistryMirror": [], + "InstallURL": "https://get.docker.com" + }, + "SwarmOptions": { + "IsSwarm": false, + "Address": "", + "Discovery": "", + "Master": false, + "Host": "tcp://0.0.0.0:3376", + "Image": "swarm:latest", + "Strategy": "spread", + "Heartbeat": 0, + "Overcommit": 0, + "TlsCaCert": "", + "TlsCert": "", + "TlsKey": "", + "TlsVerify": false, + "ArbitraryFlags": [] + }, + "AuthOptions": { + "StorePath": "", + "CaCertPath": "/Users/catbug/.docker/machine/certs/ca.pem", + "CaCertRemotePath": "", + "ServerCertPath": "/Users/catbug/.docker/machine/machines/foobar/server.pem", + "ServerKeyPath": "/Users/catbug/.docker/machine/machines/foobar/server-key.pem", + "ClientKeyPath": "/Users/catbug/.docker/machine/certs/key.pem", + "ServerCertRemotePath": "", + "ServerKeyRemotePath": "", + "PrivateKeyPath": "/Users/catbug/.docker/machine/certs/ca-key.pem", + "ClientCertPath": "/Users/catbug/.docker/machine/certs/cert.pem" + } + }, + "StorePath": "/Users/catbug/.docker/machine/machines/foobar" +}`) +) + +func TestMigrateHostV1ToHostV2(t *testing.T) { + h := &Host{} + expectedGlobalStorePath := "/Users/catbug/.docker/machine" + expectedCaPrivateKeyPath := "/Users/catbug/.docker/machine/certs/ca-key.pem" + migratedHost, migrationPerformed, err := MigrateHost(h, v1conf) + if err != nil { + t.Fatalf("Error attempting to migrate host: %s", err) + } + + if !migrationPerformed { + t.Fatal("Expected a migration to be reported as performed but it was not") + } + + if migratedHost.HostOptions.AuthOptions.StorePath != expectedGlobalStorePath { + t.Fatalf("Expected %q, got %q for the store path in AuthOptions", migratedHost.HostOptions.AuthOptions.StorePath, expectedGlobalStorePath) + } + + if migratedHost.HostOptions.AuthOptions.CaPrivateKeyPath != expectedCaPrivateKeyPath { + t.Fatalf("Expected %q, got %q for the private key path in AuthOptions", migratedHost.HostOptions.AuthOptions.CaPrivateKeyPath, expectedCaPrivateKeyPath) + } + + if migratedHost.HostOptions.AuthOptions.CertDir != filepath.Join(expectedGlobalStorePath, "certs") { + t.Fatalf("Expected %q, got %q for the cert dir in AuthOptions", migratedHost.HostOptions.AuthOptions.CaPrivateKeyPath, expectedGlobalStorePath) + } +} diff --git a/libmachine/host_test.go b/libmachine/host_test.go deleted file mode 100644 index 720aa06afa..0000000000 --- a/libmachine/host_test.go +++ /dev/null @@ -1,315 +0,0 @@ -package libmachine - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "testing" - - "github.com/docker/machine/drivers/fakedriver" - _ "github.com/docker/machine/drivers/none" - "github.com/docker/machine/libmachine/auth" - "github.com/docker/machine/libmachine/engine" - "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/state" - - "github.com/stretchr/testify/assert" -) - -const ( - hostTestName = "test-host" - hostTestDriverName = "none" - hostTestCaCert = "test-cert" - hostTestPrivateKey = "test-key" -) - -var ( - hostTestStorePath string - stdout *os.File -) - -func init() { - stdout = os.Stdout -} - -func getTestStore() (Store, error) { - tmpDir, err := ioutil.TempDir("", "machine-test-") - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - hostTestStorePath = tmpDir - - os.Setenv("MACHINE_STORAGE_PATH", tmpDir) - - return NewFilestore(tmpDir, hostTestCaCert, hostTestPrivateKey), nil -} - -func cleanup() { - os.Stdout = stdout - os.RemoveAll(hostTestStorePath) -} - -func getTestDriverFlags() *DriverOptionsMock { - name := hostTestName - flags := &DriverOptionsMock{ - Data: map[string]interface{}{ - "name": name, - "url": "unix:///var/run/docker.sock", - "swarm": false, - "swarm-host": "", - "swarm-master": false, - "swarm-discovery": "", - }, - } - return flags -} - -func getDefaultTestHost() (*Host, error) { - 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, hostOptions) - if err != nil { - return nil, err - } - - flags := getTestDriverFlags() - if err := host.Driver.SetConfigFromFlags(flags); err != nil { - return nil, err - } - - return host, nil -} - -func TestLoadHostDoesNotExist(t *testing.T) { - _, err := LoadHost("nope-not-here", "/nope/doesnotexist") - if err == nil { - t.Fatalf("expected error for non-existent host") - } -} - -func TestLoadHostExists(t *testing.T) { - host, err := getDefaultTestHost() - if err != nil { - t.Fatal(err) - } - authOptions := host.HostOptions.AuthOptions - if host.Name != hostTestName { - t.Fatalf("expected name %s; received %s", hostTestName, host.Name) - } - - if host.DriverName != hostTestDriverName { - t.Fatalf("expected driver %s; received %s", hostTestDriverName, host.DriverName) - } - - if authOptions.CaCertPath != hostTestCaCert { - t.Fatalf("expected ca cert path %s; received %s", hostTestCaCert, authOptions.CaCertPath) - } - - if authOptions.PrivateKeyPath != hostTestPrivateKey { - t.Fatalf("expected key path %s; received %s", hostTestPrivateKey, authOptions.PrivateKeyPath) - } -} - -func TestValidateHostnameValid(t *testing.T) { - hosts := []string{ - "zomg", - "test-ing", - "some.h0st", - } - - for _, v := range hosts { - isValid := ValidateHostName(v) - if !isValid { - t.Fatalf("Thought a valid hostname was invalid: %s", v) - } - } -} - -func TestValidateHostnameInvalid(t *testing.T) { - hosts := []string{ - "zom_g", - "test$ing", - "some😄host", - } - - for _, v := range hosts { - isValid := ValidateHostName(v) - if isValid { - t.Fatalf("Thought an invalid hostname was valid: %s", v) - } - } -} - -func TestHostOptions(t *testing.T) { - 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) - } - - if err := host.SaveConfig(); err != nil { - t.Fatal(err) - } - - if err := host.LoadConfig(); err != nil { - t.Fatal(err) - } - - // cleanup - if err := store.Remove(hostTestName, true); err != nil { - t.Fatal(err) - } -} - -func TestPrintIPEmptyGivenLocalEngine(t *testing.T) { - defer cleanup() - host, _ := getDefaultTestHost() - - out, w := captureStdout() - - assert.Nil(t, host.PrintIP()) - w.Close() - - assert.Equal(t, "", strings.TrimSpace(<-out)) -} - -func TestPrintIPPrintsGivenRemoteEngine(t *testing.T) { - defer cleanup() - host, _ := getDefaultTestHost() - host.Driver = &fakedriver.FakeDriver{} - - out, w := captureStdout() - - assert.Nil(t, host.PrintIP()) - - w.Close() - - assert.Equal(t, "1.2.3.4", strings.TrimSpace(<-out)) -} - -func captureStdout() (chan string, *os.File) { - r, w, _ := os.Pipe() - - // This is reversed in cleanup() - os.Stdout = w - - out := make(chan string) - - go func() { - var testOutput bytes.Buffer - io.Copy(&testOutput, r) - out <- testOutput.String() - }() - - return out, w -} - -func TestGetHostListItems(t *testing.T) { - defer cleanup() - - hostListItemsChan := make(chan HostListItem) - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - hosts := []Host{ - { - Name: "foo", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Running, - }, - StorePath: store.GetPath(), - HostOptions: &HostOptions{ - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Address: "", - Discovery: "", - }, - }, - }, - { - Name: "bar", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Stopped, - }, - StorePath: store.GetPath(), - HostOptions: &HostOptions{ - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Address: "", - Discovery: "", - }, - }, - }, - { - Name: "baz", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Running, - }, - StorePath: store.GetPath(), - HostOptions: &HostOptions{ - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Address: "", - Discovery: "", - }, - }, - }, - } - - for _, h := range hosts { - if err := store.Save(&h); err != nil { - t.Fatal(err) - } - } - - expected := map[string]state.State{ - "foo": state.Running, - "bar": state.Stopped, - "baz": state.Running, - } - - items := []HostListItem{} - for _, host := range hosts { - go getHostState(host, hostListItemsChan) - } - - for i := 0; i < len(hosts); i++ { - items = append(items, <-hostListItemsChan) - } - - for _, item := range items { - if expected[item.Name] != item.State { - t.Fatal("Expected state did not match for item", item) - } - } -} diff --git a/libmachine/hosttest/default_test_host.go b/libmachine/hosttest/default_test_host.go new file mode 100644 index 0000000000..0ddc7c4317 --- /dev/null +++ b/libmachine/hosttest/default_test_host.go @@ -0,0 +1,79 @@ +package hosttest + +import ( + "github.com/docker/machine/drivers/none" + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/libmachine/version" +) + +const ( + DefaultHostName = "test-host" + HostTestDriverName = "none" + HostTestCaCert = "test-cert" + HostTestPrivateKey = "test-key" +) + +type DriverOptionsMock struct { + Data map[string]interface{} +} + +func (d DriverOptionsMock) String(key string) string { + return d.Data[key].(string) +} + +func (d DriverOptionsMock) StringSlice(key string) []string { + return d.Data[key].([]string) +} + +func (d DriverOptionsMock) Int(key string) int { + return d.Data[key].(int) +} + +func (d DriverOptionsMock) Bool(key string) bool { + return d.Data[key].(bool) +} + +func GetTestDriverFlags() *DriverOptionsMock { + flags := &DriverOptionsMock{ + Data: map[string]interface{}{ + "name": DefaultHostName, + "url": "unix:///var/run/docker.sock", + "swarm": false, + "swarm-host": "", + "swarm-master": false, + "swarm-discovery": "", + }, + } + return flags +} + +func GetDefaultTestHost() (*host.Host, error) { + hostOptions := &host.HostOptions{ + EngineOptions: &engine.EngineOptions{}, + SwarmOptions: &swarm.SwarmOptions{}, + AuthOptions: &auth.AuthOptions{ + CaCertPath: HostTestCaCert, + CaPrivateKeyPath: HostTestPrivateKey, + }, + } + + driver := none.NewDriver(DefaultHostName, "/tmp/artifacts") + + host := &host.Host{ + ConfigVersion: version.ConfigVersion, + Name: DefaultHostName, + Driver: driver, + DriverName: "none", + HostOptions: hostOptions, + } + + flags := GetTestDriverFlags() + if err := host.Driver.SetConfigFromFlags(flags); err != nil { + return nil, err + } + + return host, nil +} diff --git a/libmachine/libmachine.go b/libmachine/libmachine.go new file mode 100644 index 0000000000..7a8511a91b --- /dev/null +++ b/libmachine/libmachine.go @@ -0,0 +1,91 @@ +package libmachine + +import ( + "fmt" + "path/filepath" + + "github.com/docker/machine/libmachine/cert" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnerror" + "github.com/docker/machine/libmachine/mcnutils" + "github.com/docker/machine/libmachine/persist" + "github.com/docker/machine/libmachine/provision" + "github.com/docker/machine/libmachine/state" +) + +func GetDefaultStore() *persist.Filestore { + homeDir := mcnutils.GetHomeDir() + certsDir := filepath.Join(homeDir, ".docker", "machine", "certs") + return &persist.Filestore{ + Path: homeDir, + CaCertPath: certsDir, + CaPrivateKeyPath: certsDir, + } +} + +// Create is the wrapper method which covers all of the boilerplate around +// actually creating, provisioning, and persisting an instance in the store. +func Create(store persist.Store, h *host.Host) error { + if err := cert.BootstrapCertificates(h.HostOptions.AuthOptions); err != nil { + return fmt.Errorf("Error generating certificates: %s", err) + } + + validName := host.ValidateHostName(h.Name) + if !validName { + return mcnerror.ErrInvalidHostname + } + + log.Info("Running pre-create checks...") + + if err := h.Driver.PreCreateCheck(); err != nil { + return fmt.Errorf("Error with pre-create check: %s", err) + } + + if err := store.Save(h); err != nil { + return fmt.Errorf("Error saving host to store before attempting creation: %s", err) + } + + log.Info("Creating machine...") + + if err := h.Driver.Create(); err != nil { + return fmt.Errorf("Error in driver during machine creation: %s", err) + } + + if err := store.Save(h); err != nil { + return fmt.Errorf("Error saving host to store after attempting creation: %s", err) + } + + // TODO: Not really a fan of just checking "none" here. + if h.Driver.DriverName() != "none" { + log.Info("Waiting for machine to be running, this may take a few minutes...") + if err := mcnutils.WaitFor(drivers.MachineInState(h.Driver, state.Running)); err != nil { + return fmt.Errorf("Error waiting for machine to be running: %s", err) + } + + log.Info("Machine is running, waiting for SSH to be available...") + if err := host.WaitForSSH(h); err != nil { + return fmt.Errorf("Error waiting for SSH: %s", err) + } + + log.Info("Detecting operating system of created instance...") + provisioner, err := provision.DetectProvisioner(h.Driver) + if err != nil { + return fmt.Errorf("Error detecting OS: %s", err) + } + + log.Info("Provisioning created instance...") + if err := provisioner.Provision(*h.HostOptions.SwarmOptions, *h.HostOptions.AuthOptions, *h.HostOptions.EngineOptions); err != nil { + return fmt.Errorf("Error running provisioning: %s", err) + } + } + + log.Debug("Reticulating splines...") + + return nil +} + +func SetDebug(val bool) { + log.IsDebug = val +} diff --git a/log/log.go b/libmachine/log/log.go similarity index 75% rename from log/log.go rename to libmachine/log/log.go index b75a1dbf88..7f530e9fe3 100644 --- a/log/log.go +++ b/libmachine/log/log.go @@ -1,13 +1,12 @@ package log import ( - "fmt" + "io" "os" - "strconv" ) // Why the interface? We may only want to print to STDOUT and STDERR for now, -// but it won't necessarily be that way forever. This interface is intended +// but it won't neccessarily be that way forever. This interface is intended // to provide a "framework" for a variety of different logging types in the // future (log to file, log to logstash, etc.) There could be a driver model // similar to what is done with OS or machine providers. @@ -34,25 +33,26 @@ type Logger interface { } var ( - l = TerminalLogger{} + l = StandardLogger{} + IsDebug bool = false ) -// TODO: I think this is superflous and can be replaced by one check for if -// debug is on that sets a variable in this module. -func isDebug() bool { - debugEnv := os.Getenv("MACHINE_DEBUG") - if debugEnv != "" { - showDebug, err := strconv.ParseBool(debugEnv) - if err != nil { - fmt.Fprintf(os.Stderr, "Error parsing boolean value from MACHINE_DEBUG: %s\n", err) - os.Exit(1) - } - return showDebug - } - return false +type Fields map[string]interface{} + +func init() { + // TODO: Is this really the best approach? I worry that it will create + // implicit behavior which may be problmatic for users of the lib. + SetOutWriter(os.Stdout) + SetErrWriter(os.Stderr) } -type Fields map[string]interface{} +func SetOutWriter(w io.Writer) { + l.OutWriter = w +} + +func SetErrWriter(w io.Writer) { + l.ErrWriter = w +} func Debug(args ...interface{}) { l.Debug(args...) @@ -70,6 +70,10 @@ func Errorf(fmtString string, args ...interface{}) { l.Errorf(fmtString, args...) } +func Errorln(args ...interface{}) { + l.Errorln(args...) +} + func Info(args ...interface{}) { l.Info(args...) } @@ -78,6 +82,10 @@ func Infof(fmtString string, args ...interface{}) { l.Infof(fmtString, args...) } +func Infoln(args ...interface{}) { + l.Infoln(args...) +} + func Fatal(args ...interface{}) { l.Fatal(args...) } diff --git a/log/log_test.go b/libmachine/log/log_test.go similarity index 100% rename from log/log_test.go rename to libmachine/log/log_test.go diff --git a/log/terminal.go b/libmachine/log/standard.go similarity index 98% rename from log/terminal.go rename to libmachine/log/standard.go index c7833fe06a..8fc8c80163 100644 --- a/log/terminal.go +++ b/libmachine/log/standard.go @@ -36,13 +36,13 @@ func (t TerminalLogger) errf(fmtString string, args ...interface{}) { } func (t TerminalLogger) Debug(args ...interface{}) { - if isDebug() { + if IsDebug { t.log(args...) } } func (t TerminalLogger) Debugf(fmtString string, args ...interface{}) { - if isDebug() { + if IsDebug { t.logf(fmtString, args...) } } diff --git a/libmachine/log/terminal.go b/libmachine/log/terminal.go new file mode 100644 index 0000000000..f579e0e159 --- /dev/null +++ b/libmachine/log/terminal.go @@ -0,0 +1,133 @@ +package log + +import ( + "fmt" + "io" + "os" + "sort" +) + +type StandardLogger struct { + // fieldOut is used to do log.WithFields correctly + fieldOut string + OutWriter io.Writer + ErrWriter io.Writer +} + +func (t StandardLogger) log(args ...interface{}) { + fmt.Fprint(t.OutWriter, args...) + fmt.Fprint(t.OutWriter, t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t StandardLogger) logf(fmtString string, args ...interface{}) { + fmt.Fprintf(t.OutWriter, fmtString, args...) + fmt.Fprint(t.OutWriter, "\n") + t.fieldOut = "" +} + +func (t StandardLogger) err(args ...interface{}) { + fmt.Fprint(t.ErrWriter, args...) + fmt.Fprint(t.ErrWriter, t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t StandardLogger) errf(fmtString string, args ...interface{}) { + fmt.Fprintf(t.ErrWriter, fmtString, args...) + fmt.Fprint(t.ErrWriter, t.fieldOut, "\n") + t.fieldOut = "" +} + +func (t StandardLogger) Debug(args ...interface{}) { + if IsDebug { + t.err(args...) + } +} + +func (t StandardLogger) Debugf(fmtString string, args ...interface{}) { + if IsDebug { + t.errf(fmtString, args...) + } +} + +func (t StandardLogger) Error(args ...interface{}) { + t.err(args...) +} + +func (t StandardLogger) Errorf(fmtString string, args ...interface{}) { + t.errf(fmtString, args...) +} + +func (t StandardLogger) Errorln(args ...interface{}) { + + t.err(args...) +} + +func (t StandardLogger) Info(args ...interface{}) { + t.log(args...) +} + +func (t StandardLogger) Infof(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t StandardLogger) Infoln(args ...interface{}) { + t.log(args...) +} + +func (t StandardLogger) Fatal(args ...interface{}) { + t.err(args...) + os.Exit(1) +} + +func (t StandardLogger) Fatalf(fmtString string, args ...interface{}) { + t.errf(fmtString, args...) + os.Exit(1) +} + +func (t StandardLogger) Print(args ...interface{}) { + t.log(args...) +} + +func (t StandardLogger) Printf(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t StandardLogger) Warn(args ...interface{}) { + t.log(args...) +} + +func (t StandardLogger) Warnf(fmtString string, args ...interface{}) { + t.logf(fmtString, args...) +} + +func (t StandardLogger) WithFields(fields Fields) Logger { + // When the user calls WithFields, we make a string which gets appended + // to the output of the final [Info|Warn|Error] call for the + // descriptive fields. Because WithFields returns the proper Logger + // (with the fieldOut string set correctly), the logrus syntax will + // still work. + kvpairs := []string{} + + // Why the string slice song and dance? Because Go's map iteration + // order is random, we will get inconsistent results if we don't sort + // the fields (or their resulting string K/V pairs, like we have here). + // Otherwise, we couldn't test this reliably. + for k, v := range fields { + kvpairs = append(kvpairs, fmt.Sprintf("%s=%v", k, v)) + } + + sort.Strings(kvpairs) + + // TODO: + // 1. Is this thread-safe? + // 2. Add more tabs? + t.fieldOut = "\t\t" + + for _, s := range kvpairs { + // TODO: Is %v the correct format string here? + t.fieldOut = fmt.Sprintf("%s %s", t.fieldOut, s) + } + + return t +} diff --git a/libmachine/errors.go b/libmachine/mcnerror/errors.go similarity index 64% rename from libmachine/errors.go rename to libmachine/mcnerror/errors.go index e973dc2597..f78eccdaad 100644 --- a/libmachine/errors.go +++ b/libmachine/mcnerror/errors.go @@ -1,4 +1,4 @@ -package libmachine +package mcnerror import ( "errors" @@ -17,3 +17,11 @@ type ErrHostDoesNotExist struct { func (e ErrHostDoesNotExist) Error() string { return fmt.Sprintf("Error: Host does not exist: %s", e.Name) } + +type ErrHostAlreadyExists struct { + Name string +} + +func (e ErrHostAlreadyExists) Error() string { + return fmt.Sprintf("Error: Host already exists: %s", e.Name) +} diff --git a/utils/b2d.go b/libmachine/mcnutils/b2d.go similarity index 90% rename from utils/b2d.go rename to libmachine/mcnutils/b2d.go index e49651a2e4..d781078767 100644 --- a/utils/b2d.go +++ b/libmachine/mcnutils/b2d.go @@ -1,4 +1,4 @@ -package utils +package mcnutils import ( "encoding/json" @@ -12,7 +12,7 @@ import ( "path/filepath" "time" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) const ( @@ -38,6 +38,7 @@ func getClient() *http.Client { } type B2dUtils struct { + storePath string isoFilename string commonIsoPath string imgCachePath string @@ -45,10 +46,10 @@ type B2dUtils struct { githubBaseUrl string } -func NewB2dUtils(githubApiBaseUrl, githubBaseUrl string) *B2dUtils { +func NewB2dUtils(githubApiBaseUrl, githubBaseUrl, storePath string) *B2dUtils { defaultBaseApiUrl := "https://api.github.com" defaultBaseUrl := "https://github.com" - imgCachePath := GetMachineCacheDir() + imgCachePath := filepath.Join(storePath, "cache") isoFilename := "boot2docker.iso" if githubApiBaseUrl == "" { @@ -60,8 +61,9 @@ func NewB2dUtils(githubApiBaseUrl, githubBaseUrl string) *B2dUtils { } return &B2dUtils{ + storePath: storePath, isoFilename: isoFilename, - imgCachePath: GetMachineCacheDir(), + imgCachePath: imgCachePath, commonIsoPath: filepath.Join(imgCachePath, isoFilename), githubApiBaseUrl: githubApiBaseUrl, githubBaseUrl: githubBaseUrl, @@ -180,8 +182,9 @@ func (b *B2dUtils) DownloadISOFromURL(latestReleaseUrl string) error { } func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error { - machinesDir := GetMachineDir() - machineIsoPath := filepath.Join(machinesDir, machineName, b.isoFilename) + // TODO: This is a bit off-color. + machineDir := filepath.Join(b.storePath, "machines", machineName) + machineIsoPath := filepath.Join(machineDir, b.isoFilename) // just in case the cache dir has been manually deleted, // check for it and recreate it if it's gone @@ -201,7 +204,7 @@ func (b *B2dUtils) CopyIsoToMachineDir(isoURL, machineName string) error { } else { // But if ISO is specified go get it directly log.Infof("Downloading %s from %s...", b.isoFilename, isoURL) - if err := b.DownloadISO(filepath.Join(machinesDir, machineName), b.isoFilename, isoURL); err != nil { + if err := b.DownloadISO(machineDir, b.isoFilename, isoURL); err != nil { return err } } diff --git a/utils/b2d_test.go b/libmachine/mcnutils/b2d_test.go similarity index 91% rename from utils/b2d_test.go rename to libmachine/mcnutils/b2d_test.go index 2fe5a38103..62187653d9 100644 --- a/utils/b2d_test.go +++ b/libmachine/mcnutils/b2d_test.go @@ -1,4 +1,4 @@ -package utils +package mcnutils import ( "fmt" @@ -16,7 +16,7 @@ func TestGetLatestBoot2DockerReleaseUrl(t *testing.T) { })) defer ts.Close() - b := NewB2dUtils(ts.URL, ts.URL) + b := NewB2dUtils(ts.URL, ts.URL, "/tmp/isos") isoUrl, err := b.GetLatestBoot2DockerReleaseURL() if err != nil { t.Fatal(err) @@ -42,7 +42,7 @@ func TestDownloadIso(t *testing.T) { t.Fatal(err) } - b := NewB2dUtils(ts.URL, ts.URL) + b := NewB2dUtils(ts.URL, ts.URL, "/tmp/artifacts") if err := b.DownloadISO(tmpDir, filename, ts.URL); err != nil { t.Fatal(err) } diff --git a/utils/utils.go b/libmachine/mcnutils/utils.go similarity index 83% rename from utils/utils.go rename to libmachine/mcnutils/utils.go index 95cbbb22ea..67d8feeb4c 100644 --- a/utils/utils.go +++ b/libmachine/mcnutils/utils.go @@ -1,4 +1,4 @@ -package utils +package mcnutils import ( "crypto/rand" @@ -8,14 +8,15 @@ import ( "io" "net" "os" - "path/filepath" "runtime" "strconv" "time" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) +// TODO: Having this here just strikes me as dangerous, but some of the drivers +// depend on it ;_; func GetHomeDir() string { if runtime.GOOS == "windows" { return os.Getenv("USERPROFILE") @@ -23,30 +24,6 @@ func GetHomeDir() string { return os.Getenv("HOME") } -func GetBaseDir() string { - baseDir := os.Getenv("MACHINE_STORAGE_PATH") - if baseDir == "" { - baseDir = filepath.Join(GetHomeDir(), ".docker", "machine") - } - return baseDir -} - -func GetDockerDir() string { - return filepath.Join(GetHomeDir(), ".docker") -} - -func GetMachineDir() string { - return filepath.Join(GetBaseDir(), "machines") -} - -func GetMachineCertDir() string { - return filepath.Join(GetBaseDir(), "certs") -} - -func GetMachineCacheDir() string { - return filepath.Join(GetBaseDir(), "cache") -} - func GetUsername() string { u := "unknown" osUser := "" diff --git a/libmachine/mcnutils/utils_test.go b/libmachine/mcnutils/utils_test.go new file mode 100644 index 0000000000..01728bccfa --- /dev/null +++ b/libmachine/mcnutils/utils_test.go @@ -0,0 +1,69 @@ +package mcnutils + +import ( + "io/ioutil" + "os" + "path/filepath" + "runtime" + "testing" +) + +func TestCopyFile(t *testing.T) { + testStr := "test-machine" + + srcFile, err := ioutil.TempFile("", "machine-test-") + if err != nil { + t.Fatal(err) + } + srcFi, err := srcFile.Stat() + if err != nil { + t.Fatal(err) + } + + srcFile.Write([]byte(testStr)) + srcFile.Close() + + srcFilePath := filepath.Join(os.TempDir(), srcFi.Name()) + + destFile, err := ioutil.TempFile("", "machine-copy-test-") + if err != nil { + t.Fatal(err) + } + + destFi, err := destFile.Stat() + if err != nil { + t.Fatal(err) + } + + destFile.Close() + + destFilePath := filepath.Join(os.TempDir(), destFi.Name()) + + if err := CopyFile(srcFilePath, destFilePath); err != nil { + t.Fatal(err) + } + + data, err := ioutil.ReadFile(destFilePath) + if err != nil { + t.Fatal(err) + } + + if string(data) != testStr { + t.Fatalf("expected data \"%s\"; received \"%\"", testStr, string(data)) + } +} + +func TestGetUsername(t *testing.T) { + currentUser := "unknown" + switch runtime.GOOS { + case "darwin", "linux": + currentUser = os.Getenv("USER") + case "windows": + currentUser = os.Getenv("USERNAME") + } + + username := GetUsername() + if username != currentUser { + t.Fatalf("expected username %s; received %s", currentUser, username) + } +} diff --git a/libmachine/migrate.go b/libmachine/migrate.go deleted file mode 100644 index 80555ec810..0000000000 --- a/libmachine/migrate.go +++ /dev/null @@ -1,65 +0,0 @@ -package libmachine - -import ( - "encoding/json" - "fmt" - - "github.com/docker/machine/drivers" - "github.com/docker/machine/version" -) - -func getMigratedHostMetadata(data []byte) (*HostMetadata, error) { - // HostMetadata is for a "first pass" so we can then load the driver - var ( - hostMetadata *HostMetadataV0 - ) - - if err := json.Unmarshal(data, &hostMetadata); err != nil { - return &HostMetadata{}, err - } - - migratedHostMetadata := MigrateHostMetadataV0ToHostMetadataV1(hostMetadata) - - return migratedHostMetadata, nil -} - -func MigrateHost(h *Host, data []byte) (*Host, error) { - migratedHostMetadata, err := getMigratedHostMetadata(data) - if err != nil { - return &Host{}, err - } - - authOptions := migratedHostMetadata.HostOptions.AuthOptions - - driver, err := drivers.NewDriver( - migratedHostMetadata.DriverName, - h.Name, - h.StorePath, - authOptions.CaCertPath, - authOptions.PrivateKeyPath, - ) - if err != nil { - return &Host{}, err - } - - for h.ConfigVersion = migratedHostMetadata.ConfigVersion; h.ConfigVersion < version.ConfigVersion; h.ConfigVersion++ { - switch h.ConfigVersion { - case 0: - hostV0 := &HostV0{ - Driver: driver, - } - if err := json.Unmarshal(data, &hostV0); err != nil { - return &Host{}, fmt.Errorf("Error unmarshalling host config version 0: %s", err) - } - h = MigrateHostV0ToHostV1(hostV0) - default: - } - } - - h.Driver = driver - if err := json.Unmarshal(data, &h); err != nil { - return &Host{}, fmt.Errorf("Error unmarshalling most recent host version: %s", err) - } - - return h, nil -} diff --git a/libmachine/migrate_v0_v1.go b/libmachine/migrate_v0_v1.go deleted file mode 100644 index 61916da407..0000000000 --- a/libmachine/migrate_v0_v1.go +++ /dev/null @@ -1,113 +0,0 @@ -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" -) - -// In the 0.0.1 => 0.0.2 transition, the JSON representation of -// machines changed from a "flat" to a more "nested" structure -// for various options and configuration settings. To preserve -// compatibility with existing machines, these migration functions -// have been introduced. They preserve backwards compat at the expense -// of some duplicated information. - -// validates host config and modifies if needed -// this is used for configuration updates -func MigrateHostV0ToHostV1(hostV0 *HostV0) *Host { - host := &Host{} - - certInfoV0 := getCertInfoFromHost(hostV0) - - host.HostOptions = &HostOptions{} - host.HostOptions.EngineOptions = &engine.EngineOptions{} - host.HostOptions.SwarmOptions = &swarm.SwarmOptions{ - Address: "", - Discovery: hostV0.SwarmDiscovery, - Host: hostV0.SwarmHost, - Master: hostV0.SwarmMaster, - } - host.HostOptions.AuthOptions = &auth.AuthOptions{ - StorePath: hostV0.StorePath, - CaCertPath: certInfoV0.CaCertPath, - CaCertRemotePath: "", - ServerCertPath: certInfoV0.ServerCertPath, - ServerKeyPath: certInfoV0.ServerKeyPath, - ClientKeyPath: certInfoV0.ClientKeyPath, - ServerCertRemotePath: "", - ServerKeyRemotePath: "", - PrivateKeyPath: certInfoV0.CaKeyPath, - ClientCertPath: certInfoV0.ClientCertPath, - } - - return host -} - -// fills nested host metadata and modifies if needed -// this is used for configuration updates -func MigrateHostMetadataV0ToHostMetadataV1(m *HostMetadataV0) *HostMetadata { - hostMetadata := &HostMetadata{} - hostMetadata.DriverName = m.DriverName - hostMetadata.HostOptions.EngineOptions = &engine.EngineOptions{} - hostMetadata.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 hostMetadata -} - -func getCertInfoFromHost(h *HostV0) 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, - } -} diff --git a/libmachine/persist/filestore.go b/libmachine/persist/filestore.go new file mode 100644 index 0000000000..8edfe8cd1f --- /dev/null +++ b/libmachine/persist/filestore.go @@ -0,0 +1,181 @@ +package persist + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "strings" + + "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/host" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnerror" + "github.com/docker/machine/libmachine/swarm" + "github.com/docker/machine/libmachine/version" +) + +type Filestore struct { + Path string + CaCertPath string + CaPrivateKeyPath string +} + +func (s Filestore) getMachinesDir() string { + return filepath.Join(s.Path, "machines") +} + +func (s Filestore) Save(host *host.Host) error { + data, err := json.Marshal(host) + if err != nil { + return err + } + + hostPath := filepath.Join(s.getMachinesDir(), host.Name) + + if err := os.MkdirAll(hostPath, 0700); err != nil { + return err + } + + if err := ioutil.WriteFile(filepath.Join(hostPath, "config.json"), data, 0600); err != nil { + return err + } + + return nil +} + +func (s Filestore) Remove(name string, force bool) error { + h, err := s.Load(name) + if err != nil { + return err + } + + if err := h.Driver.Remove(); err != nil { + if !force { + return err + } + } + + hostPath := filepath.Join(s.getMachinesDir(), name) + return os.RemoveAll(hostPath) +} + +func (s Filestore) List() ([]*host.Host, error) { + dir, err := ioutil.ReadDir(s.getMachinesDir()) + if err != nil && !os.IsNotExist(err) { + return nil, err + } + + hosts := []*host.Host{} + + for _, file := range dir { + if file.IsDir() && !strings.HasPrefix(file.Name(), ".") { + host, err := s.Load(file.Name()) + if err != nil { + log.Errorf("error loading host %q: %s", file.Name(), err) + continue + } + hosts = append(hosts, host) + } + } + return hosts, nil +} + +func (s Filestore) Exists(name string) (bool, error) { + _, err := os.Stat(filepath.Join(s.getMachinesDir(), name)) + + if os.IsNotExist(err) { + return false, nil + } else if err == nil { + return true, nil + } + + return false, err +} + +func (s Filestore) loadConfig(h *host.Host) error { + data, err := ioutil.ReadFile(filepath.Join(s.getMachinesDir(), h.Name, "config.json")) + if err != nil { + return err + } + + // Remember the machine name so we don't have to pass it through each + // struct in the migration. + name := h.Name + + migratedHost, migrationPerformed, err := host.MigrateHost(h, data) + if err != nil { + return fmt.Errorf("Error getting migrated host: %s", err) + } + + *h = *migratedHost + + h.Name = name + + // If we end up performing a migration, we should save afterwards so we don't have to do it again on subsequent invocations. + if migrationPerformed { + if err := s.Save(h); err != nil { + return fmt.Errorf("Error saving config after migration was performed: %s", err) + } + } + + return nil + +} + +func (s Filestore) Load(name string) (*host.Host, error) { + hostPath := filepath.Join(s.getMachinesDir(), name) + + if _, err := os.Stat(hostPath); os.IsNotExist(err) { + return nil, mcnerror.ErrHostDoesNotExist{ + Name: name, + } + } + + host := &host.Host{ + Name: name, + } + + if err := s.loadConfig(host); err != nil { + return nil, err + } + + return host, nil +} + +func (s Filestore) NewHost(driver drivers.Driver) (*host.Host, error) { + certDir := filepath.Join(s.Path, "certs") + + hostOptions := &host.HostOptions{ + AuthOptions: &auth.AuthOptions{ + CertDir: certDir, + CaCertPath: filepath.Join(certDir, "ca.pem"), + CaPrivateKeyPath: filepath.Join(certDir, "ca-key.pem"), + ClientCertPath: filepath.Join(certDir, "cert.pem"), + ClientKeyPath: filepath.Join(certDir, "key.pem"), + ServerCertPath: filepath.Join(s.getMachinesDir(), "server.pem"), + ServerKeyPath: filepath.Join(s.getMachinesDir(), "server-key.pem"), + }, + EngineOptions: &engine.EngineOptions{ + InstallURL: "https://get.docker.com", + StorageDriver: "aufs", + TlsVerify: true, + }, + SwarmOptions: &swarm.SwarmOptions{ + Host: "tcp://0.0.0.0:3376", + Image: "swarm:latest", + Strategy: "spread", + }, + } + + return &host.Host{ + ConfigVersion: version.ConfigVersion, + Name: driver.GetMachineName(), + Driver: driver, + DriverName: driver.DriverName(), + HostOptions: hostOptions, + }, nil +} diff --git a/libmachine/persist/filestore_test.go b/libmachine/persist/filestore_test.go new file mode 100644 index 0000000000..e81918935d --- /dev/null +++ b/libmachine/persist/filestore_test.go @@ -0,0 +1,184 @@ +package persist + +import ( + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "testing" + + _ "github.com/docker/machine/drivers/none" + "github.com/docker/machine/libmachine/hosttest" +) + +func cleanup() { + os.RemoveAll(os.Getenv("MACHINE_STORAGE_PATH")) +} + +func getTestStore() Filestore { + tmpDir, err := ioutil.TempDir("", "machine-test-") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + os.Setenv("MACHINE_STORAGE_PATH", tmpDir) + + return Filestore{ + Path: tmpDir, + CaCertPath: filepath.Join(tmpDir, "certs", "ca-cert.pem"), + CaPrivateKeyPath: filepath.Join(tmpDir, "certs", "ca-key.pem"), + } +} + +func TestStoreSave(t *testing.T) { + defer cleanup() + + store := getTestStore() + + h, err := hosttest.GetDefaultTestHost() + if err != nil { + t.Fatal(err) + } + + if err := store.Save(h); err != nil { + t.Fatal(err) + } + + path := filepath.Join(store.getMachinesDir(), h.Name) + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Fatalf("Host path doesn't exist: %s", path) + } +} + +func TestStoreRemove(t *testing.T) { + defer cleanup() + + store := getTestStore() + + h, err := hosttest.GetDefaultTestHost() + if err != nil { + t.Fatal(err) + } + + if err := store.Save(h); err != nil { + t.Fatal(err) + } + + path := filepath.Join(store.getMachinesDir(), h.Name) + if _, err := os.Stat(path); os.IsNotExist(err) { + t.Fatalf("Host path doesn't exist: %s", path) + } + + err = store.Remove(h.Name, false) + if err != nil { + t.Fatal(err) + } + + if _, err := os.Stat(path); err == nil { + t.Fatalf("Host path still exists after remove: %s", path) + } +} + +func TestStoreList(t *testing.T) { + defer cleanup() + + store := getTestStore() + + h, err := hosttest.GetDefaultTestHost() + if err != nil { + t.Fatal(err) + } + + if err := store.Save(h); err != nil { + t.Fatal(err) + } + + hosts, err := store.List() + if len(hosts) != 1 { + t.Fatalf("List returned %d items, expected 1", len(hosts)) + } + + if hosts[0].Name != h.Name { + t.Fatalf("hosts[0] name is incorrect, got: %s", hosts[0].Name) + } +} + +func TestStoreExists(t *testing.T) { + defer cleanup() + store := getTestStore() + + h, err := hosttest.GetDefaultTestHost() + if err != nil { + t.Fatal(err) + } + + exists, err := store.Exists(h.Name) + if exists { + t.Fatal("Host should not exist before saving") + } + + if err := store.Save(h); err != nil { + t.Fatal(err) + } + + exists, err = store.Exists(h.Name) + if err != nil { + t.Fatal(err) + } + + if !exists { + t.Fatal("Host should exist after saving") + } + + if err := store.Remove(h.Name, true); err != nil { + t.Fatal(err) + } + + exists, err = store.Exists(h.Name) + if err != nil { + t.Fatal(err) + } + + if exists { + t.Fatal("Host should not exist after removing") + } +} + +func TestStoreLoad(t *testing.T) { + defer cleanup() + + expectedURL := "unix:///foo/baz" + flags := hosttest.GetTestDriverFlags() + flags.Data["url"] = expectedURL + + store := getTestStore() + + h, err := hosttest.GetDefaultTestHost() + if err != nil { + t.Fatal(err) + } + + if err := h.Driver.SetConfigFromFlags(flags); err != nil { + t.Fatal(err) + } + + if err := store.Save(h); err != nil { + t.Fatal(err) + } + + h, err = store.Load(h.Name) + if err != nil { + log.Fatal(err) + } + + actualURL, err := h.GetURL() + if err != nil { + t.Fatal(err) + } + + if actualURL != expectedURL { + t.Fatalf("GetURL is not %q, got %q", expectedURL, actualURL) + } +} diff --git a/libmachine/persist/store.go b/libmachine/persist/store.go new file mode 100644 index 0000000000..e96e26c8e3 --- /dev/null +++ b/libmachine/persist/store.go @@ -0,0 +1,26 @@ +package persist + +import ( + "github.com/docker/machine/libmachine/drivers" + "github.com/docker/machine/libmachine/host" +) + +type Store interface { + // Exists returns whether a machine exists or not + Exists(name string) (bool, error) + + // NewHost will initialize a new host machine + NewHost(driver drivers.Driver) (*host.Host, error) + + // List returns a list of all hosts in the store + List() ([]*host.Host, error) + + // Get loads a host by name + Load(name string) (*host.Host, error) + + // Remove removes a machine from the store + Remove(name string, force bool) error + + // Save persists a machine in the store + Save(host *host.Host) error +} diff --git a/libmachine/persisttest/default_test_store.go b/libmachine/persisttest/default_test_store.go new file mode 100644 index 0000000000..96b1dbe27d --- /dev/null +++ b/libmachine/persisttest/default_test_store.go @@ -0,0 +1,34 @@ +package persisttest + +import ( + "fmt" + "io/ioutil" + "os" + + "github.com/docker/machine/libmachine/hosttest" + "github.com/docker/machine/libmachine/persist" +) + +var ( + TestStoreDir = "" +) + +func Cleanup() error { + return os.RemoveAll(TestStoreDir) +} + +func GetDefaultTestStore() (persist.Filestore, error) { + tmpDir, err := ioutil.TempDir("", "machine-test-") + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + TestStoreDir = tmpDir + + return persist.Filestore{ + Path: tmpDir, + CaCertPath: hosttest.HostTestCaCert, + CaPrivateKeyPath: hosttest.HostTestPrivateKey, + }, nil +} diff --git a/libmachine/provider.go b/libmachine/provider.go deleted file mode 100644 index df0199600f..0000000000 --- a/libmachine/provider.go +++ /dev/null @@ -1,93 +0,0 @@ -package libmachine - -import ( - "fmt" - "os" - "path/filepath" - - "github.com/docker/machine/drivers" - "github.com/docker/machine/utils" -) - -type Provider struct { - store Store -} - -func New(store Store) (*Provider, error) { - return &Provider{ - store: store, - }, nil -} - -func (provider *Provider) Create(name string, driverName string, hostOptions *HostOptions, driverConfig drivers.DriverOptions) (*Host, error) { - validName := ValidateHostName(name) - if !validName { - return nil, ErrInvalidHostname - } - exists, err := provider.store.Exists(name) - if err != nil { - return nil, err - } - if exists { - return nil, fmt.Errorf("Machine %s already exists", name) - } - - hostPath := filepath.Join(utils.GetMachineDir(), name) - - host, err := NewHost(name, driverName, hostOptions) - if err != nil { - return host, err - } - if driverConfig != nil { - if err := host.Driver.SetConfigFromFlags(driverConfig); err != nil { - return host, err - } - } - - if err := host.Driver.PreCreateCheck(); err != nil { - return nil, err - } - - if err := os.MkdirAll(hostPath, 0700); err != nil { - return nil, err - } - - if err := host.SaveConfig(); err != nil { - return host, err - } - - if err := host.Create(name); err != nil { - return host, err - } - - return host, nil -} - -func (provider *Provider) Exists(name string) (bool, error) { - return provider.store.Exists(name) -} - -func (provider *Provider) GetActive() (*Host, error) { - return provider.store.GetActive() -} - -func (provider *Provider) List() ([]*Host, error) { - return provider.store.List() -} - -func (provider *Provider) Get(name string) (*Host, error) { - return provider.store.Get(name) -} - -func (provider *Provider) Remove(name string, force bool) error { - host, err := provider.store.Get(name) - if err != nil { - return err - } - if err := host.Remove(force); err != nil { - if !force { - return err - } - } - return provider.store.Remove(name, force) -} diff --git a/libmachine/provider/provider.go b/libmachine/provider/provider.go new file mode 100644 index 0000000000..294c283c6f --- /dev/null +++ b/libmachine/provider/provider.go @@ -0,0 +1,13 @@ +package provider + +import "github.com/docker/machine/libmachine/host" + +type Provider interface { + // IsValid checks whether or not the Provider can successfully create + // machines. If the check does not pass, the provider is no good. + IsValid() bool + + // Create calls out to the driver this provider is associated with, to + // actually create the resource. + Create() (host.Host, error) +} diff --git a/libmachine/provider_test.go b/libmachine/provider_test.go deleted file mode 100644 index 7ef83e6f57..0000000000 --- a/libmachine/provider_test.go +++ /dev/null @@ -1 +0,0 @@ -package libmachine diff --git a/libmachine/provision/boot2docker.go b/libmachine/provision/boot2docker.go index 8aa707728c..d16d90e29f 100644 --- a/libmachine/provision/boot2docker.go +++ b/libmachine/provision/boot2docker.go @@ -6,15 +6,16 @@ import ( "path" "text/template" - "github.com/docker/machine/drivers" + "github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" + "github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" ) func init() { @@ -56,7 +57,7 @@ func (provisioner *Boot2DockerProvisioner) upgradeIso() error { return err } - if err := utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Stopped)); err != nil { + if err := mcnutils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Stopped)); err != nil { return err } @@ -64,7 +65,10 @@ func (provisioner *Boot2DockerProvisioner) upgradeIso() error { log.Infof("Upgrading machine %s...", machineName) - b2dutils := utils.NewB2dUtils("", "") + // TODO: Ideally, we should not read from mcndirs directory at all. + // The driver should be able to communicate how and where to place the + // relevant files. + b2dutils := mcnutils.NewB2dUtils("", "", mcndirs.GetBaseDir()) // Usually we call this implicitly, but call it here explicitly to get // the latest boot2docker ISO. @@ -83,7 +87,7 @@ func (provisioner *Boot2DockerProvisioner) upgradeIso() error { return err } - return utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Running)) + return mcnutils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Running)) } func (provisioner *Boot2DockerProvisioner) Package(name string, action pkgaction.PackageAction) error { @@ -197,7 +201,7 @@ func (provisioner *Boot2DockerProvisioner) Provision(swarmOptions swarm.SwarmOpt // b2d hosts need to wait for the daemon to be up // before continuing with provisioning - if err := utils.WaitForDocker(ip, 2376); err != nil { + if err := mcnutils.WaitForDocker(ip, 2376); err != nil { return err } diff --git a/libmachine/provision/centos.go b/libmachine/provision/centos.go index b348e005e0..dff8aa4106 100644 --- a/libmachine/provision/centos.go +++ b/libmachine/provision/centos.go @@ -1,7 +1,7 @@ package provision import ( - "github.com/docker/machine/drivers" + "github.com/docker/machine/libmachine/drivers" ) func init() { diff --git a/libmachine/provision/configure_swarm.go b/libmachine/provision/configure_swarm.go index 1fa131cde5..69e4e79de3 100644 --- a/libmachine/provision/configure_swarm.go +++ b/libmachine/provision/configure_swarm.go @@ -8,8 +8,8 @@ import ( "text/template" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" ) type SwarmCommandContext struct { @@ -51,6 +51,8 @@ func configureSwarm(p Provisioner, swarmOptions swarm.SwarmOptions, authOptions return nil } + log.Info("Configuring swarm...") + ip, err := p.GetDriver().GetIP() if err != nil { return err diff --git a/libmachine/provision/coreos.go b/libmachine/provision/coreos.go index 5bb5185927..21d7247297 100644 --- a/libmachine/provision/coreos.go +++ b/libmachine/provision/coreos.go @@ -5,14 +5,14 @@ import ( "fmt" "text/template" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" ) const ( @@ -63,7 +63,7 @@ func (provisioner *CoreOSProvisioner) Service(name string, action serviceaction. if err != nil { return err } - if err := utils.WaitForDocker(ip, 2376); err != nil { + if err := mcnutils.WaitForDocker(ip, 2376); err != nil { return err } } diff --git a/libmachine/provision/debian.go b/libmachine/provision/debian.go index 061cd6830f..17ad60b1f7 100644 --- a/libmachine/provision/debian.go +++ b/libmachine/provision/debian.go @@ -5,14 +5,14 @@ import ( "fmt" "text/template" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" ) func init() { @@ -154,7 +154,7 @@ func (provisioner *DebianProvisioner) Provision(swarmOptions swarm.SwarmOptions, } log.Debug("waiting for docker daemon") - if err := utils.WaitFor(provisioner.dockerDaemonResponding); err != nil { + if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil { return err } diff --git a/libmachine/provision/fedora.go b/libmachine/provision/fedora.go index a201fe7a9d..4b688087ea 100644 --- a/libmachine/provision/fedora.go +++ b/libmachine/provision/fedora.go @@ -1,7 +1,7 @@ package provision import ( - "github.com/docker/machine/drivers" + "github.com/docker/machine/libmachine/drivers" ) func init() { diff --git a/libmachine/provision/generic.go b/libmachine/provision/generic.go index 1b174f24b1..4a7938947d 100644 --- a/libmachine/provision/generic.go +++ b/libmachine/provision/generic.go @@ -5,8 +5,8 @@ import ( "fmt" "text/template" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" "github.com/docker/machine/libmachine/swarm" ) diff --git a/libmachine/provision/os_release.go b/libmachine/provision/os_release.go index e10e1077fb..bfbefbdcaf 100644 --- a/libmachine/provision/os_release.go +++ b/libmachine/provision/os_release.go @@ -7,7 +7,7 @@ import ( "reflect" "strings" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) // The /etc/os-release file contains operating system identification data diff --git a/libmachine/provision/provisioner.go b/libmachine/provision/provisioner.go index c2316e5423..8087bc111f 100644 --- a/libmachine/provision/provisioner.go +++ b/libmachine/provision/provisioner.go @@ -3,13 +3,13 @@ package provision import ( "fmt" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" ) var provisioners = make(map[string]*RegisteredProvisioner) diff --git a/libmachine/provision/rancheros.go b/libmachine/provision/rancheros.go index 1ab8b52e4b..005db00ae6 100644 --- a/libmachine/provision/rancheros.go +++ b/libmachine/provision/rancheros.go @@ -6,15 +6,16 @@ import ( "net/http" "strings" - "github.com/docker/machine/drivers" + "github.com/docker/machine/commands/mcndirs" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" + "github.com/docker/machine/libmachine/state" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/state" - "github.com/docker/machine/utils" ) const ( @@ -168,7 +169,7 @@ func (provisioner *RancherProvisioner) upgradeIso() error { return err } - if err := utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Stopped)); err != nil { + if err := mcnutils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Stopped)); err != nil { return err } @@ -176,7 +177,10 @@ func (provisioner *RancherProvisioner) upgradeIso() error { log.Infof("Upgrading machine %s...", machineName) - b2dutils := utils.NewB2dUtils("", "") + // TODO: Ideally, we should not read from mcndirs directory at all. + // The driver should be able to communicate how and where to place the + // relevant files. + b2dutils := mcnutils.NewB2dUtils("", "", mcndirs.GetBaseDir()) url, err := provisioner.getLatestISOURL() if err != nil { @@ -198,7 +202,7 @@ func (provisioner *RancherProvisioner) upgradeIso() error { return err } - return utils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Running)) + return mcnutils.WaitFor(drivers.MachineInState(provisioner.Driver, state.Running)) } func (provisioner *RancherProvisioner) getLatestISOURL() (string, error) { diff --git a/libmachine/provision/redhat.go b/libmachine/provision/redhat.go index 6ca52ac93e..e64f530a0f 100644 --- a/libmachine/provision/redhat.go +++ b/libmachine/provision/redhat.go @@ -6,15 +6,15 @@ import ( "fmt" "text/template" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" + "github.com/docker/machine/libmachine/ssh" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/utils" ) var ( @@ -225,7 +225,7 @@ func (provisioner *RedHatProvisioner) Provision(swarmOptions swarm.SwarmOptions, return err } - if err := utils.WaitFor(provisioner.dockerDaemonResponding); err != nil { + if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil { return err } diff --git a/libmachine/provision/ubuntu.go b/libmachine/provision/ubuntu.go index 77a0024cf1..838ad2e2a0 100644 --- a/libmachine/provision/ubuntu.go +++ b/libmachine/provision/ubuntu.go @@ -3,14 +3,14 @@ package provision import ( "fmt" - "github.com/docker/machine/drivers" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/drivers" "github.com/docker/machine/libmachine/engine" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/pkgaction" "github.com/docker/machine/libmachine/provision/serviceaction" "github.com/docker/machine/libmachine/swarm" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" ) func init() { @@ -137,7 +137,7 @@ func (provisioner *UbuntuProvisioner) Provision(swarmOptions swarm.SwarmOptions, return err } - if err := utils.WaitFor(provisioner.dockerDaemonResponding); err != nil { + if err := mcnutils.WaitFor(provisioner.dockerDaemonResponding); err != nil { return err } diff --git a/libmachine/provision/utils.go b/libmachine/provision/utils.go index aa523e5e93..bedd6dda71 100644 --- a/libmachine/provision/utils.go +++ b/libmachine/provision/utils.go @@ -10,9 +10,10 @@ import ( "strings" "github.com/docker/machine/libmachine/auth" + "github.com/docker/machine/libmachine/cert" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "github.com/docker/machine/libmachine/provision/serviceaction" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" ) type DockerOptions struct { @@ -67,36 +68,35 @@ func ConfigureAuth(p Provisioner) error { return err } - // copy certs to client dir for docker client - machineDir := filepath.Join(utils.GetMachineDir(), machineName) + log.Info("Copying certs to the local machine directory...") - if err := utils.CopyFile(authOptions.CaCertPath, filepath.Join(machineDir, "ca.pem")); err != nil { + if err := mcnutils.CopyFile(authOptions.CaCertPath, filepath.Join(authOptions.StorePath, "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 { + if err := mcnutils.CopyFile(authOptions.ClientCertPath, filepath.Join(authOptions.StorePath, "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 { + if err := mcnutils.CopyFile(authOptions.ClientKeyPath, filepath.Join(authOptions.StorePath, "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, + authOptions.CaPrivateKeyPath, org, ) // TODO: Switch to passing just authOptions to this func // instead of all these individual fields - err = utils.GenerateCert( + err = cert.GenerateCert( []string{ip}, authOptions.ServerCertPath, authOptions.ServerKeyPath, authOptions.CaCertPath, - authOptions.PrivateKeyPath, + authOptions.CaPrivateKeyPath, org, bits, ) @@ -124,6 +124,8 @@ func ConfigureAuth(p Provisioner) error { return err } + log.Info("Copying certs to the remote machine...") + // printf will choke if we don't pass a format string because of the // dashes, so that's the reason for the '%%s' certTransferCmdFmt := "printf '%%s' '%s' | sudo tee %s" @@ -164,6 +166,8 @@ func ConfigureAuth(p Provisioner) error { return err } + log.Info("Setting Docker configuration on the remote daemon...") + if _, err = p.SSHCommand(fmt.Sprintf("printf %%s \"%s\" | sudo tee %s", dkrcfg.EngineOptions, dkrcfg.EngineOptionsPath)); err != nil { return err } @@ -173,7 +177,7 @@ func ConfigureAuth(p Provisioner) error { } // TODO: Do not hardcode daemon port, ask the driver - if err := utils.WaitForDocker(ip, dockerPort); err != nil { + if err := mcnutils.WaitForDocker(ip, dockerPort); err != nil { return err } diff --git a/ssh/client.go b/libmachine/ssh/client.go similarity index 97% rename from ssh/client.go rename to libmachine/ssh/client.go index 105ef8d6f9..6951abb22f 100644 --- a/ssh/client.go +++ b/libmachine/ssh/client.go @@ -7,8 +7,8 @@ import ( "os/exec" "github.com/docker/docker/pkg/term" - "github.com/docker/machine/log" - "github.com/docker/machine/utils" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/mcnutils" "golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh/terminal" ) @@ -139,7 +139,7 @@ func (client NativeClient) dialSuccess() bool { } func (client NativeClient) session(command string) (*ssh.Session, error) { - if err := utils.WaitFor(client.dialSuccess); err != nil { + if err := mcnutils.WaitFor(client.dialSuccess); err != nil { return nil, fmt.Errorf("Error attempting SSH client dial: %s", err) } diff --git a/ssh/keys.go b/libmachine/ssh/keys.go similarity index 92% rename from ssh/keys.go rename to libmachine/ssh/keys.go index 636a684b4b..6e7304ab58 100644 --- a/ssh/keys.go +++ b/libmachine/ssh/keys.go @@ -107,16 +107,16 @@ func (kp *KeyPair) Fingerprint() string { func GenerateSSHKey(path string) error { if _, err := os.Stat(path); err != nil { if !os.IsNotExist(err) { - return err + return fmt.Errorf("Desired directory for SSH keys does not exist: %s", err) } kp, err := NewKeyPair() if err != nil { - return err + return fmt.Errorf("Error generating key pair: %s", err) } if err := kp.WriteToFile(path, fmt.Sprintf("%s.pub", path)); err != nil { - return err + return fmt.Errorf("Error writing keys to file(s): %s", err) } } diff --git a/ssh/keys_test.go b/libmachine/ssh/keys_test.go similarity index 100% rename from ssh/keys_test.go rename to libmachine/ssh/keys_test.go diff --git a/ssh/ssh.go b/libmachine/ssh/ssh.go similarity index 86% rename from ssh/ssh.go rename to libmachine/ssh/ssh.go index 3abb264a23..e85c981738 100644 --- a/ssh/ssh.go +++ b/libmachine/ssh/ssh.go @@ -4,7 +4,7 @@ import ( "net" "time" - "github.com/docker/machine/log" + "github.com/docker/machine/libmachine/log" ) func WaitForTCP(addr string) error { diff --git a/ssh/ssh_test.go b/libmachine/ssh/ssh_test.go similarity index 100% rename from ssh/ssh_test.go rename to libmachine/ssh/ssh_test.go diff --git a/state/state.go b/libmachine/state/state.go similarity index 100% rename from state/state.go rename to libmachine/state/state.go diff --git a/state/state_test.go b/libmachine/state/state_test.go similarity index 100% rename from state/state_test.go rename to libmachine/state/state_test.go diff --git a/libmachine/store.go b/libmachine/store.go deleted file mode 100644 index c7df890c03..0000000000 --- a/libmachine/store.go +++ /dev/null @@ -1,22 +0,0 @@ -package libmachine - -type Store interface { - // Exists returns whether a machine exists or not - Exists(name string) (bool, error) - // GetActive returns the active host - GetActive() (*Host, error) - // GetPath returns the path to the store - GetPath() string - // GetCACertPath returns the CA certificate - GetCACertificatePath() (string, error) - // GetPrivateKeyPath returns the private key - GetPrivateKeyPath() (string, error) - // List returns a list of hosts - List() ([]*Host, error) - // Load loads a host by name - Get(name string) (*Host, error) - // Remove removes a machine from the store - Remove(name string, force bool) error - // Save persists a machine in the store - Save(host *Host) error -} diff --git a/libmachine/swarm/swarm.go b/libmachine/swarm/swarm.go index f0e33dad0f..364305b4e0 100644 --- a/libmachine/swarm/swarm.go +++ b/libmachine/swarm/swarm.go @@ -14,9 +14,5 @@ type SwarmOptions struct { Strategy string Heartbeat int Overcommit float64 - TlsCaCert string - TlsCert string - TlsKey string - TlsVerify bool ArbitraryFlags []string } diff --git a/libmachine/swarm/swarm_test.go b/libmachine/swarm/swarm_test.go deleted file mode 100644 index 6f225b055b..0000000000 --- a/libmachine/swarm/swarm_test.go +++ /dev/null @@ -1 +0,0 @@ -package swarm diff --git a/libmachine/version/version.go b/libmachine/version/version.go new file mode 100644 index 0000000000..68669cfa3d --- /dev/null +++ b/libmachine/version/version.go @@ -0,0 +1,11 @@ +package version + +var ( + // ApiVersion dictates which version of the libmachine API this is. + ApiVersion = 1 + + // ConfigVersion dictates which version of the config.json format is + // used. It needs to be bumped if there is a breaking change, and + // therefore migration, introduced to the config file format. + ConfigVersion = 2 +) diff --git a/main.go b/main.go index 46014a9915..2b8c0a35d1 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,17 @@ package main import ( + "fmt" "os" "path" + "strconv" "github.com/codegangsta/cli" "github.com/docker/machine/commands" - "github.com/docker/machine/log" - "github.com/docker/machine/ssh" - "github.com/docker/machine/utils" + "github.com/docker/machine/commands/mcndirs" + "github.com/docker/machine/libmachine/log" + "github.com/docker/machine/libmachine/ssh" "github.com/docker/machine/version" ) @@ -44,13 +46,28 @@ Options: {{.}}{{end}}{{ end }} ` -func main() { +func setDebugOutputLevel() { + // TODO: I'm not really a fan of this method and really would rather + // use -v / --verbose TBQH for _, f := range os.Args { if f == "-D" || f == "--debug" || f == "-debug" { - os.Setenv("MACHINE_DEBUG", "1") + log.IsDebug = true + } + } + + debugEnv := os.Getenv("MACHINE_DEBUG") + if debugEnv != "" { + showDebug, err := strconv.ParseBool(debugEnv) + if err != nil { + fmt.Fprintln(os.Stderr, "Error parsing boolean value from MACHINE_DEBUG: %s", err) + os.Exit(1) } + log.IsDebug = showDebug } +} +func main() { + setDebugOutputLevel() cli.AppHelpTemplate = AppHelpTemplate cli.CommandHelpTemplate = CommandHelpTemplate app := cli.NewApp() @@ -58,7 +75,6 @@ func main() { app.Author = "Docker Machine Contributors" app.Email = "https://github.com/docker/machine" app.Before = func(c *cli.Context) error { - os.Setenv("MACHINE_STORAGE_PATH", c.GlobalString("storage-path")) if c.GlobalBool("native-ssh") { ssh.SetDefaultClient(ssh.Native) } @@ -77,7 +93,7 @@ func main() { cli.StringFlag{ EnvVar: "MACHINE_STORAGE_PATH", Name: "s, storage-path", - Value: utils.GetBaseDir(), + Value: mcndirs.GetBaseDir(), Usage: "Configures storage path", }, cli.StringFlag{ diff --git a/main_test.go b/main_test.go deleted file mode 100644 index 06ab7d0f9a..0000000000 --- a/main_test.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/script/test b/script/test index b297812c13..f562f488dd 100755 --- a/script/test +++ b/script/test @@ -9,4 +9,4 @@ fi echo $ARGS docker build -t docker-machine . -exec docker run --rm docker-machine godep go test -v -short $ARGS +exec docker run --rm docker-machine godep go test -race -v -short $ARGS diff --git a/test/integration/core/arbitrary-engine-envs.bats b/test/integration/core/arbitrary-engine-envs.bats index 4e8fe89a7e..e459515407 100644 --- a/test/integration/core/arbitrary-engine-envs.bats +++ b/test/integration/core/arbitrary-engine-envs.bats @@ -12,8 +12,7 @@ run machine create -d $DRIVER \ } @test "$DRIVER: test docker process envs" { - # get pid of docker process, check process envs for set Environment Variable from above test - run machine ssh $NAME 'pgrep -f "docker -d" | xargs -I % sudo cat /proc/%/environ | grep -q "TEST=VALUE"' + run machine ssh $NAME 'pgrep -f "docker daemon" | xargs -I % sudo cat /proc/%/environ | grep -q "TEST=VALUE"' [ $status -eq 0 ] } diff --git a/test/integration/core/core-commands.bats b/test/integration/core/core-commands.bats index 027be89aea..4065ebcd6e 100644 --- a/test/integration/core/core-commands.bats +++ b/test/integration/core/core-commands.bats @@ -64,7 +64,7 @@ load ${BASE_TEST_DIR}/helpers.bash @test "$DRIVER: env should show an error when machine is stopped" { run machine env $NAME [ "$status" -eq 1 ] - [[ ${output} == *"not running. Please start this with"* ]] + [[ ${output} == *"not running. Please start"* ]] } @test "$DRIVER: machine should not allow upgrade when stopped" { diff --git a/test/integration/core/env_shell.bats b/test/integration/core/env_shell.bats new file mode 100644 index 0000000000..5cc07dc623 --- /dev/null +++ b/test/integration/core/env_shell.bats @@ -0,0 +1,40 @@ +#!/usr/bin/env bats + +load ${BASE_TEST_DIR}/helpers.bash + +@test "$DRIVER: create" { + run machine create -d $DRIVER $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: test powershell notation" { + run machine env --shell powershell $NAME + [[ ${lines[0]} == "\$Env:DOCKER_TLS_VERIFY = \"1\"" ]] + [[ ${lines[1]} == "\$Env:DOCKER_HOST = \"$(machine url $NAME)\"" ]] + [[ ${lines[2]} == "\$Env:DOCKER_CERT_PATH = \"$MACHINE_STORAGE_PATH/certs\"" ]] + [[ ${lines[3]} == "\$Env:DOCKER_MACHINE_NAME = \"$NAME\"" ]] +} + +@test "$DRIVER: test bash / zsh notation" { + run machine env $NAME + [[ ${lines[0]} == "export DOCKER_TLS_VERIFY=\"1\"" ]] + [[ ${lines[1]} == "export DOCKER_HOST=\"$(machine url $NAME)\"" ]] + [[ ${lines[2]} == "export DOCKER_CERT_PATH=\"$MACHINE_STORAGE_PATH/certs\"" ]] + [[ ${lines[3]} == "export DOCKER_MACHINE_NAME=\"$NAME\"" ]] +} + +@test "$DRIVER: test cmd.exe notation" { + run machine env --shell cmd $NAME + [[ ${lines[0]} == "set DOCKER_TLS_VERIFY=1" ]] + [[ ${lines[1]} == "set DOCKER_HOST=$(machine url $NAME)" ]] + [[ ${lines[2]} == "set DOCKER_CERT_PATH=$MACHINE_STORAGE_PATH/certs" ]] + [[ ${lines[3]} == "set DOCKER_MACHINE_NAME=$NAME" ]] +} + +@test "$DRIVER: test fish notation" { + run machine env --shell fish $NAME + [[ ${lines[0]} == "set -x DOCKER_TLS_VERIFY \"1\";" ]] + [[ ${lines[1]} == "set -x DOCKER_HOST \"$(machine url $NAME)\";" ]] + [[ ${lines[2]} == "set -x DOCKER_CERT_PATH \"$MACHINE_STORAGE_PATH/certs\";" ]] + [[ ${lines[3]} == "set -x DOCKER_MACHINE_NAME \"$NAME\";" ]] +} diff --git a/test/integration/core/inspect_format.bats b/test/integration/core/inspect_format.bats new file mode 100644 index 0000000000..52710cf5a9 --- /dev/null +++ b/test/integration/core/inspect_format.bats @@ -0,0 +1,23 @@ +#!/usr/bin/env bats + +load ${BASE_TEST_DIR}/helpers.bash + +@test "$DRIVER: create" { + run machine create -d $DRIVER $NAME + [ "$status" -eq 0 ] +} + +@test "$DRIVER: inspect format template" { + run machine inspect -f '{{.DriverName}}' $NAME + [[ "$output" == "$DRIVER" ]] +} + +@test "$DRIVER: inspect format template json directive" { + run machine inspect -f '{{json .DriverName}}' $NAME + [[ "$output" == "\"$DRIVER\"" ]] +} + +@test "$DRIVER: inspect format template pretty json directive" { + linecount=$(machine inspect -f '{{prettyjson .Driver}}' $NAME | wc -l) + [[ "$linecount" -gt 1 ]] +} diff --git a/version/version.go b/version/version.go index 441c35d86b..79b1f38af7 100644 --- a/version/version.go +++ b/version/version.go @@ -1,11 +1,6 @@ package version var ( - // ConfigVersion dictates which version of the config.json format is - // used. It needs to be bumped if there is a breaking change, and - // therefore migration, introduced to the config file format. - ConfigVersion = 1 - // Version should be updated by hand at each release Version = "0.5.0-dev" diff --git a/version/version_test.go b/version/version_test.go deleted file mode 100644 index f37d99d0c2..0000000000 --- a/version/version_test.go +++ /dev/null @@ -1 +0,0 @@ -package version