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