diff --git a/commands/active.go b/commands/active.go index 7bebc37ac4..e2d41be147 100644 --- a/commands/active.go +++ b/commands/active.go @@ -8,7 +8,9 @@ import ( ) func cmdActive(c *cli.Context) { - name := c.Args().First() + if len(c.Args()) > 0 { + log.Fatal("Error: Too many arguments given.") + } certInfo := getCertPathInfo(c) defaultStore, err := getDefaultStore( @@ -25,24 +27,12 @@ func cmdActive(c *cli.Context) { log.Fatal(err) } - if name == "" { - host, err := mcn.GetActive() - if err != nil { - log.Fatalf("error getting active host: %v", err) - } - if host != nil { - fmt.Println(host.Name) - } - } else if name != "" { - host, err := mcn.Get(name) - if err != nil { - log.Fatalf("error loading host: %v", err) - } + host, err := mcn.GetActive() + if err != nil { + log.Fatalf("Error getting active host: %s", err) + } - if err := mcn.SetActive(host); err != nil { - log.Fatalf("error setting active host: %v", err) - } - } else { - cli.ShowCommandHelp(c, "active") + if host != nil { + fmt.Println(host.Name) } } diff --git a/commands/commands.go b/commands/commands.go index 4b407d81a7..639ab4a2d8 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -32,12 +32,13 @@ import ( "github.com/docker/machine/libmachine/auth" "github.com/docker/machine/libmachine/swarm" "github.com/docker/machine/log" - "github.com/docker/machine/state" "github.com/docker/machine/utils" ) var ( - ErrUnknownShell = errors.New("unknown shell") + ErrUnknownShell = errors.New("Error: Unknown shell") + ErrNoMachineSpecified = errors.New("Error: Expected to get one or more machine names as arguments.") + ErrExpectedOneMachine = errors.New("Error: Expected one machine name as an argument.") ) type machineConfig struct { @@ -54,17 +55,8 @@ type machineConfig struct { SwarmOptions swarm.SwarmOptions } -type hostListItem struct { - Name string - Active bool - DriverName string - State state.State - URL string - SwarmOptions swarm.SwarmOptions -} - -func sortHostListItemsByName(items []hostListItem) { - m := make(map[string]hostListItem, len(items)) +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) @@ -226,13 +218,13 @@ var sharedCreateFlags = []cli.Flag{ var Commands = []cli.Command{ { Name: "active", - Usage: "Get or set the active machine", + Usage: "Print which machine is active", Action: cmdActive, }, { Name: "config", Usage: "Print the connection config for machine", - Description: "Argument is a machine name. Will use the active machine if none is provided.", + Description: "Argument is a machine name.", Action: cmdConfig, Flags: []cli.Flag{ cli.BoolFlag{ @@ -253,7 +245,7 @@ var Commands = []cli.Command{ { Name: "env", Usage: "Display the commands to set up the environment for the Docker client", - Description: "Argument is a machine name. Will use the active machine if none is provided.", + Description: "Argument is a machine name.", Action: cmdEnv, Flags: []cli.Flag{ cli.BoolFlag{ @@ -273,7 +265,7 @@ var Commands = []cli.Command{ { Name: "inspect", Usage: "Inspect information about a machine", - Description: "Argument is a machine name. Will use the active machine if none is provided.", + Description: "Argument is a machine name.", Action: cmdInspect, Flags: []cli.Flag{ cli.StringFlag{ @@ -286,13 +278,13 @@ var Commands = []cli.Command{ { Name: "ip", Usage: "Get the IP address of a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdIp, }, { Name: "kill", Usage: "Kill a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdKill, }, { @@ -309,7 +301,7 @@ var Commands = []cli.Command{ { Name: "regenerate-certs", Usage: "Regenerate TLS Certificates for a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdRegenerateCerts, Flags: []cli.Flag{ cli.BoolFlag{ @@ -321,7 +313,7 @@ var Commands = []cli.Command{ { Name: "restart", Usage: "Restart a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdRestart, }, { @@ -338,32 +330,32 @@ var Commands = []cli.Command{ }, { Name: "ssh", - Usage: "Log into or run a command on a machine with SSH", - Description: "Arguments are [machine-name] command - Will use the active machine if none is provided.", + Usage: "Log into or run a command on a machine with SSH.", + Description: "Arguments are [machine-name] [command]", Action: cmdSsh, }, { Name: "start", Usage: "Start a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdStart, }, { Name: "stop", Usage: "Stop a machine", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdStop, }, { Name: "upgrade", Usage: "Upgrade a machine to the latest version of Docker", - Description: "Argument(s) are one or more machine names. Will use the active machine if none is provided.", + Description: "Argument(s) are one or more machine names.", Action: cmdUpgrade, }, { Name: "url", Usage: "Get the URL of a machine", - Description: "Argument is a machine name. Will use the active machine if none is provided.", + Description: "Argument is a machine name.", Action: cmdUrl, }, } @@ -443,31 +435,8 @@ func runActionWithContext(actionName string, c *cli.Context) error { return err } - // No args specified, so use active. if len(machines) == 0 { - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - mcn, err := newMcn(defaultStore) - if err != nil { - log.Fatal(err) - } - - activeHost, err := mcn.GetActive() - if err != nil { - log.Fatalf("Unable to get active host: %v", err) - } - if activeHost == nil { - log.Fatalf("There is no active host. Please set it with %s active .", c.App.Name) - } - machines = []*libmachine.Host{activeHost} + log.Fatal(ErrNoMachineSpecified) } runActionForeachMachine(actionName, machines) @@ -530,18 +499,6 @@ func getHost(c *cli.Context) *libmachine.Host { log.Fatal(err) } - if name == "" { - host, err := mcn.GetActive() - if err != nil { - log.Fatalf("unable to get active host: %v", err) - } - - if host == nil { - log.Fatal("unable to get active host, active file not found") - } - return host - } - host, err := mcn.Get(name) if err != nil { log.Fatalf("unable to load host: %v", err) @@ -549,35 +506,23 @@ func getHost(c *cli.Context) *libmachine.Host { return host } -func getHostState(host libmachine.Host, store libmachine.Store, hostListItems chan<- hostListItem) { - currentState, err := host.Driver.GetState() +func getDefaultMcn(c *cli.Context) *libmachine.Machine { + certInfo := getCertPathInfo(c) + defaultStore, err := getDefaultStore( + c.GlobalString("storage-path"), + certInfo.CaCertPath, + certInfo.CaKeyPath, + ) if err != nil { - log.Errorf("error getting state for host %s: %s", host.Name, err) + log.Fatal(err) } - url, err := host.GetURL() + mcn, err := newMcn(defaultStore) if err != nil { - if err == drivers.ErrHostIsNotRunning { - url = "" - } else { - log.Errorf("error getting URL for host %s: %s", host.Name, err) - } + log.Fatal(err) } - isActive, err := store.IsActive(&host) - if err != nil { - log.Debugf("error determining whether host %q is active: %s", - host.Name, err) - } - - hostListItems <- hostListItem{ - Name: host.Name, - Active: isActive, - DriverName: host.Driver.DriverName(), - State: currentState, - URL: url, - SwarmOptions: *host.HostOptions.SwarmOptions, - } + return mcn } func getMachineConfig(c *cli.Context) (*machineConfig, error) { @@ -597,33 +542,16 @@ func getMachineConfig(c *cli.Context) (*machineConfig, error) { log.Fatal(err) } - var machine *libmachine.Host + m, err := mcn.Get(name) - if name == "" { - m, err := mcn.GetActive() - if err != nil { - log.Fatalf("error getting active host: %v", err) - } - if m == nil { - return nil, fmt.Errorf("There is no active host") - } - machine = m - } else { - m, err := mcn.Get(name) - if err != nil { - return nil, fmt.Errorf("Error loading machine config: %s", err) - } - machine = m - } - - machineDir := filepath.Join(utils.GetMachineDir(), machine.Name) + 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 := machine.GetURL() + machineUrl, err := m.GetURL() if err != nil { if err == drivers.ErrHostIsNotRunning { machineUrl = "" @@ -641,8 +569,8 @@ func getMachineConfig(c *cli.Context) (*machineConfig, error) { caKeyPath: caKey, caCertPath: caCert, serverKeyPath: serverKey, - AuthOptions: *machine.HostOptions.AuthOptions, - SwarmOptions: *machine.HostOptions.SwarmOptions, + AuthOptions: *m.HostOptions.AuthOptions, + SwarmOptions: *m.HostOptions.SwarmOptions, }, nil } diff --git a/commands/commands_test.go b/commands/commands_test.go index 7506c07948..888df8260d 100644 --- a/commands/commands_test.go +++ b/commands/commands_test.go @@ -119,91 +119,6 @@ func (d DriverOptionsMock) Int(key string) int { func (d DriverOptionsMock) Bool(key string) bool { return d.Data[key].(bool) } -func TestGetHostState(t *testing.T) { - defer cleanup() - - hostListItems := make(chan hostListItem) - - store, err := getTestStore() - if err != nil { - t.Fatal(err) - } - - hosts := []libmachine.Host{ - { - Name: "foo", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Running, - }, - StorePath: store.GetPath(), - HostOptions: &libmachine.HostOptions{ - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Address: "", - Discovery: "", - }, - }, - }, - { - Name: "bar", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Stopped, - }, - StorePath: store.GetPath(), - HostOptions: &libmachine.HostOptions{ - SwarmOptions: &swarm.SwarmOptions{ - Master: false, - Address: "", - Discovery: "", - }, - }, - }, - { - Name: "baz", - DriverName: "fakedriver", - Driver: &fakedriver.FakeDriver{ - MockState: state.Running, - }, - StorePath: store.GetPath(), - HostOptions: &libmachine.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, store, hostListItems) - } - - for i := 0; i < len(hosts); i++ { - items = append(items, <-hostListItems) - } - - for _, item := range items { - if expected[item.Name] != item.State { - t.Fatal("Expected state did not match for item", item) - } - } -} func TestRunActionForeachMachine(t *testing.T) { storePath, err := ioutil.TempDir("", ".docker") diff --git a/commands/config.go b/commands/config.go index e77cfc4043..09a5c998a2 100644 --- a/commands/config.go +++ b/commands/config.go @@ -11,6 +11,9 @@ import ( ) func cmdConfig(c *cli.Context) { + if len(c.Args()) != 1 { + log.Fatal(ErrExpectedOneMachine) + } cfg, err := getMachineConfig(c) if err != nil { log.Fatal(err) diff --git a/commands/config_test.go b/commands/config_test.go index e2f2cf884f..90dca426a9 100644 --- a/commands/config_test.go +++ b/commands/config_test.go @@ -56,10 +56,6 @@ func TestCmdConfig(t *testing.T) { t.Fatal(err) } - if err := store.SetActive(host); err != nil { - t.Fatalf("error setting active host: %v", err) - } - outStr := make(chan string) go func() { @@ -69,6 +65,7 @@ func TestCmdConfig(t *testing.T) { }() set := flag.NewFlagSet("config", 0) + set.Parse([]string{"test-a"}) globalSet := flag.NewFlagSet("test", 0) globalSet.String("storage-path", store.GetPath(), "") diff --git a/commands/create.go b/commands/create.go index c4a2e8d7e7..ce892c8252 100644 --- a/commands/create.go +++ b/commands/create.go @@ -85,14 +85,10 @@ func cmdCreate(c *cli.Context) { }, } - host, err := mcn.Create(name, driver, hostOptions, c) + _, err = mcn.Create(name, driver, hostOptions, c) if err != nil { log.Errorf("Error creating machine: %s", err) - log.Warn("You will want to check the provider to make sure the machine and associated resources were properly removed.") - log.Fatal("Error creating machine") - } - if err := mcn.SetActive(host); err != nil { - log.Fatalf("error setting active host: %v", err) + log.Fatal("You will want to check the provider to make sure the machine and associated resources were properly removed.") } info := fmt.Sprintf("%s env %s", c.App.Name, name) diff --git a/commands/env.go b/commands/env.go index 81a52eec72..73e6167b6d 100644 --- a/commands/env.go +++ b/commands/env.go @@ -28,6 +28,9 @@ type ShellConfig struct { } func cmdEnv(c *cli.Context) { + if len(c.Args()) > 1 { + log.Fatal("Error: Expected either a machine name, or -u flag to unset the variables in the arguments.") + } userShell := c.String("shell") if userShell == "" { shell, err := detectShell() diff --git a/commands/env_test.go b/commands/env_test.go index a90aa63b23..4447e0cc20 100644 --- a/commands/env_test.go +++ b/commands/env_test.go @@ -69,10 +69,6 @@ func TestCmdEnvBash(t *testing.T) { t.Fatalf("error loading host: %v", err) } - if err := mcn.SetActive(host); err != nil { - t.Fatalf("error setting active host: %v", err) - } - outStr := make(chan string) go func() { @@ -82,6 +78,7 @@ func TestCmdEnvBash(t *testing.T) { }() set := flag.NewFlagSet("config", 0) + set.Parse([]string{"test-a"}) c := cli.NewContext(nil, set, set) c.App = &cli.App{ Name: "docker-machine-test", @@ -170,10 +167,6 @@ func TestCmdEnvFish(t *testing.T) { t.Fatalf("error loading host: %v", err) } - if err := mcn.SetActive(host); err != nil { - t.Fatalf("error setting active host: %v", err) - } - outStr := make(chan string) go func() { @@ -183,6 +176,7 @@ func TestCmdEnvFish(t *testing.T) { }() set := flag.NewFlagSet("config", 0) + set.Parse([]string{"test-a"}) c := cli.NewContext(nil, set, set) c.App = &cli.App{ Name: "docker-machine-test", @@ -271,10 +265,6 @@ func TestCmdEnvPowerShell(t *testing.T) { t.Fatalf("error loading host: %v", err) } - if err := mcn.SetActive(host); err != nil { - t.Fatalf("error setting active host: %v", err) - } - outStr := make(chan string) go func() { @@ -284,6 +274,7 @@ func TestCmdEnvPowerShell(t *testing.T) { }() set := flag.NewFlagSet("config", 0) + set.Parse([]string{"test-a"}) set.String("shell", "powershell", "") c := cli.NewContext(nil, set, set) c.App = &cli.App{ diff --git a/commands/inspect_test.go b/commands/inspect_test.go index b0853e3c72..04fe5d9356 100644 --- a/commands/inspect_test.go +++ b/commands/inspect_test.go @@ -18,17 +18,17 @@ import ( ) func TestCmdInspectFormat(t *testing.T) { - actual, host := runInspectCommand(t, []string{}) + actual, host := runInspectCommand(t, []string{"test-a"}) expected, _ := json.MarshalIndent(host, "", " ") assert.Equal(t, string(expected), actual) - actual, _ = runInspectCommand(t, []string{"--format", "{{.DriverName}}"}) + actual, _ = runInspectCommand(t, []string{"--format", "{{.DriverName}}", "test-a"}) assert.Equal(t, "none", actual) - actual, _ = runInspectCommand(t, []string{"--format", "{{json .DriverName}}"}) + actual, _ = runInspectCommand(t, []string{"--format", "{{json .DriverName}}", "test-a"}) assert.Equal(t, "\"none\"", actual) - actual, _ = runInspectCommand(t, []string{"--format", "{{prettyjson .Driver}}"}) + actual, _ = runInspectCommand(t, []string{"--format", "{{prettyjson .Driver}}", "test-a"}) assert.Equal(t, "{\n \"IPAddress\": \"\",\n \"URL\": \"unix:///var/run/docker.sock\"\n}", actual) } diff --git a/commands/ls.go b/commands/ls.go index aafebdcc8a..4ea6357340 100644 --- a/commands/ls.go +++ b/commands/ls.go @@ -2,45 +2,28 @@ package commands import ( "fmt" + "log" "os" "text/tabwriter" - "github.com/docker/machine/log" - "github.com/codegangsta/cli" + "github.com/docker/machine/libmachine" ) func cmdLs(c *cli.Context) { quiet := c.Bool("quiet") - certInfo := getCertPathInfo(c) - defaultStore, err := getDefaultStore( - c.GlobalString("storage-path"), - certInfo.CaCertPath, - certInfo.CaKeyPath, - ) - if err != nil { - log.Fatal(err) - } - - mcn, err := newMcn(defaultStore) - if err != nil { - log.Fatal(err) - } - - hostList, err := mcn.List() - if err != nil { - log.Fatal(err) - } - w := tabwriter.NewWriter(os.Stdout, 5, 1, 3, ' ', 0) if !quiet { fmt.Fprintln(w, "NAME\tACTIVE\tDRIVER\tSTATE\tURL\tSWARM") } - items := []hostListItem{} - hostListItems := make(chan hostListItem) + mcn := getDefaultMcn(c) + hostList, err := mcn.List() + if err != nil { + log.Fatal(err) + } swarmMasters := make(map[string]string) swarmInfo := make(map[string]string) @@ -55,20 +38,12 @@ func cmdLs(c *cli.Context) { if swarmOptions.Discovery != "" { swarmInfo[host.Name] = swarmOptions.Discovery } - - go getHostState(*host, defaultStore, hostListItems) } else { fmt.Fprintf(w, "%s\n", host.Name) } } - if !quiet { - for i := 0; i < len(hostList); i++ { - items = append(items, <-hostListItems) - } - } - - close(hostListItems) + items := libmachine.GetHostListItems(hostList) sortHostListItemsByName(items) diff --git a/commands/ssh.go b/commands/ssh.go index 392d3e3f40..6e77a63a76 100644 --- a/commands/ssh.go +++ b/commands/ssh.go @@ -14,8 +14,14 @@ import ( func cmdSsh(c *cli.Context) { var ( - err error + output ssh.Output + err error ) + + if len(c.Args()) == 0 { + log.Fatal("Error: Please specify a machine name.") + } + name := c.Args().First() certInfo := getCertPathInfo(c) @@ -33,19 +39,6 @@ func cmdSsh(c *cli.Context) { log.Fatal(err) } - if name == "" { - host, err := mcn.GetActive() - if err != nil { - log.Fatalf("unable to get active host: %v", err) - } - - if host == nil { - log.Fatalf("There is no active host. Please set it with %s active .", c.App.Name) - } - - name = host.Name - } - host, err := mcn.Get(name) if err != nil { log.Fatal(err) @@ -60,13 +53,13 @@ func cmdSsh(c *cli.Context) { } } - var output ssh.Output - - if len(c.Args()) <= 1 { + if len(c.Args()) == 1 { err = host.CreateSSHShell() } else { - var cmd string - var args []string = c.Args() + var ( + cmd string + args []string = c.Args() + ) for i, arg := range args { if arg == "--" { diff --git a/docs/index.md b/docs/index.md index 21f706b223..f6c3c50a47 100644 --- a/docs/index.md +++ b/docs/index.md @@ -141,7 +141,6 @@ INFO[0011] Creating SSH key... INFO[0012] Creating VirtualBox VM... INFO[0019] Starting VirtualBox VM... INFO[0020] Waiting for VM to start... -INFO[0053] "dev" has been created and is now the active machine. INFO[0053] To see how to connect Docker to this machine, run: docker-machine env dev" ``` @@ -151,12 +150,9 @@ again: ``` $ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM -dev * virtualbox Running tcp://192.168.99.100:2376 +dev virtualbox Running tcp://192.168.99.100:2376 ``` -The `*` next to `dev` indicates that it is the active host. - - Next, as noted in the output of the `docker-machine create` command, we have to tell Docker to talk to that machine. You can do this with the `docker-machine env` command. For example, @@ -249,17 +245,8 @@ run `docker-machine create` again. All created machines will appear in the output of `docker-machine ls`. If you are finished using a host for the time being, you can stop it with -`docker-machine stop` and later start it again with `docker-machine start`: - -``` -$ docker-machine stop -$ docker-machine start -``` - -If they aren't passed any arguments, commands such as `docker-machine stop` will run -against the active host (in this case, the VirtualBox VM). You can also specify -a host to run a command against as an argument. For instance, you could also -have written: +`docker-machine stop` and later start it again with `docker-machine start`. +Make sure to specify the machine name as an argument: ``` $ docker-machine stop dev @@ -306,7 +293,7 @@ $ docker-machine create \ INFO[0000] Creating SSH key... INFO[0000] Creating Digital Ocean droplet... INFO[0002] Waiting for SSH... -INFO[0085] "staging" has been created and is now the active machine +INFO[0085] "staging" has been created. INFO[0085] To see how to connect Docker to this machine, run: docker-machine env staging" ``` @@ -326,28 +313,24 @@ Docker will be installed on the remote machine and the daemon will be configured to accept remote connections over TCP using TLS for authentication. Once this is finished, the host is ready for connection. -And then from this point, the remote host behaves much like the local host we -created in the last section. If we look at `docker-machine`, we’ll see it is now -the active host: +To prepare the Docker client to send commands to the remote server we have +created, we can use the subshell method again: + +``` +$ eval "$(docker-machine env staging)" +``` + +From this point, the remote host behaves much like the local host we created in +the last section. If we look at `docker-machine ls`, we'll see it is now the +"active" host, indicated by an asterisk (`*`) in that column: ``` -$ docker-machine active dev $ docker-machine ls NAME ACTIVE DRIVER STATE URL dev virtualbox Running tcp://192.168.99.103:2376 staging * digitalocean Running tcp://104.236.50.118:2376 ``` -To select an active host, you can use the `docker-machine active` command. - -``` -$ docker-machine active dev -$ docker-machine ls -NAME ACTIVE DRIVER STATE URL -dev * virtualbox Running tcp://192.168.99.103:2376 -staging digitalocean Running tcp://104.236.50.118:2376 -``` - To remove a host and all of its containers and images, use `docker-machine rm`: ``` @@ -458,18 +441,18 @@ Nodes: 1 #### active -Get or set the active machine. +See which machine is "active" (a machine is considered active if the +`DOCKER_HOST` environment variable points to it). ``` $ docker-machine ls NAME ACTIVE DRIVER STATE URL dev virtualbox Running tcp://192.168.99.103:2376 staging * digitalocean Running tcp://104.236.50.118:2376 -$ docker-machine active dev -$ docker-machine ls -NAME ACTIVE DRIVER STATE URL -dev * virtualbox Running tcp://192.168.99.103:2376 -staging digitalocean Running tcp://104.236.50.118:2376 +$ echo $DOCKER_HOST +tcp://104.236.50.118:2376 +$ docker-machine active +staging ``` #### create @@ -483,8 +466,7 @@ INFO[0000] Creating SSH key... INFO[0000] Creating VirtualBox VM... INFO[0007] Starting VirtualBox VM... INFO[0007] Waiting for VM to start... -INFO[0038] "dev" has been created and is now the active machine. -INFO[0038] To see how to connect Docker to this machine, run: docker-machine env dev" +INFO[0038] To see how to connect Docker to this machine, run: docker-machine env dev ``` ##### Filtering create flags by driver in the help text @@ -687,7 +669,7 @@ Usage: docker-machine inspect [OPTIONS] [arg...] Inspect information about a machine Description: - Argument is a machine name. Will use the active machine if none is provided. + Argument is a machine name. Options: --format, -f Format the output using the given go template. diff --git a/libmachine/filestore.go b/libmachine/filestore.go index a9fde8fbaf..18db82a50c 100644 --- a/libmachine/filestore.go +++ b/libmachine/filestore.go @@ -2,6 +2,7 @@ package libmachine import ( "encoding/json" + "errors" "io/ioutil" "os" "path/filepath" @@ -111,39 +112,23 @@ func (s Filestore) Get(name string) (*Host, error) { } func (s Filestore) GetActive() (*Host, error) { - hostName, err := ioutil.ReadFile(s.activePath()) - if os.IsNotExist(err) { - return nil, nil - } else if err != nil { + hosts, err := s.List() + if err != nil { return nil, err } - return s.Get(string(hostName)) -} -func (s Filestore) IsActive(host *Host) (bool, error) { - active, err := s.GetActive() - if err != nil { - return false, err + dockerHost := os.Getenv("DOCKER_HOST") + hostListItems := GetHostListItems(hosts) + + for _, item := range hostListItems { + if dockerHost == item.URL { + host, err := s.Get(item.Name) + if err != nil { + return nil, err + } + return host, nil + } } - if active == nil { - return false, nil - } - return active.Name == host.Name, nil -} -func (s Filestore) SetActive(host *Host) error { - if err := os.MkdirAll(filepath.Dir(s.activePath()), 0700); err != nil { - return err - } - return ioutil.WriteFile(s.activePath(), []byte(host.Name), 0600) -} - -func (s Filestore) RemoveActive() error { - return os.Remove(s.activePath()) -} - -// activePath returns the path to the file that stores the name of the -// active host -func (s Filestore) activePath() string { - return filepath.Join(utils.GetMachineDir(), ".active") + return nil, errors.New("Active host not found") } diff --git a/libmachine/filestore_test.go b/libmachine/filestore_test.go index 2a889e1dcf..f0bc140a38 100644 --- a/libmachine/filestore_test.go +++ b/libmachine/filestore_test.go @@ -193,10 +193,10 @@ func TestStoreGetSetActive(t *testing.T) { t.Fatal(err) } - // No hosts set + // No host set host, err := store.GetActive() - if err != nil { - t.Fatal(err) + if err == nil { + t.Fatal("Expected an error because there is no active host set") } if host != nil { @@ -213,12 +213,13 @@ func TestStoreGetSetActive(t *testing.T) { t.Fatal(err) } - originalHost := host - - if err := store.SetActive(originalHost); err != nil { + 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) @@ -226,25 +227,4 @@ func TestStoreGetSetActive(t *testing.T) { if host.Name != host.Name { t.Fatalf("Active host is not 'test', got %s", host.Name) } - isActive, err := store.IsActive(host) - if err != nil { - t.Fatal(err) - } - if isActive != true { - t.Fatal("IsActive: Active host is not test") - } - - // remove active host altogether - if err := store.RemoveActive(); err != nil { - t.Fatal(err) - } - - host, err = store.GetActive() - if err != nil { - t.Fatal(err) - } - - if host != nil { - t.Fatalf("Active host %s is not nil", host.Name) - } } diff --git a/libmachine/host.go b/libmachine/host.go index a44c8508a8..5f28861ff7 100644 --- a/libmachine/host.go +++ b/libmachine/host.go @@ -64,6 +64,15 @@ type HostMetadata struct { ClientCertPath string } +type HostListItem struct { + Name string + Active bool + DriverName string + State state.State + URL string + SwarmOptions swarm.SwarmOptions +} + func NewHost(name, driverName string, hostOptions *HostOptions) (*Host, error) { authOptions := hostOptions.AuthOptions storePath := filepath.Join(utils.GetMachineDir(), name) @@ -375,3 +384,46 @@ func WaitForSSH(h *Host) error { } return nil } + +func getHostState(host Host, hostListItemsChan 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) + } + } + + dockerHost := os.Getenv("DOCKER_HOST") + + hostListItemsChan <- HostListItem{ + Name: host.Name, + Active: dockerHost == url && currentState != state.Stopped, + DriverName: host.Driver.DriverName(), + State: currentState, + URL: url, + SwarmOptions: *host.HostOptions.SwarmOptions, + } +} + +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_test.go b/libmachine/host_test.go index 38d4b42e8f..720aa06afa 100644 --- a/libmachine/host_test.go +++ b/libmachine/host_test.go @@ -14,6 +14,7 @@ import ( "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" ) @@ -226,3 +227,89 @@ func captureStdout() (chan string, *os.File) { 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/machine.go b/libmachine/machine.go index c3f71c5ba1..a6642a9fb9 100644 --- a/libmachine/machine.go +++ b/libmachine/machine.go @@ -60,10 +60,6 @@ func (m *Machine) Create(name string, driverName string, hostOptions *HostOption return host, err } - if err := m.store.SetActive(host); err != nil { - return nil, err - } - return host, nil } @@ -75,10 +71,6 @@ func (m *Machine) GetActive() (*Host, error) { return m.store.GetActive() } -func (m *Machine) IsActive(host *Host) (bool, error) { - return m.store.IsActive(host) -} - func (m *Machine) List() ([]*Host, error) { return m.store.List() } @@ -88,17 +80,6 @@ func (m *Machine) Get(name string) (*Host, error) { } func (m *Machine) Remove(name string, force bool) error { - active, err := m.store.GetActive() - if err != nil { - return err - } - - if active != nil && active.Name == name { - if err := m.RemoveActive(); err != nil { - return err - } - } - host, err := m.store.Get(name) if err != nil { return err @@ -110,11 +91,3 @@ func (m *Machine) Remove(name string, force bool) error { } return m.store.Remove(name, force) } - -func (m *Machine) RemoveActive() error { - return m.store.RemoveActive() -} - -func (m *Machine) SetActive(host *Host) error { - return m.store.SetActive(host) -} diff --git a/libmachine/store.go b/libmachine/store.go index 3d99385a04..c7df890c03 100644 --- a/libmachine/store.go +++ b/libmachine/store.go @@ -11,18 +11,12 @@ type Store interface { GetCACertificatePath() (string, error) // GetPrivateKeyPath returns the private key GetPrivateKeyPath() (string, error) - // IsActive returns whether the host is active or not - IsActive(host *Host) (bool, 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 - // RemoveActive removes the active machine from the store - RemoveActive() error // Save persists a machine in the store Save(host *Host) error - // SetActive sets the specified host as the active host - SetActive(host *Host) error } diff --git a/utils/utils.go b/utils/utils.go index 1ee33828b3..0efb810dcb 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -112,7 +112,7 @@ func WaitForDocker(ip string, daemonPort int) error { return WaitFor(func() bool { conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", ip, daemonPort)) if err != nil { - log.Debugf("Got an error it was %s", err) + log.Debugf("Daemon not responding yet: ", err) return false } conn.Close()