github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration-cli/docker_api_swarm_service_test.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package main 5 6 import ( 7 "context" 8 "fmt" 9 "strconv" 10 "strings" 11 "testing" 12 "time" 13 14 "github.com/docker/docker/api/types" 15 "github.com/docker/docker/api/types/swarm" 16 "github.com/docker/docker/integration-cli/checker" 17 "github.com/docker/docker/integration-cli/cli" 18 "github.com/docker/docker/integration-cli/cli/build" 19 "github.com/docker/docker/integration-cli/daemon" 20 testdaemon "github.com/docker/docker/testutil/daemon" 21 "golang.org/x/sys/unix" 22 "gotest.tools/v3/assert" 23 "gotest.tools/v3/icmd" 24 "gotest.tools/v3/poll" 25 ) 26 27 func setPortConfig(portConfig []swarm.PortConfig) testdaemon.ServiceConstructor { 28 return func(s *swarm.Service) { 29 if s.Spec.EndpointSpec == nil { 30 s.Spec.EndpointSpec = &swarm.EndpointSpec{} 31 } 32 s.Spec.EndpointSpec.Ports = portConfig 33 } 34 } 35 36 func (s *DockerSwarmSuite) TestAPIServiceUpdatePort(c *testing.T) { 37 d := s.AddDaemon(c, true, true) 38 39 // Create a service with a port mapping of 8080:8081. 40 portConfig := []swarm.PortConfig{{TargetPort: 8081, PublishedPort: 8080}} 41 serviceID := d.CreateService(c, simpleTestService, setInstances(1), setPortConfig(portConfig)) 42 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 43 44 // Update the service: changed the port mapping from 8080:8081 to 8082:8083. 45 updatedPortConfig := []swarm.PortConfig{{TargetPort: 8083, PublishedPort: 8082}} 46 remoteService := d.GetService(c, serviceID) 47 d.UpdateService(c, remoteService, setPortConfig(updatedPortConfig)) 48 49 // Inspect the service and verify port mapping. 50 updatedService := d.GetService(c, serviceID) 51 assert.Assert(c, updatedService.Spec.EndpointSpec != nil) 52 assert.Equal(c, len(updatedService.Spec.EndpointSpec.Ports), 1) 53 assert.Equal(c, updatedService.Spec.EndpointSpec.Ports[0].TargetPort, uint32(8083)) 54 assert.Equal(c, updatedService.Spec.EndpointSpec.Ports[0].PublishedPort, uint32(8082)) 55 } 56 57 func (s *DockerSwarmSuite) TestAPISwarmServicesEmptyList(c *testing.T) { 58 d := s.AddDaemon(c, true, true) 59 60 services := d.ListServices(c) 61 assert.Assert(c, services != nil) 62 assert.Assert(c, len(services) == 0, "services: %#v", services) 63 } 64 65 func (s *DockerSwarmSuite) TestAPISwarmServicesCreate(c *testing.T) { 66 d := s.AddDaemon(c, true, true) 67 68 instances := 2 69 id := d.CreateService(c, simpleTestService, setInstances(instances)) 70 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 71 72 client := d.NewClientT(c) 73 defer client.Close() 74 75 options := types.ServiceInspectOptions{InsertDefaults: true} 76 77 // insertDefaults inserts UpdateConfig when service is fetched by ID 78 resp, _, err := client.ServiceInspectWithRaw(context.Background(), id, options) 79 out := fmt.Sprintf("%+v", resp) 80 assert.NilError(c, err) 81 assert.Assert(c, strings.Contains(out, "UpdateConfig")) 82 83 // insertDefaults inserts UpdateConfig when service is fetched by ID 84 resp, _, err = client.ServiceInspectWithRaw(context.Background(), "top", options) 85 out = fmt.Sprintf("%+v", resp) 86 assert.NilError(c, err) 87 assert.Assert(c, strings.Contains(out, "UpdateConfig")) 88 89 service := d.GetService(c, id) 90 instances = 5 91 d.UpdateService(c, service, setInstances(instances)) 92 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 93 94 d.RemoveService(c, service.ID) 95 poll.WaitOn(c, pollCheck(c, d.CheckActiveContainerCount, checker.Equals(0)), poll.WithTimeout(defaultReconciliationTimeout)) 96 } 97 98 func (s *DockerSwarmSuite) TestAPISwarmServicesMultipleAgents(c *testing.T) { 99 d1 := s.AddDaemon(c, true, true) 100 d2 := s.AddDaemon(c, true, false) 101 d3 := s.AddDaemon(c, true, false) 102 103 time.Sleep(1 * time.Second) // make sure all daemons are ready to accept tasks 104 105 instances := 9 106 id := d1.CreateService(c, simpleTestService, setInstances(instances)) 107 108 poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout)) 109 poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout)) 110 poll.WaitOn(c, pollCheck(c, d3.CheckActiveContainerCount, checker.GreaterThan(0)), poll.WithTimeout(defaultReconciliationTimeout)) 111 112 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 113 114 // reconciliation on d2 node down 115 d2.Stop(c) 116 117 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 118 119 // test downscaling 120 instances = 5 121 d1.UpdateService(c, d1.GetService(c, id), setInstances(instances)) 122 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 123 } 124 125 func (s *DockerSwarmSuite) TestAPISwarmServicesCreateGlobal(c *testing.T) { 126 d1 := s.AddDaemon(c, true, true) 127 d2 := s.AddDaemon(c, true, false) 128 d3 := s.AddDaemon(c, true, false) 129 130 d1.CreateService(c, simpleTestService, setGlobalMode) 131 132 poll.WaitOn(c, pollCheck(c, d1.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 133 poll.WaitOn(c, pollCheck(c, d2.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 134 poll.WaitOn(c, pollCheck(c, d3.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 135 136 d4 := s.AddDaemon(c, true, false) 137 d5 := s.AddDaemon(c, true, false) 138 139 poll.WaitOn(c, pollCheck(c, d4.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 140 poll.WaitOn(c, pollCheck(c, d5.CheckActiveContainerCount, checker.Equals(1)), poll.WithTimeout(defaultReconciliationTimeout)) 141 } 142 143 func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *testing.T) { 144 const nodeCount = 3 145 var daemons [nodeCount]*daemon.Daemon 146 for i := 0; i < nodeCount; i++ { 147 daemons[i] = s.AddDaemon(c, true, i == 0) 148 } 149 // wait for nodes ready 150 poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second)) 151 152 // service image at start 153 image1 := "busybox:latest" 154 // target image in update 155 image2 := "busybox:test" 156 157 // create a different tag 158 for _, d := range daemons { 159 out, err := d.Cmd("tag", image1, image2) 160 assert.NilError(c, err, out) 161 } 162 163 // create service 164 instances := 5 165 parallelism := 2 166 rollbackParallelism := 3 167 id := daemons[0].CreateService(c, serviceForUpdate, setInstances(instances)) 168 169 // wait for tasks ready 170 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 171 172 // issue service update 173 service := daemons[0].GetService(c, id) 174 daemons[0].UpdateService(c, service, setImage(image2)) 175 176 // first batch 177 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 178 179 // 2nd batch 180 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 181 182 // 3nd batch 183 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 184 185 // Roll back to the previous version. This uses the CLI because 186 // rollback used to be a client-side operation. 187 out, err := daemons[0].Cmd("service", "update", "--detach", "--rollback", id) 188 assert.NilError(c, err, out) 189 190 // first batch 191 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 192 193 // 2nd batch 194 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 195 } 196 197 func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *testing.T) { 198 d := s.AddDaemon(c, true, true) 199 200 // service image at start 201 image1 := "busybox:latest" 202 // target image in update 203 image2 := "testhealth:latest" 204 205 // service started from this image won't pass health check 206 result := cli.BuildCmd(c, image2, cli.Daemon(d), 207 build.WithDockerfile(`FROM busybox 208 HEALTHCHECK --interval=1s --timeout=30s --retries=1024 \ 209 CMD cat /status`), 210 ) 211 result.Assert(c, icmd.Success) 212 213 // create service 214 instances := 5 215 parallelism := 2 216 rollbackParallelism := 3 217 id := d.CreateService(c, serviceForUpdate, setInstances(instances), setUpdateOrder(swarm.UpdateOrderStartFirst), setRollbackOrder(swarm.UpdateOrderStartFirst)) 218 219 checkStartingTasks := func(expected int) []swarm.Task { 220 var startingTasks []swarm.Task 221 poll.WaitOn(c, pollCheck(c, func(c *testing.T) (interface{}, string) { 222 tasks := d.GetServiceTasks(c, id) 223 startingTasks = nil 224 for _, t := range tasks { 225 if t.Status.State == swarm.TaskStateStarting { 226 startingTasks = append(startingTasks, t) 227 } 228 } 229 return startingTasks, "" 230 }, checker.HasLen(expected)), poll.WithTimeout(defaultReconciliationTimeout)) 231 232 return startingTasks 233 } 234 235 makeTasksHealthy := func(tasks []swarm.Task) { 236 for _, t := range tasks { 237 containerID := t.Status.ContainerStatus.ContainerID 238 d.Cmd("exec", containerID, "touch", "/status") 239 } 240 } 241 242 // wait for tasks ready 243 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 244 245 // issue service update 246 service := d.GetService(c, id) 247 d.UpdateService(c, service, setImage(image2)) 248 249 // first batch 250 251 // The old tasks should be running, and the new ones should be starting. 252 startingTasks := checkStartingTasks(parallelism) 253 254 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 255 256 // make it healthy 257 makeTasksHealthy(startingTasks) 258 259 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 260 261 // 2nd batch 262 263 // The old tasks should be running, and the new ones should be starting. 264 startingTasks = checkStartingTasks(parallelism) 265 266 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - parallelism, image2: parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 267 268 // make it healthy 269 makeTasksHealthy(startingTasks) 270 271 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 272 273 // 3nd batch 274 275 // The old tasks should be running, and the new ones should be starting. 276 startingTasks = checkStartingTasks(1) 277 278 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 279 280 // make it healthy 281 makeTasksHealthy(startingTasks) 282 283 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 284 285 // Roll back to the previous version. This uses the CLI because 286 // rollback is a client-side operation. 287 out, err := d.Cmd("service", "update", "--detach", "--rollback", id) 288 assert.NilError(c, err, out) 289 290 // first batch 291 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})), poll.WithTimeout(defaultReconciliationTimeout)) 292 293 // 2nd batch 294 poll.WaitOn(c, pollCheck(c, d.CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 295 } 296 297 func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *testing.T) { 298 const nodeCount = 3 299 var daemons [nodeCount]*daemon.Daemon 300 for i := 0; i < nodeCount; i++ { 301 daemons[i] = s.AddDaemon(c, true, i == 0) 302 } 303 // wait for nodes ready 304 poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second)) 305 306 // service image at start 307 image1 := "busybox:latest" 308 // target image in update 309 image2 := "busybox:badtag" 310 311 // create service 312 instances := 5 313 id := daemons[0].CreateService(c, serviceForUpdate, setInstances(instances)) 314 315 // wait for tasks ready 316 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 317 318 // issue service update 319 service := daemons[0].GetService(c, id) 320 daemons[0].UpdateService(c, service, setImage(image2), setFailureAction(swarm.UpdateFailureActionPause), setMaxFailureRatio(0.25), setParallelism(1)) 321 322 // should update 2 tasks and then pause 323 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceUpdateState(id), checker.Equals(swarm.UpdateStatePaused)), poll.WithTimeout(defaultReconciliationTimeout)) 324 v, _ := daemons[0].CheckServiceRunningTasks(id)(c) 325 assert.Assert(c, v == instances-2) 326 327 // Roll back to the previous version. This uses the CLI because 328 // rollback used to be a client-side operation. 329 out, err := daemons[0].Cmd("service", "update", "--detach", "--rollback", id) 330 assert.NilError(c, err, out) 331 332 poll.WaitOn(c, pollCheck(c, daemons[0].CheckRunningTaskImages, checker.DeepEquals(map[string]int{image1: instances})), poll.WithTimeout(defaultReconciliationTimeout)) 333 } 334 335 func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintRole(c *testing.T) { 336 const nodeCount = 3 337 var daemons [nodeCount]*daemon.Daemon 338 for i := 0; i < nodeCount; i++ { 339 daemons[i] = s.AddDaemon(c, true, i == 0) 340 } 341 // wait for nodes ready 342 poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second)) 343 344 // create service 345 constraints := []string{"node.role==worker"} 346 instances := 3 347 id := daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 348 // wait for tasks ready 349 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 350 // validate tasks are running on worker nodes 351 tasks := daemons[0].GetServiceTasks(c, id) 352 for _, task := range tasks { 353 node := daemons[0].GetNode(c, task.NodeID) 354 assert.Equal(c, node.Spec.Role, swarm.NodeRoleWorker) 355 } 356 // remove service 357 daemons[0].RemoveService(c, id) 358 359 // create service 360 constraints = []string{"node.role!=worker"} 361 id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 362 // wait for tasks ready 363 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 364 tasks = daemons[0].GetServiceTasks(c, id) 365 // validate tasks are running on manager nodes 366 for _, task := range tasks { 367 node := daemons[0].GetNode(c, task.NodeID) 368 assert.Equal(c, node.Spec.Role, swarm.NodeRoleManager) 369 } 370 // remove service 371 daemons[0].RemoveService(c, id) 372 373 // create service 374 constraints = []string{"node.role==nosuchrole"} 375 id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 376 // wait for tasks created 377 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 378 // let scheduler try 379 time.Sleep(250 * time.Millisecond) 380 // validate tasks are not assigned to any node 381 tasks = daemons[0].GetServiceTasks(c, id) 382 for _, task := range tasks { 383 assert.Equal(c, task.NodeID, "") 384 } 385 } 386 387 func (s *DockerSwarmSuite) TestAPISwarmServiceConstraintLabel(c *testing.T) { 388 const nodeCount = 3 389 var daemons [nodeCount]*daemon.Daemon 390 for i := 0; i < nodeCount; i++ { 391 daemons[i] = s.AddDaemon(c, true, i == 0) 392 } 393 // wait for nodes ready 394 poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second)) 395 nodes := daemons[0].ListNodes(c) 396 assert.Equal(c, len(nodes), nodeCount) 397 398 // add labels to nodes 399 daemons[0].UpdateNode(c, nodes[0].ID, func(n *swarm.Node) { 400 n.Spec.Annotations.Labels = map[string]string{ 401 "security": "high", 402 } 403 }) 404 for i := 1; i < nodeCount; i++ { 405 daemons[0].UpdateNode(c, nodes[i].ID, func(n *swarm.Node) { 406 n.Spec.Annotations.Labels = map[string]string{ 407 "security": "low", 408 } 409 }) 410 } 411 412 // create service 413 instances := 3 414 constraints := []string{"node.labels.security==high"} 415 id := daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 416 // wait for tasks ready 417 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 418 tasks := daemons[0].GetServiceTasks(c, id) 419 // validate all tasks are running on nodes[0] 420 for _, task := range tasks { 421 assert.Assert(c, task.NodeID == nodes[0].ID) 422 } 423 // remove service 424 daemons[0].RemoveService(c, id) 425 426 // create service 427 constraints = []string{"node.labels.security!=high"} 428 id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 429 // wait for tasks ready 430 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 431 tasks = daemons[0].GetServiceTasks(c, id) 432 // validate all tasks are NOT running on nodes[0] 433 for _, task := range tasks { 434 assert.Assert(c, task.NodeID != nodes[0].ID) 435 } 436 // remove service 437 daemons[0].RemoveService(c, id) 438 439 constraints = []string{"node.labels.security==medium"} 440 id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 441 // wait for tasks created 442 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 443 // let scheduler try 444 time.Sleep(250 * time.Millisecond) 445 tasks = daemons[0].GetServiceTasks(c, id) 446 // validate tasks are not assigned 447 for _, task := range tasks { 448 assert.Assert(c, task.NodeID == "") 449 } 450 // remove service 451 daemons[0].RemoveService(c, id) 452 453 // multiple constraints 454 constraints = []string{ 455 "node.labels.security==high", 456 fmt.Sprintf("node.id==%s", nodes[1].ID), 457 } 458 id = daemons[0].CreateService(c, simpleTestService, setConstraints(constraints), setInstances(instances)) 459 // wait for tasks created 460 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 461 // let scheduler try 462 time.Sleep(250 * time.Millisecond) 463 tasks = daemons[0].GetServiceTasks(c, id) 464 // validate tasks are not assigned 465 for _, task := range tasks { 466 assert.Assert(c, task.NodeID == "") 467 } 468 // make nodes[1] fulfills the constraints 469 daemons[0].UpdateNode(c, nodes[1].ID, func(n *swarm.Node) { 470 n.Spec.Annotations.Labels = map[string]string{ 471 "security": "high", 472 } 473 }) 474 // wait for tasks ready 475 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 476 tasks = daemons[0].GetServiceTasks(c, id) 477 for _, task := range tasks { 478 assert.Assert(c, task.NodeID == nodes[1].ID) 479 } 480 } 481 482 func (s *DockerSwarmSuite) TestAPISwarmServicePlacementPrefs(c *testing.T) { 483 const nodeCount = 3 484 var daemons [nodeCount]*daemon.Daemon 485 for i := 0; i < nodeCount; i++ { 486 daemons[i] = s.AddDaemon(c, true, i == 0) 487 } 488 // wait for nodes ready 489 poll.WaitOn(c, pollCheck(c, daemons[0].CheckNodeReadyCount, checker.Equals(nodeCount)), poll.WithTimeout(5*time.Second)) 490 nodes := daemons[0].ListNodes(c) 491 assert.Equal(c, len(nodes), nodeCount) 492 493 // add labels to nodes 494 daemons[0].UpdateNode(c, nodes[0].ID, func(n *swarm.Node) { 495 n.Spec.Annotations.Labels = map[string]string{ 496 "rack": "a", 497 } 498 }) 499 for i := 1; i < nodeCount; i++ { 500 daemons[0].UpdateNode(c, nodes[i].ID, func(n *swarm.Node) { 501 n.Spec.Annotations.Labels = map[string]string{ 502 "rack": "b", 503 } 504 }) 505 } 506 507 // create service 508 instances := 4 509 prefs := []swarm.PlacementPreference{{Spread: &swarm.SpreadOver{SpreadDescriptor: "node.labels.rack"}}} 510 id := daemons[0].CreateService(c, simpleTestService, setPlacementPrefs(prefs), setInstances(instances)) 511 // wait for tasks ready 512 poll.WaitOn(c, pollCheck(c, daemons[0].CheckServiceRunningTasks(id), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 513 tasks := daemons[0].GetServiceTasks(c, id) 514 // validate all tasks are running on nodes[0] 515 tasksOnNode := make(map[string]int) 516 for _, task := range tasks { 517 tasksOnNode[task.NodeID]++ 518 } 519 assert.Assert(c, tasksOnNode[nodes[0].ID] == 2) 520 assert.Assert(c, tasksOnNode[nodes[1].ID] == 1) 521 assert.Assert(c, tasksOnNode[nodes[2].ID] == 1) 522 } 523 524 func (s *DockerSwarmSuite) TestAPISwarmServicesStateReporting(c *testing.T) { 525 testRequires(c, testEnv.IsLocalDaemon) 526 testRequires(c, DaemonIsLinux) 527 528 d1 := s.AddDaemon(c, true, true) 529 d2 := s.AddDaemon(c, true, true) 530 d3 := s.AddDaemon(c, true, false) 531 532 time.Sleep(1 * time.Second) // make sure all daemons are ready to accept 533 534 instances := 9 535 d1.CreateService(c, simpleTestService, setInstances(instances)) 536 537 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 538 539 getContainers := func() map[string]*daemon.Daemon { 540 m := make(map[string]*daemon.Daemon) 541 for _, d := range []*daemon.Daemon{d1, d2, d3} { 542 for _, id := range d.ActiveContainers(c) { 543 m[id] = d 544 } 545 } 546 return m 547 } 548 549 containers := getContainers() 550 assert.Assert(c, len(containers) == instances) 551 var toRemove string 552 for i := range containers { 553 toRemove = i 554 } 555 556 _, err := containers[toRemove].Cmd("stop", toRemove) 557 assert.NilError(c, err) 558 559 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 560 561 containers2 := getContainers() 562 assert.Assert(c, len(containers2) == instances) 563 for i := range containers { 564 if i == toRemove { 565 assert.Assert(c, containers2[i] == nil) 566 } else { 567 assert.Assert(c, containers2[i] != nil) 568 } 569 } 570 571 containers = containers2 572 for i := range containers { 573 toRemove = i 574 } 575 576 // try with killing process outside of docker 577 pidStr, err := containers[toRemove].Cmd("inspect", "-f", "{{.State.Pid}}", toRemove) 578 assert.NilError(c, err) 579 pid, err := strconv.Atoi(strings.TrimSpace(pidStr)) 580 assert.NilError(c, err) 581 assert.NilError(c, unix.Kill(pid, unix.SIGKILL)) 582 583 time.Sleep(time.Second) // give some time to handle the signal 584 585 poll.WaitOn(c, pollCheck(c, reducedCheck(sumAsIntegers, d1.CheckActiveContainerCount, d2.CheckActiveContainerCount, d3.CheckActiveContainerCount), checker.Equals(instances)), poll.WithTimeout(defaultReconciliationTimeout)) 586 587 containers2 = getContainers() 588 assert.Assert(c, len(containers2) == instances) 589 for i := range containers { 590 if i == toRemove { 591 assert.Assert(c, containers2[i] == nil) 592 } else { 593 assert.Assert(c, containers2[i] != nil) 594 } 595 } 596 }