integration-cli/docker_cli_swarm_test.go
a933ac3c
 // +build !windows
 
 package main
 
 import (
8b1f72ad
 	"bytes"
af185a38
 	"encoding/json"
 	"fmt"
d7178982
 	"io/ioutil"
af185a38
 	"net/http"
 	"net/http/httptest"
 	"os"
d59d19c3
 	"os/exec"
ee3105c6
 	"path/filepath"
4bc91cee
 	"strings"
a933ac3c
 	"time"
 
2b5ef9bf
 	"github.com/cloudflare/cfssl/helpers"
abf31ee0
 	"github.com/docker/docker/api/types"
91e197d6
 	"github.com/docker/docker/api/types/swarm"
33968e6c
 	"github.com/docker/docker/integration-cli/checker"
50c4475d
 	"github.com/docker/docker/integration-cli/cli"
48de91a3
 	"github.com/docker/docker/integration-cli/daemon"
d59d19c3
 	"github.com/docker/docker/pkg/testutil"
ecbb0e62
 	icmd "github.com/docker/docker/pkg/testutil/cmd"
b0401a71
 	"github.com/docker/docker/pkg/testutil/tempfile"
af185a38
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/ipamapi"
 	remoteipam "github.com/docker/libnetwork/ipams/remote/api"
a933ac3c
 	"github.com/go-check/check"
af185a38
 	"github.com/vishvananda/netlink"
a933ac3c
 )
 
 func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	getSpec := func() swarm.Spec {
48de91a3
 		sw := d.GetSwarm(c)
e6923f6d
 		return sw.Spec
a933ac3c
 	}
 
2cc5bd33
 	out, err := d.Cmd("swarm", "update", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s")
a933ac3c
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 
 	spec := getSpec()
 	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
8f7a8c75
 	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
a933ac3c
 
 	// setting anything under 30m for cert-expiry is not allowed
 	out, err = d.Cmd("swarm", "update", "--cert-expiry", "15m")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "minimum certificate expiry time")
 	spec = getSpec()
 	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
9b96b2d2
 
 	// passing an external CA (this is without starting a root rotation) does not fail
b0401a71
 	cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org",
 		"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
 		cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
 
 	expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
 	c.Assert(err, checker.IsNil)
9b96b2d2
 
 	spec = getSpec()
b0401a71
 	c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
 	c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
 	c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
 
 	// passing an invalid external CA fails
 	tempFile := tempfile.NewTempFile(c, "testfile", "fakecert")
 	defer tempFile.Remove()
 
 	result := cli.Docker(cli.Args("swarm", "update",
 		"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://something.org,cacert=%s", tempFile.Name())),
 		cli.Daemon(d.Daemon))
 	result.Assert(c, icmd.Expected{
 		ExitCode: 125,
 		Err:      "must be in PEM format",
 	})
a933ac3c
 }
fb3eb1c2
 
 func (s *DockerSwarmSuite) TestSwarmInit(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
 	getSpec := func() swarm.Spec {
48de91a3
 		sw := d.GetSwarm(c)
e6923f6d
 		return sw.Spec
fb3eb1c2
 	}
 
b0401a71
 	// passing an invalid external CA fails
 	tempFile := tempfile.NewTempFile(c, "testfile", "fakecert")
 	defer tempFile.Remove()
 
 	result := cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
 		"--external-ca", fmt.Sprintf("protocol=cfssl,url=https://somethingelse.org,cacert=%s", tempFile.Name())),
 		cli.Daemon(d.Daemon))
 	result.Assert(c, icmd.Expected{
 		ExitCode: 125,
 		Err:      "must be in PEM format",
 	})
 
9b96b2d2
 	cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s",
b0401a71
 		"--external-ca", "protocol=cfssl,url=https://something.org",
 		"--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"),
50c4475d
 		cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
fb3eb1c2
 
b0401a71
 	expected, err := ioutil.ReadFile("fixtures/https/ca.pem")
 	c.Assert(err, checker.IsNil)
 
fb3eb1c2
 	spec := getSpec()
 	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 30*time.Hour)
8f7a8c75
 	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 11*time.Second)
b0401a71
 	c.Assert(spec.CAConfig.ExternalCAs, checker.HasLen, 2)
 	c.Assert(spec.CAConfig.ExternalCAs[0].CACert, checker.Equals, "")
 	c.Assert(spec.CAConfig.ExternalCAs[1].CACert, checker.Equals, string(expected))
fb3eb1c2
 
 	c.Assert(d.Leave(true), checker.IsNil)
eeaa6c96
 	cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
fb3eb1c2
 
 	spec = getSpec()
 	c.Assert(spec.CAConfig.NodeCertExpiry, checker.Equals, 90*24*time.Hour)
8f7a8c75
 	c.Assert(spec.Dispatcher.HeartbeatPeriod, checker.Equals, 5*time.Second)
fb3eb1c2
 }
 
 func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *check.C) {
 	testRequires(c, IPv6)
 	d1 := s.AddDaemon(c, false, false)
eeaa6c96
 	cli.Docker(cli.Args("swarm", "init", "--listen-add", "::1"), cli.Daemon(d1.Daemon)).Assert(c, icmd.Success)
fb3eb1c2
 
 	d2 := s.AddDaemon(c, false, false)
eeaa6c96
 	cli.Docker(cli.Args("swarm", "join", "::1"), cli.Daemon(d2.Daemon)).Assert(c, icmd.Success)
fb3eb1c2
 
eeaa6c96
 	out := cli.Docker(cli.Args("info"), cli.Daemon(d2.Daemon)).Assert(c, icmd.Success).Combined()
fb3eb1c2
 	c.Assert(out, checker.Contains, "Swarm: active")
 }
d7178982
 
eeac8719
 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 	out, err := d.Cmd("swarm", "init", "--advertise-addr", "0.0.0.0")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "advertise address must be a non-zero IP address")
 }
 
d7178982
 func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *check.C) {
 	// init swarm mode and stop a daemon
 	d := s.AddDaemon(c, true, true)
48de91a3
 	info, err := d.SwarmInfo()
d7178982
 	c.Assert(err, checker.IsNil)
 	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
c502fb49
 	d.Stop(c)
d7178982
 
 	// start a daemon with --cluster-store and --cluster-advertise
c502fb49
 	err = d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375")
d7178982
 	c.Assert(err, checker.NotNil)
48de91a3
 	content, err := d.ReadLogFile()
 	c.Assert(err, checker.IsNil)
d7178982
 	c.Assert(string(content), checker.Contains, "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")
 
 	// start a daemon with --live-restore
c502fb49
 	err = d.StartWithError("--live-restore")
d7178982
 	c.Assert(err, checker.NotNil)
48de91a3
 	content, err = d.ReadLogFile()
 	c.Assert(err, checker.IsNil)
d7178982
 	c.Assert(string(content), checker.Contains, "--live-restore daemon configuration is incompatible with swarm mode")
 	// restart for teardown
c502fb49
 	d.Start(c)
d7178982
 }
4bc91cee
 
6212ea66
 func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}", "busybox", "top")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
6212ea66
 
48de91a3
 	containers := d.ActiveContainers()
6212ea66
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0])
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.Split(out, "\n")[0], checker.Equals, "test-1", check.Commentf("hostname with templating invalid"))
 }
 
1d600ebc
 // Test case for #24270
 func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name1 := "redis-cluster-md5"
 	name2 := "redis-cluster"
 	name3 := "other-cluster"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name1, "busybox", "top")
1d600ebc
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", name2, "busybox", "top")
1d600ebc
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", name3, "busybox", "top")
1d600ebc
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	filter1 := "name=redis-cluster-md5"
 	filter2 := "name=redis-cluster"
 
 	// We search checker.Contains with `name+" "` to prevent prefix only.
 	out, err = d.Cmd("service", "ls", "--filter", filter1)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+" ")
 	c.Assert(out, checker.Not(checker.Contains), name2+" ")
 	c.Assert(out, checker.Not(checker.Contains), name3+" ")
 
 	out, err = d.Cmd("service", "ls", "--filter", filter2)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+" ")
 	c.Assert(out, checker.Contains, name2+" ")
 	c.Assert(out, checker.Not(checker.Contains), name3+" ")
 
 	out, err = d.Cmd("service", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+" ")
 	c.Assert(out, checker.Contains, name2+" ")
 	c.Assert(out, checker.Contains, name3+" ")
 }
e734fa58
 
 func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("node", "inspect", "--format", "{{ .Description.Hostname }}", "self")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	name := strings.TrimSpace(out)
 
 	filter := "name=" + name[:4]
 
 	out, err = d.Cmd("node", "ls", "--filter", filter)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name)
 
 	out, err = d.Cmd("node", "ls", "--filter", "name=none")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), name)
 }
 
 func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "redis-cluster-md5"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top")
