github.com/rumpl/bof@v23.0.0-rc.2+incompatible/integration/container/health_test.go (about) 1 package container // import "github.com/docker/docker/integration/container" 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/docker/docker/api/types" 10 containertypes "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/client" 12 "github.com/docker/docker/integration/internal/container" 13 "gotest.tools/v3/assert" 14 "gotest.tools/v3/poll" 15 "gotest.tools/v3/skip" 16 ) 17 18 // TestHealthCheckWorkdir verifies that health-checks inherit the containers' 19 // working-dir. 20 func TestHealthCheckWorkdir(t *testing.T) { 21 skip.If(t, testEnv.OSType == "windows", "FIXME") 22 defer setupTest(t)() 23 ctx := context.Background() 24 client := testEnv.APIClient() 25 26 cID := container.Run(ctx, t, client, container.WithTty(true), container.WithWorkingDir("/foo"), func(c *container.TestContainerConfig) { 27 c.Config.Healthcheck = &containertypes.HealthConfig{ 28 Test: []string{"CMD-SHELL", "if [ \"$PWD\" = \"/foo\" ]; then exit 0; else exit 1; fi;"}, 29 Interval: 50 * time.Millisecond, 30 Retries: 3, 31 } 32 }) 33 34 poll.WaitOn(t, pollForHealthStatus(ctx, client, cID, types.Healthy), poll.WithDelay(100*time.Millisecond)) 35 } 36 37 // GitHub #37263 38 // Do not stop healthchecks just because we sent a signal to the container 39 func TestHealthKillContainer(t *testing.T) { 40 skip.If(t, testEnv.OSType == "windows", "Windows only supports SIGKILL and SIGTERM? See https://github.com/moby/moby/issues/39574") 41 defer setupTest(t)() 42 43 ctx := context.Background() 44 client := testEnv.APIClient() 45 46 id := container.Run(ctx, t, client, func(c *container.TestContainerConfig) { 47 cmd := ` 48 # Set the initial HEALTH value so the healthcheck passes 49 HEALTH="1" 50 echo $HEALTH > /health 51 52 # Any time doHealth is run we flip the value 53 # This lets us use kill signals to determine when healtchecks have run. 54 doHealth() { 55 case "$HEALTH" in 56 "0") 57 HEALTH="1" 58 ;; 59 "1") 60 HEALTH="0" 61 ;; 62 esac 63 echo $HEALTH > /health 64 } 65 66 trap 'doHealth' USR1 67 68 while true; do sleep 1; done 69 ` 70 c.Config.Cmd = []string{"/bin/sh", "-c", cmd} 71 c.Config.Healthcheck = &containertypes.HealthConfig{ 72 Test: []string{"CMD-SHELL", `[ "$(cat /health)" = "1" ]`}, 73 Interval: time.Second, 74 Retries: 5, 75 } 76 }) 77 78 ctxPoll, cancel := context.WithTimeout(ctx, 30*time.Second) 79 defer cancel() 80 poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "healthy"), poll.WithDelay(100*time.Millisecond)) 81 82 err := client.ContainerKill(ctx, id, "SIGUSR1") 83 assert.NilError(t, err) 84 85 ctxPoll, cancel = context.WithTimeout(ctx, 30*time.Second) 86 defer cancel() 87 poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "unhealthy"), poll.WithDelay(100*time.Millisecond)) 88 89 err = client.ContainerKill(ctx, id, "SIGUSR1") 90 assert.NilError(t, err) 91 92 ctxPoll, cancel = context.WithTimeout(ctx, 30*time.Second) 93 defer cancel() 94 poll.WaitOn(t, pollForHealthStatus(ctxPoll, client, id, "healthy"), poll.WithDelay(100*time.Millisecond)) 95 } 96 97 // TestHealthCheckProcessKilled verifies that health-checks exec get killed on time-out. 98 func TestHealthCheckProcessKilled(t *testing.T) { 99 skip.If(t, testEnv.RuntimeIsWindowsContainerd(), "FIXME: Broken on Windows + containerd combination") 100 defer setupTest(t)() 101 ctx := context.Background() 102 apiClient := testEnv.APIClient() 103 104 cID := container.Run(ctx, t, apiClient, func(c *container.TestContainerConfig) { 105 c.Config.Healthcheck = &containertypes.HealthConfig{ 106 Test: []string{"CMD", "sh", "-c", "sleep 60"}, 107 Interval: 100 * time.Millisecond, 108 Timeout: 50 * time.Millisecond, 109 Retries: 1, 110 } 111 }) 112 poll.WaitOn(t, pollForHealthCheckLog(ctx, apiClient, cID, "Health check exceeded timeout (50ms)")) 113 } 114 115 func pollForHealthCheckLog(ctx context.Context, client client.APIClient, containerID string, expected string) func(log poll.LogT) poll.Result { 116 return func(log poll.LogT) poll.Result { 117 inspect, err := client.ContainerInspect(ctx, containerID) 118 if err != nil { 119 return poll.Error(err) 120 } 121 healthChecksTotal := len(inspect.State.Health.Log) 122 if healthChecksTotal > 0 { 123 output := inspect.State.Health.Log[healthChecksTotal-1].Output 124 if output == expected { 125 return poll.Success() 126 } 127 return poll.Error(fmt.Errorf("expected %q, got %q", expected, output)) 128 } 129 return poll.Continue("waiting for container healthcheck logs") 130 } 131 } 132 133 func pollForHealthStatus(ctx context.Context, client client.APIClient, containerID string, healthStatus string) func(log poll.LogT) poll.Result { 134 return func(log poll.LogT) poll.Result { 135 inspect, err := client.ContainerInspect(ctx, containerID) 136 137 switch { 138 case err != nil: 139 return poll.Error(err) 140 case inspect.State.Health.Status == healthStatus: 141 return poll.Success() 142 default: 143 return poll.Continue("waiting for container to become %s", healthStatus) 144 } 145 } 146 }