github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+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/assert" 33 "gotest.tools/fs" 34 "gotest.tools/icmd" 35 "gotest.tools/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, "%s", 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+" ")) 214 assert.Assert(c, !strings.Contains(out, name2+" ")) 215 assert.Assert(c, !strings.Contains(out, name3+" ")) 216 out, err = d.Cmd("service", "ls", "--filter", filter2) 217 assert.NilError(c, err, out) 218 assert.Assert(c, strings.Contains(out, name1+" ")) 219 assert.Assert(c, strings.Contains(out, name2+" ")) 220 assert.Assert(c, !strings.Contains(out, name3+" ")) 221 out, err = d.Cmd("service", "ls") 222 assert.NilError(c, err, out) 223 assert.Assert(c, strings.Contains(out, name1+" ")) 224 assert.Assert(c, strings.Contains(out, name2+" ")) 225 assert.Assert(c, strings.Contains(out, name3+" ")) 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)) 241 out, err = d.Cmd("node", "ls", "--filter", "name=none") 242 assert.NilError(c, err, out) 243 assert.Assert(c, !strings.Contains(out, name)) 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")) 262 assert.Assert(c, strings.Contains(out, name+".2")) 263 assert.Assert(c, strings.Contains(out, name+".3")) 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")) 267 assert.Assert(c, !strings.Contains(out, name+".2")) 268 assert.Assert(c, !strings.Contains(out, name+".3")) 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)) 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")) 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")) 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")) 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")) 559 assert.Assert(c, strings.Contains(out, name+".2")) 560 assert.Assert(c, strings.Contains(out, name+".3")) 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")) 564 assert.Assert(c, !strings.Contains(out, name+".2")) 565 assert.Assert(c, !strings.Contains(out, name+".3")) 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")) 569 assert.Assert(c, !strings.Contains(out, name+".2")) 570 assert.Assert(c, !strings.Contains(out, name+".3")) 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)) 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)) 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)) 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")) 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]")) 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 assert.Assert(c, strings.Contains(result.Combined(), "Error: This node is not part of a swarm")) 1028 assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key")) 1029 out, err := d.Cmd("swarm", "init") 1030 assert.NilError(c, err, out) 1031 1032 // unlocking an unlocked swarm should return an error - it does not even ask for the key 1033 cmd = d.Command("swarm", "unlock") 1034 result = icmd.RunCmd(cmd) 1035 result.Assert(c, icmd.Expected{ 1036 ExitCode: 1, 1037 }) 1038 assert.Assert(c, strings.Contains(result.Combined(), "Error: swarm is not locked")) 1039 assert.Assert(c, !strings.Contains(result.Combined(), "Please enter unlock key")) 1040 } 1041 1042 func (s *DockerSwarmSuite) TestSwarmInitLocked(c *testing.T) { 1043 d := s.AddDaemon(c, false, false) 1044 1045 outs, err := d.Cmd("swarm", "init", "--autolock") 1046 assert.Assert(c, err == nil, "%s", outs) 1047 unlockKey := getUnlockKey(d, c, outs) 1048 1049 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1050 1051 // It starts off locked 1052 d.RestartNode(c) 1053 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1054 1055 cmd := d.Command("swarm", "unlock") 1056 cmd.Stdin = bytes.NewBufferString("wrong-secret-key") 1057 icmd.RunCmd(cmd).Assert(c, icmd.Expected{ 1058 ExitCode: 1, 1059 Err: "invalid key", 1060 }) 1061 1062 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1063 1064 cmd = d.Command("swarm", "unlock") 1065 cmd.Stdin = bytes.NewBufferString(unlockKey) 1066 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1067 1068 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1069 1070 outs, err = d.Cmd("node", "ls") 1071 assert.Assert(c, err == nil, "%s", outs) 1072 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1073 outs, err = d.Cmd("swarm", "update", "--autolock=false") 1074 assert.Assert(c, err == nil, "%s", outs) 1075 1076 checkSwarmLockedToUnlocked(c, d) 1077 1078 outs, err = d.Cmd("node", "ls") 1079 assert.Assert(c, err == nil, "%s", outs) 1080 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1081 } 1082 1083 func (s *DockerSwarmSuite) TestSwarmLeaveLocked(c *testing.T) { 1084 d := s.AddDaemon(c, false, false) 1085 1086 outs, err := d.Cmd("swarm", "init", "--autolock") 1087 assert.Assert(c, err == nil, "%s", outs) 1088 1089 // It starts off locked 1090 d.RestartNode(c) 1091 1092 info := d.SwarmInfo(c) 1093 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateLocked) 1094 1095 outs, _ = d.Cmd("node", "ls") 1096 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1097 // `docker swarm leave` a locked swarm without --force will return an error 1098 outs, _ = d.Cmd("swarm", "leave") 1099 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and locked.")) 1100 // It is OK for user to leave a locked swarm with --force 1101 outs, err = d.Cmd("swarm", "leave", "--force") 1102 assert.Assert(c, err == nil, "%s", outs) 1103 1104 info = d.SwarmInfo(c) 1105 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateInactive) 1106 1107 outs, err = d.Cmd("swarm", "init") 1108 assert.Assert(c, err == nil, "%s", outs) 1109 1110 info = d.SwarmInfo(c) 1111 assert.Equal(c, info.LocalNodeState, swarm.LocalNodeStateActive) 1112 } 1113 1114 func (s *DockerSwarmSuite) TestSwarmLockUnlockCluster(c *testing.T) { 1115 d1 := s.AddDaemon(c, true, true) 1116 d2 := s.AddDaemon(c, true, true) 1117 d3 := s.AddDaemon(c, true, true) 1118 1119 // they start off unlocked 1120 d2.RestartNode(c) 1121 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1122 1123 // stop this one so it does not get autolock info 1124 d2.Stop(c) 1125 1126 // enable autolock 1127 outs, err := d1.Cmd("swarm", "update", "--autolock") 1128 assert.Assert(c, err == nil, "%s", outs) 1129 unlockKey := getUnlockKey(d1, c, outs) 1130 1131 // The ones that got the cluster update should be set to locked 1132 for _, d := range []*daemon.Daemon{d1, d3} { 1133 checkSwarmUnlockedToLocked(c, d) 1134 1135 cmd := d.Command("swarm", "unlock") 1136 cmd.Stdin = bytes.NewBufferString(unlockKey) 1137 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1138 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1139 } 1140 1141 // d2 never got the cluster update, so it is still set to unlocked 1142 d2.StartNode(c) 1143 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1144 1145 // d2 is now set to lock 1146 checkSwarmUnlockedToLocked(c, d2) 1147 1148 // leave it locked, and set the cluster to no longer autolock 1149 outs, err = d1.Cmd("swarm", "update", "--autolock=false") 1150 assert.Assert(c, err == nil, "out: %v", outs) 1151 1152 // the ones that got the update are now set to unlocked 1153 for _, d := range []*daemon.Daemon{d1, d3} { 1154 checkSwarmLockedToUnlocked(c, d) 1155 } 1156 1157 // d2 still locked 1158 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateLocked) 1159 1160 // unlock it 1161 cmd := d2.Command("swarm", "unlock") 1162 cmd.Stdin = bytes.NewBufferString(unlockKey) 1163 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1164 assert.Equal(c, getNodeStatus(c, d2), swarm.LocalNodeStateActive) 1165 1166 // once it's caught up, d2 is set to not be locked 1167 checkSwarmLockedToUnlocked(c, d2) 1168 1169 // managers who join now are never set to locked in the first place 1170 d4 := s.AddDaemon(c, true, true) 1171 d4.RestartNode(c) 1172 assert.Equal(c, getNodeStatus(c, d4), swarm.LocalNodeStateActive) 1173 } 1174 1175 func (s *DockerSwarmSuite) TestSwarmJoinPromoteLocked(c *testing.T) { 1176 d1 := s.AddDaemon(c, true, true) 1177 1178 // enable autolock 1179 outs, err := d1.Cmd("swarm", "update", "--autolock") 1180 assert.Assert(c, err == nil, "out: %v", outs) 1181 unlockKey := getUnlockKey(d1, c, outs) 1182 1183 // joined workers start off unlocked 1184 d2 := s.AddDaemon(c, true, false) 1185 d2.RestartNode(c) 1186 poll.WaitOn(c, pollCheck(c, d2.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second)) 1187 1188 // promote worker 1189 outs, err = d1.Cmd("node", "promote", d2.NodeID()) 1190 assert.NilError(c, err) 1191 assert.Assert(c, strings.Contains(outs, "promoted to a manager in the swarm")) 1192 // join new manager node 1193 d3 := s.AddDaemon(c, true, true) 1194 1195 // both new nodes are locked 1196 for _, d := range []*daemon.Daemon{d2, d3} { 1197 checkSwarmUnlockedToLocked(c, d) 1198 1199 cmd := d.Command("swarm", "unlock") 1200 cmd.Stdin = bytes.NewBufferString(unlockKey) 1201 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1202 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1203 } 1204 1205 // demote manager back to worker - workers are not locked 1206 outs, err = d1.Cmd("node", "demote", d3.NodeID()) 1207 assert.NilError(c, err) 1208 assert.Assert(c, strings.Contains(outs, "demoted in the swarm")) 1209 // Wait for it to actually be demoted, for the key and cert to be replaced. 1210 // Then restart and assert that the node is not locked. If we don't wait for the cert 1211 // to be replaced, then the node still has the manager TLS key which is still locked 1212 // (because we never want a manager TLS key to be on disk unencrypted if the cluster 1213 // is set to autolock) 1214 poll.WaitOn(c, pollCheck(c, d3.CheckControlAvailable, checker.False()), poll.WithTimeout(defaultReconciliationTimeout)) 1215 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 1216 certBytes, err := ioutil.ReadFile(filepath.Join(d3.Folder, "root", "swarm", "certificates", "swarm-node.crt")) 1217 if err != nil { 1218 return "", fmt.Sprintf("error: %v", err) 1219 } 1220 certs, err := helpers.ParseCertificatesPEM(certBytes) 1221 if err == nil && len(certs) > 0 && len(certs[0].Subject.OrganizationalUnit) > 0 { 1222 return certs[0].Subject.OrganizationalUnit[0], "" 1223 } 1224 return "", "could not get organizational unit from certificate" 1225 }, checker.Equals("swarm-worker")), poll.WithTimeout(defaultReconciliationTimeout)) 1226 1227 // by now, it should *never* be locked on restart 1228 d3.RestartNode(c) 1229 poll.WaitOn(c, pollCheck(c, d3.CheckLocalNodeState, checker.Equals(swarm.LocalNodeStateActive)), poll.WithTimeout(time.Second)) 1230 } 1231 1232 func (s *DockerSwarmSuite) TestSwarmRotateUnlockKey(c *testing.T) { 1233 d := s.AddDaemon(c, true, true) 1234 1235 outs, err := d.Cmd("swarm", "update", "--autolock") 1236 assert.Assert(c, err == nil, "out: %v", outs) 1237 unlockKey := getUnlockKey(d, c, outs) 1238 1239 // Rotate multiple times 1240 for i := 0; i != 3; i++ { 1241 outs, err = d.Cmd("swarm", "unlock-key", "-q", "--rotate") 1242 assert.Assert(c, err == nil, "out: %v", outs) 1243 // Strip \n 1244 newUnlockKey := outs[:len(outs)-1] 1245 assert.Assert(c, newUnlockKey != "") 1246 assert.Assert(c, newUnlockKey != unlockKey) 1247 1248 d.RestartNode(c) 1249 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1250 1251 outs, _ = d.Cmd("node", "ls") 1252 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1253 cmd := d.Command("swarm", "unlock") 1254 cmd.Stdin = bytes.NewBufferString(unlockKey) 1255 result := icmd.RunCmd(cmd) 1256 1257 if result.Error == nil { 1258 // On occasion, the daemon may not have finished 1259 // rotating the KEK before restarting. The test is 1260 // intentionally written to explore this behavior. 1261 // When this happens, unlocking with the old key will 1262 // succeed. If we wait for the rotation to happen and 1263 // restart again, the new key should be required this 1264 // time. 1265 1266 time.Sleep(3 * time.Second) 1267 1268 d.RestartNode(c) 1269 1270 cmd = d.Command("swarm", "unlock") 1271 cmd.Stdin = bytes.NewBufferString(unlockKey) 1272 result = icmd.RunCmd(cmd) 1273 } 1274 result.Assert(c, icmd.Expected{ 1275 ExitCode: 1, 1276 Err: "invalid key", 1277 }) 1278 1279 outs, _ = d.Cmd("node", "ls") 1280 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1281 cmd = d.Command("swarm", "unlock") 1282 cmd.Stdin = bytes.NewBufferString(newUnlockKey) 1283 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1284 1285 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1286 1287 retry := 0 1288 for { 1289 // an issue sometimes prevents leader to be available right away 1290 outs, err = d.Cmd("node", "ls") 1291 if err != nil && retry < 5 { 1292 if strings.Contains(outs, "swarm does not have a leader") { 1293 retry++ 1294 time.Sleep(3 * time.Second) 1295 continue 1296 } 1297 } 1298 assert.NilError(c, err) 1299 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1300 break 1301 } 1302 1303 unlockKey = newUnlockKey 1304 } 1305 } 1306 1307 // This differs from `TestSwarmRotateUnlockKey` because that one rotates a single node, which is the leader. 1308 // This one keeps the leader up, and asserts that other manager nodes in the cluster also have their unlock 1309 // key rotated. 1310 func (s *DockerSwarmSuite) TestSwarmClusterRotateUnlockKey(c *testing.T) { 1311 if runtime.GOARCH == "s390x" { 1312 c.Skip("Disabled on s390x") 1313 } 1314 if runtime.GOARCH == "ppc64le" { 1315 c.Skip("Disabled on ppc64le") 1316 } 1317 1318 d1 := s.AddDaemon(c, true, true) // leader - don't restart this one, we don't want leader election delays 1319 d2 := s.AddDaemon(c, true, true) 1320 d3 := s.AddDaemon(c, true, true) 1321 1322 outs, err := d1.Cmd("swarm", "update", "--autolock") 1323 assert.Assert(c, err == nil, "%s", outs) 1324 unlockKey := getUnlockKey(d1, c, outs) 1325 1326 // Rotate multiple times 1327 for i := 0; i != 3; i++ { 1328 outs, err = d1.Cmd("swarm", "unlock-key", "-q", "--rotate") 1329 assert.Assert(c, err == nil, "%s", outs) 1330 // Strip \n 1331 newUnlockKey := outs[:len(outs)-1] 1332 assert.Assert(c, newUnlockKey != "") 1333 assert.Assert(c, newUnlockKey != unlockKey) 1334 1335 d2.RestartNode(c) 1336 d3.RestartNode(c) 1337 1338 for _, d := range []*daemon.Daemon{d2, d3} { 1339 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateLocked) 1340 1341 outs, _ := d.Cmd("node", "ls") 1342 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1343 cmd := d.Command("swarm", "unlock") 1344 cmd.Stdin = bytes.NewBufferString(unlockKey) 1345 result := icmd.RunCmd(cmd) 1346 1347 if result.Error == nil { 1348 // On occasion, the daemon may not have finished 1349 // rotating the KEK before restarting. The test is 1350 // intentionally written to explore this behavior. 1351 // When this happens, unlocking with the old key will 1352 // succeed. If we wait for the rotation to happen and 1353 // restart again, the new key should be required this 1354 // time. 1355 1356 time.Sleep(3 * time.Second) 1357 1358 d.RestartNode(c) 1359 1360 cmd = d.Command("swarm", "unlock") 1361 cmd.Stdin = bytes.NewBufferString(unlockKey) 1362 result = icmd.RunCmd(cmd) 1363 } 1364 result.Assert(c, icmd.Expected{ 1365 ExitCode: 1, 1366 Err: "invalid key", 1367 }) 1368 1369 outs, _ = d.Cmd("node", "ls") 1370 assert.Assert(c, strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1371 cmd = d.Command("swarm", "unlock") 1372 cmd.Stdin = bytes.NewBufferString(newUnlockKey) 1373 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1374 1375 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1376 1377 retry := 0 1378 for { 1379 // an issue sometimes prevents leader to be available right away 1380 outs, err = d.Cmd("node", "ls") 1381 if err != nil && retry < 5 { 1382 if strings.Contains(outs, "swarm does not have a leader") { 1383 retry++ 1384 time.Sleep(3 * time.Second) 1385 continue 1386 } 1387 } 1388 assert.Assert(c, err == nil, "%s", outs) 1389 assert.Assert(c, !strings.Contains(outs, "Swarm is encrypted and needs to be unlocked")) 1390 break 1391 } 1392 } 1393 1394 unlockKey = newUnlockKey 1395 } 1396 } 1397 1398 func (s *DockerSwarmSuite) TestSwarmAlternateLockUnlock(c *testing.T) { 1399 d := s.AddDaemon(c, true, true) 1400 1401 for i := 0; i < 2; i++ { 1402 // set to lock 1403 outs, err := d.Cmd("swarm", "update", "--autolock") 1404 assert.Assert(c, err == nil, "out: %v", outs) 1405 assert.Assert(c, strings.Contains(outs, "docker swarm unlock")) 1406 unlockKey := getUnlockKey(d, c, outs) 1407 1408 checkSwarmUnlockedToLocked(c, d) 1409 1410 cmd := d.Command("swarm", "unlock") 1411 cmd.Stdin = bytes.NewBufferString(unlockKey) 1412 icmd.RunCmd(cmd).Assert(c, icmd.Success) 1413 1414 assert.Equal(c, getNodeStatus(c, d), swarm.LocalNodeStateActive) 1415 1416 outs, err = d.Cmd("swarm", "update", "--autolock=false") 1417 assert.Assert(c, err == nil, "out: %v", outs) 1418 1419 checkSwarmLockedToUnlocked(c, d) 1420 } 1421 } 1422 1423 func (s *DockerSwarmSuite) TestExtraHosts(c *testing.T) { 1424 d := s.AddDaemon(c, true, true) 1425 1426 // Create a service 1427 name := "top" 1428 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", name, "--host=example.com:1.2.3.4", "busybox", "top") 1429 assert.NilError(c, err, out) 1430 1431 // Make sure task has been deployed. 1432 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1433 1434 // We need to get the container id. 1435 out, err = d.Cmd("ps", "-a", "-q", "--no-trunc") 1436 assert.NilError(c, err, out) 1437 id := strings.TrimSpace(out) 1438 1439 // Compare against expected output. 1440 expectedOutput := "1.2.3.4\texample.com" 1441 out, err = d.Cmd("exec", id, "cat", "/etc/hosts") 1442 assert.NilError(c, err, out) 1443 assert.Assert(c, strings.Contains(out, expectedOutput), "Expected '%s', but got %q", expectedOutput, out) 1444 } 1445 1446 func (s *DockerSwarmSuite) TestSwarmManagerAddress(c *testing.T) { 1447 d1 := s.AddDaemon(c, true, true) 1448 d2 := s.AddDaemon(c, true, false) 1449 d3 := s.AddDaemon(c, true, false) 1450 1451 // Manager Addresses will always show Node 1's address 1452 expectedOutput := fmt.Sprintf("Manager Addresses:\n 127.0.0.1:%d\n", d1.SwarmPort) 1453 1454 out, err := d1.Cmd("info") 1455 assert.NilError(c, err, out) 1456 assert.Assert(c, strings.Contains(out, expectedOutput)) 1457 1458 out, err = d2.Cmd("info") 1459 assert.NilError(c, err, out) 1460 assert.Assert(c, strings.Contains(out, expectedOutput)) 1461 1462 out, err = d3.Cmd("info") 1463 assert.NilError(c, err, out) 1464 assert.Assert(c, strings.Contains(out, expectedOutput)) 1465 } 1466 1467 func (s *DockerSwarmSuite) TestSwarmNetworkIPAMOptions(c *testing.T) { 1468 d := s.AddDaemon(c, true, true) 1469 1470 out, err := d.Cmd("network", "create", "-d", "overlay", "--ipam-opt", "foo=bar", "foo") 1471 assert.NilError(c, err, out) 1472 assert.Assert(c, strings.TrimSpace(out) != "") 1473 1474 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1475 assert.NilError(c, err, out) 1476 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "foo:bar")) 1477 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "com.docker.network.ipam.serial:true")) 1478 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--network=foo", "--name", "top", "busybox", "top") 1479 assert.NilError(c, err, out) 1480 1481 // make sure task has been deployed. 1482 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1483 1484 out, err = d.Cmd("network", "inspect", "--format", "{{.IPAM.Options}}", "foo") 1485 assert.NilError(c, err, out) 1486 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "foo:bar")) 1487 assert.Assert(c, strings.Contains(strings.TrimSpace(out), "com.docker.network.ipam.serial:true")) 1488 } 1489 1490 // Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID. 1491 // e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1". 1492 func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *testing.T) { 1493 d := s.AddDaemon(c, true, true) 1494 out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress") 1495 assert.NilError(c, err, "out: %v", out) 1496 ingressID := strings.TrimSpace(out) 1497 assert.Assert(c, ingressID != "") 1498 1499 // create a network of which name is the prefix of the ID of an overlay network 1500 // (ingressID in this case) 1501 newNetName := ingressID[0:2] 1502 out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName) 1503 // In #27866, it was failing because of "network with name %s already exists" 1504 assert.NilError(c, err, "out: %v", out) 1505 out, err = d.Cmd("network", "rm", newNetName) 1506 assert.NilError(c, err, "out: %v", out) 1507 } 1508 1509 // Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303 1510 // This test creates two networks with the same name sequentially, with various drivers. 1511 // Since the operations in this test are done sequentially, the 2nd call should fail with 1512 // "network with name FOO already exists". 1513 // Note that it is to ok have multiple networks with the same name if the operations are done 1514 // in parallel. (#18864) 1515 func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *testing.T) { 1516 d := s.AddDaemon(c, true, true) 1517 drivers := []string{"bridge", "overlay"} 1518 for i, driver1 := range drivers { 1519 nwName := fmt.Sprintf("network-test-%d", i) 1520 for _, driver2 := range drivers { 1521 c.Logf("Creating a network named %q with %q, then %q", 1522 nwName, driver1, driver2) 1523 out, err := d.Cmd("network", "create", "--driver", driver1, nwName) 1524 assert.NilError(c, err, "out: %v", out) 1525 out, err = d.Cmd("network", "create", "--driver", driver2, nwName) 1526 assert.Assert(c, strings.Contains(out, fmt.Sprintf("network with name %s already exists", nwName))) 1527 assert.ErrorContains(c, err, "") 1528 c.Logf("As expected, the attempt to network %q with %q failed: %s", 1529 nwName, driver2, out) 1530 out, err = d.Cmd("network", "rm", nwName) 1531 assert.NilError(c, err, "out: %v", out) 1532 } 1533 } 1534 } 1535 1536 func (s *DockerSwarmSuite) TestSwarmPublishDuplicatePorts(c *testing.T) { 1537 d := s.AddDaemon(c, true, true) 1538 1539 out, err := d.Cmd("service", "create", "--no-resolve-image", "--detach=true", "--publish", "5005:80", "--publish", "5006:80", "--publish", "80", "--publish", "80", "busybox", "top") 1540 assert.NilError(c, err, out) 1541 id := strings.TrimSpace(out) 1542 1543 // make sure task has been deployed. 1544 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1545 1546 // Total len = 4, with 2 dynamic ports and 2 non-dynamic ports 1547 // Dynamic ports are likely to be 30000 and 30001 but doesn't matter 1548 out, err = d.Cmd("service", "inspect", "--format", "{{.Endpoint.Ports}} len={{len .Endpoint.Ports}}", id) 1549 assert.NilError(c, err, out) 1550 assert.Assert(c, strings.Contains(out, "len=4")) 1551 assert.Assert(c, strings.Contains(out, "{ tcp 80 5005 ingress}")) 1552 assert.Assert(c, strings.Contains(out, "{ tcp 80 5006 ingress}")) 1553 } 1554 1555 func (s *DockerSwarmSuite) TestSwarmJoinWithDrain(c *testing.T) { 1556 d := s.AddDaemon(c, true, true) 1557 1558 out, err := d.Cmd("node", "ls") 1559 assert.NilError(c, err) 1560 assert.Assert(c, !strings.Contains(out, "Drain")) 1561 out, err = d.Cmd("swarm", "join-token", "-q", "manager") 1562 assert.NilError(c, err) 1563 assert.Assert(c, strings.TrimSpace(out) != "") 1564 1565 token := strings.TrimSpace(out) 1566 1567 d1 := s.AddDaemon(c, false, false) 1568 1569 out, err = d1.Cmd("swarm", "join", "--availability=drain", "--token", token, d.SwarmListenAddr()) 1570 assert.NilError(c, err) 1571 assert.Assert(c, strings.TrimSpace(out) != "") 1572 1573 out, err = d.Cmd("node", "ls") 1574 assert.NilError(c, err) 1575 assert.Assert(c, strings.Contains(out, "Drain")) 1576 out, err = d1.Cmd("node", "ls") 1577 assert.NilError(c, err) 1578 assert.Assert(c, strings.Contains(out, "Drain")) 1579 } 1580 1581 func (s *DockerSwarmSuite) TestSwarmInitWithDrain(c *testing.T) { 1582 d := s.AddDaemon(c, false, false) 1583 1584 out, err := d.Cmd("swarm", "init", "--availability", "drain") 1585 assert.NilError(c, err, "out: %v", out) 1586 1587 out, err = d.Cmd("node", "ls") 1588 assert.NilError(c, err) 1589 assert.Assert(c, strings.Contains(out, "Drain")) 1590 } 1591 1592 func (s *DockerSwarmSuite) TestSwarmReadonlyRootfs(c *testing.T) { 1593 testRequires(c, DaemonIsLinux, UserNamespaceROMount) 1594 1595 d := s.AddDaemon(c, true, true) 1596 1597 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--read-only", "busybox", "top") 1598 assert.NilError(c, err, out) 1599 1600 // make sure task has been deployed. 1601 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1602 1603 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.ReadOnly }}", "top") 1604 assert.NilError(c, err, out) 1605 assert.Equal(c, strings.TrimSpace(out), "true") 1606 1607 containers := d.ActiveContainers(c) 1608 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.HostConfig.ReadonlyRootfs}}", containers[0]) 1609 assert.NilError(c, err, out) 1610 assert.Equal(c, strings.TrimSpace(out), "true") 1611 } 1612 1613 func (s *DockerSwarmSuite) TestNetworkInspectWithDuplicateNames(c *testing.T) { 1614 d := s.AddDaemon(c, true, true) 1615 1616 name := "foo" 1617 options := types.NetworkCreate{ 1618 CheckDuplicate: false, 1619 Driver: "bridge", 1620 } 1621 1622 cli := d.NewClientT(c) 1623 defer cli.Close() 1624 1625 n1, err := cli.NetworkCreate(context.Background(), name, options) 1626 assert.NilError(c, err) 1627 1628 // Full ID always works 1629 out, err := d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1630 assert.NilError(c, err, out) 1631 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1632 1633 // Name works if it is unique 1634 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1635 assert.NilError(c, err, out) 1636 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1637 1638 n2, err := cli.NetworkCreate(context.Background(), name, options) 1639 assert.NilError(c, err) 1640 // Full ID always works 1641 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1642 assert.NilError(c, err, out) 1643 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1644 1645 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID) 1646 assert.NilError(c, err, out) 1647 assert.Equal(c, strings.TrimSpace(out), n2.ID) 1648 1649 // Name with duplicates 1650 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1651 assert.ErrorContains(c, err, "", out) 1652 assert.Assert(c, strings.Contains(out, "2 matches found based on name")) 1653 out, err = d.Cmd("network", "rm", n2.ID) 1654 assert.NilError(c, err, out) 1655 1656 // Duplicates with name but with different driver 1657 options.Driver = "overlay" 1658 1659 n2, err = cli.NetworkCreate(context.Background(), name, options) 1660 assert.NilError(c, err) 1661 1662 // Full ID always works 1663 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n1.ID) 1664 assert.NilError(c, err, out) 1665 assert.Equal(c, strings.TrimSpace(out), n1.ID) 1666 1667 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", n2.ID) 1668 assert.NilError(c, err, out) 1669 assert.Equal(c, strings.TrimSpace(out), n2.ID) 1670 1671 // Name with duplicates 1672 out, err = d.Cmd("network", "inspect", "--format", "{{.ID}}", name) 1673 assert.ErrorContains(c, err, "", out) 1674 assert.Assert(c, strings.Contains(out, "2 matches found based on name")) 1675 } 1676 1677 func (s *DockerSwarmSuite) TestSwarmStopSignal(c *testing.T) { 1678 testRequires(c, DaemonIsLinux, UserNamespaceROMount) 1679 1680 d := s.AddDaemon(c, true, true) 1681 1682 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top", "--stop-signal=SIGHUP", "busybox", "top") 1683 assert.NilError(c, err, out) 1684 1685 // make sure task has been deployed. 1686 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 1687 1688 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top") 1689 assert.NilError(c, err, out) 1690 assert.Equal(c, strings.TrimSpace(out), "SIGHUP") 1691 1692 containers := d.ActiveContainers(c) 1693 out, err = d.Cmd("inspect", "--type", "container", "--format", "{{.Config.StopSignal}}", containers[0]) 1694 assert.NilError(c, err, out) 1695 assert.Equal(c, strings.TrimSpace(out), "SIGHUP") 1696 1697 out, err = d.Cmd("service", "update", "--detach", "--stop-signal=SIGUSR1", "top") 1698 assert.NilError(c, err, out) 1699 1700 out, err = d.Cmd("service", "inspect", "--format", "{{ .Spec.TaskTemplate.ContainerSpec.StopSignal }}", "top") 1701 assert.NilError(c, err, out) 1702 assert.Equal(c, strings.TrimSpace(out), "SIGUSR1") 1703 } 1704 1705 func (s *DockerSwarmSuite) TestSwarmServiceLsFilterMode(c *testing.T) { 1706 d := s.AddDaemon(c, true, true) 1707 1708 out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top1", "busybox", "top") 1709 assert.NilError(c, err, out) 1710 assert.Assert(c, strings.TrimSpace(out) != "") 1711 1712 out, err = d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name", "top2", "--mode=global", "busybox", "top") 1713 assert.NilError(c, err, out) 1714 assert.Assert(c, strings.TrimSpace(out) != "") 1715 1716 // make sure task has been deployed. 1717 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(2)), poll.WithTimeout(defaultReconciliationTimeout)) 1718 1719 out, err = d.Cmd("service", "ls") 1720 assert.NilError(c, err, out) 1721 assert.Assert(c, strings.Contains(out, "top1")) 1722 assert.Assert(c, strings.Contains(out, "top2")) 1723 assert.Assert(c, !strings.Contains(out, "localnet")) 1724 out, err = d.Cmd("service", "ls", "--filter", "mode=global") 1725 assert.Assert(c, !strings.Contains(out, "top1")) 1726 assert.Assert(c, strings.Contains(out, "top2")) 1727 assert.NilError(c, err, out) 1728 1729 out, err = d.Cmd("service", "ls", "--filter", "mode=replicated") 1730 assert.NilError(c, err, out) 1731 assert.Assert(c, strings.Contains(out, "top1")) 1732 assert.Assert(c, !strings.Contains(out, "top2")) 1733 } 1734 1735 func (s *DockerSwarmSuite) TestSwarmInitUnspecifiedDataPathAddr(c *testing.T) { 1736 d := s.AddDaemon(c, false, false) 1737 1738 out, err := d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0") 1739 assert.ErrorContains(c, err, "") 1740 assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP")) 1741 out, err = d.Cmd("swarm", "init", "--data-path-addr", "0.0.0.0:2000") 1742 assert.ErrorContains(c, err, "") 1743 assert.Assert(c, strings.Contains(out, "data path address must be a non-zero IP")) 1744 } 1745 1746 func (s *DockerSwarmSuite) TestSwarmJoinLeave(c *testing.T) { 1747 d := s.AddDaemon(c, true, true) 1748 1749 out, err := d.Cmd("swarm", "join-token", "-q", "worker") 1750 assert.NilError(c, err) 1751 assert.Assert(c, strings.TrimSpace(out) != "") 1752 1753 token := strings.TrimSpace(out) 1754 1755 // Verify that back to back join/leave does not cause panics 1756 d1 := s.AddDaemon(c, false, false) 1757 for i := 0; i < 10; i++ { 1758 out, err = d1.Cmd("swarm", "join", "--token", token, d.SwarmListenAddr()) 1759 assert.NilError(c, err) 1760 assert.Assert(c, strings.TrimSpace(out) != "") 1761 1762 _, err = d1.Cmd("swarm", "leave") 1763 assert.NilError(c, err) 1764 } 1765 } 1766 1767 const defaultRetryCount = 10 1768 1769 func waitForEvent(c *testing.T, d *daemon.Daemon, since string, filter string, event string, retry int) string { 1770 if retry < 1 { 1771 c.Fatalf("retry count %d is invalid. It should be no less than 1", retry) 1772 return "" 1773 } 1774 var out string 1775 for i := 0; i < retry; i++ { 1776 until := daemonUnixTime(c) 1777 var err error 1778 if len(filter) > 0 { 1779 out, err = d.Cmd("events", "--since", since, "--until", until, filter) 1780 } else { 1781 out, err = d.Cmd("events", "--since", since, "--until", until) 1782 } 1783 assert.NilError(c, err, out) 1784 if strings.Contains(out, event) { 1785 return strings.TrimSpace(out) 1786 } 1787 // no need to sleep after last retry 1788 if i < retry-1 { 1789 time.Sleep(200 * time.Millisecond) 1790 } 1791 } 1792 c.Fatalf("docker events output '%s' doesn't contain event '%s'", out, event) 1793 return "" 1794 } 1795 1796 func (s *DockerSwarmSuite) TestSwarmClusterEventsSource(c *testing.T) { 1797 d1 := s.AddDaemon(c, true, true) 1798 d2 := s.AddDaemon(c, true, true) 1799 d3 := s.AddDaemon(c, true, false) 1800 1801 // create a network 1802 out, err := d1.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1803 assert.NilError(c, err, out) 1804 networkID := strings.TrimSpace(out) 1805 assert.Assert(c, networkID != "") 1806 1807 // d1, d2 are managers that can get swarm events 1808 waitForEvent(c, d1, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1809 waitForEvent(c, d2, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1810 1811 // d3 is a worker, not able to get cluster events 1812 out = waitForEvent(c, d3, "0", "-f scope=swarm", "", 1) 1813 assert.Assert(c, !strings.Contains(out, "network create ")) 1814 } 1815 1816 func (s *DockerSwarmSuite) TestSwarmClusterEventsScope(c *testing.T) { 1817 d := s.AddDaemon(c, true, true) 1818 1819 // create a service 1820 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1821 assert.NilError(c, err, out) 1822 serviceID := strings.Split(out, "\n")[0] 1823 1824 // scope swarm filters cluster events 1825 out = waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount) 1826 assert.Assert(c, !strings.Contains(out, "container create ")) 1827 // all events are returned if scope is not specified 1828 waitForEvent(c, d, "0", "", "service create "+serviceID, 1) 1829 waitForEvent(c, d, "0", "", "container create ", defaultRetryCount) 1830 1831 // scope local only shows non-cluster events 1832 out = waitForEvent(c, d, "0", "-f scope=local", "container create ", 1) 1833 assert.Assert(c, !strings.Contains(out, "service create ")) 1834 } 1835 1836 func (s *DockerSwarmSuite) TestSwarmClusterEventsType(c *testing.T) { 1837 d := s.AddDaemon(c, true, true) 1838 1839 // create a service 1840 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1841 assert.NilError(c, err, out) 1842 serviceID := strings.Split(out, "\n")[0] 1843 1844 // create a network 1845 out, err = d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1846 assert.NilError(c, err, out) 1847 networkID := strings.TrimSpace(out) 1848 assert.Assert(c, networkID != "") 1849 1850 // filter by service 1851 out = waitForEvent(c, d, "0", "-f type=service", "service create "+serviceID, defaultRetryCount) 1852 assert.Assert(c, !strings.Contains(out, "network create")) 1853 // filter by network 1854 out = waitForEvent(c, d, "0", "-f type=network", "network create "+networkID, defaultRetryCount) 1855 assert.Assert(c, !strings.Contains(out, "service create")) 1856 } 1857 1858 func (s *DockerSwarmSuite) TestSwarmClusterEventsService(c *testing.T) { 1859 d := s.AddDaemon(c, true, true) 1860 1861 // create a service 1862 out, err := d.Cmd("service", "create", "--no-resolve-image", "--name", "test", "--detach=false", "busybox", "top") 1863 assert.NilError(c, err, out) 1864 serviceID := strings.Split(out, "\n")[0] 1865 1866 // validate service create event 1867 waitForEvent(c, d, "0", "-f scope=swarm", "service create "+serviceID, defaultRetryCount) 1868 1869 t1 := daemonUnixTime(c) 1870 out, err = d.Cmd("service", "update", "--force", "--detach=false", "test") 1871 assert.NilError(c, err, out) 1872 1873 // wait for service update start 1874 out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1875 assert.Assert(c, strings.Contains(out, "updatestate.new=updating")) 1876 // allow service update complete. This is a service with 1 instance 1877 time.Sleep(400 * time.Millisecond) 1878 out = waitForEvent(c, d, t1, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1879 assert.Assert(c, strings.Contains(out, "updatestate.new=completed, updatestate.old=updating")) 1880 // scale service 1881 t2 := daemonUnixTime(c) 1882 out, err = d.Cmd("service", "scale", "test=3") 1883 assert.NilError(c, err, out) 1884 1885 out = waitForEvent(c, d, t2, "-f scope=swarm", "service update "+serviceID, defaultRetryCount) 1886 assert.Assert(c, strings.Contains(out, "replicas.new=3, replicas.old=1")) 1887 // remove service 1888 t3 := daemonUnixTime(c) 1889 out, err = d.Cmd("service", "rm", "test") 1890 assert.NilError(c, err, out) 1891 1892 waitForEvent(c, d, t3, "-f scope=swarm", "service remove "+serviceID, defaultRetryCount) 1893 } 1894 1895 func (s *DockerSwarmSuite) TestSwarmClusterEventsNode(c *testing.T) { 1896 d1 := s.AddDaemon(c, true, true) 1897 s.AddDaemon(c, true, true) 1898 d3 := s.AddDaemon(c, true, true) 1899 1900 d3ID := d3.NodeID() 1901 waitForEvent(c, d1, "0", "-f scope=swarm", "node create "+d3ID, defaultRetryCount) 1902 1903 t1 := daemonUnixTime(c) 1904 out, err := d1.Cmd("node", "update", "--availability=pause", d3ID) 1905 assert.NilError(c, err, out) 1906 1907 // filter by type 1908 out = waitForEvent(c, d1, t1, "-f type=node", "node update "+d3ID, defaultRetryCount) 1909 assert.Assert(c, strings.Contains(out, "availability.new=pause, availability.old=active")) 1910 t2 := daemonUnixTime(c) 1911 out, err = d1.Cmd("node", "demote", d3ID) 1912 assert.NilError(c, err, out) 1913 1914 waitForEvent(c, d1, t2, "-f type=node", "node update "+d3ID, defaultRetryCount) 1915 1916 t3 := daemonUnixTime(c) 1917 out, err = d1.Cmd("node", "rm", "-f", d3ID) 1918 assert.NilError(c, err, out) 1919 1920 // filter by scope 1921 waitForEvent(c, d1, t3, "-f scope=swarm", "node remove "+d3ID, defaultRetryCount) 1922 } 1923 1924 func (s *DockerSwarmSuite) TestSwarmClusterEventsNetwork(c *testing.T) { 1925 d := s.AddDaemon(c, true, true) 1926 1927 // create a network 1928 out, err := d.Cmd("network", "create", "--attachable", "-d", "overlay", "foo") 1929 assert.NilError(c, err, out) 1930 networkID := strings.TrimSpace(out) 1931 1932 waitForEvent(c, d, "0", "-f scope=swarm", "network create "+networkID, defaultRetryCount) 1933 1934 // remove network 1935 t1 := daemonUnixTime(c) 1936 out, err = d.Cmd("network", "rm", "foo") 1937 assert.NilError(c, err, out) 1938 1939 // filtered by network 1940 waitForEvent(c, d, t1, "-f type=network", "network remove "+networkID, defaultRetryCount) 1941 } 1942 1943 func (s *DockerSwarmSuite) TestSwarmClusterEventsSecret(c *testing.T) { 1944 d := s.AddDaemon(c, true, true) 1945 1946 testName := "test_secret" 1947 id := d.CreateSecret(c, swarm.SecretSpec{ 1948 Annotations: swarm.Annotations{ 1949 Name: testName, 1950 }, 1951 Data: []byte("TESTINGDATA"), 1952 }) 1953 assert.Assert(c, id != "", "secrets: %s", id) 1954 1955 waitForEvent(c, d, "0", "-f scope=swarm", "secret create "+id, defaultRetryCount) 1956 1957 t1 := daemonUnixTime(c) 1958 d.DeleteSecret(c, id) 1959 // filtered by secret 1960 waitForEvent(c, d, t1, "-f type=secret", "secret remove "+id, defaultRetryCount) 1961 } 1962 1963 func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *testing.T) { 1964 d := s.AddDaemon(c, true, true) 1965 1966 testName := "test_config" 1967 id := d.CreateConfig(c, swarm.ConfigSpec{ 1968 Annotations: swarm.Annotations{ 1969 Name: testName, 1970 }, 1971 Data: []byte("TESTINGDATA"), 1972 }) 1973 assert.Assert(c, id != "", "configs: %s", id) 1974 1975 waitForEvent(c, d, "0", "-f scope=swarm", "config create "+id, defaultRetryCount) 1976 1977 t1 := daemonUnixTime(c) 1978 d.DeleteConfig(c, id) 1979 // filtered by config 1980 waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount) 1981 } 1982 1983 func getUnlockKey(d *daemon.Daemon, c *testing.T, autolockOutput string) string { 1984 unlockKey, err := d.Cmd("swarm", "unlock-key", "-q") 1985 assert.Assert(c, err == nil, "%s", unlockKey) 1986 unlockKey = strings.TrimSuffix(unlockKey, "\n") 1987 1988 // Check that "docker swarm init --autolock" or "docker swarm update --autolock" 1989 // contains all the expected strings, including the unlock key 1990 assert.Assert(c, strings.Contains(autolockOutput, "docker swarm unlock")) 1991 assert.Assert(c, strings.Contains(autolockOutput, unlockKey)) 1992 return unlockKey 1993 }