e734fa58
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
63c0366b
 	// make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 3)
63c0366b
 
e734fa58
 	filter := "name=redis-cluster"
 
0aa4e1e6
 	out, err = d.Cmd("node", "ps", "--filter", filter, "self")
e734fa58
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name+".1")
 	c.Assert(out, checker.Contains, name+".2")
 	c.Assert(out, checker.Contains, name+".3")
 
0aa4e1e6
 	out, err = d.Cmd("node", "ps", "--filter", "name=none", "self")
e734fa58
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), name+".1")
 	c.Assert(out, checker.Not(checker.Contains), name+".2")
 	c.Assert(out, checker.Not(checker.Contains), name+".3")
 }
b487497c
 
 // Test case for #25375
 func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
f151c297
 	name := "top"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--label", "x=y", "busybox", "top")
f151c297
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
b487497c
 
f151c297
 	out, err = d.Cmd("service", "update", "--publish-add", "80:80", name)
 	c.Assert(err, checker.IsNil)
b487497c
 
f151c297
 	out, err = d.CmdRetryOutOfSequence("service", "update", "--publish-add", "80:80", name)
 	c.Assert(err, checker.IsNil)
b487497c
 
f151c297
 	out, err = d.CmdRetryOutOfSequence("service", "update", "--publish-add", "80:80", "--publish-add", "80:20", name)
 	c.Assert(err, checker.NotNil)
75bf18c9
 
f151c297
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "[{ tcp 80 80 ingress}]")
b487497c
 }
b31969ee
 
 func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--user", "root:root", "--group", "wheel", "--group", "audio", "--group", "staff", "--group", "777", "busybox", "top")
b31969ee
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	// make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
b31969ee
 
 	out, err = d.Cmd("ps", "-q")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	container := strings.TrimSpace(out)
 
ef4bcf23
 	out, err = d.Cmd("exec", container, "id")
b31969ee
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777")
 }
c9fb551d
 
 func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	out, err = d.Cmd("run", "-id", "--restart=always", "--net=foo", "--name=test", "busybox", "top")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c9fb551d
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	out, err = d.Cmd("ps", "-q")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c9fb551d
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
c502fb49
 	d.Restart(c)
c9fb551d
 
 	out, err = d.Cmd("ps", "-q")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c9fb551d
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 }
de487116
 
c5dd4d70
 func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c5dd4d70
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	_, err = d.Cmd("run", "-d", "--net=foo", "--name=first", "--net-alias=first-alias", "busybox", "top")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c5dd4d70
 
 	_, err = d.Cmd("run", "-d", "--net=foo", "--name=second", "busybox", "top")
6212ea66
 	c.Assert(err, checker.IsNil, check.Commentf(out))
c5dd4d70
 
ef794c36
 	_, err = d.Cmd("run", "-d", "--net=foo", "--net-alias=third-alias", "busybox", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// ping first container and its alias, also ping third and anonymous container by its alias
c5dd4d70
 	_, err = d.Cmd("exec", "second", "ping", "-c", "1", "first")
6212ea66
 	c.Assert(err, check.IsNil, check.Commentf(out))
c5dd4d70
 	_, err = d.Cmd("exec", "second", "ping", "-c", "1", "first-alias")
6212ea66
 	c.Assert(err, check.IsNil, check.Commentf(out))
ef794c36
 	_, err = d.Cmd("exec", "second", "ping", "-c", "1", "third-alias")
 	c.Assert(err, check.IsNil, check.Commentf(out))
c5dd4d70
 }
 
5f17e0f6
 func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "testnet")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	networkID := strings.TrimSpace(out)
 
 	out, err = d.Cmd("run", "-d", "--net", networkID, "busybox", "top")
 	c.Assert(err, checker.IsNil)
 	cID := strings.TrimSpace(out)
48de91a3
 	d.WaitRun(cID)
5f17e0f6
 
 	_, err = d.Cmd("rm", "-f", cID)
 	c.Assert(err, checker.IsNil)
 
ba0afd70
 	_, err = d.Cmd("network", "rm", "testnet")
5f17e0f6
 	c.Assert(err, checker.IsNil)
 
 	checkNetwork := func(*check.C) (interface{}, check.CommentInterface) {
 		out, err := d.Cmd("network", "ls")
 		c.Assert(err, checker.IsNil)
 		return out, nil
 	}
 
 	waitAndAssert(c, 3*time.Second, checkNetwork, checker.Not(checker.Contains), "testnet")
 }
 
abcb699a
 func (s *DockerSwarmSuite) TestOverlayAttachable(c *check.C) {
e42d1bb4
 	d := s.AddDaemon(c, true, true)
abcb699a
 
e42d1bb4
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "ovnet")
abcb699a
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// validate attachable
e42d1bb4
 	out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
abcb699a
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 
 	// validate containers can attache to this overlay network
e42d1bb4
 	out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "busybox", "top")
abcb699a
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// redo validation, there was a bug that the value of attachable changes after
 	// containers attach to the network
e42d1bb4
 	out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet")
abcb699a
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 }
 
3cedca5d
 func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create an attachable swarm network
 	nwName := "attovl"
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", nwName)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// Connect a container to the network
 	out, err = d.Cmd("run", "-d", "--network", nwName, "--name", "c1", "busybox", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// Leave the swarm
 	err = d.Leave(true)
 	c.Assert(err, checker.IsNil)
 
 	// Check the container is disconnected
 	out, err = d.Cmd("inspect", "c1", "--format", "{{.NetworkSettings.Networks."+nwName+"}}")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "<no value>")
 
 	// Check the network is gone
 	out, err = d.Cmd("network", "ls", "--format", "{{.Name}}")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), nwName)
 }
 
91820b69
 func (s *DockerSwarmSuite) TestOverlayAttachableReleaseResourcesOnFailure(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create attachable network
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "--subnet", "10.10.9.0/24", "ovnet")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// Attach a container with specific IP
 	out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "--ip", "10.10.9.33", "busybox", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
2b62eb43
 	// Attempt to attach another container with same IP, must fail
91820b69
 	_, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c2", "--ip", "10.10.9.33", "busybox", "top")
 	c.Assert(err, checker.NotNil)
 
 	// Remove first container
 	out, err = d.Cmd("rm", "-f", "c1")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// Verify the network can be removed, no phantom network attachment task left over
 	out, err = d.Cmd("network", "rm", "ovnet")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 }
 
d59d19c3
 func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *check.C) {
de487116
 	d := s.AddDaemon(c, true, true)
 
d59d19c3
 	// Ingress network can be removed
 	out, _, err := testutil.RunCommandPipelineWithOutput(
 		exec.Command("echo", "Y"),
 		exec.Command("docker", "-H", d.Sock(), "network", "rm", "ingress"),
 	)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// And recreated
 	out, err = d.Cmd("network", "create", "-d", "overlay", "--ingress", "new-ingress")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// But only one is allowed
 	out, err = d.Cmd("network", "create", "-d", "overlay", "--ingress", "another-ingress")
 	c.Assert(err, checker.NotNil)
 	c.Assert(strings.TrimSpace(out), checker.Contains, "is already present")
 
 	// It cannot be removed if it is being used
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", "srv1", "-p", "9000:8000", "busybox", "top")
d59d19c3
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	out, _, err = testutil.RunCommandPipelineWithOutput(
 		exec.Command("echo", "Y"),
 		exec.Command("docker", "-H", d.Sock(), "network", "rm", "new-ingress"),
 	)
 	c.Assert(err, checker.NotNil)
 	c.Assert(strings.TrimSpace(out), checker.Contains, "ingress network cannot be removed because service")
 
 	// But it can be removed once no more services depend on it
 	out, err = d.Cmd("service", "update", "--publish-rm", "9000:8000", "srv1")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	out, _, err = testutil.RunCommandPipelineWithOutput(
 		exec.Command("echo", "Y"),
 		exec.Command("docker", "-H", d.Sock(), "network", "rm", "new-ingress"),
 	)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// A service which needs the ingress network cannot be created if no ingress is present
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", "srv2", "-p", "500:500", "busybox", "top")
de487116
 	c.Assert(err, checker.NotNil)
d59d19c3
 	c.Assert(strings.TrimSpace(out), checker.Contains, "no ingress network is present")
 
 	// An existing service cannot be updated to use the ingress nw if the nw is not present
 	out, err = d.Cmd("service", "update", "--publish-add", "9000:8000", "srv1")
 	c.Assert(err, checker.NotNil)
 	c.Assert(strings.TrimSpace(out), checker.Contains, "no ingress network is present")
 
 	// But services which do not need routing mesh can be created regardless
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", "srv3", "--endpoint-mode", "dnsrr", "busybox", "top")
d59d19c3
 	c.Assert(err, checker.IsNil, check.Commentf(out))
de487116
 }
