github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/integration/container/wait_test.go (about) 1 package container // import "github.com/docker/docker/integration/container" 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/docker/docker/api/types" 9 containertypes "github.com/docker/docker/api/types/container" 10 "github.com/docker/docker/integration/internal/container" 11 "github.com/docker/docker/testutil/request" 12 "gotest.tools/v3/assert" 13 is "gotest.tools/v3/assert/cmp" 14 "gotest.tools/v3/poll" 15 "gotest.tools/v3/skip" 16 ) 17 18 func TestWaitNonBlocked(t *testing.T) { 19 defer setupTest(t)() 20 cli := request.NewAPIClient(t) 21 22 testCases := []struct { 23 doc string 24 cmd string 25 expectedCode int64 26 }{ 27 { 28 doc: "wait-nonblocking-exit-0", 29 cmd: "exit 0", 30 expectedCode: 0, 31 }, 32 { 33 doc: "wait-nonblocking-exit-random", 34 cmd: "exit 99", 35 expectedCode: 99, 36 }, 37 } 38 39 for _, tc := range testCases { 40 tc := tc 41 t.Run(tc.doc, func(t *testing.T) { 42 t.Parallel() 43 ctx := context.Background() 44 containerID := container.Run(ctx, t, cli, container.WithCmd("sh", "-c", tc.cmd)) 45 poll.WaitOn(t, container.IsInState(ctx, cli, containerID, "exited"), poll.WithTimeout(30*time.Second), poll.WithDelay(100*time.Millisecond)) 46 47 waitResC, errC := cli.ContainerWait(ctx, containerID, "") 48 select { 49 case err := <-errC: 50 assert.NilError(t, err) 51 case waitRes := <-waitResC: 52 assert.Check(t, is.Equal(tc.expectedCode, waitRes.StatusCode)) 53 } 54 }) 55 } 56 } 57 58 func TestWaitBlocked(t *testing.T) { 59 // Windows busybox does not support trap in this way, not sleep with sub-second 60 // granularity. It will always exit 0x40010004. 61 skip.If(t, testEnv.DaemonInfo.OSType != "linux") 62 defer setupTest(t)() 63 cli := request.NewAPIClient(t) 64 65 testCases := []struct { 66 doc string 67 cmd string 68 expectedCode int64 69 }{ 70 { 71 doc: "test-wait-blocked-exit-zero", 72 cmd: "trap 'exit 0' TERM; while true; do usleep 10; done", 73 expectedCode: 0, 74 }, 75 { 76 doc: "test-wait-blocked-exit-random", 77 cmd: "trap 'exit 99' TERM; while true; do usleep 10; done", 78 expectedCode: 99, 79 }, 80 } 81 for _, tc := range testCases { 82 tc := tc 83 t.Run(tc.doc, func(t *testing.T) { 84 t.Parallel() 85 ctx := context.Background() 86 containerID := container.Run(ctx, t, cli, container.WithCmd("sh", "-c", tc.cmd)) 87 poll.WaitOn(t, container.IsInState(ctx, cli, containerID, "running"), poll.WithTimeout(30*time.Second), poll.WithDelay(100*time.Millisecond)) 88 89 waitResC, errC := cli.ContainerWait(ctx, containerID, "") 90 91 err := cli.ContainerStop(ctx, containerID, containertypes.StopOptions{}) 92 assert.NilError(t, err) 93 94 select { 95 case err := <-errC: 96 assert.NilError(t, err) 97 case waitRes := <-waitResC: 98 assert.Check(t, is.Equal(tc.expectedCode, waitRes.StatusCode)) 99 case <-time.After(2 * time.Second): 100 t.Fatal("timeout waiting for `docker wait`") 101 } 102 }) 103 } 104 } 105 106 func TestWaitConditions(t *testing.T) { 107 defer setupTest(t)() 108 cli := request.NewAPIClient(t) 109 110 testCases := []struct { 111 doc string 112 waitCond containertypes.WaitCondition 113 runOpts []func(*container.TestContainerConfig) 114 }{ 115 { 116 doc: "default", 117 }, 118 { 119 doc: "not-running", 120 waitCond: containertypes.WaitConditionNotRunning, 121 }, 122 { 123 doc: "next-exit", 124 waitCond: containertypes.WaitConditionNextExit, 125 }, 126 { 127 doc: "removed", 128 waitCond: containertypes.WaitConditionRemoved, 129 runOpts: []func(*container.TestContainerConfig){container.WithAutoRemove}, 130 }, 131 } 132 133 for _, tc := range testCases { 134 tc := tc 135 t.Run(tc.doc, func(t *testing.T) { 136 t.Parallel() 137 ctx := context.Background() 138 opts := append([]func(*container.TestContainerConfig){ 139 container.WithCmd("sh", "-c", "read -r; exit 99"), 140 func(tcc *container.TestContainerConfig) { 141 tcc.Config.AttachStdin = true 142 tcc.Config.OpenStdin = true 143 }, 144 }, tc.runOpts...) 145 containerID := container.Create(ctx, t, cli, opts...) 146 t.Logf("ContainerID = %v", containerID) 147 148 streams, err := cli.ContainerAttach(ctx, containerID, types.ContainerAttachOptions{Stream: true, Stdin: true}) 149 assert.NilError(t, err) 150 defer streams.Close() 151 152 assert.NilError(t, cli.ContainerStart(ctx, containerID, types.ContainerStartOptions{})) 153 waitResC, errC := cli.ContainerWait(ctx, containerID, tc.waitCond) 154 select { 155 case err := <-errC: 156 t.Fatalf("ContainerWait() err = %v", err) 157 case res := <-waitResC: 158 t.Fatalf("ContainerWait() sent exit code (%v) before ContainerStart()", res) 159 default: 160 } 161 162 info, _ := cli.ContainerInspect(ctx, containerID) 163 assert.Equal(t, "running", info.State.Status) 164 165 _, err = streams.Conn.Write([]byte("\n")) 166 assert.NilError(t, err) 167 168 select { 169 case err := <-errC: 170 assert.NilError(t, err) 171 case waitRes := <-waitResC: 172 assert.Check(t, is.Equal(int64(99), waitRes.StatusCode)) 173 case <-time.After(15 * time.Second): 174 info, _ := cli.ContainerInspect(ctx, containerID) 175 t.Fatalf("Timed out waiting for container exit code (status = %q)", info.State.Status) 176 } 177 }) 178 } 179 } 180 181 func TestWaitRestartedContainer(t *testing.T) { 182 defer setupTest(t)() 183 cli := request.NewAPIClient(t) 184 185 testCases := []struct { 186 doc string 187 waitCond containertypes.WaitCondition 188 }{ 189 { 190 doc: "default", 191 }, 192 { 193 doc: "not-running", 194 waitCond: containertypes.WaitConditionNotRunning, 195 }, 196 { 197 doc: "next-exit", 198 waitCond: containertypes.WaitConditionNextExit, 199 }, 200 } 201 202 // We can't catch the SIGTERM in the Windows based busybox image 203 isWindowDaemon := testEnv.DaemonInfo.OSType == "windows" 204 205 for _, tc := range testCases { 206 tc := tc 207 t.Run(tc.doc, func(t *testing.T) { 208 t.Parallel() 209 ctx := context.Background() 210 containerID := container.Run(ctx, t, cli, 211 container.WithCmd("sh", "-c", "trap 'exit 5' SIGTERM; while true; do sleep 0.1; done"), 212 ) 213 defer cli.ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{Force: true}) 214 215 poll.WaitOn(t, container.IsInState(ctx, cli, containerID, "running"), poll.WithTimeout(30*time.Second), poll.WithDelay(100*time.Millisecond)) 216 217 // Container is running now, wait for exit 218 waitResC, errC := cli.ContainerWait(ctx, containerID, tc.waitCond) 219 220 timeout := 5 221 // On Windows it will always timeout, because our process won't receive SIGTERM 222 // Skip to force killing immediately 223 if isWindowDaemon { 224 timeout = 0 225 } 226 227 err := cli.ContainerRestart(ctx, containerID, containertypes.StopOptions{Timeout: &timeout, Signal: "SIGTERM"}) 228 assert.NilError(t, err) 229 230 select { 231 case err := <-errC: 232 t.Fatalf("Unexpected error: %v", err) 233 case <-time.After(time.Second * 3): 234 t.Fatalf("Wait should end after restart") 235 case waitRes := <-waitResC: 236 expectedCode := int64(5) 237 238 if !isWindowDaemon { 239 assert.Check(t, is.Equal(expectedCode, waitRes.StatusCode)) 240 } 241 } 242 }) 243 } 244 }