Signed-off-by: Dong Chen <dongluo.chen@docker.com>
(cherry picked from commit d327765a62a99dc63e9a8c16ac291861cee066f3)
Signed-off-by: Tibor Vass <tibor@docker.com>
| ... | ... |
@@ -148,6 +148,39 @@ func (d *SwarmDaemon) getServiceTasks(c *check.C, service string) []swarm.Task {
|
| 148 | 148 |
return tasks |
| 149 | 149 |
} |
| 150 | 150 |
|
| 151 |
+func (d *SwarmDaemon) checkRunningTaskImages(c *check.C) (interface{}, check.CommentInterface) {
|
|
| 152 |
+ var tasks []swarm.Task |
|
| 153 |
+ |
|
| 154 |
+ filterArgs := filters.NewArgs() |
|
| 155 |
+ filterArgs.Add("desired-state", "running")
|
|
| 156 |
+ filters, err := filters.ToParam(filterArgs) |
|
| 157 |
+ c.Assert(err, checker.IsNil) |
|
| 158 |
+ |
|
| 159 |
+ status, out, err := d.SockRequest("GET", "/tasks?filters="+filters, nil)
|
|
| 160 |
+ c.Assert(status, checker.Equals, http.StatusOK, check.Commentf("output: %q", string(out)))
|
|
| 161 |
+ c.Assert(err, checker.IsNil, check.Commentf(string(out))) |
|
| 162 |
+ c.Assert(json.Unmarshal(out, &tasks), checker.IsNil) |
|
| 163 |
+ |
|
| 164 |
+ result := make(map[string]int) |
|
| 165 |
+ for _, task := range tasks {
|
|
| 166 |
+ if task.Status.State == swarm.TaskStateRunning {
|
|
| 167 |
+ result[task.Spec.ContainerSpec.Image]++ |
|
| 168 |
+ } |
|
| 169 |
+ } |
|
| 170 |
+ return result, nil |
|
| 171 |
+} |
|
| 172 |
+ |
|
| 173 |
+func (d *SwarmDaemon) checkNodeReadyCount(c *check.C) (interface{}, check.CommentInterface) {
|
|
| 174 |
+ nodes := d.listNodes(c) |
|
| 175 |
+ var readyCount int |
|
| 176 |
+ for _, node := range nodes {
|
|
| 177 |
+ if node.Status.State == swarm.NodeStateReady {
|
|
| 178 |
+ readyCount++ |
|
| 179 |
+ } |
|
| 180 |
+ } |
|
| 181 |
+ return readyCount, nil |
|
| 182 |
+} |
|
| 183 |
+ |
|
| 151 | 184 |
func (d *SwarmDaemon) getTask(c *check.C, id string) swarm.Task {
|
| 152 | 185 |
var task swarm.Task |
| 153 | 186 |
|
| ... | ... |
@@ -371,6 +371,52 @@ func (s *DockerSwarmSuite) TestApiSwarmServicesCreateGlobal(c *check.C) {
|
| 371 | 371 |
waitAndAssert(c, defaultReconciliationTimeout, d5.checkActiveContainerCount, checker.Equals, 1) |
| 372 | 372 |
} |
| 373 | 373 |
|
| 374 |
+func (s *DockerSwarmSuite) TestApiSwarmServicesUpdate(c *check.C) {
|
|
| 375 |
+ const nodeCount = 3 |
|
| 376 |
+ var daemons [nodeCount]*SwarmDaemon |
|
| 377 |
+ for i := 0; i < nodeCount; i++ {
|
|
| 378 |
+ daemons[i] = s.AddDaemon(c, true, i == 0) |
|
| 379 |
+ } |
|
| 380 |
+ // wait for nodes ready |
|
| 381 |
+ waitAndAssert(c, 5*time.Second, daemons[0].checkNodeReadyCount, checker.Equals, nodeCount) |
|
| 382 |
+ |
|
| 383 |
+ // service image at start |
|
| 384 |
+ image1 := "busybox:latest" |
|
| 385 |
+ // target image in update |
|
| 386 |
+ image2 := "busybox:test" |
|
| 387 |
+ |
|
| 388 |
+ // create a different tag |
|
| 389 |
+ for _, d := range daemons {
|
|
| 390 |
+ out, err := d.Cmd("tag", image1, image2)
|
|
| 391 |
+ c.Assert(err, checker.IsNil, check.Commentf(out)) |
|
| 392 |
+ } |
|
| 393 |
+ |
|
| 394 |
+ // create service |
|
| 395 |
+ instances := 5 |
|
| 396 |
+ parallelism := 2 |
|
| 397 |
+ id := daemons[0].createService(c, serviceForUpdate, setInstances(instances)) |
|
| 398 |
+ |
|
| 399 |
+ // wait for tasks ready |
|
| 400 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkRunningTaskImages, checker.DeepEquals, |
|
| 401 |
+ map[string]int{image1: instances})
|
|
| 402 |
+ |
|
| 403 |
+ // issue service update |
|
| 404 |
+ service := daemons[0].getService(c, id) |
|
| 405 |
+ daemons[0].updateService(c, service, setImage(image2)) |
|
| 406 |
+ |
|
| 407 |
+ // first batch |
|
| 408 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkRunningTaskImages, checker.DeepEquals, |
|
| 409 |
+ map[string]int{image1: instances - parallelism, image2: parallelism})
|
|
| 410 |
+ |
|
| 411 |
+ // 2nd batch |
|
| 412 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkRunningTaskImages, checker.DeepEquals, |
|
| 413 |
+ map[string]int{image1: instances - 2*parallelism, image2: 2 * parallelism})
|
|
| 414 |
+ |
|
| 415 |
+ // 3nd batch |
|
| 416 |
+ waitAndAssert(c, defaultReconciliationTimeout, daemons[0].checkRunningTaskImages, checker.DeepEquals, |
|
| 417 |
+ map[string]int{image2: instances})
|
|
| 418 |
+} |
|
| 419 |
+ |
|
| 374 | 420 |
func (s *DockerSwarmSuite) TestApiSwarmServicesStateReporting(c *check.C) {
|
| 375 | 421 |
testRequires(c, Network) |
| 376 | 422 |
testRequires(c, SameHostDaemon) |
| ... | ... |
@@ -754,6 +800,29 @@ func simpleTestService(s *swarm.Service) {
|
| 754 | 754 |
s.Spec.Name = "top" |
| 755 | 755 |
} |
| 756 | 756 |
|
| 757 |
+func serviceForUpdate(s *swarm.Service) {
|
|
| 758 |
+ var ureplicas uint64 |
|
| 759 |
+ ureplicas = 1 |
|
| 760 |
+ s.Spec = swarm.ServiceSpec{
|
|
| 761 |
+ TaskTemplate: swarm.TaskSpec{
|
|
| 762 |
+ ContainerSpec: swarm.ContainerSpec{
|
|
| 763 |
+ Image: "busybox:latest", |
|
| 764 |
+ Command: []string{"/bin/top"},
|
|
| 765 |
+ }, |
|
| 766 |
+ }, |
|
| 767 |
+ Mode: swarm.ServiceMode{
|
|
| 768 |
+ Replicated: &swarm.ReplicatedService{
|
|
| 769 |
+ Replicas: &ureplicas, |
|
| 770 |
+ }, |
|
| 771 |
+ }, |
|
| 772 |
+ UpdateConfig: &swarm.UpdateConfig{
|
|
| 773 |
+ Parallelism: 2, |
|
| 774 |
+ Delay: 8 * time.Second, |
|
| 775 |
+ }, |
|
| 776 |
+ } |
|
| 777 |
+ s.Spec.Name = "updatetest" |
|
| 778 |
+} |
|
| 779 |
+ |
|
| 757 | 780 |
func setInstances(replicas int) serviceConstructor {
|
| 758 | 781 |
ureplicas := uint64(replicas) |
| 759 | 782 |
return func(s *swarm.Service) {
|
| ... | ... |
@@ -765,6 +834,12 @@ func setInstances(replicas int) serviceConstructor {
|
| 765 | 765 |
} |
| 766 | 766 |
} |
| 767 | 767 |
|
| 768 |
+func setImage(image string) serviceConstructor {
|
|
| 769 |
+ return func(s *swarm.Service) {
|
|
| 770 |
+ s.Spec.TaskTemplate.ContainerSpec.Image = image |
|
| 771 |
+ } |
|
| 772 |
+} |
|
| 773 |
+ |
|
| 768 | 774 |
func setGlobalMode(s *swarm.Service) {
|
| 769 | 775 |
s.Spec.Mode = swarm.ServiceMode{
|
| 770 | 776 |
Global: &swarm.GlobalService{},
|