f676fc93
 
4777fb6c
 func (s *DockerSwarmSuite) TestSwarmCreateServiceWithNoIngressNetwork(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Remove ingress network
 	out, _, err := testutil.RunCommandPipelineWithOutput(
 		exec.Command("echo", "Y"),
 		exec.Command("docker", "-H", d.Sock(), "network", "rm", "ingress"),
 	)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// Create a overlay network and launch a service on it
 	// Make sure nothing panics because ingress network is missing
 	out, err = d.Cmd("network", "create", "-d", "overlay", "another-network")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", "srv4", "--network", "another-network", "busybox", "top")
4777fb6c
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 }
 
f676fc93
 // Test case for #24108, also the case from:
 // https://github.com/docker/docker/pull/24620#issuecomment-233715656
 func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "redis-cluster-md5"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top")
f676fc93
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	filter := "name=redis-cluster"
 
f8229aea
 	checkNumTasks := func(*check.C) (interface{}, check.CommentInterface) {
 		out, err := d.Cmd("service", "ps", "--filter", filter, name)
 		c.Assert(err, checker.IsNil)
 		return len(strings.Split(out, "\n")) - 2, nil // includes header and nl in last line
 	}
 
 	// wait until all tasks have been created
 	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 3)
 
f676fc93
 	out, err = d.Cmd("service", "ps", "--filter", filter, name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name+".1")
 	c.Assert(out, checker.Contains, name+".2")
 	c.Assert(out, checker.Contains, name+".3")
 
 	out, err = d.Cmd("service", "ps", "--filter", "name="+name+".1", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name+".1")
 	c.Assert(out, checker.Not(checker.Contains), name+".2")
 	c.Assert(out, checker.Not(checker.Contains), name+".3")
 
 	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), name+".1")
 	c.Assert(out, checker.Not(checker.Contains), name+".2")
 	c.Assert(out, checker.Not(checker.Contains), name+".3")
 
 	name = "redis-cluster-sha1"
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--mode=global", "busybox", "top")
f676fc93
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
f8229aea
 	waitAndAssert(c, defaultReconciliationTimeout, checkNumTasks, checker.Equals, 1)
 
f676fc93
 	filter = "name=redis-cluster"
 	out, err = d.Cmd("service", "ps", "--filter", filter, name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name)
 
 	out, err = d.Cmd("service", "ps", "--filter", "name="+name, name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name)
 
 	out, err = d.Cmd("service", "ps", "--filter", "name=none", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), name)
 }
5280ba83
 
 func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a bare container
 	out, err := d.Cmd("run", "-d", "--name=bare-container", "busybox", "top")
 	c.Assert(err, checker.IsNil)
 	bareID := strings.TrimSpace(out)[:12]
 	// Create a service
 	name := "busybox-top"
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", name, "busybox", "top")
5280ba83
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	// make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckServiceRunningTasks(name), checker.Equals, 1)
5280ba83
 
 	// Filter non-tasks
 	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=false")
 	c.Assert(err, checker.IsNil)
 	psOut := strings.TrimSpace(out)
 	c.Assert(psOut, checker.Equals, bareID, check.Commentf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out))
 
 	// Filter tasks
 	out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=true")
 	c.Assert(err, checker.IsNil)
 	lines := strings.Split(strings.Trim(out, "\n "), "\n")
 	c.Assert(lines, checker.HasLen, 1)
 	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))
 }
af185a38
 
 const globalNetworkPlugin = "global-network-plugin"
 const globalIPAMPlugin = "global-ipam-plugin"
 
 func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
 
 	mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType)
 	})
 
 	// Network driver implementation
 	mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, `{"Scope":"global"}`)
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, "null")
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, "null")
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, "null")
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, "null")
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`)
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 
 		veth := &netlink.Veth{
 			LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"}
 		if err := netlink.LinkAdd(veth); err != nil {
 			fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`)
 		} else {
 			fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`)
 		}
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, "null")
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		if link, err := netlink.LinkByName("cnt0"); err == nil {
 			netlink.LinkDel(link)
 		}
 		fmt.Fprintf(w, "null")
 	})
 
 	// IPAM Driver implementation
 	var (
 		poolRequest       remoteipam.RequestPoolRequest
 		poolReleaseReq    remoteipam.ReleasePoolRequest
 		addressRequest    remoteipam.RequestAddressRequest
 		addressReleaseReq remoteipam.ReleaseAddressRequest
 		lAS               = "localAS"
 		gAS               = "globalAS"
 		pool              = "172.28.0.0/16"
 		poolID            = lAS + "/" + pool
 		gw                = "172.28.255.254/16"
 	)
 
 	mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`)
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&poolRequest)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS {
 			fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`)
 		} else if poolRequest.Pool != "" && poolRequest.Pool != pool {
 			fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`)
 		} else {
 			fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`)
 		}
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&addressRequest)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		// make sure libnetwork is now querying on the expected pool id
 		if addressRequest.PoolID != poolID {
 			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
 		} else if addressRequest.Address != "" {
 			fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`)
 		} else {
 			fmt.Fprintf(w, `{"Address":"`+gw+`"}`)
 		}
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&addressReleaseReq)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		// make sure libnetwork is now asking to release the expected address from the expected poolid
 		if addressRequest.PoolID != poolID {
 			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
 		} else if addressReleaseReq.Address != gw {
 			fmt.Fprintf(w, `{"Error":"unknown address"}`)
 		} else {
 			fmt.Fprintf(w, "null")
 		}
 	})
 
 	mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
 		err := json.NewDecoder(r.Body).Decode(&poolReleaseReq)
 		if err != nil {
 			http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
 			return
 		}
 		w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
 		// make sure libnetwork is now asking to release the expected poolid
 		if addressRequest.PoolID != poolID {
 			fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
 		} else {
 			fmt.Fprintf(w, "null")
 		}
 	})
 
 	err := os.MkdirAll("/etc/docker/plugins", 0755)
 	c.Assert(err, checker.IsNil)
 
 	fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
 	err = ioutil.WriteFile(fileName, []byte(url), 0644)
 	c.Assert(err, checker.IsNil)
 
 	ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
 	err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
 	c.Assert(err, checker.IsNil)
 }
 
 func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) {
0510afc8
 	mux := http.NewServeMux()
 	s.server = httptest.NewServer(mux)
 	c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server"))
 	setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin)
 	defer func() {
 		s.server.Close()
 		err := os.RemoveAll("/etc/docker/plugins")
 		c.Assert(err, checker.IsNil)
 	}()
 
af185a38
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo")
2cc2d059
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "not supported in swarm mode")
af185a38
 }
ee3105c6
 
 // Test case for #24712
 func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
48de91a3
 	path := filepath.Join(d.Folder, "env.txt")
ee3105c6
 	err := ioutil.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644)
 	c.Assert(err, checker.IsNil)
 
 	name := "worker"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--env-file", path, "--env", "VAR1=B", "--env", "VAR1=C", "--env", "VAR2=", "--env", "VAR2", "--name", name, "busybox", "top")
ee3105c6
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	// The complete env is [VAR1=A VAR2=A VAR1=B VAR1=C VAR2= VAR2] and duplicates will be removed => [VAR1=C VAR2]
 	out, err = d.Cmd("inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.Env }}", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, "[VAR1=C VAR2]")
 }
599be5a5
 
 func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
 
 	ttyCheck := "if [ -t 0 ]; then echo TTY > /status && top; else echo none > /status && top; fi"
 
 	// Without --tty
 	expectedOutput := "none"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", ttyCheck)
599be5a5
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
599be5a5
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
 	c.Assert(err, checker.IsNil)
 	id := strings.TrimSpace(out)
 
 	out, err = d.Cmd("exec", id, "cat", "/status")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
 
 	// Remove service
 	out, err = d.Cmd("service", "rm", name)
 	c.Assert(err, checker.IsNil)
 	// Make sure container has been destroyed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
599be5a5
 
 	// With --tty
 	expectedOutput = "TTY"
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck)
599be5a5
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
599be5a5
 
 	// We need to get the container id.
 	out, err = d.Cmd("ps", "-a", "-q", "--no-trunc")
 	c.Assert(err, checker.IsNil)
 	id = strings.TrimSpace(out)
 
 	out, err = d.Cmd("exec", id, "cat", "/status")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
 }
 
 func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
