github.com/wozhu6104/docker@v20.10.10+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  }