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