d012569b
 	_, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "busybox", "top")
599be5a5
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
599be5a5
 
 	out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "false")
 
 	_, err = d.Cmd("service", "update", "--tty", name)
 	c.Assert(err, checker.IsNil)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 }
9e8adbec
 
0f2669a6
 func (s *DockerSwarmSuite) TestSwarmServiceNetworkUpdate(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	result := icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "foo"))
 	result.Assert(c, icmd.Success)
 	fooNetwork := strings.TrimSpace(string(result.Combined()))
 
 	result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "bar"))
 	result.Assert(c, icmd.Success)
 	barNetwork := strings.TrimSpace(string(result.Combined()))
 
 	result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "baz"))
 	result.Assert(c, icmd.Success)
 	bazNetwork := strings.TrimSpace(string(result.Combined()))
 
 	// Create a service
 	name := "top"
d012569b
 	result = icmd.RunCmd(d.Command("service", "create", "--no-resolve-image", "--network", "foo", "--network", "bar", "--name", name, "busybox", "top"))
0f2669a6
 	result.Assert(c, icmd.Success)
 
 	// Make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
 		map[string]int{fooNetwork: 1, barNetwork: 1})
 
 	// Remove a network
 	result = icmd.RunCmd(d.Command("service", "update", "--network-rm", "foo", name))
 	result.Assert(c, icmd.Success)
 
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
 		map[string]int{barNetwork: 1})
 
 	// Add a network
 	result = icmd.RunCmd(d.Command("service", "update", "--network-add", "baz", name))
 	result.Assert(c, icmd.Success)
 
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskNetworks, checker.DeepEquals,
 		map[string]int{barNetwork: 1, bazNetwork: 1})
 }
 
9e8adbec
 func (s *DockerSwarmSuite) TestDNSConfig(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
d012569b
 	_, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-option=timeout:3", "busybox", "top")
9e8adbec
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
9e8adbec
 
 	// We need to get the container id.
 	out, err := d.Cmd("ps", "-a", "-q", "--no-trunc")
 	c.Assert(err, checker.IsNil)
 	id := strings.TrimSpace(out)
 
 	// Compare against expected output.
 	expectedOutput1 := "nameserver 1.2.3.4"
 	expectedOutput2 := "search example.com"
 	expectedOutput3 := "options timeout:3"
 	out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput1, check.Commentf("Expected '%s', but got %q", expectedOutput1, out))
 	c.Assert(out, checker.Contains, expectedOutput2, check.Commentf("Expected '%s', but got %q", expectedOutput2, out))
 	c.Assert(out, checker.Contains, expectedOutput3, check.Commentf("Expected '%s', but got %q", expectedOutput3, out))
 }
a39c0cf0
 
 func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
d012569b
 	_, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "busybox", "top")
a39c0cf0
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
a39c0cf0
 
9b6b3c20
 	_, err = d.Cmd("service", "update", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-option-add=timeout:3", name)
a39c0cf0
 	c.Assert(err, checker.IsNil)
 
 	out, err := d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Equals, "{[1.2.3.4] [example.com] [timeout:3]}")
 }
8b1f72ad
 
48de91a3
 func getNodeStatus(c *check.C, d *daemon.Swarm) swarm.LocalNodeState {
 	info, err := d.SwarmInfo()
be260397
 	c.Assert(err, checker.IsNil)
 	return info.LocalNodeState
 }
 
48de91a3
 func checkSwarmLockedToUnlocked(c *check.C, d *daemon.Swarm, unlockKey string) {
c502fb49
 	d.Restart(c)
6a5b8a64
 	status := getNodeStatus(c, d)
 	if status == swarm.LocalNodeStateLocked {
 		// it must not have updated to be unlocked in time - unlock, wait 3 seconds, and try again
48de91a3
 		cmd := d.Command("swarm", "unlock")
6a5b8a64
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
6a5b8a64
 
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 
 		time.Sleep(3 * time.Second)
c502fb49
 		d.Restart(c)
6a5b8a64
 	}
 
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 }
 
48de91a3
 func checkSwarmUnlockedToLocked(c *check.C, d *daemon.Swarm) {
c502fb49
 	d.Restart(c)
6a5b8a64
 	status := getNodeStatus(c, d)
 	if status == swarm.LocalNodeStateActive {
 		// it must not have updated to be unlocked in time - wait 3 seconds, and try again
 		time.Sleep(3 * time.Second)
c502fb49
 		d.Restart(c)
6a5b8a64
 	}
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
 }
 
0270645c
 func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
a6a0880a
 	// unlocking a normal engine should return an error - it does not even ask for the key
0270645c
 	cmd := d.Command("swarm", "unlock")
ecbb0e62
 	result := icmd.RunCmd(cmd)
 	result.Assert(c, icmd.Expected{
 		ExitCode: 1,
 	})
 	c.Assert(result.Combined(), checker.Contains, "Error: This node is not part of a swarm")
 	c.Assert(result.Combined(), checker.Not(checker.Contains), "Please enter unlock key")
0270645c
 
ecbb0e62
 	_, err := d.Cmd("swarm", "init")
0270645c
 	c.Assert(err, checker.IsNil)
 
a6a0880a
 	// unlocking an unlocked swarm should return an error - it does not even ask for the key
0270645c
 	cmd = d.Command("swarm", "unlock")
ecbb0e62
 	result = icmd.RunCmd(cmd)
 	result.Assert(c, icmd.Expected{
 		ExitCode: 1,
 	})
 	c.Assert(result.Combined(), checker.Contains, "Error: swarm is not locked")
 	c.Assert(result.Combined(), checker.Not(checker.Contains), "Please enter unlock key")
0270645c
 }
 
8b1f72ad
 func (s *DockerSwarmSuite) TestSwarmInitLocked(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
c3c70c4b
 	outs, err := d.Cmd("swarm", "init", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 	var unlockKey string
 	for _, line := range strings.Split(outs, "\n") {
 		if strings.Contains(line, "SWMKEY") {
 			unlockKey = strings.TrimSpace(line)
 			break
 		}
 	}
 
 	c.Assert(unlockKey, checker.Not(checker.Equals), "")
8b1f72ad
 
c3c70c4b
 	outs, err = d.Cmd("swarm", "unlock-key", "-q")
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
8b1f72ad
 
be260397
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
8b1f72ad
 
6a5b8a64
 	// It starts off locked
c502fb49
 	d.Restart(c)
be260397
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
8b1f72ad
 
48de91a3
 	cmd := d.Command("swarm", "unlock")
8b1f72ad
 	cmd.Stdin = bytes.NewBufferString("wrong-secret-key")
ecbb0e62
 	icmd.RunCmd(cmd).Assert(c, icmd.Expected{
 		ExitCode: 1,
 		Err:      "invalid key",
 	})
8b1f72ad
 
be260397
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
 
48de91a3
 	cmd = d.Command("swarm", "unlock")
c3c70c4b
 	cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 	icmd.RunCmd(cmd).Assert(c, icmd.Success)
8b1f72ad
 
be260397
 	c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
c3c70c4b
 
 	outs, err = d.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
 
 	outs, err = d.Cmd("swarm", "update", "--autolock=false")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
6a5b8a64
 	checkSwarmLockedToUnlocked(c, d, unlockKey)
c3c70c4b
 
 	outs, err = d.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
8b1f72ad
 }
 
 func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
c3c70c4b
 	outs, err := d.Cmd("swarm", "init", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
8b1f72ad
 
6a5b8a64
 	// It starts off locked
c502fb49
 	d.Restart(c, "--swarm-default-advertise-addr=lo")
8b1f72ad
 
48de91a3
 	info, err := d.SwarmInfo()
8b1f72ad
 	c.Assert(err, checker.IsNil)
 	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateLocked)
 
c3c70c4b
 	outs, _ = d.Cmd("node", "ls")
 	c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
 
7bdd1a4f
 	// `docker swarm leave` a locked swarm without --force will return an error
 	outs, _ = d.Cmd("swarm", "leave")
 	c.Assert(outs, checker.Contains, "Swarm is encrypted and locked.")
 
 	// It is OK for user to leave a locked swarm with --force
c3c70c4b
 	outs, err = d.Cmd("swarm", "leave", "--force")
8b1f72ad
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
48de91a3
 	info, err = d.SwarmInfo()
8b1f72ad
 	c.Assert(err, checker.IsNil)
 	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateInactive)
 
 	outs, err = d.Cmd("swarm", "init")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
48de91a3
 	info, err = d.SwarmInfo()
8b1f72ad
 	c.Assert(err, checker.IsNil)
 	c.Assert(info.LocalNodeState, checker.Equals, swarm.LocalNodeStateActive)
 }
