Merge pull request #1103 from nathanleclaire/active_based_on_env

Read active host from DOCKER_HOST instead of from file
This commit is contained in:
Evan Hazlett 2015-05-04 18:13:48 -07:00
commit e4b6ff1ae9
19 changed files with 265 additions and 421 deletions

View File

@ -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)
}
}

View File

@ -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 <machine name>.", 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
}

View File

@ -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")

View File

@ -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)

View File

@ -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(), "")

View File

@ -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)

View File

@ -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()

View File

@ -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{

View File

@ -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)
}

View File

@ -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)

View File

@ -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 <machine name>.", 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 == "--" {

View File

@ -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`, well 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.

View File

@ -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")
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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)
}
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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()