github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/integration-cli/docker_cli_health_test.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "strconv" 6 "strings" 7 "time" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/integration-cli/checker" 11 "github.com/docker/docker/integration-cli/cli/build" 12 "github.com/go-check/check" 13 ) 14 15 func waitForHealthStatus(c *check.C, name string, prev string, expected string) { 16 prev = prev + "\n" 17 expected = expected + "\n" 18 for { 19 out, _ := dockerCmd(c, "inspect", "--format={{.State.Health.Status}}", name) 20 if out == expected { 21 return 22 } 23 c.Check(out, checker.Equals, prev) 24 if out != prev { 25 return 26 } 27 time.Sleep(100 * time.Millisecond) 28 } 29 } 30 31 func getHealth(c *check.C, name string) *types.Health { 32 out, _ := dockerCmd(c, "inspect", "--format={{json .State.Health}}", name) 33 var health types.Health 34 err := json.Unmarshal([]byte(out), &health) 35 c.Check(err, checker.Equals, nil) 36 return &health 37 } 38 39 func (s *DockerSuite) TestHealth(c *check.C) { 40 testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows 41 42 existingContainers := ExistingContainerIDs(c) 43 44 imageName := "testhealth" 45 buildImageSuccessfully(c, imageName, build.WithDockerfile(`FROM busybox 46 RUN echo OK > /status 47 CMD ["/bin/sleep", "120"] 48 STOPSIGNAL SIGKILL 49 HEALTHCHECK --interval=1s --timeout=30s \ 50 CMD cat /status`)) 51 52 // No health status before starting 53 name := "test_health" 54 cid, _ := dockerCmd(c, "create", "--name", name, imageName) 55 out, _ := dockerCmd(c, "ps", "-a", "--format={{.ID}} {{.Status}}") 56 out = RemoveOutputForExistingElements(out, existingContainers) 57 c.Check(out, checker.Equals, cid[:12]+" Created\n") 58 59 // Inspect the options 60 out, _ = dockerCmd(c, "inspect", 61 "--format=timeout={{.Config.Healthcheck.Timeout}} interval={{.Config.Healthcheck.Interval}} retries={{.Config.Healthcheck.Retries}} test={{.Config.Healthcheck.Test}}", name) 62 c.Check(out, checker.Equals, "timeout=30s interval=1s retries=0 test=[CMD-SHELL cat /status]\n") 63 64 // Start 65 dockerCmd(c, "start", name) 66 waitForHealthStatus(c, name, "starting", "healthy") 67 68 // Make it fail 69 dockerCmd(c, "exec", name, "rm", "/status") 70 waitForHealthStatus(c, name, "healthy", "unhealthy") 71 72 // Inspect the status 73 out, _ = dockerCmd(c, "inspect", "--format={{.State.Health.Status}}", name) 74 c.Check(out, checker.Equals, "unhealthy\n") 75 76 // Make it healthy again 77 dockerCmd(c, "exec", name, "touch", "/status") 78 waitForHealthStatus(c, name, "unhealthy", "healthy") 79 80 // Remove container 81 dockerCmd(c, "rm", "-f", name) 82 83 // Disable the check from the CLI 84 out, _ = dockerCmd(c, "create", "--name=noh", "--no-healthcheck", imageName) 85 out, _ = dockerCmd(c, "inspect", "--format={{.Config.Healthcheck.Test}}", "noh") 86 c.Check(out, checker.Equals, "[NONE]\n") 87 dockerCmd(c, "rm", "noh") 88 89 // Disable the check with a new build 90 buildImageSuccessfully(c, "no_healthcheck", build.WithDockerfile(`FROM testhealth 91 HEALTHCHECK NONE`)) 92 93 out, _ = dockerCmd(c, "inspect", "--format={{.ContainerConfig.Healthcheck.Test}}", "no_healthcheck") 94 c.Check(out, checker.Equals, "[NONE]\n") 95 96 // Enable the checks from the CLI 97 _, _ = dockerCmd(c, "run", "-d", "--name=fatal_healthcheck", 98 "--health-interval=1s", 99 "--health-retries=3", 100 "--health-cmd=cat /status", 101 "no_healthcheck") 102 waitForHealthStatus(c, "fatal_healthcheck", "starting", "healthy") 103 health := getHealth(c, "fatal_healthcheck") 104 c.Check(health.Status, checker.Equals, "healthy") 105 c.Check(health.FailingStreak, checker.Equals, 0) 106 last := health.Log[len(health.Log)-1] 107 c.Check(last.ExitCode, checker.Equals, 0) 108 c.Check(last.Output, checker.Equals, "OK\n") 109 110 // Fail the check 111 dockerCmd(c, "exec", "fatal_healthcheck", "rm", "/status") 112 waitForHealthStatus(c, "fatal_healthcheck", "healthy", "unhealthy") 113 114 failsStr, _ := dockerCmd(c, "inspect", "--format={{.State.Health.FailingStreak}}", "fatal_healthcheck") 115 fails, err := strconv.Atoi(strings.TrimSpace(failsStr)) 116 c.Check(err, check.IsNil) 117 c.Check(fails >= 3, checker.Equals, true) 118 dockerCmd(c, "rm", "-f", "fatal_healthcheck") 119 120 // Check timeout 121 // Note: if the interval is too small, it seems that Docker spends all its time running health 122 // checks and never gets around to killing it. 123 _, _ = dockerCmd(c, "run", "-d", "--name=test", 124 "--health-interval=1s", "--health-cmd=sleep 5m", "--health-timeout=1s", imageName) 125 waitForHealthStatus(c, "test", "starting", "unhealthy") 126 health = getHealth(c, "test") 127 last = health.Log[len(health.Log)-1] 128 c.Check(health.Status, checker.Equals, "unhealthy") 129 c.Check(last.ExitCode, checker.Equals, -1) 130 c.Check(last.Output, checker.Equals, "Health check exceeded timeout (1s)") 131 dockerCmd(c, "rm", "-f", "test") 132 133 // Check JSON-format 134 buildImageSuccessfully(c, imageName, build.WithDockerfile(`FROM busybox 135 RUN echo OK > /status 136 CMD ["/bin/sleep", "120"] 137 STOPSIGNAL SIGKILL 138 HEALTHCHECK --interval=1s --timeout=30s \ 139 CMD ["cat", "/my status"]`)) 140 out, _ = dockerCmd(c, "inspect", 141 "--format={{.Config.Healthcheck.Test}}", imageName) 142 c.Check(out, checker.Equals, "[CMD cat /my status]\n") 143 144 } 145 146 // GitHub #33021 147 func (s *DockerSuite) TestUnsetEnvVarHealthCheck(c *check.C) { 148 testRequires(c, DaemonIsLinux) // busybox doesn't work on Windows 149 150 imageName := "testhealth" 151 buildImageSuccessfully(c, imageName, build.WithDockerfile(`FROM busybox 152 HEALTHCHECK --interval=1s --timeout=5s --retries=5 CMD /bin/sh -c "sleep 1" 153 ENTRYPOINT /bin/sh -c "sleep 600"`)) 154 155 name := "env_test_health" 156 // No health status before starting 157 dockerCmd(c, "run", "-d", "--name", name, "-e", "FOO", imageName) 158 defer func() { 159 dockerCmd(c, "rm", "-f", name) 160 dockerCmd(c, "rmi", imageName) 161 }() 162 163 // Start 164 dockerCmd(c, "start", name) 165 waitForHealthStatus(c, name, "starting", "healthy") 166 167 }