c3c70c4b
 
be260397
 func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *check.C) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	// they start off unlocked
c502fb49
 	d2.Restart(c)
be260397
 	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
 
 	// stop this one so it does not get autolock info
c502fb49
 	d2.Stop(c)
be260397
 
 	// enable autolock
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 	var unlockKey string
 	for _, line := range strings.Split(outs, "\n") {
 		if strings.Contains(line, "SWMKEY") {
 			unlockKey = strings.TrimSpace(line)
 			break
 		}
 	}
 
 	c.Assert(unlockKey, checker.Not(checker.Equals), "")
 
 	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
 
 	// The ones that got the cluster update should be set to locked
48de91a3
 	for _, d := range []*daemon.Swarm{d1, d3} {
6a5b8a64
 		checkSwarmUnlockedToLocked(c, d)
be260397
 
48de91a3
 		cmd := d.Command("swarm", "unlock")
be260397
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
be260397
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 	}
 
 	// d2 never got the cluster update, so it is still set to unlocked
c502fb49
 	d2.Start(c)
be260397
 	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
 
 	// d2 is now set to lock
6a5b8a64
 	checkSwarmUnlockedToLocked(c, d2)
be260397
 
 	// leave it locked, and set the cluster to no longer autolock
 	outs, err = d1.Cmd("swarm", "update", "--autolock=false")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	// the ones that got the update are now set to unlocked
48de91a3
 	for _, d := range []*daemon.Swarm{d1, d3} {
6a5b8a64
 		checkSwarmLockedToUnlocked(c, d, unlockKey)
be260397
 	}
 
 	// d2 still locked
 	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateLocked)
 
 	// unlock it
48de91a3
 	cmd := d2.Command("swarm", "unlock")
be260397
 	cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 	icmd.RunCmd(cmd).Assert(c, icmd.Success)
be260397
 	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
 
 	// once it's caught up, d2 is set to not be locked
6a5b8a64
 	checkSwarmLockedToUnlocked(c, d2, unlockKey)
be260397
 
6a5b8a64
 	// managers who join now are never set to locked in the first place
be260397
 	d4 := s.AddDaemon(c, true, true)
c502fb49
 	d4.Restart(c)
be260397
 	c.Assert(getNodeStatus(c, d4), checker.Equals, swarm.LocalNodeStateActive)
 }
 
 func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *check.C) {
 	d1 := s.AddDaemon(c, true, true)
 
 	// enable autolock
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 	var unlockKey string
 	for _, line := range strings.Split(outs, "\n") {
 		if strings.Contains(line, "SWMKEY") {
 			unlockKey = strings.TrimSpace(line)
 			break
 		}
 	}
 
 	c.Assert(unlockKey, checker.Not(checker.Equals), "")
 
 	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
 
 	// joined workers start off unlocked
 	d2 := s.AddDaemon(c, true, false)
c502fb49
 	d2.Restart(c)
be260397
 	c.Assert(getNodeStatus(c, d2), checker.Equals, swarm.LocalNodeStateActive)
 
 	// promote worker
 	outs, err = d1.Cmd("node", "promote", d2.Info.NodeID)
 	c.Assert(err, checker.IsNil)
 	c.Assert(outs, checker.Contains, "promoted to a manager in the swarm")
 
 	// join new manager node
 	d3 := s.AddDaemon(c, true, true)
 
 	// both new nodes are locked
48de91a3
 	for _, d := range []*daemon.Swarm{d2, d3} {
6a5b8a64
 		checkSwarmUnlockedToLocked(c, d)
be260397
 
48de91a3
 		cmd := d.Command("swarm", "unlock")
be260397
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
be260397
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 	}
 
 	// demote manager back to worker - workers are not locked
 	outs, err = d1.Cmd("node", "demote", d3.Info.NodeID)
 	c.Assert(err, checker.IsNil)
 	c.Assert(outs, checker.Contains, "demoted in the swarm")
 
e831a7cd
 	// Wait for it to actually be demoted, for the key and cert to be replaced.
 	// Then restart and assert that the node is not locked.  If we don't wait for the cert
 	// to be replaced, then the node still has the manager TLS key which is still locked
 	// (because we never want a manager TLS key to be on disk unencrypted if the cluster
 	// is set to autolock)
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d3.CheckControlAvailable, checker.False)
e831a7cd
 	waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
2b5ef9bf
 		certBytes, err := ioutil.ReadFile(filepath.Join(d3.Folder, "root", "swarm", "certificates", "swarm-node.crt"))
e831a7cd
 		if err != nil {
 			return "", check.Commentf("error: %v", err)
 		}
2b5ef9bf
 		certs, err := helpers.ParseCertificatesPEM(certBytes)
 		if err == nil && len(certs) > 0 && len(certs[0].Subject.OrganizationalUnit) > 0 {
 			return certs[0].Subject.OrganizationalUnit[0], nil
 		}
 		return "", check.Commentf("could not get organizational unit from certificate")
 	}, checker.Equals, "swarm-worker")
be260397
 
e831a7cd
 	// by now, it should *never* be locked on restart
c502fb49
 	d3.Restart(c)
e831a7cd
 	c.Assert(getNodeStatus(c, d3), checker.Equals, swarm.LocalNodeStateActive)
be260397
 }
 
c3c70c4b
 func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	outs, err := d.Cmd("swarm", "update", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 	var unlockKey string
 	for _, line := range strings.Split(outs, "\n") {
 		if strings.Contains(line, "SWMKEY") {
 			unlockKey = strings.TrimSpace(line)
 			break
 		}
 	}
 
 	c.Assert(unlockKey, checker.Not(checker.Equals), "")
 
 	outs, err = d.Cmd("swarm", "unlock-key", "-q")
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
 
 	// Rotate multiple times
 	for i := 0; i != 3; i++ {
 		outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate")
 		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 		// Strip \n
 		newUnlockKey := outs[:len(outs)-1]
 		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
81f3e699
 		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
c3c70c4b
 
c502fb49
 		d.Restart(c)
be260397
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
c3c70c4b
 
 		outs, _ = d.Cmd("node", "ls")
 		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
 
48de91a3
 		cmd := d.Command("swarm", "unlock")
c3c70c4b
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 		result := icmd.RunCmd(cmd)
c3c70c4b
 
ecbb0e62
 		if result.Error == nil {
c3c70c4b
 			// On occasion, the daemon may not have finished
 			// rotating the KEK before restarting. The test is
 			// intentionally written to explore this behavior.
 			// When this happens, unlocking with the old key will
 			// succeed. If we wait for the rotation to happen and
 			// restart again, the new key should be required this
 			// time.
 
 			time.Sleep(3 * time.Second)
 
c502fb49
 			d.Restart(c)
c3c70c4b
 
48de91a3
 			cmd = d.Command("swarm", "unlock")
c3c70c4b
 			cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 			result = icmd.RunCmd(cmd)
c3c70c4b
 		}
ecbb0e62
 		result.Assert(c, icmd.Expected{
 			ExitCode: 1,
 			Err:      "invalid key",
 		})
c3c70c4b
 
 		outs, _ = d.Cmd("node", "ls")
 		c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
 
48de91a3
 		cmd = d.Command("swarm", "unlock")
c3c70c4b
 		cmd.Stdin = bytes.NewBufferString(newUnlockKey)
ecbb0e62
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
c3c70c4b
 
be260397
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
c3c70c4b
 
 		outs, err = d.Cmd("node", "ls")
 		c.Assert(err, checker.IsNil)
 		c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
81f3e699
 
 		unlockKey = newUnlockKey
c3c70c4b
 	}
 }
ea9a23cc
 
