docker_api_swarm_service_test.go 25 KB

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