docker_api_swarm_service_test.go 23 KB

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