be260397
 // This differs from `TestSwarmRotateUnlockKey` because that one rotates a single node, which is the leader.
 // This one keeps the leader up, and asserts that other manager nodes in the cluster also have their unlock
 // key rotated.
 func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *check.C) {
 	d1 := s.AddDaemon(c, true, true) // leader - don't restart this one, we don't want leader election delays
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	outs, err := d1.Cmd("swarm", "update", "--autolock")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
 	c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 	var unlockKey string
 	for _, line := range strings.Split(outs, "\n") {
 		if strings.Contains(line, "SWMKEY") {
 			unlockKey = strings.TrimSpace(line)
 			break
 		}
 	}
 
 	c.Assert(unlockKey, checker.Not(checker.Equals), "")
 
 	outs, err = d1.Cmd("swarm", "unlock-key", "-q")
 	c.Assert(outs, checker.Equals, unlockKey+"\n")
 
 	// Rotate multiple times
 	for i := 0; i != 3; i++ {
 		outs, err = d1.Cmd("swarm", "unlock-key", "-q", "--rotate")
 		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 		// Strip \n
 		newUnlockKey := outs[:len(outs)-1]
 		c.Assert(newUnlockKey, checker.Not(checker.Equals), "")
 		c.Assert(newUnlockKey, checker.Not(checker.Equals), unlockKey)
 
c502fb49
 		d2.Restart(c)
 		d3.Restart(c)
be260397
 
48de91a3
 		for _, d := range []*daemon.Swarm{d2, d3} {
be260397
 			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateLocked)
 
 			outs, _ := d.Cmd("node", "ls")
 			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
 
48de91a3
 			cmd := d.Command("swarm", "unlock")
be260397
 			cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 			result := icmd.RunCmd(cmd)
be260397
 
ecbb0e62
 			if result.Error == nil {
be260397
 				// On occasion, the daemon may not have finished
 				// rotating the KEK before restarting. The test is
 				// intentionally written to explore this behavior.
 				// When this happens, unlocking with the old key will
 				// succeed. If we wait for the rotation to happen and
 				// restart again, the new key should be required this
 				// time.
 
 				time.Sleep(3 * time.Second)
 
c502fb49
 				d.Restart(c)
be260397
 
48de91a3
 				cmd = d.Command("swarm", "unlock")
be260397
 				cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 				result = icmd.RunCmd(cmd)
be260397
 			}
ecbb0e62
 			result.Assert(c, icmd.Expected{
 				ExitCode: 1,
 				Err:      "invalid key",
 			})
be260397
 
 			outs, _ = d.Cmd("node", "ls")
 			c.Assert(outs, checker.Contains, "Swarm is encrypted and needs to be unlocked")
 
48de91a3
 			cmd = d.Command("swarm", "unlock")
be260397
 			cmd.Stdin = bytes.NewBufferString(newUnlockKey)
ecbb0e62
 			icmd.RunCmd(cmd).Assert(c, icmd.Success)
be260397
 
 			c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 
 			outs, err = d.Cmd("node", "ls")
 			c.Assert(err, checker.IsNil)
 			c.Assert(outs, checker.Not(checker.Contains), "Swarm is encrypted and needs to be unlocked")
 		}
 
 		unlockKey = newUnlockKey
 	}
 }
 
 func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	var unlockKey string
 	for i := 0; i < 2; i++ {
 		// set to lock
 		outs, err := d.Cmd("swarm", "update", "--autolock")
 		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 		c.Assert(outs, checker.Contains, "docker swarm unlock")
 
 		for _, line := range strings.Split(outs, "\n") {
 			if strings.Contains(line, "SWMKEY") {
 				unlockKey = strings.TrimSpace(line)
 				break
 			}
 		}
 
 		c.Assert(unlockKey, checker.Not(checker.Equals), "")
6a5b8a64
 		checkSwarmUnlockedToLocked(c, d)
be260397
 
48de91a3
 		cmd := d.Command("swarm", "unlock")
be260397
 		cmd.Stdin = bytes.NewBufferString(unlockKey)
ecbb0e62
 		icmd.RunCmd(cmd).Assert(c, icmd.Success)
be260397
 
 		c.Assert(getNodeStatus(c, d), checker.Equals, swarm.LocalNodeStateActive)
 
 		outs, err = d.Cmd("swarm", "update", "--autolock=false")
 		c.Assert(err, checker.IsNil, check.Commentf("out: %v", outs))
 
6a5b8a64
 		checkSwarmLockedToUnlocked(c, d, unlockKey)
be260397
 	}
 }
 
ea9a23cc
 func (s *DockerSwarmSuite) TestExtraHosts(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// Create a service
 	name := "top"
d012569b
 	_, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top")
ea9a23cc
 	c.Assert(err, checker.IsNil)
 
 	// Make sure task has been deployed.
48de91a3
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
ea9a23cc
 
 	// We need to get the container id.
 	out, err := d.Cmd("ps", "-a", "-q", "--no-trunc")
 	c.Assert(err, checker.IsNil)
 	id := strings.TrimSpace(out)
 
 	// Compare against expected output.
 	expectedOutput := "1.2.3.4\texample.com"
 	out, err = d.Cmd("exec", id, "cat", "/etc/hosts")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput, check.Commentf("Expected '%s', but got %q", expectedOutput, out))
 }
828bd441
 
 func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *check.C) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, false)
 	d3 := s.AddDaemon(c, true, false)
 
 	// Manager Addresses will always show Node 1's address
48de91a3
 	expectedOutput := fmt.Sprintf("Manager Addresses:\n  127.0.0.1:%d\n", d1.Port)
828bd441
 
 	out, err := d1.Cmd("info")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput)
 
 	out, err = d2.Cmd("info")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput)
 
 	out, err = d3.Cmd("info")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, expectedOutput)
 }
b6857e91
 
 func (s *DockerSwarmSuite) TestSwarmServiceInspectPretty(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "top"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", name, "--limit-cpu=0.5", "busybox", "top")
b6857e91
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	expectedOutput := `
 Resources:
  Limits:
   CPU:		0.5`
 	out, err = d.Cmd("service", "inspect", "--pretty", name)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, expectedOutput, check.Commentf(out))
 }
4d958e99
 
 func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("network", "create", "-d", "overlay", "--ipam-opt", "foo=bar", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "map[foo:bar]")
 
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--network=foo", "--name", "top", "busybox", "top")
4d958e99
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "map[foo:bar]")
 }
5aa5a1cb
 
62cd3b39
 func (s *DockerTrustedSwarmSuite) TestTrustedServiceCreate(c *check.C) {
 	d := s.swarmSuite.AddDaemon(c, true, true)
 
 	// Attempt creating a service from an image that is known to notary.
 	repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
 
 	name := "trusted"
d012569b
 	cli.Docker(cli.Args("-D", "service", "create", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
ecbb0e62
 		Err: "resolved image tag to",
 	})
62cd3b39
 
ecbb0e62
 	out, err := d.Cmd("service", "inspect", "--pretty", name)
62cd3b39
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, repoName+"@", check.Commentf(out))
 
 	// Try trusted service create on an untrusted tag.
 
 	repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
 	// tag the image and upload it to the private registry
b0ba39d4
 	cli.DockerCmd(c, "tag", "busybox", repoName)
 	cli.DockerCmd(c, "push", repoName)
 	cli.DockerCmd(c, "rmi", repoName)
62cd3b39
 
 	name = "untrusted"
d012569b
 	cli.Docker(cli.Args("service", "create", "--no-resolve-image", "--name", name, repoName, "top"), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
ecbb0e62
 		ExitCode: 1,
 		Err:      "Error: remote trust data does not exist",
 	})
62cd3b39
 
 	out, err = d.Cmd("service", "inspect", "--pretty", name)
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 }
 
 func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) {
 	d := s.swarmSuite.AddDaemon(c, true, true)
 
 	// Attempt creating a service from an image that is known to notary.
 	repoName := s.trustSuite.setupTrustedImage(c, "trusted-pull")
 
 	name := "myservice"
 
 	// Create a service without content trust
d012569b
 	cli.Docker(cli.Args("service", "create", "--no-resolve-image", "--name", name, repoName, "top"), cli.Daemon(d.Daemon)).Assert(c, icmd.Success)
62cd3b39
 
b0ba39d4
 	result := cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon))
 	c.Assert(result.Error, checker.IsNil, check.Commentf(result.Combined()))
62cd3b39
 	// Daemon won't insert the digest because this is disabled by
 	// DOCKER_SERVICE_PREFER_OFFLINE_IMAGE.
b0ba39d4
 	c.Assert(result.Combined(), check.Not(checker.Contains), repoName+"@", check.Commentf(result.Combined()))
62cd3b39
 
d012569b
 	cli.Docker(cli.Args("-D", "service", "update", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
ecbb0e62
 		Err: "resolved image tag to",
 	})
62cd3b39
 
b0ba39d4
 	cli.Docker(cli.Args("service", "inspect", "--pretty", name), cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
 		Out: repoName + "@",
 	})
62cd3b39
 
 	// Try trusted service update on an untrusted tag.
 
 	repoName = fmt.Sprintf("%v/untrustedservicecreate/createtest:latest", privateRegistryURL)
 	// tag the image and upload it to the private registry
b0ba39d4
 	cli.DockerCmd(c, "tag", "busybox", repoName)
 	cli.DockerCmd(c, "push", repoName)
 	cli.DockerCmd(c, "rmi", repoName)
62cd3b39
 
