github.com/ttys3/engine@v17.12.1-ce-rc2+incompatible/integration-cli/docker_api_swarm_service_test.go (about)

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