github.com/flavio/docker@v0.1.3-0.20170117145210-f63d1a6eec47/integration-cli/docker_cli_service_health_test.go (about) 1 // +build !windows 2 3 package main 4 5 import ( 6 "strconv" 7 "strings" 8 9 "github.com/docker/docker/api/types/swarm" 10 "github.com/docker/docker/daemon/cluster/executor/container" 11 "github.com/docker/docker/integration-cli/checker" 12 "github.com/go-check/check" 13 ) 14 15 // start a service, and then make its task unhealthy during running 16 // finally, unhealthy task should be detected and killed 17 func (s *DockerSwarmSuite) TestServiceHealthRun(c *check.C) { 18 testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows 19 20 d := s.AddDaemon(c, true, true) 21 22 // build image with health-check 23 // note: use `daemon.buildImageWithOut` to build, do not use `buildImage` to build 24 imageName := "testhealth" 25 _, _, err := d.BuildImageWithOut(imageName, 26 `FROM busybox 27 RUN touch /status 28 HEALTHCHECK --interval=1s --timeout=1s --retries=1\ 29 CMD cat /status`, 30 true) 31 c.Check(err, check.IsNil) 32 33 serviceName := "healthServiceRun" 34 out, err := d.Cmd("service", "create", "--name", serviceName, imageName, "top") 35 c.Assert(err, checker.IsNil, check.Commentf(out)) 36 id := strings.TrimSpace(out) 37 38 var tasks []swarm.Task 39 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 40 tasks = d.GetServiceTasks(c, id) 41 return tasks, nil 42 }, checker.HasLen, 1) 43 44 task := tasks[0] 45 46 // wait for task to start 47 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 48 task = d.GetTask(c, task.ID) 49 return task.Status.State, nil 50 }, checker.Equals, swarm.TaskStateRunning) 51 containerID := task.Status.ContainerStatus.ContainerID 52 53 // wait for container to be healthy 54 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 55 out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID) 56 return strings.TrimSpace(out), nil 57 }, checker.Equals, "healthy") 58 59 // make it fail 60 d.Cmd("exec", containerID, "rm", "/status") 61 // wait for container to be unhealthy 62 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 63 out, _ := d.Cmd("inspect", "--format={{.State.Health.Status}}", containerID) 64 return strings.TrimSpace(out), nil 65 }, checker.Equals, "unhealthy") 66 67 // Task should be terminated 68 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 69 task = d.GetTask(c, task.ID) 70 return task.Status.State, nil 71 }, checker.Equals, swarm.TaskStateFailed) 72 73 if !strings.Contains(task.Status.Err, container.ErrContainerUnhealthy.Error()) { 74 c.Fatal("unhealthy task exits because of other error") 75 } 76 } 77 78 // start a service whose task is unhealthy at beginning 79 // its tasks should be blocked in starting stage, until health check is passed 80 func (s *DockerSwarmSuite) TestServiceHealthStart(c *check.C) { 81 testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows 82 83 d := s.AddDaemon(c, true, true) 84 85 // service started from this image won't pass health check 86 imageName := "testhealth" 87 _, _, err := d.BuildImageWithOut(imageName, 88 `FROM busybox 89 HEALTHCHECK --interval=1s --timeout=1s --retries=1024\ 90 CMD cat /status`, 91 true) 92 c.Check(err, check.IsNil) 93 94 serviceName := "healthServiceStart" 95 out, err := d.Cmd("service", "create", "--name", serviceName, imageName, "top") 96 c.Assert(err, checker.IsNil, check.Commentf(out)) 97 id := strings.TrimSpace(out) 98 99 var tasks []swarm.Task 100 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 101 tasks = d.GetServiceTasks(c, id) 102 return tasks, nil 103 }, checker.HasLen, 1) 104 105 task := tasks[0] 106 107 // wait for task to start 108 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 109 task = d.GetTask(c, task.ID) 110 return task.Status.State, nil 111 }, checker.Equals, swarm.TaskStateStarting) 112 113 containerID := task.Status.ContainerStatus.ContainerID 114 115 // wait for health check to work 116 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 117 out, _ := d.Cmd("inspect", "--format={{.State.Health.FailingStreak}}", containerID) 118 failingStreak, _ := strconv.Atoi(strings.TrimSpace(out)) 119 return failingStreak, nil 120 }, checker.GreaterThan, 0) 121 122 // task should be blocked at starting status 123 task = d.GetTask(c, task.ID) 124 c.Assert(task.Status.State, check.Equals, swarm.TaskStateStarting) 125 126 // make it healthy 127 d.Cmd("exec", containerID, "touch", "/status") 128 129 // Task should be at running status 130 waitAndAssert(c, defaultReconciliationTimeout, func(c *check.C) (interface{}, check.CommentInterface) { 131 task = d.GetTask(c, task.ID) 132 return task.Status.State, nil 133 }, checker.Equals, swarm.TaskStateRunning) 134 }