Browse code

Add integration test for START_FIRST update order

Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>

Aaron Lehmann authored on 2017/01/19 07:16:47
Showing 2 changed files
... ...
@@ -175,6 +175,115 @@ func (s *DockerSwarmSuite) TestAPISwarmServicesUpdate(c *check.C) {
175 175
 		map[string]int{image1: instances})
176 176
 }
177 177
 
178
+func (s *DockerSwarmSuite) TestAPISwarmServicesUpdateStartFirst(c *check.C) {
179
+	d := s.AddDaemon(c, true, true)
180
+
181
+	// service image at start
182
+	image1 := "busybox:latest"
183
+	// target image in update
184
+	image2 := "testhealth"
185
+
186
+	// service started from this image won't pass health check
187
+	_, _, err := d.BuildImageWithOut(image2,
188
+		`FROM busybox
189
+		HEALTHCHECK --interval=1s --timeout=1s --retries=1024\
190
+		  CMD cat /status`,
191
+		true)
192
+	c.Check(err, check.IsNil)
193
+
194
+	// create service
195
+	instances := 5
196
+	parallelism := 2
197
+	rollbackParallelism := 3
198
+	id := d.CreateService(c, serviceForUpdate, setInstances(instances), setUpdateOrder(swarm.UpdateOrderStartFirst), setRollbackOrder(swarm.UpdateOrderStartFirst))
199
+
200
+	checkStartingTasks := func(expected int) []swarm.Task {
201
+		var startingTasks []swarm.Task
202
+		waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) {
203
+			tasks := d.GetServiceTasks(c, id)
204
+			startingTasks = nil
205
+			for _, t := range tasks {
206
+				if t.Status.State == swarm.TaskStateStarting {
207
+					startingTasks = append(startingTasks, t)
208
+				}
209
+			}
210
+			return startingTasks, nil
211
+		}, checker.HasLen, expected)
212
+
213
+		return startingTasks
214
+	}
215
+
216
+	makeTasksHealthy := func(tasks []swarm.Task) {
217
+		for _, t := range tasks {
218
+			containerID := t.Status.ContainerStatus.ContainerID
219
+			d.Cmd("exec", containerID, "touch", "/status")
220
+		}
221
+	}
222
+
223
+	// wait for tasks ready
224
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
225
+		map[string]int{image1: instances})
226
+
227
+	// issue service update
228
+	service := d.GetService(c, id)
229
+	d.UpdateService(c, service, setImage(image2))
230
+
231
+	// first batch
232
+
233
+	// The old tasks should be running, and the new ones should be starting.
234
+	startingTasks := checkStartingTasks(parallelism)
235
+
236
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
237
+		map[string]int{image1: instances})
238
+
239
+	// make it healthy
240
+	makeTasksHealthy(startingTasks)
241
+
242
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
243
+		map[string]int{image1: instances - parallelism, image2: parallelism})
244
+
245
+	// 2nd batch
246
+
247
+	// The old tasks should be running, and the new ones should be starting.
248
+	startingTasks = checkStartingTasks(parallelism)
249
+
250
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
251
+		map[string]int{image1: instances - parallelism, image2: parallelism})
252
+
253
+	// make it healthy
254
+	makeTasksHealthy(startingTasks)
255
+
256
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
257
+		map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
258
+
259
+	// 3nd batch
260
+
261
+	// The old tasks should be running, and the new ones should be starting.
262
+	startingTasks = checkStartingTasks(1)
263
+
264
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
265
+		map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
266
+
267
+	// make it healthy
268
+	makeTasksHealthy(startingTasks)
269
+
270
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
271
+		map[string]int{image2: instances})
272
+
273
+	// Roll back to the previous version. This uses the CLI because
274
+	// rollback is a client-side operation.
275
+	out, err := d.Cmd("service", "update", "--rollback", id)
276
+	c.Assert(err, checker.IsNil, check.Commentf(out))
277
+
278
+	// first batch
279
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
280
+		map[string]int{image2: instances - rollbackParallelism, image1: rollbackParallelism})
281
+
282
+	// 2nd batch
283
+	waitAndAssert(c, defaultReconciliationTimeout, d.CheckRunningTaskImages, checker.DeepEquals,
284
+		map[string]int{image1: instances})
285
+}
286
+
178 287
 func (s *DockerSwarmSuite) TestAPISwarmServicesFailedUpdate(c *check.C) {
179 288
 	const nodeCount = 3
180 289
 	var daemons [nodeCount]*daemon.Swarm
... ...
@@ -596,6 +596,24 @@ func setInstances(replicas int) daemon.ServiceConstructor {
596 596
 	}
597 597
 }
598 598
 
599
+func setUpdateOrder(order string) daemon.ServiceConstructor {
600
+	return func(s *swarm.Service) {
601
+		if s.Spec.UpdateConfig == nil {
602
+			s.Spec.UpdateConfig = &swarm.UpdateConfig{}
603
+		}
604
+		s.Spec.UpdateConfig.Order = order
605
+	}
606
+}
607
+
608
+func setRollbackOrder(order string) daemon.ServiceConstructor {
609
+	return func(s *swarm.Service) {
610
+		if s.Spec.RollbackConfig == nil {
611
+			s.Spec.RollbackConfig = &swarm.UpdateConfig{}
612
+		}
613
+		s.Spec.RollbackConfig.Order = order
614
+	}
615
+}
616
+
599 617
 func setImage(image string) daemon.ServiceConstructor {
600 618
 	return func(s *swarm.Service) {
601 619
 		s.Spec.TaskTemplate.ContainerSpec.Image = image