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