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