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  }