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  }