wait_test.go 6.7 KB

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