github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration-cli/docker_cli_swarm_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "bytes" 7 "context" 8 "encoding/json" 9 "encoding/pem" 10 "fmt" 11 "io/ioutil" 12 "net/http" 13 "net/http/httptest" 14 "os" 15 "path/filepath" 16 "runtime" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/cloudflare/cfssl/helpers" 22 "github.com/docker/docker/api/types" 23 "github.com/docker/docker/api/types/swarm" 24 "github.com/docker/docker/integration-cli/checker" 25 "github.com/docker/docker/integration-cli/cli" 26 "github.com/docker/docker/integration-cli/daemon" 27 "github.com/docker/libnetwork/driverapi" 28 "github.com/docker/libnetwork/ipamapi" 29 remoteipam "github.com/docker/libnetwork/ipams/remote/api" 30 "github.com/docker/swarmkit/ca/keyutils" 31 "github.com/vishvananda/netlink" 32 "gotest.tools/v3/assert" 33 "gotest.tools/v3/fs" 34 "gotest.tools/v3/icmd" 35 "gotest.tools/v3/poll" 36 ) 37 38 func (s *DockerSwarmSuite) TestSwarmUpdate(c *testing.T) { 39 d := s.AddDaemon(c, true, true) 40 41 getSpec := func() swarm.Spec { 42 sw := d.GetSwarm(c) 43 return sw.Spec 44 } 45 46 out, err := d.Cmd("swarm", "update", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s") 47 assert.NilError(c, err, out) 48 49 spec := getSpec() 50 assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour) 51 assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 11*time.Second) 52 53 // setting anything under 30m for cert-expiry is not allowed 54 out, err = d.Cmd("swarm", "update", "--cert-expiry", "15m") 55 assert.ErrorContains(c, err, "") 56 assert.Assert(c, strings.Contains(out, "minimum certificate expiry time")) 57 spec = getSpec() 58 assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour) 59 60 // passing an external CA (this is without starting a root rotation) does not fail 61 cli.Docker(cli.Args("swarm", "update", "--external-ca", "protocol=cfssl,url=https://something.org", 62 "--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"), 63 cli.Daemon(d)).Assert(c, icmd.Success) 64 65 expected, err := ioutil.ReadFile("fixtures/https/ca.pem") 66 assert.NilError(c, err) 67 68 spec = getSpec() 69 assert.Equal(c, len(spec.CAConfig.ExternalCAs), 2) 70 assert.Equal(c, spec.CAConfig.ExternalCAs[0].CACert, "") 71 assert.Equal(c, spec.CAConfig.ExternalCAs[1].CACert, string(expected)) 72 73 // passing an invalid external CA fails 74 tempFile := fs.NewFile(c, "testfile", fs.WithContent("fakecert")) 75 defer tempFile.Remove() 76 77 result := cli.Docker(cli.Args("swarm", "update", 78 "--external-ca", fmt.Sprintf("protocol=cfssl,url=https://something.org,cacert=%s", tempFile.Path())), 79 cli.Daemon(d)) 80 result.Assert(c, icmd.Expected{ 81 ExitCode: 125, 82 Err: "must be in PEM format", 83 }) 84 } 85 86 func (s *DockerSwarmSuite) TestSwarmInit(c *testing.T) { 87 d := s.AddDaemon(c, false, false) 88 89 getSpec := func() swarm.Spec { 90 sw := d.GetSwarm(c) 91 return sw.Spec 92 } 93 94 // passing an invalid external CA fails 95 tempFile := fs.NewFile(c, "testfile", fs.WithContent("fakecert")) 96 defer tempFile.Remove() 97 98 result := cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s", 99 "--external-ca", fmt.Sprintf("protocol=cfssl,url=https://somethingelse.org,cacert=%s", tempFile.Path())), 100 cli.Daemon(d)) 101 result.Assert(c, icmd.Expected{ 102 ExitCode: 125, 103 Err: "must be in PEM format", 104 }) 105 106 cli.Docker(cli.Args("swarm", "init", "--cert-expiry", "30h", "--dispatcher-heartbeat", "11s", 107 "--external-ca", "protocol=cfssl,url=https://something.org", 108 "--external-ca", "protocol=cfssl,url=https://somethingelse.org,cacert=fixtures/https/ca.pem"), 109 cli.Daemon(d)).Assert(c, icmd.Success) 110 111 expected, err := ioutil.ReadFile("fixtures/https/ca.pem") 112 assert.NilError(c, err) 113 114 spec := getSpec() 115 assert.Equal(c, spec.CAConfig.NodeCertExpiry, 30*time.Hour) 116 assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 11*time.Second) 117 assert.Equal(c, len(spec.CAConfig.ExternalCAs), 2) 118 assert.Equal(c, spec.CAConfig.ExternalCAs[0].CACert, "") 119 assert.Equal(c, spec.CAConfig.ExternalCAs[1].CACert, string(expected)) 120 121 assert.Assert(c, d.SwarmLeave(c, true) == nil) 122 cli.Docker(cli.Args("swarm", "init"), cli.Daemon(d)).Assert(c, icmd.Success) 123 124 spec = getSpec() 125 assert.Equal(c, spec.CAConfig.NodeCertExpiry, 90*24*time.Hour) 126 assert.Equal(c, spec.Dispatcher.HeartbeatPeriod, 5*time.Second) 127 } 128 129 func (s *DockerSwarmSuite) TestSwarmInitIPv6(c *testing.T) { 130 testRequires(c, IPv6) 131 d1 := s.AddDaemon(c, false, false) 132 cli.Docker(cli.Args("swarm", "init", "--listen-add", "::1"), cli.Daemon(d1)).Assert(c, icmd.Success) 133 134 d2 := s.AddDaemon(c, false, false) 135 cli.Docker(cli.Args("swarm", "join", "::1"), cli.Daemon(d2)).Assert(c, icmd.Success) 136 137 out := cli.Docker(cli.Args("info"), cli.Daemon(d2)).Assert(c, icmd.Success).Combined() 138 assert.Assert(c, strings.Contains(out, "Swarm: active")) 139 } 140 141 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedAdvertiseAddr(c *testing.T) { 142 d := s.AddDaemon(c, false, false) 143 out, err := d.Cmd("swarm", "init", "--advertise-addr", "0.0.0.0") 144 assert.ErrorContains(c, err, "") 145 assert.Assert(c, strings.Contains(out, "advertise address must be a non-zero IP address")) 146 } 147 148 func (s *DockerSwarmSuite) TestSwarmIncompatibleDaemon(c *testing.T) { 149 // init swarm mode and stop a daemon 150 d := s.AddDaemon(c, true, true) 151 info := d.SwarmInfo(c) 152 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive) 153 d.Stop(c) 154 155 // start a daemon with --cluster-store and --cluster-advertise 156 err := d.StartWithError("--cluster-store=consul://consuladdr:consulport/some/path", "--cluster-advertise=1.1.1.1:2375") 157 assert.ErrorContains(c, err, "") 158 content, err := d.ReadLogFile() 159 assert.NilError(c, err) 160 assert.Assert(c, strings.Contains(string(content), "--cluster-store and --cluster-advertise daemon configurations are incompatible with swarm mode")) 161 // start a daemon with --live-restore 162 err = d.StartWithError("--live-restore") 163 assert.ErrorContains(c, err, "") 164 content, err = d.ReadLogFile() 165 assert.NilError(c, err) 166 assert.Assert(c, strings.Contains(string(content), "--live-restore daemon configuration is incompatible with swarm mode")) 167 // restart for teardown 168 d.StartNode(c) 169 } 170 171 func (s *DockerSwarmSuite) TestSwarmServiceTemplatingHostname(c *testing.T) { 172 d := s.AddDaemon(c, true, true) 173 hostname, err := d.Cmd("node", "inspect", "--format", "{{.Description.Hostname}}", "self") 174 assert.Assert(c, err == nil, hostname) 175 176 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "test", "--hostname", "{{.Service.Name}}-{{.Task.Slot}}-{{.Node.Hostname}}", "busybox", "top") 177 assert.NilError(c, err, out) 178 179 // make sure task has been deployed. 180 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 181 182 containers := d.ActiveContainers(c) 183 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.Hostname}}", containers[0]) 184 assert.NilError(c, err, out) 185 assert.Equal(c, strings.Split(out, "\n")[0], "test-1-"+strings.Split(hostname, "\n")[0], "hostname with templating invalid") 186 } 187 188 // Test case for #24270 189 func (s *DockerSwarmSuite) TestSwarmServiceListFilter(c *testing.T) { 190 d := s.AddDaemon(c, true, true) 191 192 name1 := "redis-cluster-md5" 193 name2 := "redis-cluster" 194 name3 := "other-cluster" 195 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name1, "busybox", "top") 196 assert.NilError(c, err, out) 197 assert.Assert(c, strings.TrimSpace(out) != "") 198 199 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name2, "busybox", "top") 200 assert.NilError(c, err, out) 201 assert.Assert(c, strings.TrimSpace(out) != "") 202 203 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name3, "busybox", "top") 204 assert.NilError(c, err, out) 205 assert.Assert(c, strings.TrimSpace(out) != "") 206 207 filter1 := "name=redis-cluster-md5" 208 filter2 := "name=redis-cluster" 209 210 // We search checker.Contains with `name+" "` to prevent prefix only. 211 out, err = d.Cmd("service", "ls", "--filter", filter1) 212 assert.NilError(c, err, out) 213 assert.Assert(c, strings.Contains(out, name1+" "), out) 214 assert.Assert(c, !strings.Contains(out, name2+" "), out) 215 assert.Assert(c, !strings.Contains(out, name3+" "), out) 216 out, err = d.Cmd("service", "ls", "--filter", filter2) 217 assert.NilError(c, err, out) 218 assert.Assert(c, strings.Contains(out, name1+" "), out) 219 assert.Assert(c, strings.Contains(out, name2+" "), out) 220 assert.Assert(c, !strings.Contains(out, name3+" "), out) 221 out, err = d.Cmd("service", "ls") 222 assert.NilError(c, err, out) 223 assert.Assert(c, strings.Contains(out, name1+" "), out) 224 assert.Assert(c, strings.Contains(out, name2+" "), out) 225 assert.Assert(c, strings.Contains(out, name3+" "), out) 226 } 227 228 func (s *DockerSwarmSuite) TestSwarmNodeListFilter(c *testing.T) { 229 d := s.AddDaemon(c, true, true) 230 231 out, err := d.Cmd("node", "inspect", "--format", "{{ .Description.Hostname }}", "self") 232 assert.NilError(c, err, out) 233 assert.Assert(c, strings.TrimSpace(out) != "") 234 name := strings.TrimSpace(out) 235 236 filter := "name=" + name[:4] 237 238 out, err = d.Cmd("node", "ls", "--filter", filter) 239 assert.NilError(c, err, out) 240 assert.Assert(c, strings.Contains(out, name), out) 241 out, err = d.Cmd("node", "ls", "--filter", "name=none") 242 assert.NilError(c, err, out) 243 assert.Assert(c, !strings.Contains(out, name), out) 244 } 245 246 func (s *DockerSwarmSuite) TestSwarmNodeTaskListFilter(c *testing.T) { 247 d := s.AddDaemon(c, true, true) 248 249 name := "redis-cluster-md5" 250 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top") 251 assert.NilError(c, err, out) 252 assert.Assert(c, strings.TrimSpace(out) != "") 253 254 // make sure task has been deployed. 255 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(3)), poll.WithTimeout(defaultReconciliationTimeout)) 256 257 filter := "name=redis-cluster" 258 259 out, err = d.Cmd("node", "ps", "--filter", filter, "self") 260 assert.NilError(c, err, out) 261 assert.Assert(c, strings.Contains(out, name+".1"), out) 262 assert.Assert(c, strings.Contains(out, name+".2"), out) 263 assert.Assert(c, strings.Contains(out, name+".3"), out) 264 out, err = d.Cmd("node", "ps", "--filter", "name=none", "self") 265 assert.NilError(c, err, out) 266 assert.Assert(c, !strings.Contains(out, name+".1"), out) 267 assert.Assert(c, !strings.Contains(out, name+".2"), out) 268 assert.Assert(c, !strings.Contains(out, name+".3"), out) 269 } 270 271 // Test case for #25375 272 func (s *DockerSwarmSuite) TestSwarmPublishAdd(c *testing.T) { 273 d := s.AddDaemon(c, true, true) 274 275 name := "top" 276 // this first command does not have to be retried because service creates 277 // don't return out of sequence errors. 278 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--label", "x=y", "busybox", "top") 279 assert.NilError(c, err, out) 280 assert.Assert(c, strings.TrimSpace(out) != "") 281 282 out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name) 283 assert.NilError(c, err, out) 284 285 out, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", name) 286 assert.NilError(c, err, out) 287 288 _, err = d.CmdRetryOutOfSequence("service", "update", "--detach", "--publish-add", "80:80", "--publish-add", "80:20", name) 289 assert.ErrorContains(c, err, "") 290 291 // this last command does not have to be retried because service inspect 292 // does not return out of sequence errors. 293 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.EndpointSpec.Ports }}", name) 294 assert.NilError(c, err, out) 295 assert.Equal(c, strings.TrimSpace(out), "[{ tcp 80 80 ingress}]") 296 } 297 298 func (s *DockerSwarmSuite) TestSwarmServiceWithGroup(c *testing.T) { 299 d := s.AddDaemon(c, true, true) 300 301 name := "top" 302 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--user", "root:root", "--group", "wheel", "--group", "audio", "--group", "staff", "--group", "777", "busybox", "top") 303 assert.NilError(c, err, out) 304 assert.Assert(c, strings.TrimSpace(out) != "") 305 306 // make sure task has been deployed. 307 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 308 309 out, err = d.Cmd("ps", "-q") 310 assert.NilError(c, err, out) 311 assert.Assert(c, strings.TrimSpace(out) != "") 312 313 container := strings.TrimSpace(out) 314 315 out, err = d.Cmd("exec", container, "id") 316 assert.NilError(c, err, out) 317 assert.Equal(c, strings.TrimSpace(out), "uid=0(root) gid=0(root) groups=10(wheel),29(audio),50(staff),777") 318 } 319 320 func (s *DockerSwarmSuite) TestSwarmContainerAutoStart(c *testing.T) { 321 d := s.AddDaemon(c, true, true) 322 323 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 324 assert.NilError(c, err, out) 325 assert.Assert(c, strings.TrimSpace(out) != "") 326 327 out, err = d.Cmd("run", "-id", "--restart=always", "--net=foo", "--name=test", "busybox", "top") 328 assert.NilError(c, err, out) 329 assert.Assert(c, strings.TrimSpace(out) != "") 330 331 out, err = d.Cmd("ps", "-q") 332 assert.NilError(c, err, out) 333 assert.Assert(c, strings.TrimSpace(out) != "") 334 335 d.RestartNode(c) 336 337 out, err = d.Cmd("ps", "-q") 338 assert.NilError(c, err, out) 339 assert.Assert(c, strings.TrimSpace(out) != "") 340 } 341 342 func (s *DockerSwarmSuite) TestSwarmContainerEndpointOptions(c *testing.T) { 343 d := s.AddDaemon(c, true, true) 344 345 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 346 assert.NilError(c, err, out) 347 assert.Assert(c, strings.TrimSpace(out) != "") 348 349 out, err = d.Cmd("run", "-d", "--net=foo", "--name=first", "--net-alias=first-alias", "busybox:glibc", "top") 350 assert.NilError(c, err, out) 351 352 out, err = d.Cmd("run", "-d", "--net=foo", "--name=second", "busybox:glibc", "top") 353 assert.NilError(c, err, out) 354 355 out, err = d.Cmd("run", "-d", "--net=foo", "--net-alias=third-alias", "busybox:glibc", "top") 356 assert.NilError(c, err, out) 357 358 // ping first container and its alias, also ping third and anonymous container by its alias 359 out, err = d.Cmd("exec", "second", "ping", "-c", "1", "first") 360 assert.NilError(c, err, out) 361 out, err = d.Cmd("exec", "second", "ping", "-c", "1", "first-alias") 362 assert.NilError(c, err, out) 363 out, err = d.Cmd("exec", "second", "ping", "-c", "1", "third-alias") 364 assert.NilError(c, err, out) 365 } 366 367 func (s *DockerSwarmSuite) TestSwarmContainerAttachByNetworkId(c *testing.T) { 368 d := s.AddDaemon(c, true, true) 369 370 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "testnet") 371 assert.NilError(c, err, out) 372 assert.Assert(c, strings.TrimSpace(out) != "") 373 networkID := strings.TrimSpace(out) 374 375 out, err = d.Cmd("run", "-d", "--net", networkID, "busybox", "top") 376 assert.NilError(c, err, out) 377 cID := strings.TrimSpace(out) 378 d.WaitRun(cID) 379 380 out, err = d.Cmd("rm", "-f", cID) 381 assert.NilError(c, err, out) 382 383 out, err = d.Cmd("network", "rm", "testnet") 384 assert.NilError(c, err, out) 385 386 checkNetwork := func(*testing.T) (interface{}, string) { 387 out, err := d.Cmd("network", "ls") 388 assert.NilError(c, err) 389 return out, "" 390 } 391 392 poll.WaitOn(c, pollCheck(c, checkNetwork, checker.Not(checker.Contains("testnet"))), poll.WithTimeout(3*time.Second)) 393 } 394 395 func (s *DockerSwarmSuite) TestOverlayAttachable(c *testing.T) { 396 d := s.AddDaemon(c, true, true) 397 398 out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "ovnet") 399 assert.NilError(c, err, out) 400 401 // validate attachable 402 out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet") 403 assert.NilError(c, err, out) 404 assert.Equal(c, strings.TrimSpace(out), "true") 405 406 // validate containers can attach to this overlay network 407 out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "busybox", "top") 408 assert.NilError(c, err, out) 409 410 // redo validation, there was a bug that the value of attachable changes after 411 // containers attach to the network 412 out, err = d.Cmd("network", "inspect", "--format", "{{json .Attachable}}", "ovnet") 413 assert.NilError(c, err, out) 414 assert.Equal(c, strings.TrimSpace(out), "true") 415 } 416 417 func (s *DockerSwarmSuite) TestOverlayAttachableOnSwarmLeave(c *testing.T) { 418 d := s.AddDaemon(c, true, true) 419 420 // Create an attachable swarm network 421 nwName := "attovl" 422 out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", nwName) 423 assert.NilError(c, err, out) 424 425 // Connect a container to the network 426 out, err = d.Cmd("run", "-d", "--network", nwName, "--name", "c1", "busybox", "top") 427 assert.NilError(c, err, out) 428 429 // Leave the swarm 430 assert.Assert(c, d.SwarmLeave(c, true) == nil) 431 432 // Check the container is disconnected 433 out, err = d.Cmd("inspect", "c1", "--format", "{{.NetworkSettings.Networks."+nwName+"}}") 434 assert.NilError(c, err, out) 435 assert.Equal(c, strings.TrimSpace(out), "<no value>") 436 437 // Check the network is gone 438 out, err = d.Cmd("network", "ls", "--format", "{{.Name}}") 439 assert.NilError(c, err, out) 440 assert.Assert(c, !strings.Contains(out, nwName), out) 441 } 442 443 func (s *DockerSwarmSuite) TestOverlayAttachableReleaseResourcesOnFailure(c *testing.T) { 444 d := s.AddDaemon(c, true, true) 445 446 // Create attachable network 447 out, err := d.Cmd("network", "create", "-d", "overlay", "--attachable", "--subnet", "10.10.9.0/24", "ovnet") 448 assert.NilError(c, err, out) 449 450 // Attach a container with specific IP 451 out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c1", "--ip", "10.10.9.33", "busybox", "top") 452 assert.NilError(c, err, out) 453 454 // Attempt to attach another container with same IP, must fail 455 out, err = d.Cmd("run", "-d", "--network", "ovnet", "--name", "c2", "--ip", "10.10.9.33", "busybox", "top") 456 assert.ErrorContains(c, err, "", out) 457 458 // Remove first container 459 out, err = d.Cmd("rm", "-f", "c1") 460 assert.NilError(c, err, out) 461 462 // Verify the network can be removed, no phantom network attachment task left over 463 out, err = d.Cmd("network", "rm", "ovnet") 464 assert.NilError(c, err, out) 465 } 466 467 func (s *DockerSwarmSuite) TestSwarmIngressNetwork(c *testing.T) { 468 d := s.AddDaemon(c, true, true) 469 470 // Ingress network can be removed 471 removeNetwork := func(name string) *icmd.Result { 472 return cli.Docker( 473 cli.Args("-H", d.Sock(), "network", "rm", name), 474 cli.WithStdin(strings.NewReader("Y"))) 475 } 476 477 result := removeNetwork("ingress") 478 result.Assert(c, icmd.Success) 479 480 // And recreated 481 out, err := d.Cmd("network", "create", "-d", "overlay", "--ingress", "new-ingress") 482 assert.NilError(c, err, out) 483 484 // But only one is allowed 485 out, err = d.Cmd("network", "create", "-d", "overlay", "--ingress", "another-ingress") 486 assert.ErrorContains(c, err, "") 487 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "is already present"), out) 488 // It cannot be removed if it is being used 489 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv1", "-p", "9000:8000", "busybox", "top") 490 assert.NilError(c, err, out) 491 492 result = removeNetwork("new-ingress") 493 result.Assert(c, icmd.Expected{ 494 ExitCode: 1, 495 Err: "ingress network cannot be removed because service", 496 }) 497 498 // But it can be removed once no more services depend on it 499 out, err = d.Cmd("service", "update", "--detach", "--publish-rm", "9000:8000", "srv1") 500 assert.NilError(c, err, out) 501 502 result = removeNetwork("new-ingress") 503 result.Assert(c, icmd.Success) 504 505 // A service which needs the ingress network cannot be created if no ingress is present 506 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv2", "-p", "500:500", "busybox", "top") 507 assert.ErrorContains(c, err, "") 508 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "no ingress network is present"), out) 509 // An existing service cannot be updated to use the ingress nw if the nw is not present 510 out, err = d.Cmd("service", "update", "--detach", "--publish-add", "9000:8000", "srv1") 511 assert.ErrorContains(c, err, "") 512 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "no ingress network is present"), out) 513 // But services which do not need routing mesh can be created regardless 514 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv3", "--endpoint-mode", "dnsrr", "busybox", "top") 515 assert.NilError(c, err, out) 516 } 517 518 func (s *DockerSwarmSuite) TestSwarmCreateServiceWithNoIngressNetwork(c *testing.T) { 519 d := s.AddDaemon(c, true, true) 520 521 // Remove ingress network 522 result := cli.Docker( 523 cli.Args("-H", d.Sock(), "network", "rm", "ingress"), 524 cli.WithStdin(strings.NewReader("Y"))) 525 result.Assert(c, icmd.Success) 526 527 // Create a overlay network and launch a service on it 528 // Make sure nothing panics because ingress network is missing 529 out, err := d.Cmd("network", "create", "-d", "overlay", "another-network") 530 assert.NilError(c, err, out) 531 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "srv4", "--network", "another-network", "busybox", "top") 532 assert.NilError(c, err, out) 533 } 534 535 // Test case for #24108, also the case from: 536 // https://github.com/docker/docker/pull/24620#issuecomment-233715656 537 func (s *DockerSwarmSuite) TestSwarmTaskListFilter(c *testing.T) { 538 d := s.AddDaemon(c, true, true) 539 540 name := "redis-cluster-md5" 541 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--replicas=3", "busybox", "top") 542 assert.NilError(c, err, out) 543 assert.Assert(c, strings.TrimSpace(out) != "") 544 545 filter := "name=redis-cluster" 546 547 checkNumTasks := func(*testing.T) (interface{}, string) { 548 out, err := d.Cmd("service", "ps", "--filter", filter, name) 549 assert.NilError(c, err, out) 550 return len(strings.Split(out, "\n")) - 2, "" // includes header and nl in last line 551 } 552 553 // wait until all tasks have been created 554 poll.WaitOn(c, pollCheck(c, checkNumTasks, checker.Equals(3)), poll.WithTimeout(defaultReconciliationTimeout)) 555 556 out, err = d.Cmd("service", "ps", "--filter", filter, name) 557 assert.NilError(c, err, out) 558 assert.Assert(c, strings.Contains(out, name+".1"), out) 559 assert.Assert(c, strings.Contains(out, name+".2"), out) 560 assert.Assert(c, strings.Contains(out, name+".3"), out) 561 out, err = d.Cmd("service", "ps", "--filter", "name="+name+".1", name) 562 assert.NilError(c, err, out) 563 assert.Assert(c, strings.Contains(out, name+".1"), out) 564 assert.Assert(c, !strings.Contains(out, name+".2"), out) 565 assert.Assert(c, !strings.Contains(out, name+".3"), out) 566 out, err = d.Cmd("service", "ps", "--filter", "name=none", name) 567 assert.NilError(c, err, out) 568 assert.Assert(c, !strings.Contains(out, name+".1"), out) 569 assert.Assert(c, !strings.Contains(out, name+".2"), out) 570 assert.Assert(c, !strings.Contains(out, name+".3"), out) 571 name = "redis-cluster-sha1" 572 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--mode=global", "busybox", "top") 573 assert.NilError(c, err, out) 574 assert.Assert(c, strings.TrimSpace(out) != "") 575 576 poll.WaitOn(c, pollCheck(c, checkNumTasks, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 577 578 filter = "name=redis-cluster" 579 out, err = d.Cmd("service", "ps", "--filter", filter, name) 580 assert.NilError(c, err, out) 581 assert.Assert(c, strings.Contains(out, name), out) 582 out, err = d.Cmd("service", "ps", "--filter", "name="+name, name) 583 assert.NilError(c, err, out) 584 assert.Assert(c, strings.Contains(out, name), out) 585 out, err = d.Cmd("service", "ps", "--filter", "name=none", name) 586 assert.NilError(c, err, out) 587 assert.Assert(c, !strings.Contains(out, name), out) 588 } 589 590 func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *testing.T) { 591 d := s.AddDaemon(c, true, true) 592 593 // Create a bare container 594 out, err := d.Cmd("run", "-d", "--name=bare-container", "busybox", "top") 595 assert.NilError(c, err, out) 596 bareID := strings.TrimSpace(out)[:12] 597 // Create a service 598 name := "busybox-top" 599 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top") 600 assert.NilError(c, err, out) 601 assert.Assert(c, strings.TrimSpace(out) != "") 602 603 // make sure task has been deployed. 604 poll.WaitOn(c, pollCheck(c, d.CheckServiceRunningTasks(name), checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 605 606 // Filter non-tasks 607 out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=false") 608 assert.NilError(c, err, out) 609 psOut := strings.TrimSpace(out) 610 assert.Equal(c, psOut, bareID, fmt.Sprintf("Expected id %s, got %s for is-task label, output %q", bareID, psOut, out)) 611 612 // Filter tasks 613 out, err = d.Cmd("ps", "-a", "-q", "--filter=is-task=true") 614 assert.NilError(c, err, out) 615 lines := strings.Split(strings.Trim(out, "\n "), "\n") 616 assert.Equal(c, len(lines), 1) 617 assert.Assert(c, lines[0] != bareID, "Expected not %s, but got it for is-task label, output %q", bareID, out) 618 } 619 620 const globalNetworkPlugin = "global-network-plugin" 621 const globalIPAMPlugin = "global-ipam-plugin" 622 623 func setupRemoteGlobalNetworkPlugin(c *testing.T, mux *http.ServeMux, url, netDrv, ipamDrv string) { 624 625 mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) { 626 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 627 fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType) 628 }) 629 630 // Network driver implementation 631 mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 632 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 633 fmt.Fprintf(w, `{"Scope":"global"}`) 634 }) 635 636 mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 637 err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest) 638 if err != nil { 639 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 640 return 641 } 642 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 643 fmt.Fprintf(w, "null") 644 }) 645 646 mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 647 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 648 fmt.Fprintf(w, "null") 649 }) 650 651 mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 652 err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest) 653 if err != nil { 654 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 655 return 656 } 657 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 658 fmt.Fprintf(w, "null") 659 }) 660 661 mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 662 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 663 fmt.Fprintf(w, "null") 664 }) 665 666 mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 667 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 668 fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`) 669 }) 670 671 mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 672 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 673 674 veth := &netlink.Veth{ 675 LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"} 676 if err := netlink.LinkAdd(veth); err != nil { 677 fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`) 678 } else { 679 fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`) 680 } 681 }) 682 683 mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 684 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 685 fmt.Fprintf(w, "null") 686 }) 687 688 mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 689 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 690 if link, err := netlink.LinkByName("cnt0"); err == nil { 691 netlink.LinkDel(link) 692 } 693 fmt.Fprintf(w, "null") 694 }) 695 696 // IPAM Driver implementation 697 var ( 698 poolRequest remoteipam.RequestPoolRequest 699 poolReleaseReq remoteipam.ReleasePoolRequest 700 addressRequest remoteipam.RequestAddressRequest 701 addressReleaseReq remoteipam.ReleaseAddressRequest 702 lAS = "localAS" 703 gAS = "globalAS" 704 pool = "172.28.0.0/16" 705 poolID = lAS + "/" + pool 706 gw = "172.28.255.254/16" 707 ) 708 709 mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 710 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 711 fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`) 712 }) 713 714 mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 715 err := json.NewDecoder(r.Body).Decode(&poolRequest) 716 if err != nil { 717 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 718 return 719 } 720 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 721 if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS { 722 fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`) 723 } else if poolRequest.Pool != "" && poolRequest.Pool != pool { 724 fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`) 725 } else { 726 fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`) 727 } 728 }) 729 730 mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 731 err := json.NewDecoder(r.Body).Decode(&addressRequest) 732 if err != nil { 733 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 734 return 735 } 736 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 737 // make sure libnetwork is now querying on the expected pool id 738 if addressRequest.PoolID != poolID { 739 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 740 } else if addressRequest.Address != "" { 741 fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`) 742 } else { 743 fmt.Fprintf(w, `{"Address":"`+gw+`"}`) 744 } 745 }) 746 747 mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 748 err := json.NewDecoder(r.Body).Decode(&addressReleaseReq) 749 if err != nil { 750 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 751 return 752 } 753 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 754 // make sure libnetwork is now asking to release the expected address from the expected poolid 755 if addressRequest.PoolID != poolID { 756 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 757 } else if addressReleaseReq.Address != gw { 758 fmt.Fprintf(w, `{"Error":"unknown address"}`) 759 } else { 760 fmt.Fprintf(w, "null") 761 } 762 }) 763 764 mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) { 765 err := json.NewDecoder(r.Body).Decode(&poolReleaseReq) 766 if err != nil { 767 http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest) 768 return 769 } 770 w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json") 771 // make sure libnetwork is now asking to release the expected poolid 772 if addressRequest.PoolID != poolID { 773 fmt.Fprintf(w, `{"Error":"unknown pool id"}`) 774 } else { 775 fmt.Fprintf(w, "null") 776 } 777 }) 778 779 err := os.MkdirAll("/etc/docker/plugins", 0755) 780 assert.NilError(c, err) 781 782 fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv) 783 err = ioutil.WriteFile(fileName, []byte(url), 0644) 784 assert.NilError(c, err) 785 786 ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv) 787 err = ioutil.WriteFile(ipamFileName, []byte(url), 0644) 788 assert.NilError(c, err) 789 } 790 791 func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *testing.T) { 792 mux := http.NewServeMux() 793 s.server = httptest.NewServer(mux) 794 assert.Assert(c, s.server != nil) // check that HTTP server has started 795 setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin) 796 defer func() { 797 s.server.Close() 798 err := os.RemoveAll("/etc/docker/plugins") 799 assert.NilError(c, err) 800 }() 801 802 d := s.AddDaemon(c, true, true) 803 804 out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo") 805 assert.ErrorContains(c, err, "", out) 806 assert.Assert(c, strings.Contains(out, "not supported in swarm mode"), out) 807 } 808 809 // Test case for #24712 810 func (s *DockerSwarmSuite) TestSwarmServiceEnvFile(c *testing.T) { 811 d := s.AddDaemon(c, true, true) 812 813 path := filepath.Join(d.Folder, "env.txt") 814 err := ioutil.WriteFile(path, []byte("VAR1=A\nVAR2=A\n"), 0644) 815 assert.NilError(c, err) 816 817 name := "worker" 818 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--env-file", path, "--env", "VAR1=B", "--env", "VAR1=C", "--env", "VAR2=", "--env", "VAR2", "--name", name, "busybox", "top") 819 assert.NilError(c, err, out) 820 assert.Assert(c, strings.TrimSpace(out) != "") 821 822 // The complete env is [VAR1=A VAR2=A VAR1=B VAR1=C VAR2= VAR2] and duplicates will be removed => [VAR1=C VAR2] 823 out, err = d.Cmd("inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.Env }}", name) 824 assert.NilError(c, err, out) 825 assert.Assert(c, strings.Contains(out, "[VAR1=C VAR2]"), out) 826 } 827 828 func (s *DockerSwarmSuite) TestSwarmServiceTTY(c *testing.T) { 829 d := s.AddDaemon(c, true, true) 830 831 name := "top" 832 833 ttyCheck := "if [ -t 0 ]; then echo TTY > /status; else echo none > /status; fi; exec top" 834 835 // Without --tty 836 expectedOutput := "none" 837 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "sh", "-c", ttyCheck) 838 assert.NilError(c, err, out) 839 840 // Make sure task has been deployed. 841 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 842 843 // We need to get the container id. 844 out, err = d.Cmd("ps", "-q", "--no-trunc") 845 assert.NilError(c, err, out) 846 id := strings.TrimSpace(out) 847 848 out, err = d.Cmd("exec", id, "cat", "/status") 849 assert.NilError(c, err, out) 850 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 851 // Remove service 852 out, err = d.Cmd("service", "rm", name) 853 assert.NilError(c, err, out) 854 // Make sure container has been destroyed. 855 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout)) 856 857 // With --tty 858 expectedOutput = "TTY" 859 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--tty", "busybox", "sh", "-c", ttyCheck) 860 assert.NilError(c, err, out) 861 862 // Make sure task has been deployed. 863 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 864 865 // We need to get the container id. 866 out, err = d.Cmd("ps", "-q", "--no-trunc") 867 assert.NilError(c, err, out) 868 id = strings.TrimSpace(out) 869 870 out, err = d.Cmd("exec", id, "cat", "/status") 871 assert.NilError(c, err, out) 872 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 873 } 874 875 func (s *DockerSwarmSuite) TestSwarmServiceTTYUpdate(c *testing.T) { 876 d := s.AddDaemon(c, true, true) 877 878 // Create a service 879 name := "top" 880 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top") 881 assert.NilError(c, err, out) 882 883 // Make sure task has been deployed. 884 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 885 886 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name) 887 assert.NilError(c, err, out) 888 assert.Equal(c, strings.TrimSpace(out), "false") 889 890 out, err = d.Cmd("service", "update", "--detach", "--tty", name) 891 assert.NilError(c, err, out) 892 893 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.TTY }}", name) 894 assert.NilError(c, err, out) 895 assert.Equal(c, strings.TrimSpace(out), "true") 896 } 897 898 func (s *DockerSwarmSuite) TestSwarmServiceNetworkUpdate(c *testing.T) { 899 d := s.AddDaemon(c, true, true) 900 901 result := icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "foo")) 902 result.Assert(c, icmd.Success) 903 fooNetwork := strings.TrimSpace(result.Combined()) 904 905 result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "bar")) 906 result.Assert(c, icmd.Success) 907 barNetwork := strings.TrimSpace(result.Combined()) 908 909 result = icmd.RunCmd(d.Command("network", "create", "-d", "overlay", "baz")) 910 result.Assert(c, icmd.Success) 911 bazNetwork := strings.TrimSpace(result.Combined()) 912 913 // Create a service 914 name := "top" 915 result = icmd.RunCmd(d.Command("service", "create", "--detach", "--no-resolve-image", "--network", "foo", "--network", "bar", "--name", name, "busybox", "top")) 916 result.Assert(c, icmd.Success) 917 918 // Make sure task has been deployed. 919 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{fooNetwork: 1, barNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout)) 920 921 // Remove a network 922 result = icmd.RunCmd(d.Command("service", "update", "--detach", "--network-rm", "foo", name)) 923 result.Assert(c, icmd.Success) 924 925 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{barNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout)) 926 927 // Add a network 928 result = icmd.RunCmd(d.Command("service", "update", "--detach", "--network-add", "baz", name)) 929 result.Assert(c, icmd.Success) 930 931 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskNetworks, checker.DeepEquals(map[string]int{barNetwork: 1, bazNetwork: 1})), poll.WithTimeout(defaultReconciliationTimeout)) 932 933 } 934 935 func (s *DockerSwarmSuite) TestDNSConfig(c *testing.T) { 936 d := s.AddDaemon(c, true, true) 937 938 // Create a service 939 name := "top" 940 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--dns=1.2.3.4", "--dns-search=example.com", "--dns-option=timeout:3", "busybox", "top") 941 assert.NilError(c, err, out) 942 943 // Make sure task has been deployed. 944 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 945 946 // We need to get the container id. 947 out, err = d.Cmd("ps", "-a", "-q", "--no-trunc") 948 assert.NilError(c, err, out) 949 id := strings.TrimSpace(out) 950 951 // Compare against expected output. 952 expectedOutput1 := "nameserver 1.2.3.4" 953 expectedOutput2 := "search example.com" 954 expectedOutput3 := "options timeout:3" 955 out, err = d.Cmd("exec", id, "cat", "/etc/resolv.conf") 956 assert.NilError(c, err, out) 957 assert.Assert(c, strings.Contains(out, expectedOutput1), "Expected '%s', but got %q", expectedOutput1, out) 958 assert.Assert(c, strings.Contains(out, expectedOutput2), "Expected '%s', but got %q", expectedOutput2, out) 959 assert.Assert(c, strings.Contains(out, expectedOutput3), "Expected '%s', but got %q", expectedOutput3, out) 960 } 961 962 func (s *DockerSwarmSuite) TestDNSConfigUpdate(c *testing.T) { 963 d := s.AddDaemon(c, true, true) 964 965 // Create a service 966 name := "top" 967 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "busybox", "top") 968 assert.NilError(c, err, out) 969 970 // Make sure task has been deployed. 971 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 972 973 out, err = d.Cmd("service", "update", "--detach", "--dns-add=1.2.3.4", "--dns-search-add=example.com", "--dns-option-add=timeout:3", name) 974 assert.NilError(c, err, out) 975 976 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.DNSConfig }}", name) 977 assert.NilError(c, err, out) 978 assert.Equal(c, strings.TrimSpace(out), "{[1.2.3.4] [example.com] [timeout:3]}") 979 } 980 981 func getNodeStatus(c *testing.T, d *daemon.Daemon) swarm.LocalNodeState { 982 info := d.SwarmInfo(c) 983 return info.LocalNodeState 984 } 985 986 func checkKeyIsEncrypted(d *daemon.Daemon) func(*testing.T) (interface{}, string) { 987 return func(c *testing.T) (interface{}, string) { 988 keyBytes, err := ioutil.ReadFile(filepath.Join(d.Folder, "root", "swarm", "certificates", "swarm-node.key")) 989 if err != nil { 990 return fmt.Errorf("error reading key: %v", err), "" 991 } 992 993 keyBlock, _ := pem.Decode(keyBytes) 994 if keyBlock == nil { 995 return fmt.Errorf("invalid PEM-encoded private key"), "" 996 } 997 998 return keyutils.IsEncryptedPEMBlock(keyBlock), "" 999 } 1000 } 1001 1002 func checkSwarmLockedToUnlocked(c *testing.T, d *daemon.Daemon) { 1003 // Wait for the PEM file to become unencrypted 1004 poll.WaitOn(c, pollCheck(c, checkKeyIsEncrypted(d), checker.Equals(false)), poll.WithTimeout(defaultReconciliationTimeout)) 1005 1006 d.RestartNode(c) 1007 poll.WaitOn(c, pollCheck(c, d.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second)) 1008 } 1009 1010 func checkSwarmUnlockedToLocked(c *testing.T, d *daemon.Daemon) { 1011 // Wait for the PEM file to become encrypted 1012 poll.WaitOn(c, pollCheck(c, checkKeyIsEncrypted(d), checker.Equals(true)), poll.WithTimeout(defaultReconciliationTimeout)) 1013 1014 d.RestartNode(c) 1015 poll.WaitOn(c, pollCheck(c, d.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateLocked)), poll.WithTimeout(time.Second)) 1016 } 1017 1018 func (s *DockerSwarmSuite) TestUnlockEngineAndUnlockedSwarm(c *testing.T) { 1019 d := s.AddDaemon(c, false, false) 1020 1021 // unlocking a normal engine should return an error - it does not even ask for the key 1022 cmd := d.Command("swarm", "unlock") 1023 result := icmd.RunCmd(cmd) 1024 result.Assert(c, icmd.Expected{ 1025 ExitCode: 1, 1026 }) 1027 out := result.Combined() 1028 assert.Assert(c, strings.Contains(result.Combined(), "Error: This node is not part of a swarm"), out) 1029 assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key"), out) 1030 out, err := d.Cmd("swarm", "init") 1031 assert.NilError(c, err, out) 1032 1033 // unlocking an unlocked swarm should return an error - it does not even ask for the key 1034 cmd = d.Command("swarm", "unlock") 1035 result = icmd.RunCmd(cmd) 1036 result.Assert(c, icmd.Expected{ 1037 ExitCode: 1, 1038 }) 1039 out = result.Combined() 1040 assert.Assert(c, strings.Contains(result.Combined(), "Error: swarm is not locked"), out) 1041 assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key"), out) 1042 } 1043 1044 func (s *DockerSwarmSuite) TestSwarmInitLocked(c *testing.T) { 1045 d := s.AddDaemon(c, false, false) 1046 1047 outs, err := d.Cmd("swarm", "init", "--autolock") 1048 assert.Assert(c, err == nil, outs) 1049 unlockKey := getUnlockKey(d, c, outs) 1050 1051 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1052 1053 // It starts off locked 1054 d.RestartNode(c) 1055 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1056 1057 cmd := d.Command("swarm", "unlock") 1058 cmd.Stdin = bytes.NewBufferString("wrong-secret-key") 1059 icmd.RunCmd(cmd).Assert(c, icmd.Expected{ 1060 ExitCode: 1, 1061 Err: "invalid key", 1062 }) 1063 1064 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1065 1066 cmd = d.Command("swarm", "unlock") 1067 cmd.Stdin = bytes.NewBufferString(unlockKey) 1068 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1069 1070 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1071 1072 outs, err = d.Cmd("node", "ls") 1073 assert.Assert(c, err == nil, outs) 1074 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1075 outs, err = d.Cmd("swarm", "update", "--autolock=false") 1076 assert.Assert(c, err == nil, outs) 1077 1078 checkSwarmLockedToUnlocked(c, d) 1079 1080 outs, err = d.Cmd("node", "ls") 1081 assert.Assert(c, err == nil, outs) 1082 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1083 } 1084 1085 func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *testing.T) { 1086 d := s.AddDaemon(c, false, false) 1087 1088 outs, err := d.Cmd("swarm", "init", "--autolock") 1089 assert.Assert(c, err == nil, outs) 1090 1091 // It starts off locked 1092 d.RestartNode(c) 1093 1094 info := d.SwarmInfo(c) 1095 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateLocked) 1096 1097 outs, _ = d.Cmd("node", "ls") 1098 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1099 // `docker swarm leave` a locked swarm without --force will return an error 1100 outs, _ = d.Cmd("swarm", "leave") 1101 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and locked."), outs) 1102 // It is OK for user to leave a locked swarm with --force 1103 outs, err = d.Cmd("swarm", "leave", "--force") 1104 assert.Assert(c, err == nil, outs) 1105 1106 info = d.SwarmInfo(c) 1107 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive) 1108 1109 outs, err = d.Cmd("swarm", "init") 1110 assert.Assert(c, err == nil, outs) 1111 1112 info = d.SwarmInfo(c) 1113 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive) 1114 } 1115 1116 func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *testing.T) { 1117 d1 := s.AddDaemon(c, true, true) 1118 d2 := s.AddDaemon(c, true, true) 1119 d3 := s.AddDaemon(c, true, true) 1120 1121 // they start off unlocked 1122 d2.RestartNode(c) 1123 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1124 1125 // stop this one so it does not get autolock info 1126 d2.Stop(c) 1127 1128 // enable autolock 1129 outs, err := d1.Cmd("swarm", "update", "--autolock") 1130 assert.Assert(c, err == nil, outs) 1131 unlockKey := getUnlockKey(d1, c, outs) 1132 1133 // The ones that got the cluster update should be set to locked 1134 for _, d := range []*daemon.Daemon{d1, d3} { 1135 checkSwarmUnlockedToLocked(c, d) 1136 1137 cmd := d.Command("swarm", "unlock") 1138 cmd.Stdin = bytes.NewBufferString(unlockKey) 1139 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1140 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1141 } 1142 1143 // d2 never got the cluster update, so it is still set to unlocked 1144 d2.StartNode(c) 1145 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1146 1147 // d2 is now set to lock 1148 checkSwarmUnlockedToLocked(c, d2) 1149 1150 // leave it locked, and set the cluster to no longer autolock 1151 outs, err = d1.Cmd("swarm", "update", "--autolock=false") 1152 assert.Assert(c, err == nil, "out: %v", outs) 1153 1154 // the ones that got the update are now set to unlocked 1155 for _, d := range []*daemon.Daemon{d1, d3} { 1156 checkSwarmLockedToUnlocked(c, d) 1157 } 1158 1159 // d2 still locked 1160 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateLocked) 1161 1162 // unlock it 1163 cmd := d2.Command("swarm", "unlock") 1164 cmd.Stdin = bytes.NewBufferString(unlockKey) 1165 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1166 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1167 1168 // once it's caught up, d2 is set to not be locked 1169 checkSwarmLockedToUnlocked(c, d2) 1170 1171 // managers who join now are never set to locked in the first place 1172 d4 := s.AddDaemon(c, true, true) 1173 d4.RestartNode(c) 1174 assert.Equal(c, getNodeStatus(c, d4), swarm.LocalNodeStateActive) 1175 } 1176 1177 func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *testing.T) { 1178 d1 := s.AddDaemon(c, true, true) 1179 1180 // enable autolock 1181 outs, err := d1.Cmd("swarm", "update", "--autolock") 1182 assert.Assert(c, err == nil, "out: %v", outs) 1183 unlockKey := getUnlockKey(d1, c, outs) 1184 1185 // joined workers start off unlocked 1186 d2 := s.AddDaemon(c, true, false) 1187 d2.RestartNode(c) 1188 poll.WaitOn(c, pollCheck(c, d2.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second)) 1189 1190 // promote worker 1191 outs, err = d1.Cmd("node", "promote", d2.NodeID()) 1192 assert.NilError(c, err) 1193 assert.Assert(c, strings.Contains(outs, "promoted to a manager in the swarm"), outs) 1194 // join new manager node 1195 d3 := s.AddDaemon(c, true, true) 1196 1197 // both new nodes are locked 1198 for _, d := range []*daemon.Daemon{d2, d3} { 1199 checkSwarmUnlockedToLocked(c, d) 1200 1201 cmd := d.Command("swarm", "unlock") 1202 cmd.Stdin = bytes.NewBufferString(unlockKey) 1203 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1204 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1205 } 1206 1207 // demote manager back to worker - workers are not locked 1208 outs, err = d1.Cmd("node", "demote", d3.NodeID()) 1209 assert.NilError(c, err) 1210 assert.Assert(c, strings.Contains(outs, "demoted in the swarm"), outs) 1211 // Wait for it to actually be demoted, for the key and cert to be replaced. 1212 // Then restart and assert that the node is not locked. If we don't wait for the cert 1213 // to be replaced, then the node still has the manager TLS key which is still locked 1214 // (because we never want a manager TLS key to be on disk unencrypted if the cluster 1215 // is set to autolock) 1216 poll.WaitOn(c, pollCheck(c, d3.CheckControlAvailable, checker.False()), poll.WithTimeout(defaultReconciliationTimeout)) 1217 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 1218 certBytes, err := ioutil.ReadFile(filepath.Join(d3.Folder, "root", "swarm", "certificates", "swarm-node.crt")) 1219 if err != nil { 1220 return "", fmt.Sprintf("error: %v", err) 1221 } 1222 certs, err := helpers.ParseCertificatesPEM(certBytes) 1223 if err == nil && len(certs) > 0 && len(certs[0].Subject.OrganizationalUnit) > 0 { 1224 return certs[0].Subject.OrganizationalUnit[0], "" 1225 } 1226 return "", "could not get organizational unit from certificate" 1227 }, checker.Equals("swarm-worker")), poll.WithTimeout(defaultReconciliationTimeout)) 1228 1229 // by now, it should *never* be locked on restart 1230 d3.RestartNode(c) 1231 poll.WaitOn(c, pollCheck(c, d3.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second)) 1232 } 1233 1234 func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *testing.T) { 1235 d := s.AddDaemon(c, true, true) 1236 1237 outs, err := d.Cmd("swarm", "update", "--autolock") 1238 assert.Assert(c, err == nil, "out: %v", outs) 1239 unlockKey := getUnlockKey(d, c, outs) 1240 1241 // Rotate multiple times 1242 for i := 0; i != 3; i++ { 1243 outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate") 1244 assert.Assert(c, err == nil, "out: %v", outs) 1245 // Strip \n 1246 newUnlockKey := outs[:len(outs)-1] 1247 assert.Assert(c, newUnlockKey != "") 1248 assert.Assert(c, newUnlockKey != unlockKey) 1249 1250 d.RestartNode(c) 1251 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1252 1253 outs, _ = d.Cmd("node", "ls") 1254 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1255 cmd := d.Command("swarm", "unlock") 1256 cmd.Stdin = bytes.NewBufferString(unlockKey) 1257 result := icmd.RunCmd(cmd) 1258 1259 if result.Error == nil { 1260 // On occasion, the daemon may not have finished 1261 // rotating the KEK before restarting. The test is 1262 // intentionally written to explore this behavior. 1263 // When this happens, unlocking with the old key will 1264 // succeed. If we wait for the rotation to happen and 1265 // restart again, the new key should be required this 1266 // time. 1267 1268 time.Sleep(3 * time.Second) 1269 1270 d.RestartNode(c) 1271 1272 cmd = d.Command("swarm", "unlock") 1273 cmd.Stdin = bytes.NewBufferString(unlockKey) 1274 result = icmd.RunCmd(cmd) 1275 } 1276 result.Assert(c, icmd.Expected{ 1277 ExitCode: 1, 1278 Err: "invalid key", 1279 }) 1280 1281 outs, _ = d.Cmd("node", "ls") 1282 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1283 cmd = d.Command("swarm", "unlock") 1284 cmd.Stdin = bytes.NewBufferString(newUnlockKey) 1285 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1286 1287 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1288 1289 retry := 0 1290 for { 1291 // an issue sometimes prevents leader to be available right away 1292 outs, err = d.Cmd("node", "ls") 1293 if err != nil && retry < 5 { 1294 if strings.Contains(outs, "swarm does not have a leader") { 1295 retry++ 1296 time.Sleep(3 * time.Second) 1297 continue 1298 } 1299 } 1300 assert.NilError(c, err) 1301 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1302 break 1303 } 1304 1305 unlockKey = newUnlockKey 1306 } 1307 } 1308 1309 // This differs from `TestSwarmRotateUnlockKey` because that one rotates a single node, which is the leader. 1310 // This one keeps the leader up, and asserts that other manager nodes in the cluster also have their unlock 1311 // key rotated. 1312 func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *testing.T) { 1313 if runtime.GOARCH == "s390x" { 1314 c.Skip("Disabled on s390x") 1315 } 1316 if runtime.GOARCH == "ppc64le" { 1317 c.Skip("Disabled on ppc64le") 1318 } 1319 1320 d1 := s.AddDaemon(c, true, true) // leader - don't restart this one, we don't want leader election delays 1321 d2 := s.AddDaemon(c, true, true) 1322 d3 := s.AddDaemon(c, true, true) 1323 1324 outs, err := d1.Cmd("swarm", "update", "--autolock") 1325 assert.Assert(c, err == nil, outs) 1326 unlockKey := getUnlockKey(d1, c, outs) 1327 1328 // Rotate multiple times 1329 for i := 0; i != 3; i++ { 1330 outs, err = d1.Cmd("swarm", "unlock-key", "-q", "--rotate") 1331 assert.Assert(c, err == nil, outs) 1332 // Strip \n 1333 newUnlockKey := outs[:len(outs)-1] 1334 assert.Assert(c, newUnlockKey != "") 1335 assert.Assert(c, newUnlockKey != unlockKey) 1336 1337 d2.RestartNode(c) 1338 d3.RestartNode(c) 1339 1340 for _, d := range []*daemon.Daemon{d2, d3} { 1341 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1342 1343 outs, _ := d.Cmd("node", "ls") 1344 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1345 cmd := d.Command("swarm", "unlock") 1346 cmd.Stdin = bytes.NewBufferString(unlockKey) 1347 result := icmd.RunCmd(cmd) 1348 1349 if result.Error == nil { 1350 // On occasion, the daemon may not have finished 1351 // rotating the KEK before restarting. The test is 1352 // intentionally written to explore this behavior. 1353 // When this happens, unlocking with the old key will 1354 // succeed. If we wait for the rotation to happen and 1355 // restart again, the new key should be required this 1356 // time. 1357 1358 time.Sleep(3 * time.Second) 1359 1360 d.RestartNode(c) 1361 1362 cmd = d.Command("swarm", "unlock") 1363 cmd.Stdin = bytes.NewBufferString(unlockKey) 1364 result = icmd.RunCmd(cmd) 1365 } 1366 result.Assert(c, icmd.Expected{ 1367 ExitCode: 1, 1368 Err: "invalid key", 1369 }) 1370 1371 outs, _ = d.Cmd("node", "ls") 1372 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1373 cmd = d.Command("swarm", "unlock") 1374 cmd.Stdin = bytes.NewBufferString(newUnlockKey) 1375 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1376 1377 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1378 1379 retry := 0 1380 for { 1381 // an issue sometimes prevents leader to be available right away 1382 outs, err = d.Cmd("node", "ls") 1383 if err != nil && retry < 5 { 1384 if strings.Contains(outs, "swarm does not have a leader") { 1385 retry++ 1386 c.Logf("[%s] got 'swarm does not have a leader'. retrying (attempt %d/5)", d.ID(), retry) 1387 time.Sleep(3 * time.Second) 1388 continue 1389 } else { 1390 c.Logf("[%s] gave error: '%v'. retrying (attempt %d/5): %s", d.ID(), err, retry, outs) 1391 } 1392 } 1393 assert.NilError(c, err, "[%s] failed after %d retries: %v (%s)", d.ID(), retry, err, outs) 1394 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked"), outs) 1395 break 1396 } 1397 } 1398 1399 unlockKey = newUnlockKey 1400 } 1401 } 1402 1403 func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *testing.T) { 1404 d := s.AddDaemon(c, true, true) 1405 1406 for i := 0; i < 2; i++ { 1407 // set to lock 1408 outs, err := d.Cmd("swarm", "update", "--autolock") 1409 assert.Assert(c, err == nil, "out: %v", outs) 1410 assert.Assert(c, strings.Contains(outs, "docker swarm unlock"), outs) 1411 unlockKey := getUnlockKey(d, c, outs) 1412 1413 checkSwarmUnlockedToLocked(c, d) 1414 1415 cmd := d.Command("swarm", "unlock") 1416 cmd.Stdin = bytes.NewBufferString(unlockKey) 1417 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1418 1419 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1420 1421 outs, err = d.Cmd("swarm", "update", "--autolock=false") 1422 assert.Assert(c, err == nil, "out: %v", outs) 1423 1424 checkSwarmLockedToUnlocked(c, d) 1425 } 1426 } 1427 1428 func (s *DockerSwarmSuite) TestExtraHosts(c *testing.T) { 1429 d := s.AddDaemon(c, true, true) 1430 1431 // Create a service 1432 name := "top" 1433 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top") 1434 assert.NilError(c, err, out) 1435 1436 // Make sure task has been deployed. 1437 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1438 1439 // We need to get the container id. 1440 out, err = d.Cmd("ps", "-a", "-q", "--no-trunc") 1441 assert.NilError(c, err, out) 1442 id := strings.TrimSpace(out) 1443 1444 // Compare against expected output. 1445 expectedOutput := "1.2.3.4\texample.com" 1446 out, err = d.Cmd("exec", id, "cat", "/etc/hosts") 1447 assert.NilError(c, err, out) 1448 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 1449 } 1450 1451 func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *testing.T) { 1452 d1 := s.AddDaemon(c, true, true) 1453 d2 := s.AddDaemon(c, true, false) 1454 d3 := s.AddDaemon(c, true, false) 1455 1456 // Manager Addresses will always show Node 1's address 1457 expectedOutput := fmt.Sprintf("127.0.0.1:%d", d1.SwarmPort) 1458 1459 out, err := d1.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}") 1460 assert.NilError(c, err, out) 1461 assert.Assert(c, strings.Contains(out, expectedOutput), out) 1462 1463 out, err = d2.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}") 1464 assert.NilError(c, err, out) 1465 assert.Assert(c, strings.Contains(out, expectedOutput), out) 1466 1467 out, err = d3.Cmd("info", "--format", "{{ (index .Swarm.RemoteManagers 0).Addr }}") 1468 assert.NilError(c, err, out) 1469 assert.Assert(c, strings.Contains(out, expectedOutput), out) 1470 } 1471 1472 func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *testing.T) { 1473 d := s.AddDaemon(c, true, true) 1474 1475 out, err := d.Cmd("network", "create", "-d", "overlay", "--ipam-opt", "foo=bar", "foo") 1476 assert.NilError(c, err, out) 1477 assert.Assert(c, strings.TrimSpace(out) != "") 1478 1479 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1480 out = strings.TrimSpace(out) 1481 assert.NilError(c, err, out) 1482 assert.Assert(c, strings.Contains(out, "foo:bar"), out) 1483 assert.Assert(c, strings.Contains(out, "com.docker.network.ipam.serial:true"), out) 1484 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--network=foo", "--name", "top", "busybox", "top") 1485 assert.NilError(c, err, out) 1486 1487 // make sure task has been deployed. 1488 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1489 1490 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1491 assert.NilError(c, err, out) 1492 assert.Assert(c, strings.Contains(out, "foo:bar"), out) 1493 assert.Assert(c, strings.Contains(out, "com.docker.network.ipam.serial:true"), out) 1494 } 1495 1496 // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID. 1497 // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1". 1498 func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *testing.T) { 1499 d := s.AddDaemon(c, true, true) 1500 out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress") 1501 assert.NilError(c, err, "out: %v", out) 1502 ingressID := strings.TrimSpace(out) 1503 assert.Assert(c, ingressID != "") 1504 1505 // create a network of which name is the prefix of the ID of an overlay network 1506 // (ingressID in this case) 1507 newNetName := ingressID[0:2] 1508 out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName) 1509 // In #27866, it was failing because of "network with name %s already exists" 1510 assert.NilError(c, err, "out: %v", out) 1511 out, err = d.Cmd("network", "rm", newNetName) 1512 assert.NilError(c, err, "out: %v", out) 1513 } 1514 1515 // Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303 1516 // This test creates two networks with the same name sequentially, with various drivers. 1517 // Since the operations in this test are done sequentially, the 2nd call should fail with 1518 // "network with name FOO already exists". 1519 // Note that it is to ok have multiple networks with the same name if the operations are done 1520 // in parallel. (#18864) 1521 func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *testing.T) { 1522 d := s.AddDaemon(c, true, true) 1523 drivers := []string{"bridge", "overlay"} 1524 for i, driver1 := range drivers { 1525 for _, driver2 := range drivers { 1526 c.Run(fmt.Sprintf("driver %s then %s", driver1, driver2), func(c *testing.T) { 1527 nwName := fmt.Sprintf("network-test-%d", i) 1528 out, err := d.Cmd("network", "create", "--driver", driver1, nwName) 1529 assert.NilError(c, err, "out: %v", out) 1530 out, err = d.Cmd("network", "create", "--driver", driver2, nwName) 1531 assert.Assert(c, strings.Contains(out, fmt.Sprintf("network with name %s already exists", nwName)), out) 1532 assert.ErrorContains(c, err, "") 1533 out, err = d.Cmd("network", "rm", nwName) 1534 assert.NilError(c, err, "out: %v", out) 1535 }) 1536 } 1537 } 1538 } 1539 1540 func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *testing.T) { 1541 d := s.AddDaemon(c, true, true) 1542 1543 out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--publish", "5005:80", "--publish", "5006:80", "--publish", "80", "--publish", "80", "busybox", "top") 1544 assert.NilError(c, err, out) 1545 id := strings.TrimSpace(out) 1546 1547 // make sure task has been deployed. 1548 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1549 1550 // Total len = 4, with 2 dynamic ports and 2 non-dynamic ports 1551 // Dynamic ports are likely to be 30000 and 30001 but doesn't matter 1552 out, err = d.Cmd("service", "inspect", "--format", "{{.Endpoint.Ports}} len={{len .Endpoint.Ports}}", id) 1553 assert.NilError(c, err, out) 1554 assert.Assert(c, strings.Contains(out, "len=4"), out) 1555 assert.Assert(c, strings.Contains(out, "{ tcp 80 5005 ingress}"), out) 1556 assert.Assert(c, strings.Contains(out, "{ tcp 80 5006 ingress}"), out) 1557 } 1558 1559 func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *testing.T) { 1560 d := s.AddDaemon(c, true, true) 1561 1562 out, err := d.Cmd("node", "ls") 1563 assert.NilError(c, err) 1564 assert.Assert(c, !strings.Contains(out, "Drain"), out) 1565 out, err = d.Cmd("swarm", "join-token", "-q", "manager") 1566 assert.NilError(c, err) 1567 assert.Assert(c, strings.TrimSpace(out) != "") 1568 1569 token := strings.TrimSpace(out) 1570 1571 d1 := s.AddDaemon(c, false, false) 1572 1573 out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.SwarmListenAddr()) 1574 assert.NilError(c, err) 1575 assert.Assert(c, strings.TrimSpace(out) != "") 1576 1577 out, err = d.Cmd("node", "ls") 1578 assert.NilError(c, err) 1579 assert.Assert(c, strings.Contains(out, "Drain"), out) 1580 out, err = d1.Cmd("node", "ls") 1581 assert.NilError(c, err) 1582 assert.Assert(c, strings.Contains(out, "Drain"), out) 1583 } 1584 1585 func (s *DockerSwarmSuite) TestSwarmInitWithDrain(c *testing.T) { 1586 d := s.AddDaemon(c, false, false) 1587 1588 out, err := d.Cmd("swarm", "init", "--availability", "drain") 1589 assert.NilError(c, err, "out: %v", out) 1590 1591 out, err = d.Cmd("node", "ls") 1592 assert.NilError(c, err) 1593 assert.Assert(c, strings.Contains(out, "Drain")) 1594 } 1595 1596 func (s *DockerSwarmSuite) TestSwarmReadonlyRootfs(c *testing.T) { 1597 testRequires(c, DaemonIsLinux, UserNamespaceROMount) 1598 1599 d := s.AddDaemon(c, true, true) 1600 1601 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--read-only", "busybox", "top") 1602 assert.NilError(c, err, out) 1603 1604 // make sure task has been deployed. 1605 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1606 1607 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.ReadOnly }}", "top") 1608 assert.NilError(c, err, out) 1609 assert.Equal(c, strings.TrimSpace(out), "true") 1610 1611 containers := d.ActiveContainers(c) 1612 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.HostConfig.ReadonlyRootfs}}", containers[0]) 1613 assert.NilError(c, err, out) 1614 assert.Equal(c, strings.TrimSpace(out), "true") 1615 } 1616 1617 func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *testing.T) { 1618 d := s.AddDaemon(c, true, true) 1619 1620 name := "foo" 1621 options := types.NetworkCreate{ 1622 CheckDuplicate: false, 1623 Driver: "bridge", 1624 } 1625 1626 cli := d.NewClientT(c) 1627 defer cli.Close() 1628 1629 n1, err := cli.NetworkCreate(context.Background(), name, options) 1630 assert.NilError(c, err) 1631 1632 // Full ID always works 1633 out, err := d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1634 assert.NilError(c, err, out) 1635 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1636 1637 // Name works if it is unique 1638 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1639 assert.NilError(c, err, out) 1640 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1641 1642 n2, err := cli.NetworkCreate(context.Background(), name, options) 1643 assert.NilError(c, err) 1644 // Full ID always works 1645 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1646 assert.NilError(c, err, out) 1647 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1648 1649 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID) 1650 assert.NilError(c, err, out) 1651 assert.Equal(c, strings.TrimSpace(out), n2.ID) 1652 1653 // Name with duplicates 1654 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1655 assert.ErrorContains(c, err, "", out) 1656 assert.Assert(c, strings.Contains(out, "2 matches found based on name"), out) 1657 out, err = d.Cmd("network", "rm", n2.ID) 1658 assert.NilError(c, err, out) 1659 1660 // Duplicates with name but with different driver 1661 options.Driver = "overlay" 1662 1663 n2, err = cli.NetworkCreate(context.Background(), name, options) 1664 assert.NilError(c, err) 1665 1666 // Full ID always works 1667 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1668 assert.NilError(c, err, out) 1669 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1670 1671 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID) 1672 assert.NilError(c, err, out) 1673 assert.Equal(c, strings.TrimSpace(out), n2.ID) 1674 1675 // Name with duplicates 1676 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1677 assert.ErrorContains(c, err, "", out) 1678 assert.Assert(c, strings.Contains(out, "2 matches found based on name"), out) 1679 } 1680 1681 func (s *DockerSwarmSuite) TestSwarmStopSignal(c *testing.T) { 1682 testRequires(c, DaemonIsLinux, UserNamespaceROMount) 1683 1684 d := s.AddDaemon(c, true, true) 1685 1686 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--stop-signal=SIGHUP", "busybox", "top") 1687 assert.NilError(c, err, out) 1688 1689 // make sure task has been deployed. 1690 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1691 1692 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top") 1693 assert.NilError(c, err, out) 1694 assert.Equal(c, strings.TrimSpace(out), "SIGHUP") 1695 1696 containers := d.ActiveContainers(c) 1697 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.StopSignal}}", containers[0]) 1698 assert.NilError(c, err, out) 1699 assert.Equal(c, strings.TrimSpace(out), "SIGHUP") 1700 1701 out, err = d.Cmd("service", "update", "--detach", "--stop-signal=SIGUSR1", "top") 1702 assert.NilError(c, err, out) 1703 1704 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top") 1705 assert.NilError(c, err, out) 1706 assert.Equal(c, strings.TrimSpace(out), "SIGUSR1") 1707 } 1708 1709 func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *testing.T) { 1710 d := s.AddDaemon(c, true, true) 1711 1712 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top1", "busybox", "top") 1713 assert.NilError(c, err, out) 1714 assert.Assert(c, strings.TrimSpace(out) != "") 1715 1716 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top2", "--mode=global", "busybox", "top") 1717 assert.NilError(c, err, out) 1718 assert.Assert(c, strings.TrimSpace(out) != "") 1719 1720 // make sure task has been deployed. 1721 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout)) 1722 1723 out, err = d.Cmd("service", "ls") 1724 assert.NilError(c, err, out) 1725 assert.Assert(c, strings.Contains(out, "top1"), out) 1726 assert.Assert(c, strings.Contains(out, "top2"), out) 1727 assert.Assert(c, !strings.Contains(out, "localnet"), out) 1728 out, err = d.Cmd("service", "ls", "--filter", "mode=global") 1729 assert.Assert(c, !strings.Contains(out, "top1"), out) 1730 assert.Assert(c, strings.Contains(out, "top2"), out) 1731 assert.NilError(c, err, out) 1732 1733 out, err = d.Cmd("service", "ls", "--filter", "mode=replicated") 1734 assert.NilError(c, err, out) 1735 assert.Assert(c, strings.Contains(out, "top1"), out) 1736 assert.Assert(c, !strings.Contains(out, "top2"), out) 1737 } 1738 1739 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedDataPathAddr(c *testing.T) { 1740 d := s.AddDaemon(c, false, false) 1741 1742 out, err := d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0") 1743 assert.ErrorContains(c, err, "") 1744 assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP"), out) 1745 out, err = d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0:2000") 1746 assert.ErrorContains(c, err, "") 1747 assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP"), out) 1748 } 1749 1750 func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *testing.T) { 1751 d := s.AddDaemon(c, true, true) 1752 1753 out, err := d.Cmd("swarm", "join-token", "-q", "worker") 1754 assert.NilError(c, err) 1755 assert.Assert(c, strings.TrimSpace(out) != "") 1756 1757 token := strings.TrimSpace(out) 1758 1759 // Verify that back to back join/leave does not cause panics 1760 d1 := s.AddDaemon(c, false, false) 1761 for i := 0; i < 10; i++ { 1762 out, err = d1.Cmd("swarm", "join", "--token", token, d.SwarmListenAddr()) 1763 assert.NilError(c, err) 1764 assert.Assert(c, strings.TrimSpace(out) != "") 1765 1766 _, err = d1.Cmd("swarm", "leave") 1767 assert.NilError(c, err) 1768 } 1769 } 1770 1771 const defaultRetryCount = 10 1772 1773 func waitForEvent(c *testing.T, d *daemon.Daemon, since string, filter string, event string, retry int) string { 1774 if retry < 1 { 1775 c.Fatalf("retry count %d is invalid. It should be no less than 1", retry) 1776 return "" 1777 } 1778 var out string 1779 for i := 0; i < retry; i++ { 1780 until := daemonUnixTime(c) 1781 var err error 1782 if len(filter) > 0 { 1783 out, err = d.Cmd("events", "--since", since, "--until", until, filter) 1784 } else { 1785 out, err = d.Cmd("events", "--since", since, "--until", until) 1786 } 1787 assert.NilError(c, err, out) 1788 if strings.Contains(out, event) { 1789 return strings.TrimSpace(out) 1790 } 1791 // no need to sleep after last retry 1792 if i < retry-1 { 1793 time.Sleep(200 * time.Millisecond) 1794 } 1795 } 1796 c.Fatalf("docker events output '%s' doesn't contain event '%s'", out, event) 1797 return "" 1798 } 1799 1800 func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *testing.T) { 1801 d1 := s.AddDaemon(c, true, true) 1802 d2 := s.AddDaemon(c, true, true) 1803 d3 := s.AddDaemon(c, true, false) 1804 1805 // create a network 1806 out, err := d1.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1807 assert.NilError(c, err, out) 1808 networkID := strings.TrimSpace(out) 1809 assert.Assert(c, networkID != "") 1810 1811 // d1, d2 are managers that can get swarm events 1812 waitForEvent(c, d1, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1813 waitForEvent(c, d2, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1814 1815 // d3 is a worker, not able to get cluster events 1816 out = waitForEvent(c, d3, "0", "-f scope=swarm", "", 1) 1817 assert.Assert(c, !strings.Contains(out, "network create "), out) 1818 } 1819 1820 func (s *DockerSwarmSuite) TestSwarmClusterEventsScope(c *testing.T) { 1821 d := s.AddDaemon(c, true, true) 1822 1823 // create a service 1824 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1825 assert.NilError(c, err, out) 1826 serviceID := strings.Split(out, "\n")[0] 1827 1828 // scope swarm filters cluster events 1829 out = waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount) 1830 assert.Assert(c, !strings.Contains(out, "container create "), out) 1831 // all events are returned if scope is not specified 1832 waitForEvent(c, d, "0", "", "service create "+serviceID, 1) 1833 waitForEvent(c, d, "0", "", "container create ", defaultRetryCount) 1834 1835 // scope local only shows non-cluster events 1836 out = waitForEvent(c, d, "0", "-f scope=local", "container create ", 1) 1837 assert.Assert(c, !strings.Contains(out, "service create "), out) 1838 } 1839 1840 func (s *DockerSwarmSuite) TestSwarmClusterEventsType(c *testing.T) { 1841 d := s.AddDaemon(c, true, true) 1842 1843 // create a service 1844 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1845 assert.NilError(c, err, out) 1846 serviceID := strings.Split(out, "\n")[0] 1847 1848 // create a network 1849 out, err = d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1850 assert.NilError(c, err, out) 1851 networkID := strings.TrimSpace(out) 1852 assert.Assert(c, networkID != "") 1853 1854 // filter by service 1855 out = waitForEvent(c, d, "0", "-f type=service", "service create "+serviceID, defaultRetryCount) 1856 assert.Assert(c, !strings.Contains(out, "network create"), out) 1857 // filter by network 1858 out = waitForEvent(c, d, "0", "-f type=network", "network create "+networkID, defaultRetryCount) 1859 assert.Assert(c, !strings.Contains(out, "service create"), out) 1860 } 1861 1862 func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *testing.T) { 1863 d := s.AddDaemon(c, true, true) 1864 1865 // create a service 1866 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1867 assert.NilError(c, err, out) 1868 serviceID := strings.Split(out, "\n")[0] 1869 1870 // validate service create event 1871 waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount) 1872 1873 t1 := daemonUnixTime(c) 1874 out, err = d.Cmd("service", "update", "--force", "--detach=false", "test") 1875 assert.NilError(c, err, out) 1876 1877 // wait for service update start 1878 out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1879 assert.Assert(c, strings.Contains(out, "updatestate.new=updating"), out) 1880 // allow service update complete. This is a service with 1 instance 1881 time.Sleep(400 * time.Millisecond) 1882 out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1883 assert.Assert(c, strings.Contains(out, "updatestate.new=completed, updatestate.old=updating"), out) 1884 // scale service 1885 t2 := daemonUnixTime(c) 1886 out, err = d.Cmd("service", "scale", "test=3") 1887 assert.NilError(c, err, out) 1888 1889 out = waitForEvent(c, d, t2, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1890 assert.Assert(c, strings.Contains(out, "replicas.new=3, replicas.old=1"), out) 1891 // remove service 1892 t3 := daemonUnixTime(c) 1893 out, err = d.Cmd("service", "rm", "test") 1894 assert.NilError(c, err, out) 1895 1896 waitForEvent(c, d, t3, "-f scope=swarm", "service remove "+serviceID, defaultRetryCount) 1897 } 1898 1899 func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *testing.T) { 1900 d1 := s.AddDaemon(c, true, true) 1901 s.AddDaemon(c, true, true) 1902 d3 := s.AddDaemon(c, true, true) 1903 1904 d3ID := d3.NodeID() 1905 waitForEvent(c, d1, "0", "-f scope=swarm", "node create "+d3ID, defaultRetryCount) 1906 1907 t1 := daemonUnixTime(c) 1908 out, err := d1.Cmd("node", "update", "--availability=pause", d3ID) 1909 assert.NilError(c, err, out) 1910 1911 // filter by type 1912 out = waitForEvent(c, d1, t1, "-f type=node", "node update "+d3ID, defaultRetryCount) 1913 assert.Assert(c, strings.Contains(out, "availability.new=pause, availability.old=active"), out) 1914 t2 := daemonUnixTime(c) 1915 out, err = d1.Cmd("node", "demote", d3ID) 1916 assert.NilError(c, err, out) 1917 1918 waitForEvent(c, d1, t2, "-f type=node", "node update "+d3ID, defaultRetryCount) 1919 1920 t3 := daemonUnixTime(c) 1921 out, err = d1.Cmd("node", "rm", "-f", d3ID) 1922 assert.NilError(c, err, out) 1923 1924 // filter by scope 1925 waitForEvent(c, d1, t3, "-f scope=swarm", "node remove "+d3ID, defaultRetryCount) 1926 } 1927 1928 func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *testing.T) { 1929 d := s.AddDaemon(c, true, true) 1930 1931 // create a network 1932 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1933 assert.NilError(c, err, out) 1934 networkID := strings.TrimSpace(out) 1935 1936 waitForEvent(c, d, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1937 1938 // remove network 1939 t1 := daemonUnixTime(c) 1940 out, err = d.Cmd("network", "rm", "foo") 1941 assert.NilError(c, err, out) 1942 1943 // filtered by network 1944 waitForEvent(c, d, t1, "-f type=network", "network remove "+networkID, defaultRetryCount) 1945 } 1946 1947 func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *testing.T) { 1948 d := s.AddDaemon(c, true, true) 1949 1950 testName := "test_secret" 1951 id := d.CreateSecret(c, swarm.SecretSpec{ 1952 Annotations: swarm.Annotations{ 1953 Name: testName, 1954 }, 1955 Data: []byte("TESTINGDATA"), 1956 }) 1957 assert.Assert(c, id != "", "secrets: %s", id) 1958 1959 waitForEvent(c, d, "0", "-f scope=swarm", "secret create "+id, defaultRetryCount) 1960 1961 t1 := daemonUnixTime(c) 1962 d.DeleteSecret(c, id) 1963 // filtered by secret 1964 waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount) 1965 } 1966 1967 func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *testing.T) { 1968 d := s.AddDaemon(c, true, true) 1969 1970 testName := "test_config" 1971 id := d.CreateConfig(c, swarm.ConfigSpec{ 1972 Annotations: swarm.Annotations{ 1973 Name: testName, 1974 }, 1975 Data: []byte("TESTINGDATA"), 1976 }) 1977 assert.Assert(c, id != "", "configs: %s", id) 1978 1979 waitForEvent(c, d, "0", "-f scope=swarm", "config create "+id, defaultRetryCount) 1980 1981 t1 := daemonUnixTime(c) 1982 d.DeleteConfig(c, id) 1983 // filtered by config 1984 waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount) 1985 } 1986 1987 func getUnlockKey(d *daemon.Daemon, c *testing.T, autolockOutput string) string { 1988 unlockKey, err := d.Cmd("swarm", "unlock-key", "-q") 1989 assert.Assert(c, err == nil, unlockKey) 1990 unlockKey = strings.TrimSuffix(unlockKey, "\n") 1991 1992 // Check that "docker swarm init --autolock" or "docker swarm update --autolock" 1993 // contains all the expected strings, including the unlock key 1994 assert.Assert(c, strings.Contains(autolockOutput, "docker swarm unlock"), autolockOutput) 1995 assert.Assert(c, strings.Contains(autolockOutput, unlockKey), autolockOutput) 1996 return unlockKey 1997 }