docker_api_swarm_service_test.go 23 KB

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