github.com/nullne/docker@v1.13.0-rc1/integration-cli/docker_cli_swarm_test.go (about)

     1  // +build !windows
     2  
     3  package main
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"os"
    13  	"path/filepath"
    14  	"strings"
    15  	"time"
    16  
    17  	"github.com/docker/docker/api/types/swarm"
    18  	"github.com/docker/docker/pkg/integration/checker"
    19  	"github.com/docker/libnetwork/driverapi"
    20  	"github.com/docker/libnetwork/ipamapi"
    21  	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
    22  	"github.com/go-check/check"
    23  	"github.com/vishvananda/netlink"
    24  )
    25  
    26  func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
    27  	d := s.AddDaemon(c, true, true)
    28  
    29  	getSpec := func() swarm.Spec {
    30  		sw := d.getSwarm(c)
    31  		return sw.Spec
    32  	}
    33  
    34  	out, err := d.Cmd("swarm", "update", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s")
    35  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    36  
    37  	spec := getSpec()
    38  	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
    39  	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
    40  
    41  	// setting anything under 30m for cert-expiry is not allowed
    42  	out, err = d.Cmd("swarm", "update", "--cert-expiry", "15m")
    43  	c.Assert(err, checker.NotNil)
    44  	c.Assert(out, checker.Contains, "minimum certificate expiry time")
    45  	spec = getSpec()
    46  	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
    47  }
    48  
    49  func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
    50  	d := s.AddDaemon(c, false, false)
    51  
    52  	getSpec := func() swarm.Spec {
    53  		sw := d.getSwarm(c)
    54  		return sw.Spec
    55  	}
    56  
    57  	out, err := d.Cmd("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s")
    58  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    59  
    60  	spec := getSpec()
    61  	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
    62  	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
    63  
    64  	c.Assert(d.Leave(true), checker.IsNil)
    65  	time.Sleep(500 * time.Millisecond) // https://github.com/docker/swarmkit/issues/1421
    66  	out, err = d.Cmd("swarm", "init")
    67  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    68  
    69  	spec = getSpec()
    70  	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 90*24*time.Hour)
    71  	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 5*time.Second)
    72  }
    73  
    74  func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
    75  	testRequires(c, IPv6)
    76  	d1 := s.AddDaemon(c, false, false)
    77  	out, err := d1.Cmd("swarm", "init", "--listen-addr", "::1")
    78  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    79  
    80  	d2 := s.AddDaemon(c, false, false)
    81  	out, err = d2.Cmd("swarm", "join", "::1")
    82  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    83  
    84  	out, err = d2.Cmd("info")
    85  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
    86  	c.Assert(out, checker.Contains, "Swarm: active")
    87  }
    88  
    89  func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
    90  	// init swarm mode and stop a daemon
    91  	d := s.AddDaemon(c, true, true)
    92  	info, err := d.info()
    93  	c.Assert(err, checker.IsNil)
    94  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
    95  	c.Assert(d.Stop(), checker.IsNil)
    96  
    97  	// start a daemon with --cluster-store and --cluster-advertise
    98  	err = d.Start("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375")
    99  	c.Assert(err, checker.NotNil)
   100  	content, _ := ioutil.ReadFile(d.logFile.Name())
   101  	c.Assert(string(content), checker.Contains, "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")
   102  
   103  	// start a daemon with --live-restore
   104  	err = d.Start("--live-restore")
   105  	c.Assert(err, checker.NotNil)
   106  	content, _ = ioutil.ReadFile(d.logFile.Name())
   107  	c.Assert(string(content), checker.Contains, "--live-restore daemon configuration is incompatible with swarm mode")
   108  	// restart for teardown
   109  	c.Assert(d.Start(), checker.IsNil)
   110  }
   111  
   112  // Test case for #24090
   113  func (s *DockerSwarmSuite) TestSwarmNodeListHostname(c *check.C) {
   114  	d := s.AddDaemon(c, true, true)
   115  
   116  	// The first line should contain "HOSTNAME"
   117  	out, err := d.Cmd("node", "ls")
   118  	c.Assert(err, checker.IsNil)
   119  	c.Assert(strings.Split(out, "\n")[0], checker.Contains, "HOSTNAME")
   120  }
   121  
   122  func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) {
   123  	d := s.AddDaemon(c, true, true)
   124  
   125  	out, err := d.Cmd("service", "create", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}", "busybox", "top")
   126  	c.Assert(err, checker.IsNil, check.Commentf(out))
   127  
   128  	// make sure task has been deployed.
   129  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   130  
   131  	containers := d.activeContainers()
   132  	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0])
   133  	c.Assert(err, checker.IsNil, check.Commentf(out))
   134  	c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1", check.Commentf("hostname with templating invalid"))
   135  }
   136  
   137  // Test case for #24270
   138  func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *check.C) {
   139  	d := s.AddDaemon(c, true, true)
   140  
   141  	name1 := "redis-cluster-md5"
   142  	name2 := "redis-cluster"
   143  	name3 := "other-cluster"
   144  	out, err := d.Cmd("service", "create", "--name", name1, "busybox", "top")
   145  	c.Assert(err, checker.IsNil)
   146  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   147  
   148  	out, err = d.Cmd("service", "create", "--name", name2, "busybox", "top")
   149  	c.Assert(err, checker.IsNil)
   150  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   151  
   152  	out, err = d.Cmd("service", "create", "--name", name3, "busybox", "top")
   153  	c.Assert(err, checker.IsNil)
   154  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   155  
   156  	filter1 := "name=redis-cluster-md5"
   157  	filter2 := "name=redis-cluster"
   158  
   159  	// We search checker.Contains with `name+" "` to prevent prefix only.
   160  	out, err = d.Cmd("service", "ls", "--filter", filter1)
   161  	c.Assert(err, checker.IsNil)
   162  	c.Assert(out, checker.Contains, name1+" ")
   163  	c.Assert(out, checker.Not(checker.Contains), name2+" ")
   164  	c.Assert(out, checker.Not(checker.Contains), name3+" ")
   165  
   166  	out, err = d.Cmd("service", "ls", "--filter", filter2)
   167  	c.Assert(err, checker.IsNil)
   168  	c.Assert(out, checker.Contains, name1+" ")
   169  	c.Assert(out, checker.Contains, name2+" ")
   170  	c.Assert(out, checker.Not(checker.Contains), name3+" ")
   171  
   172  	out, err = d.Cmd("service", "ls")
   173  	c.Assert(err, checker.IsNil)
   174  	c.Assert(out, checker.Contains, name1+" ")
   175  	c.Assert(out, checker.Contains, name2+" ")
   176  	c.Assert(out, checker.Contains, name3+" ")
   177  }
   178  
   179  func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *check.C) {
   180  	d := s.AddDaemon(c, true, true)
   181  
   182  	out, err := d.Cmd("node", "inspect", "--format", "{{ .Description.Hostname }}", "self")
   183  	c.Assert(err, checker.IsNil)
   184  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   185  	name := strings.TrimSpace(out)
   186  
   187  	filter := "name=" + name[:4]
   188  
   189  	out, err = d.Cmd("node", "ls", "--filter", filter)
   190  	c.Assert(err, checker.IsNil)
   191  	c.Assert(out, checker.Contains, name)
   192  
   193  	out, err = d.Cmd("node", "ls", "--filter", "name=none")
   194  	c.Assert(err, checker.IsNil)
   195  	c.Assert(out, checker.Not(checker.Contains), name)
   196  }
   197  
   198  func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *check.C) {
   199  	d := s.AddDaemon(c, true, true)
   200  
   201  	name := "redis-cluster-md5"
   202  	out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top")
   203  	c.Assert(err, checker.IsNil)
   204  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   205  
   206  	// make sure task has been deployed.
   207  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3)
   208  
   209  	filter := "name=redis-cluster"
   210  
   211  	out, err = d.Cmd("node", "ps", "--filter", filter, "self")
   212  	c.Assert(err, checker.IsNil)
   213  	c.Assert(out, checker.Contains, name+".1")
   214  	c.Assert(out, checker.Contains, name+".2")
   215  	c.Assert(out, checker.Contains, name+".3")
   216  
   217  	out, err = d.Cmd("node", "ps", "--filter", "name=none", "self")
   218  	c.Assert(err, checker.IsNil)
   219  	c.Assert(out, checker.Not(checker.Contains), name+".1")
   220  	c.Assert(out, checker.Not(checker.Contains), name+".2")
   221  	c.Assert(out, checker.Not(checker.Contains), name+".3")
   222  
   223  	out, err = d.Cmd("node", "ps", "--filter", "desired-state=running", "self")
   224  	c.Assert(err, checker.IsNil)
   225  	c.Assert(out, checker.Contains, name+".1")
   226  	c.Assert(out, checker.Contains, name+".2")
   227  	c.Assert(out, checker.Contains, name+".3")
   228  
   229  	out, err = d.Cmd("node", "ps", "--filter", "desired-state=shutdown", "self")
   230  	c.Assert(err, checker.IsNil)
   231  	c.Assert(out, checker.Not(checker.Contains), name+".1")
   232  	c.Assert(out, checker.Not(checker.Contains), name+".2")
   233  	c.Assert(out, checker.Not(checker.Contains), name+".3")
   234  }
   235  
   236  func (s *DockerSwarmSuite) TestSwarmServiceTaskListAll(c *check.C) {
   237  	d := s.AddDaemon(c, true, true)
   238  
   239  	name := "service-task-list-1"
   240  	out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top")
   241  	c.Assert(err, checker.IsNil)
   242  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   243  
   244  	// make sure task has been deployed.
   245  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3)
   246  
   247  	out, err = d.Cmd("service", "ps", name)
   248  	c.Assert(err, checker.IsNil)
   249  	c.Assert(out, checker.Contains, name+".1")
   250  	c.Assert(out, checker.Contains, name+".2")
   251  	c.Assert(out, checker.Contains, name+".3")
   252  
   253  	// Get the last container id so we can restart it to cause a task error in the history
   254  	containerID, err := d.Cmd("ps", "-q", "-l")
   255  	c.Assert(err, checker.IsNil)
   256  
   257  	_, err = d.Cmd("stop", strings.TrimSpace(containerID))
   258  	c.Assert(err, checker.IsNil)
   259  
   260  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3)
   261  
   262  	out, err = d.Cmd("service", "ps", name)
   263  	c.Assert(err, checker.IsNil)
   264  	c.Assert(out, checker.Count, name, 3)
   265  
   266  	out, err = d.Cmd("service", "ps", name, "-a")
   267  	c.Assert(err, checker.IsNil)
   268  	c.Assert(out, checker.Count, name, 4)
   269  }
   270  
   271  func (s *DockerSwarmSuite) TestSwarmNodeTaskListAll(c *check.C) {
   272  	d := s.AddDaemon(c, true, true)
   273  
   274  	name := "node-task-list"
   275  	out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top")
   276  	c.Assert(err, checker.IsNil)
   277  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   278  
   279  	// make sure task has been deployed.
   280  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3)
   281  
   282  	out, err = d.Cmd("service", "ps", name)
   283  	c.Assert(err, checker.IsNil)
   284  	c.Assert(out, checker.Contains, name+".1")
   285  	c.Assert(out, checker.Contains, name+".2")
   286  	c.Assert(out, checker.Contains, name+".3")
   287  
   288  	// Get the last container id so we can restart it to cause a task error in the history
   289  	containerID, err := d.Cmd("ps", "-q", "-l")
   290  	c.Assert(err, checker.IsNil)
   291  
   292  	_, err = d.Cmd("stop", strings.TrimSpace(containerID))
   293  	c.Assert(err, checker.IsNil)
   294  
   295  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 3)
   296  
   297  	out, err = d.Cmd("node", "ps", "self")
   298  	c.Assert(err, checker.IsNil)
   299  	c.Assert(out, checker.Count, name, 3)
   300  
   301  	out, err = d.Cmd("node", "ps", "self", "-a")
   302  	c.Assert(err, checker.IsNil)
   303  	c.Assert(out, checker.Count, name, 4)
   304  }
   305  
   306  // Test case for #25375
   307  func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
   308  	d := s.AddDaemon(c, true, true)
   309  
   310  	name := "top"
   311  	out, err := d.Cmd("service", "create", "--name", name, "--label", "x=y", "busybox", "top")
   312  	c.Assert(err, checker.IsNil)
   313  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   314  
   315  	out, err = d.Cmd("service", "update", "--publish-add", "80:80", name)
   316  	c.Assert(err, checker.IsNil)
   317  
   318  	out, err = d.cmdRetryOutOfSequence("service", "update", "--publish-add", "80:80", name)
   319  	c.Assert(err, checker.IsNil)
   320  
   321  	out, err = d.cmdRetryOutOfSequence("service", "update", "--publish-add", "80:80", "--publish-add", "80:20", name)
   322  	c.Assert(err, checker.NotNil)
   323  
   324  	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
   325  	c.Assert(err, checker.IsNil)
   326  	c.Assert(strings.TrimSpace(out), checker.Equals, "[{ tcp 80 80 ingress}]")
   327  }
   328  
   329  func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *check.C) {
   330  	d := s.AddDaemon(c, true, true)
   331  
   332  	name := "top"
   333  	out, err := d.Cmd("service", "create", "--name", name, "--user", "root:root", "--group", "wheel", "--group", "audio", "--group", "staff", "--group", "777", "busybox", "top")
   334  	c.Assert(err, checker.IsNil)
   335  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   336  
   337  	// make sure task has been deployed.
   338  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   339  
   340  	out, err = d.Cmd("ps", "-q")
   341  	c.Assert(err, checker.IsNil)
   342  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   343  
   344  	container := strings.TrimSpace(out)
   345  
   346  	out, err = d.Cmd("exec", container, "id")
   347  	c.Assert(err, checker.IsNil)
   348  	c.Assert(strings.TrimSpace(out), checker.Equals, "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777")
   349  }
   350  
   351  func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *check.C) {
   352  	d := s.AddDaemon(c, true, true)
   353  
   354  	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
   355  	c.Assert(err, checker.IsNil)
   356  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   357  
   358  	out, err = d.Cmd("run", "-id", "--restart=always", "--net=foo", "--name=test", "busybox", "top")
   359  	c.Assert(err, checker.IsNil, check.Commentf(out))
   360  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   361  
   362  	out, err = d.Cmd("ps", "-q")
   363  	c.Assert(err, checker.IsNil, check.Commentf(out))
   364  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   365  
   366  	d.Restart()
   367  
   368  	out, err = d.Cmd("ps", "-q")
   369  	c.Assert(err, checker.IsNil, check.Commentf(out))
   370  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   371  }
   372  
   373  func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *check.C) {
   374  	d := s.AddDaemon(c, true, true)
   375  
   376  	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
   377  	c.Assert(err, checker.IsNil, check.Commentf(out))
   378  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   379  
   380  	_, err = d.Cmd("run", "-d", "--net=foo", "--name=first", "--net-alias=first-alias", "busybox", "top")
   381  	c.Assert(err, checker.IsNil, check.Commentf(out))
   382  
   383  	_, err = d.Cmd("run", "-d", "--net=foo", "--name=second", "busybox", "top")
   384  	c.Assert(err, checker.IsNil, check.Commentf(out))
   385  
   386  	// ping first container and its alias
   387  	_, err = d.Cmd("exec", "second", "ping", "-c", "1", "first")
   388  	c.Assert(err, check.IsNil, check.Commentf(out))
   389  	_, err = d.Cmd("exec", "second", "ping", "-c", "1", "first-alias")
   390  	c.Assert(err, check.IsNil, check.Commentf(out))
   391  }
   392  
   393  func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *check.C) {
   394  	d := s.AddDaemon(c, true, true)
   395  
   396  	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "testnet")
   397  	c.Assert(err, checker.IsNil)
   398  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   399  	networkID := strings.TrimSpace(out)
   400  
   401  	out, err = d.Cmd("run", "-d", "--net", networkID, "busybox", "top")
   402  	c.Assert(err, checker.IsNil)
   403  	cID := strings.TrimSpace(out)
   404  	d.waitRun(cID)
   405  
   406  	_, err = d.Cmd("rm", "-f", cID)
   407  	c.Assert(err, checker.IsNil)
   408  
   409  	out, err = d.Cmd("network", "rm", "testnet")
   410  	c.Assert(err, checker.IsNil)
   411  
   412  	checkNetwork := func(*check.C) (interface{}, check.CommentInterface) {
   413  		out, err := d.Cmd("network", "ls")
   414  		c.Assert(err, checker.IsNil)
   415  		return out, nil
   416  	}
   417  
   418  	waitAndAssert(c, 3*time.Second, checkNetwork, checker.Not(checker.Contains), "testnet")
   419  }
   420  
   421  func (s *DockerSwarmSuite) TestSwarmRemoveInternalNetwork(c *check.C) {
   422  	d := s.AddDaemon(c, true, true)
   423  
   424  	name := "ingress"
   425  	out, err := d.Cmd("network", "rm", name)
   426  	c.Assert(err, checker.NotNil)
   427  	c.Assert(strings.TrimSpace(out), checker.Contains, name)
   428  	c.Assert(strings.TrimSpace(out), checker.Contains, "is a pre-defined network and cannot be removed")
   429  }
   430  
   431  // Test case for #24108, also the case from:
   432  // https://github.com/docker/docker/pull/24620#issuecomment-233715656
   433  func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *check.C) {
   434  	d := s.AddDaemon(c, true, true)
   435  
   436  	name := "redis-cluster-md5"
   437  	out, err := d.Cmd("service", "create", "--name", name, "--replicas=3", "busybox", "top")
   438  	c.Assert(err, checker.IsNil)
   439  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   440  
   441  	filter := "name=redis-cluster"
   442  
   443  	checkNumTasks := func(*check.C) (interface{}, check.CommentInterface) {
   444  		out, err := d.Cmd("service", "ps", "--filter", filter, name)
   445  		c.Assert(err, checker.IsNil)
   446  		return len(strings.Split(out, "\n")) - 2, nil // includes header and nl in last line
   447  	}
   448  
   449  	// wait until all tasks have been created
   450  	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 3)
   451  
   452  	out, err = d.Cmd("service", "ps", "--filter", filter, name)
   453  	c.Assert(err, checker.IsNil)
   454  	c.Assert(out, checker.Contains, name+".1")
   455  	c.Assert(out, checker.Contains, name+".2")
   456  	c.Assert(out, checker.Contains, name+".3")
   457  
   458  	out, err = d.Cmd("service", "ps", "--filter", "name="+name+".1", name)
   459  	c.Assert(err, checker.IsNil)
   460  	c.Assert(out, checker.Contains, name+".1")
   461  	c.Assert(out, checker.Not(checker.Contains), name+".2")
   462  	c.Assert(out, checker.Not(checker.Contains), name+".3")
   463  
   464  	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
   465  	c.Assert(err, checker.IsNil)
   466  	c.Assert(out, checker.Not(checker.Contains), name+".1")
   467  	c.Assert(out, checker.Not(checker.Contains), name+".2")
   468  	c.Assert(out, checker.Not(checker.Contains), name+".3")
   469  
   470  	name = "redis-cluster-sha1"
   471  	out, err = d.Cmd("service", "create", "--name", name, "--mode=global", "busybox", "top")
   472  	c.Assert(err, checker.IsNil)
   473  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   474  
   475  	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 1)
   476  
   477  	filter = "name=redis-cluster"
   478  	out, err = d.Cmd("service", "ps", "--filter", filter, name)
   479  	c.Assert(err, checker.IsNil)
   480  	c.Assert(out, checker.Contains, name)
   481  
   482  	out, err = d.Cmd("service", "ps", "--filter", "name="+name, name)
   483  	c.Assert(err, checker.IsNil)
   484  	c.Assert(out, checker.Contains, name)
   485  
   486  	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
   487  	c.Assert(err, checker.IsNil)
   488  	c.Assert(out, checker.Not(checker.Contains), name)
   489  }
   490  
   491  func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) {
   492  	d := s.AddDaemon(c, true, true)
   493  
   494  	// Create a bare container
   495  	out, err := d.Cmd("run", "-d", "--name=bare-container", "busybox", "top")
   496  	c.Assert(err, checker.IsNil)
   497  	bareID := strings.TrimSpace(out)[:12]
   498  	// Create a service
   499  	name := "busybox-top"
   500  	out, err = d.Cmd("service", "create", "--name", name, "busybox", "top")
   501  	c.Assert(err, checker.IsNil)
   502  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   503  
   504  	// make sure task has been deployed.
   505  	waitAndAssert(c, defaultReconciliationTimeout, d.checkServiceRunningTasks(name), checker.Equals, 1)
   506  
   507  	// Filter non-tasks
   508  	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=false")
   509  	c.Assert(err, checker.IsNil)
   510  	psOut := strings.TrimSpace(out)
   511  	c.Assert(psOut, checker.Equals, bareID, check.Commentf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out))
   512  
   513  	// Filter tasks
   514  	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=true")
   515  	c.Assert(err, checker.IsNil)
   516  	lines := strings.Split(strings.Trim(out, "\n "), "\n")
   517  	c.Assert(lines, checker.HasLen, 1)
   518  	c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out))
   519  }
   520  
   521  const globalNetworkPlugin = "global-network-plugin"
   522  const globalIPAMPlugin = "global-ipam-plugin"
   523  
   524  func (s *DockerSwarmSuite) SetUpSuite(c *check.C) {
   525  	mux := http.NewServeMux()
   526  	s.server = httptest.NewServer(mux)
   527  	c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server"))
   528  	setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin)
   529  }
   530  
   531  func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
   532  
   533  	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
   534  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   535  		fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType)
   536  	})
   537  
   538  	// Network driver implementation
   539  	mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   540  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   541  		fmt.Fprintf(w, `{"Scope":"global"}`)
   542  	})
   543  
   544  	mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   545  		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
   546  		if err != nil {
   547  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   548  			return
   549  		}
   550  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   551  		fmt.Fprintf(w, "null")
   552  	})
   553  
   554  	mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   555  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   556  		fmt.Fprintf(w, "null")
   557  	})
   558  
   559  	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   560  		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
   561  		if err != nil {
   562  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   563  			return
   564  		}
   565  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   566  		fmt.Fprintf(w, "null")
   567  	})
   568  
   569  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   570  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   571  		fmt.Fprintf(w, "null")
   572  	})
   573  
   574  	mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   575  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   576  		fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`)
   577  	})
   578  
   579  	mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   580  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   581  
   582  		veth := &netlink.Veth{
   583  			LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"}
   584  		if err := netlink.LinkAdd(veth); err != nil {
   585  			fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`)
   586  		} else {
   587  			fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`)
   588  		}
   589  	})
   590  
   591  	mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   592  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   593  		fmt.Fprintf(w, "null")
   594  	})
   595  
   596  	mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   597  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   598  		if link, err := netlink.LinkByName("cnt0"); err == nil {
   599  			netlink.LinkDel(link)
   600  		}
   601  		fmt.Fprintf(w, "null")
   602  	})
   603  
   604  	// IPAM Driver implementation
   605  	var (
   606  		poolRequest       remoteipam.RequestPoolRequest
   607  		poolReleaseReq    remoteipam.ReleasePoolRequest
   608  		addressRequest    remoteipam.RequestAddressRequest
   609  		addressReleaseReq remoteipam.ReleaseAddressRequest
   610  		lAS               = "localAS"
   611  		gAS               = "globalAS"
   612  		pool              = "172.28.0.0/16"
   613  		poolID            = lAS + "/" + pool
   614  		gw                = "172.28.255.254/16"
   615  	)
   616  
   617  	mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   618  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   619  		fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`)
   620  	})
   621  
   622  	mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   623  		err := json.NewDecoder(r.Body).Decode(&poolRequest)
   624  		if err != nil {
   625  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   626  			return
   627  		}
   628  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   629  		if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS {
   630  			fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`)
   631  		} else if poolRequest.Pool != "" && poolRequest.Pool != pool {
   632  			fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`)
   633  		} else {
   634  			fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`)
   635  		}
   636  	})
   637  
   638  	mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   639  		err := json.NewDecoder(r.Body).Decode(&addressRequest)
   640  		if err != nil {
   641  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   642  			return
   643  		}
   644  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   645  		// make sure libnetwork is now querying on the expected pool id
   646  		if addressRequest.PoolID != poolID {
   647  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   648  		} else if addressRequest.Address != "" {
   649  			fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`)
   650  		} else {
   651  			fmt.Fprintf(w, `{"Address":"`+gw+`"}`)
   652  		}
   653  	})
   654  
   655  	mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   656  		err := json.NewDecoder(r.Body).Decode(&addressReleaseReq)
   657  		if err != nil {
   658  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   659  			return
   660  		}
   661  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   662  		// make sure libnetwork is now asking to release the expected address from the expected poolid
   663  		if addressRequest.PoolID != poolID {
   664  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   665  		} else if addressReleaseReq.Address != gw {
   666  			fmt.Fprintf(w, `{"Error":"unknown address"}`)
   667  		} else {
   668  			fmt.Fprintf(w, "null")
   669  		}
   670  	})
   671  
   672  	mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
   673  		err := json.NewDecoder(r.Body).Decode(&poolReleaseReq)
   674  		if err != nil {
   675  			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
   676  			return
   677  		}
   678  		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
   679  		// make sure libnetwork is now asking to release the expected poolid
   680  		if addressRequest.PoolID != poolID {
   681  			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
   682  		} else {
   683  			fmt.Fprintf(w, "null")
   684  		}
   685  	})
   686  
   687  	err := os.MkdirAll("/etc/docker/plugins", 0755)
   688  	c.Assert(err, checker.IsNil)
   689  
   690  	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
   691  	err = ioutil.WriteFile(fileName, []byte(url), 0644)
   692  	c.Assert(err, checker.IsNil)
   693  
   694  	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
   695  	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
   696  	c.Assert(err, checker.IsNil)
   697  }
   698  
   699  func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) {
   700  	d := s.AddDaemon(c, true, true)
   701  
   702  	out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo")
   703  	c.Assert(err, checker.IsNil)
   704  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   705  
   706  	name := "top"
   707  	out, err = d.Cmd("service", "create", "--name", name, "--network", "foo", "busybox", "top")
   708  	c.Assert(err, checker.IsNil)
   709  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   710  
   711  	out, err = d.Cmd("service", "inspect", "--format", "{{range .Spec.Networks}}{{.Target}}{{end}}", name)
   712  	c.Assert(err, checker.IsNil)
   713  	c.Assert(strings.TrimSpace(out), checker.Equals, "foo")
   714  }
   715  
   716  // Test case for #24712
   717  func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) {
   718  	d := s.AddDaemon(c, true, true)
   719  
   720  	path := filepath.Join(d.folder, "env.txt")
   721  	err := ioutil.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644)
   722  	c.Assert(err, checker.IsNil)
   723  
   724  	name := "worker"
   725  	out, err := d.Cmd("service", "create", "--env-file", path, "--env", "VAR1=B", "--env", "VAR1=C", "--env", "VAR2=", "--env", "VAR2", "--name", name, "busybox", "top")
   726  	c.Assert(err, checker.IsNil)
   727  	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
   728  
   729  	// The complete env is [VAR1=A VAR2=A VAR1=B VAR1=C VAR2= VAR2] and duplicates will be removed => [VAR1=C VAR2]
   730  	out, err = d.Cmd("inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.Env }}", name)
   731  	c.Assert(err, checker.IsNil)
   732  	c.Assert(out, checker.Contains, "[VAR1=C VAR2]")
   733  }
   734  
   735  func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) {
   736  	d := s.AddDaemon(c, true, true)
   737  
   738  	name := "top"
   739  
   740  	ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi"
   741  
   742  	// Without --tty
   743  	expectedOutput := "none"
   744  	out, err := d.Cmd("service", "create", "--name", name, "busybox", "sh", "-c", ttyCheck)
   745  	c.Assert(err, checker.IsNil)
   746  
   747  	// Make sure task has been deployed.
   748  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   749  
   750  	// We need to get the container id.
   751  	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
   752  	c.Assert(err, checker.IsNil)
   753  	id := strings.TrimSpace(out)
   754  
   755  	out, err = d.Cmd("exec", id, "cat", "/status")
   756  	c.Assert(err, checker.IsNil)
   757  	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
   758  
   759  	// Remove service
   760  	out, err = d.Cmd("service", "rm", name)
   761  	c.Assert(err, checker.IsNil)
   762  	// Make sure container has been destroyed.
   763  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 0)
   764  
   765  	// With --tty
   766  	expectedOutput = "TTY"
   767  	out, err = d.Cmd("service", "create", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck)
   768  	c.Assert(err, checker.IsNil)
   769  
   770  	// Make sure task has been deployed.
   771  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   772  
   773  	// We need to get the container id.
   774  	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
   775  	c.Assert(err, checker.IsNil)
   776  	id = strings.TrimSpace(out)
   777  
   778  	out, err = d.Cmd("exec", id, "cat", "/status")
   779  	c.Assert(err, checker.IsNil)
   780  	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
   781  }
   782  
   783  func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) {
   784  	d := s.AddDaemon(c, true, true)
   785  
   786  	// Create a service
   787  	name := "top"
   788  	_, err := d.Cmd("service", "create", "--name", name, "busybox", "top")
   789  	c.Assert(err, checker.IsNil)
   790  
   791  	// Make sure task has been deployed.
   792  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   793  
   794  	out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
   795  	c.Assert(err, checker.IsNil)
   796  	c.Assert(strings.TrimSpace(out), checker.Equals, "false")
   797  
   798  	_, err = d.Cmd("service", "update", "--tty", name)
   799  	c.Assert(err, checker.IsNil)
   800  
   801  	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
   802  	c.Assert(err, checker.IsNil)
   803  	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
   804  }
   805  
   806  func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) {
   807  	d := s.AddDaemon(c, true, true)
   808  
   809  	// Create a service
   810  	name := "top"
   811  	_, err := d.Cmd("service", "create", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-option=timeout:3", "busybox", "top")
   812  	c.Assert(err, checker.IsNil)
   813  
   814  	// Make sure task has been deployed.
   815  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   816  
   817  	// We need to get the container id.
   818  	out, err := d.Cmd("ps", "-a", "-q", "--no-trunc")
   819  	c.Assert(err, checker.IsNil)
   820  	id := strings.TrimSpace(out)
   821  
   822  	// Compare against expected output.
   823  	expectedOutput1 := "nameserver 1.2.3.4"
   824  	expectedOutput2 := "search example.com"
   825  	expectedOutput3 := "options timeout:3"
   826  	out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf")
   827  	c.Assert(err, checker.IsNil)
   828  	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
   829  	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
   830  	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
   831  }
   832  
   833  func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
   834  	d := s.AddDaemon(c, true, true)
   835  
   836  	// Create a service
   837  	name := "top"
   838  	_, err := d.Cmd("service", "create", "--name", name, "busybox", "top")
   839  	c.Assert(err, checker.IsNil)
   840  
   841  	// Make sure task has been deployed.
   842  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
   843  
   844  	_, err = d.Cmd("service", "update", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-option-add=timeout:3", name)
   845  	c.Assert(err, checker.IsNil)
   846  
   847  	out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name)
   848  	c.Assert(err, checker.IsNil)
   849  	c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
   850  }
   851  
   852  func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
   853  	d := s.AddDaemon(c, false, false)
   854  
   855  	outs, err := d.Cmd("swarm", "init", "--autolock")
   856  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   857  
   858  	c.Assert(outs, checker.Contains, "docker swarm unlock")
   859  
   860  	var unlockKey string
   861  	for _, line := range strings.Split(outs, "\n") {
   862  		if strings.Contains(line, "SWMKEY") {
   863  			unlockKey = strings.TrimSpace(line)
   864  			break
   865  		}
   866  	}
   867  
   868  	c.Assert(unlockKey, checker.Not(checker.Equals), "")
   869  
   870  	outs, err = d.Cmd("swarm", "unlock-key", "-q")
   871  	c.Assert(outs, checker.Equals, unlockKey+"\n")
   872  
   873  	info, err := d.info()
   874  	c.Assert(err, checker.IsNil)
   875  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   876  
   877  	c.Assert(d.Restart(), checker.IsNil)
   878  
   879  	info, err = d.info()
   880  	c.Assert(err, checker.IsNil)
   881  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
   882  
   883  	cmd := d.command("swarm", "unlock")
   884  	cmd.Stdin = bytes.NewBufferString("wrong-secret-key")
   885  	out, err := cmd.CombinedOutput()
   886  	c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out)))
   887  	c.Assert(string(out), checker.Contains, "invalid key")
   888  
   889  	cmd = d.command("swarm", "unlock")
   890  	cmd.Stdin = bytes.NewBufferString(unlockKey)
   891  	out, err = cmd.CombinedOutput()
   892  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
   893  
   894  	info, err = d.info()
   895  	c.Assert(err, checker.IsNil)
   896  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   897  
   898  	outs, err = d.Cmd("node", "ls")
   899  	c.Assert(err, checker.IsNil)
   900  	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
   901  
   902  	outs, err = d.Cmd("swarm", "update", "--autolock=false")
   903  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   904  
   905  	// Wait for autolock to be turned off
   906  	time.Sleep(time.Second)
   907  
   908  	c.Assert(d.Restart(), checker.IsNil)
   909  
   910  	info, err = d.info()
   911  	c.Assert(err, checker.IsNil)
   912  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   913  
   914  	outs, err = d.Cmd("node", "ls")
   915  	c.Assert(err, checker.IsNil)
   916  	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
   917  }
   918  
   919  func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
   920  	d := s.AddDaemon(c, false, false)
   921  
   922  	outs, err := d.Cmd("swarm", "init", "--autolock")
   923  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   924  
   925  	c.Assert(d.Restart("--swarm-default-advertise-addr=lo"), checker.IsNil)
   926  
   927  	info, err := d.info()
   928  	c.Assert(err, checker.IsNil)
   929  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
   930  
   931  	outs, _ = d.Cmd("node", "ls")
   932  	c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
   933  
   934  	outs, err = d.Cmd("swarm", "leave", "--force")
   935  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   936  
   937  	info, err = d.info()
   938  	c.Assert(err, checker.IsNil)
   939  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
   940  
   941  	outs, err = d.Cmd("swarm", "init")
   942  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   943  
   944  	info, err = d.info()
   945  	c.Assert(err, checker.IsNil)
   946  	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
   947  }
   948  
   949  func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
   950  	d := s.AddDaemon(c, true, true)
   951  
   952  	outs, err := d.Cmd("swarm", "update", "--autolock")
   953  	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   954  
   955  	c.Assert(outs, checker.Contains, "docker swarm unlock")
   956  
   957  	var unlockKey string
   958  	for _, line := range strings.Split(outs, "\n") {
   959  		if strings.Contains(line, "SWMKEY") {
   960  			unlockKey = strings.TrimSpace(line)
   961  			break
   962  		}
   963  	}
   964  
   965  	c.Assert(unlockKey, checker.Not(checker.Equals), "")
   966  
   967  	outs, err = d.Cmd("swarm", "unlock-key", "-q")
   968  	c.Assert(outs, checker.Equals, unlockKey+"\n")
   969  
   970  	// Rotate multiple times
   971  	for i := 0; i != 3; i++ {
   972  		outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate")
   973  		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
   974  		// Strip \n
   975  		newUnlockKey := outs[:len(outs)-1]
   976  		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
   977  		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
   978  
   979  		c.Assert(d.Restart(), checker.IsNil)
   980  
   981  		info, err := d.info()
   982  		c.Assert(err, checker.IsNil)
   983  		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
   984  
   985  		outs, _ = d.Cmd("node", "ls")
   986  		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
   987  
   988  		cmd := d.command("swarm", "unlock")
   989  		cmd.Stdin = bytes.NewBufferString(unlockKey)
   990  		out, err := cmd.CombinedOutput()
   991  
   992  		if err == nil {
   993  			// On occasion, the daemon may not have finished
   994  			// rotating the KEK before restarting. The test is
   995  			// intentionally written to explore this behavior.
   996  			// When this happens, unlocking with the old key will
   997  			// succeed. If we wait for the rotation to happen and
   998  			// restart again, the new key should be required this
   999  			// time.
  1000  
  1001  			time.Sleep(3 * time.Second)
  1002  
  1003  			c.Assert(d.Restart(), checker.IsNil)
  1004  
  1005  			cmd = d.command("swarm", "unlock")
  1006  			cmd.Stdin = bytes.NewBufferString(unlockKey)
  1007  			out, err = cmd.CombinedOutput()
  1008  		}
  1009  		c.Assert(err, checker.NotNil, check.Commentf("out: %v", string(out)))
  1010  		c.Assert(string(out), checker.Contains, "invalid key")
  1011  
  1012  		outs, _ = d.Cmd("node", "ls")
  1013  		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
  1014  
  1015  		cmd = d.command("swarm", "unlock")
  1016  		cmd.Stdin = bytes.NewBufferString(newUnlockKey)
  1017  		out, err = cmd.CombinedOutput()
  1018  		c.Assert(err, checker.IsNil, check.Commentf("out: %v", string(out)))
  1019  
  1020  		info, err = d.info()
  1021  		c.Assert(err, checker.IsNil)
  1022  		c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
  1023  
  1024  		outs, err = d.Cmd("node", "ls")
  1025  		c.Assert(err, checker.IsNil)
  1026  		c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
  1027  
  1028  		unlockKey = newUnlockKey
  1029  	}
  1030  }
  1031  
  1032  func (s *DockerSwarmSuite) TestExtraHosts(c *check.C) {
  1033  	d := s.AddDaemon(c, true, true)
  1034  
  1035  	// Create a service
  1036  	name := "top"
  1037  	_, err := d.Cmd("service", "create", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top")
  1038  	c.Assert(err, checker.IsNil)
  1039  
  1040  	// Make sure task has been deployed.
  1041  	waitAndAssert(c, defaultReconciliationTimeout, d.checkActiveContainerCount, checker.Equals, 1)
  1042  
  1043  	// We need to get the container id.
  1044  	out, err := d.Cmd("ps", "-a", "-q", "--no-trunc")
  1045  	c.Assert(err, checker.IsNil)
  1046  	id := strings.TrimSpace(out)
  1047  
  1048  	// Compare against expected output.
  1049  	expectedOutput := "1.2.3.4\texample.com"
  1050  	out, err = d.Cmd("exec", id, "cat", "/etc/hosts")
  1051  	c.Assert(err, checker.IsNil)
  1052  	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
  1053  }
  1054  
  1055  func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
  1056  	d1 := s.AddDaemon(c, true, true)
  1057  	d2 := s.AddDaemon(c, true, false)
  1058  	d3 := s.AddDaemon(c, true, false)
  1059  
  1060  	// Manager Addresses will always show Node 1's address
  1061  	expectedOutput := fmt.Sprintf("Manager Addresses:\n  127.0.0.1:%d\n", d1.port)
  1062  
  1063  	out, err := d1.Cmd("info")
  1064  	c.Assert(err, checker.IsNil)
  1065  	c.Assert(out, checker.Contains, expectedOutput)
  1066  
  1067  	out, err = d2.Cmd("info")
  1068  	c.Assert(err, checker.IsNil)
  1069  	c.Assert(out, checker.Contains, expectedOutput)
  1070  
  1071  	out, err = d3.Cmd("info")
  1072  	c.Assert(err, checker.IsNil)
  1073  	c.Assert(out, checker.Contains, expectedOutput)
  1074  }