d012569b
 	cli.Docker(cli.Args("service", "update", "--no-resolve-image", "--image", repoName, name), trustedCmd, cli.Daemon(d.Daemon)).Assert(c, icmd.Expected{
ecbb0e62
 		ExitCode: 1,
 		Err:      "Error: remote trust data does not exist",
 	})
62cd3b39
 }
edfbc3b8
 
 // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID.
 // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1".
 func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 	out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 	ingressID := strings.TrimSpace(out)
 	c.Assert(ingressID, checker.Not(checker.Equals), "")
 
 	// create a network of which name is the prefix of the ID of an overlay network
 	// (ingressID in this case)
 	newNetName := ingressID[0:2]
 	out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName)
 	// In #27866, it was failing because of "network with name %s already exists"
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 	out, err = d.Cmd("network", "rm", newNetName)
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 }
 
 // Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303
 // This test creates two networks with the same name sequentially, with various drivers.
 // Since the operations in this test are done sequentially, the 2nd call should fail with
 // "network with name FOO already exists".
 // Note that it is to ok have multiple networks with the same name if the operations are done
 // in parallel. (#18864)
 func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 	drivers := []string{"bridge", "overlay"}
 	for i, driver1 := range drivers {
 		nwName := fmt.Sprintf("network-test-%d", i)
 		for _, driver2 := range drivers {
 			c.Logf("Creating a network named %q with %q, then %q",
 				nwName, driver1, driver2)
 			out, err := d.Cmd("network", "create", "--driver", driver1, nwName)
 			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 			out, err = d.Cmd("network", "create", "--driver", driver2, nwName)
 			c.Assert(out, checker.Contains,
 				fmt.Sprintf("network with name %s already exists", nwName))
 			c.Assert(err, checker.NotNil)
 			c.Logf("As expected, the attempt to network %q with %q failed: %s",
 				nwName, driver2, out)
 			out, err = d.Cmd("network", "rm", nwName)
 			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 		}
 	}
 }
70942352
 
 func (s *DockerSwarmSuite) TestSwarmServicePsMultipleServiceIDs(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name1 := "top1"
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", name1, "--replicas=3", "busybox", "top")
70942352
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	id1 := strings.TrimSpace(out)
 
 	name2 := "top2"
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--name", name2, "--replicas=3", "busybox", "top")
70942352
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 	id2 := strings.TrimSpace(out)
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 6)
 
 	out, err = d.Cmd("service", "ps", name1)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+".1")
 	c.Assert(out, checker.Contains, name1+".2")
 	c.Assert(out, checker.Contains, name1+".3")
 	c.Assert(out, checker.Not(checker.Contains), name2+".1")
 	c.Assert(out, checker.Not(checker.Contains), name2+".2")
 	c.Assert(out, checker.Not(checker.Contains), name2+".3")
 
 	out, err = d.Cmd("service", "ps", name1, name2)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+".1")
 	c.Assert(out, checker.Contains, name1+".2")
 	c.Assert(out, checker.Contains, name1+".3")
 	c.Assert(out, checker.Contains, name2+".1")
 	c.Assert(out, checker.Contains, name2+".2")
 	c.Assert(out, checker.Contains, name2+".3")
 
 	// Name Prefix
 	out, err = d.Cmd("service", "ps", "to")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+".1")
 	c.Assert(out, checker.Contains, name1+".2")
 	c.Assert(out, checker.Contains, name1+".3")
 	c.Assert(out, checker.Contains, name2+".1")
 	c.Assert(out, checker.Contains, name2+".2")
 	c.Assert(out, checker.Contains, name2+".3")
 
 	// Name Prefix (no hit)
 	out, err = d.Cmd("service", "ps", "noname")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "no such services: noname")
 
 	out, err = d.Cmd("service", "ps", id1)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+".1")
 	c.Assert(out, checker.Contains, name1+".2")
 	c.Assert(out, checker.Contains, name1+".3")
 	c.Assert(out, checker.Not(checker.Contains), name2+".1")
 	c.Assert(out, checker.Not(checker.Contains), name2+".2")
 	c.Assert(out, checker.Not(checker.Contains), name2+".3")
 
 	out, err = d.Cmd("service", "ps", id1, id2)
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, name1+".1")
 	c.Assert(out, checker.Contains, name1+".2")
 	c.Assert(out, checker.Contains, name1+".3")
 	c.Assert(out, checker.Contains, name2+".1")
 	c.Assert(out, checker.Contains, name2+".2")
 	c.Assert(out, checker.Contains, name2+".3")
 }
cb59bd0c
 
 func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--publish", "5005:80", "--publish", "5006:80", "--publish", "80", "--publish", "80", "busybox", "top")
cb59bd0c
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	id := strings.TrimSpace(out)
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
 
 	// Total len = 4, with 2 dynamic ports and 2 non-dynamic ports
 	// Dynamic ports are likely to be 30000 and 30001 but doesn't matter
 	out, err = d.Cmd("service", "inspect", "--format", "{{.Endpoint.Ports}} len={{len .Endpoint.Ports}}", id)
 	c.Assert(err, check.IsNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "len=4")
24cd5444
 	c.Assert(out, checker.Contains, "{ tcp 80 5005 ingress}")
 	c.Assert(out, checker.Contains, "{ tcp 80 5006 ingress}")
cb59bd0c
 }
a8e7e37a
 
 func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Not(checker.Contains), "Drain")
 
 	out, err = d.Cmd("swarm", "join-token", "-q", "manager")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	token := strings.TrimSpace(out)
 
 	d1 := s.AddDaemon(c, false, false)
 
 	out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.ListenAddr)
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	out, err = d.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, "Drain")
 
 	out, err = d1.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, "Drain")
 }
0f30c644
 
 func (s *DockerSwarmSuite) TestSwarmInitWithDrain(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
 	out, err := d.Cmd("swarm", "init", "--availability", "drain")
 	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
 
 	out, err = d.Cmd("node", "ls")
 	c.Assert(err, checker.IsNil)
 	c.Assert(out, checker.Contains, "Drain")
 }
499a0dd4
 
 func (s *DockerSwarmSuite) TestSwarmReadonlyRootfs(c *check.C) {
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	d := s.AddDaemon(c, true, true)
 
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "top", "--read-only", "busybox", "top")
499a0dd4
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.ReadOnly }}", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 
 	containers := d.ActiveContainers()
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.HostConfig.ReadonlyRootfs}}", containers[0])
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "true")
 }
abf31ee0
 
 func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	name := "foo"
 	networkCreateRequest := types.NetworkCreateRequest{
 		Name: name,
 		NetworkCreate: types.NetworkCreate{
 			CheckDuplicate: false,
 			Driver:         "bridge",
 		},
 	}
 
 	var n1 types.NetworkCreateResponse
 	status, body, err := d.SockRequest("POST", "/networks/create", networkCreateRequest)
 	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
 	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
 	c.Assert(json.Unmarshal(body, &n1), checker.IsNil)
 
 	// Full ID always works
 	out, err := d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
 
 	// Name works if it is unique
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
 
 	var n2 types.NetworkCreateResponse
 	status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
 	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
 	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
 	c.Assert(json.Unmarshal(body, &n2), checker.IsNil)
 
 	// Full ID always works
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n2.ID)
 
 	// Name with duplicates
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "network foo is ambiguous (2 matches found based on name)")
 
 	out, err = d.Cmd("network", "rm", n2.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
39bcaee4
 	// Duplicates with name but with different driver
abf31ee0
 	networkCreateRequest.NetworkCreate.Driver = "overlay"
 
 	status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
 	c.Assert(err, checker.IsNil, check.Commentf(string(body)))
 	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
 	c.Assert(json.Unmarshal(body, &n2), checker.IsNil)
 
 	// Full ID always works
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n1.ID)
 
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, n2.ID)
 
 	// Name with duplicates
 	out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name)
 	c.Assert(err, checker.NotNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "network foo is ambiguous (2 matches found based on name)")
 }
c2d49ec2
 
 func (s *DockerSwarmSuite) TestSwarmStopSignal(c *check.C) {
 	testRequires(c, DaemonIsLinux, UserNamespaceROMount)
 
 	d := s.AddDaemon(c, true, true)
 
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "top", "--stop-signal=SIGHUP", "busybox", "top")
c2d49ec2
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGHUP")
 
 	containers := d.ActiveContainers()
 	out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.StopSignal}}", containers[0])
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGHUP")
 
 	out, err = d.Cmd("service", "update", "--stop-signal=SIGUSR1", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Equals, "SIGUSR1")
 }
43a1bd56
 
 func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "top1", "busybox", "top")
43a1bd56
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
d012569b
 	out, err = d.Cmd("service", "create", "--no-resolve-image", "--name", "top2", "--mode=global", "busybox", "top")
