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