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