43a1bd56
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	// make sure task has been deployed.
 	waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 2)
 
 	out, err = d.Cmd("service", "ls")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "top1")
 	c.Assert(out, checker.Contains, "top2")
 	c.Assert(out, checker.Not(checker.Contains), "localnet")
 
 	out, err = d.Cmd("service", "ls", "--filter", "mode=global")
 	c.Assert(out, checker.Not(checker.Contains), "top1")
 	c.Assert(out, checker.Contains, "top2")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	out, err = d.Cmd("service", "ls", "--filter", "mode=replicated")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	c.Assert(out, checker.Contains, "top1")
 	c.Assert(out, checker.Not(checker.Contains), "top2")
 }
8dc8cd47
 
 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedDataPathAddr(c *check.C) {
 	d := s.AddDaemon(c, false, false)
 
 	out, err := d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "data path address must be a non-zero IP")
 
 	out, err = d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0:2000")
 	c.Assert(err, checker.NotNil)
 	c.Assert(out, checker.Contains, "data path address must be a non-zero IP")
 }
e2ec0067
 
 func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	out, err := d.Cmd("swarm", "join-token", "-q", "worker")
 	c.Assert(err, checker.IsNil)
 	c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 	token := strings.TrimSpace(out)
 
 	// Verify that back to back join/leave does not cause panics
 	d1 := s.AddDaemon(c, false, false)
 	for i := 0; i < 10; i++ {
 		out, err = d1.Cmd("swarm", "join", "--token", token, d.ListenAddr)
 		c.Assert(err, checker.IsNil)
 		c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
 
 		_, err = d1.Cmd("swarm", "leave")
 		c.Assert(err, checker.IsNil)
 	}
 }
59d45c38
 
59b2d047
 const defaultRetryCount = 10
 
 func waitForEvent(c *check.C, d *daemon.Swarm, since string, filter string, event string, retry int) string {
 	if retry < 1 {
 		c.Fatalf("retry count %d is invalid. It should be no less than 1", retry)
 		return ""
 	}
 	var out string
 	for i := 0; i < retry; i++ {
 		until := daemonUnixTime(c)
 		var err error
 		if len(filter) > 0 {
 			out, err = d.Cmd("events", "--since", since, "--until", until, filter)
 		} else {
 			out, err = d.Cmd("events", "--since", since, "--until", until)
 		}
 		c.Assert(err, checker.IsNil, check.Commentf(out))
 		if strings.Contains(out, event) {
 			return strings.TrimSpace(out)
 		}
 		// no need to sleep after last retry
 		if i < retry-1 {
 			time.Sleep(200 * time.Millisecond)
 		}
 	}
 	c.Fatalf("docker events output '%s' doesn't contain event '%s'", out, event)
 	return ""
 }
 
59d45c38
 func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *check.C) {
 	d1 := s.AddDaemon(c, true, true)
 	d2 := s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, false)
 
 	// create a network
 	out, err := d1.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	networkID := strings.TrimSpace(out)
 	c.Assert(networkID, checker.Not(checker.Equals), "")
 
59b2d047
 	// d1, d2 are managers that can get swarm events
 	waitForEvent(c, d1, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount)
 	waitForEvent(c, d2, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount)
59d45c38
 
 	// d3 is a worker, not able to get cluster events
59b2d047
 	out = waitForEvent(c, d3, "0", "-f scope=swarm", "", 1)
 	c.Assert(out, checker.Not(checker.Contains), "network create ")
59d45c38
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsScope(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
59d45c38
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	serviceID := strings.Split(out, "\n")[0]
 
 	// scope swarm filters cluster events
59b2d047
 	out = waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount)
 	c.Assert(out, checker.Not(checker.Contains), "container create ")
59d45c38
 
59b2d047
 	// all events are returned if scope is not specified
 	waitForEvent(c, d, "0", "", "service create "+serviceID, 1)
 	waitForEvent(c, d, "0", "", "container create ", defaultRetryCount)
59d45c38
 
59b2d047
 	// scope local only shows non-cluster events
 	out = waitForEvent(c, d, "0", "-f scope=local", "container create ", 1)
 	c.Assert(out, checker.Not(checker.Contains), "service create ")
59d45c38
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsType(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
59d45c38
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	serviceID := strings.Split(out, "\n")[0]
 
 	// create a network
 	out, err = d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	networkID := strings.TrimSpace(out)
 	c.Assert(networkID, checker.Not(checker.Equals), "")
 
 	// filter by service
59b2d047
 	out = waitForEvent(c, d, "0", "-f type=service", "service create "+serviceID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Not(checker.Contains), "network create")
 
 	// filter by network
59b2d047
 	out = waitForEvent(c, d, "0", "-f type=network", "network create "+networkID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Not(checker.Contains), "service create")
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a service
d012569b
 	out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top")
59d45c38
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	serviceID := strings.Split(out, "\n")[0]
 
 	// validate service create event
59b2d047
 	waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount)
59d45c38
 
59b2d047
 	t1 := daemonUnixTime(c)
59d45c38
 	out, err = d.Cmd("service", "update", "--force", "--detach=false", "test")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
59b2d047
 	// wait for service update start
 	out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Contains, "updatestate.new=updating")
59b2d047
 
 	// allow service update complete. This is a service with 1 instance
 	time.Sleep(400 * time.Millisecond)
 	out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Contains, "updatestate.new=completed, updatestate.old=updating")
 
 	// scale service
59b2d047
 	t2 := daemonUnixTime(c)
59d45c38
 	out, err = d.Cmd("service", "scale", "test=3")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
59b2d047
 	out = waitForEvent(c, d, t2, "-f scope=swarm", "service update "+serviceID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Contains, "replicas.new=3, replicas.old=1")
 
 	// remove service
59b2d047
 	t3 := daemonUnixTime(c)
59d45c38
 	out, err = d.Cmd("service", "rm", "test")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
59b2d047
 	waitForEvent(c, d, t3, "-f scope=swarm", "service remove "+serviceID, defaultRetryCount)
59d45c38
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *check.C) {
 	d1 := s.AddDaemon(c, true, true)
 	s.AddDaemon(c, true, true)
 	d3 := s.AddDaemon(c, true, true)
 
 	d3ID := d3.NodeID
59b2d047
 	waitForEvent(c, d1, "0", "-f scope=swarm", "node create "+d3ID, defaultRetryCount)
59d45c38
 
59b2d047
 	t1 := daemonUnixTime(c)
 	out, err := d1.Cmd("node", "update", "--availability=pause", d3ID)
59d45c38
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// filter by type
59b2d047
 	out = waitForEvent(c, d1, t1, "-f type=node", "node update "+d3ID, defaultRetryCount)
59d45c38
 	c.Assert(out, checker.Contains, "availability.new=pause, availability.old=active")
 
59b2d047
 	t2 := daemonUnixTime(c)
59d45c38
 	out, err = d1.Cmd("node", "demote", d3ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
59b2d047
 	waitForEvent(c, d1, t2, "-f type=node", "node update "+d3ID, defaultRetryCount)
59d45c38
 
59b2d047
 	t3 := daemonUnixTime(c)
59d45c38
 	out, err = d1.Cmd("node", "rm", "-f", d3ID)
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
59b2d047
 	// filter by scope
 	waitForEvent(c, d1, t3, "-f scope=swarm", "node remove "+d3ID, defaultRetryCount)
59d45c38
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	// create a network
 	out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 	networkID := strings.TrimSpace(out)
 
59b2d047
 	waitForEvent(c, d, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount)
59d45c38
 
 	// remove network
59b2d047
 	t1 := daemonUnixTime(c)
59d45c38
 	out, err = d.Cmd("network", "rm", "foo")
 	c.Assert(err, checker.IsNil, check.Commentf(out))
 
 	// filtered by network
59b2d047
 	waitForEvent(c, d, t1, "-f type=network", "network remove "+networkID, defaultRetryCount)
59d45c38
 }
 
 func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *check.C) {
 	d := s.AddDaemon(c, true, true)
 
 	testName := "test_secret"
 	id := d.CreateSecret(c, swarm.SecretSpec{
 		Annotations: swarm.Annotations{
 			Name: testName,
 		},
 		Data: []byte("TESTINGDATA"),
 	})
 	c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id))
 
59b2d047
 	waitForEvent(c, d, "0", "-f scope=swarm", "secret create "+id, defaultRetryCount)
59d45c38
 
59b2d047
 	t1 := daemonUnixTime(c)
59d45c38
 	d.DeleteSecret(c, id)
 	// filtered by secret
59b2d047
 	waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount)
59d45c38
 }