Browse code

integration-cli: Migrate restart suite

Migrate non-redundant `DockerCLIRestartSuite` cases to modern integration tests:

- `TestRestartStoppedContainer` -> `TestContainerRestartStoppedContainer`
- `TestRestartWithVolumes` -> `TestContainerRestartWithVolumes`
- `TestRestartContainerSuccess` -> `TestContainerRestartPolicyAfterProcessExit/direct-process-exit`
- `TestRestartPolicyAfterRestart` -> `TestContainerRestartPolicyAfterProcessExit/after-manual-restart`
- `TestRestartWithPolicyUserDefinedNetwork` -> `TestContainerRestartPolicyUserDefinedNetwork`

Drop redundant deprecated cases:

- `TestRestartRunningContainer`
covered by `TestWaitRestartedContainer`.
- `TestRestartPolicyNO`, `TestRestartPolicyAlways`, `TestRestartPolicyOnFailure`
covered by modern create/restart-policy tests.
- `TestRestartAutoRemoveContainer`
covered by `TestContainerWithAutoRemoveCanBeRestarted`.
- `TestRestartDisconnectedContainer`
low-value adjacent coverage already exercised by networking restart tests.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2026/06/17 21:28:48
Showing 3 changed files
... ...
@@ -291,12 +291,6 @@ func TestDockerCLIPushSuite(t *testing.T) {
291 291
 	suite.Run(ctx, t, &DockerCLIPushSuite{ds: &DockerSuite{}})
292 292
 }
293 293
 
294
-func TestDockerCLIRestartSuite(t *testing.T) {
295
-	ctx := testutil.StartSpan(baseContext, t)
296
-	ensureTestEnvSetup(ctx, t)
297
-	suite.Run(ctx, t, &DockerCLIRestartSuite{ds: &DockerSuite{}})
298
-}
299
-
300 294
 func TestDockerCLIRmiSuite(t *testing.T) {
301 295
 	ctx := testutil.StartSpan(baseContext, t)
302 296
 	ensureTestEnvSetup(ctx, t)
303 297
deleted file mode 100644
... ...
@@ -1,277 +0,0 @@
1
-package main
2
-
3
-import (
4
-	"context"
5
-	"os"
6
-	"strconv"
7
-	"strings"
8
-	"testing"
9
-	"time"
10
-
11
-	"github.com/moby/moby/v2/integration-cli/checker"
12
-	"github.com/moby/moby/v2/integration-cli/cli"
13
-	"gotest.tools/v3/assert"
14
-	is "gotest.tools/v3/assert/cmp"
15
-	"gotest.tools/v3/poll"
16
-	"gotest.tools/v3/skip"
17
-)
18
-
19
-type DockerCLIRestartSuite struct {
20
-	ds *DockerSuite
21
-}
22
-
23
-func (s *DockerCLIRestartSuite) TearDownTest(ctx context.Context, t *testing.T) {
24
-	s.ds.TearDownTest(ctx, t)
25
-}
26
-
27
-func (s *DockerCLIRestartSuite) OnTimeout(t *testing.T) {
28
-	s.ds.OnTimeout(t)
29
-}
30
-
31
-func (s *DockerCLIRestartSuite) TestRestartStoppedContainer(c *testing.T) {
32
-	cID := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && exit 0").Stdout()
33
-	cID = strings.TrimSpace(cID)
34
-
35
-	getLogs := func(t *testing.T) (any, string) {
36
-		out := cli.DockerCmd(t, "logs", cID).Combined()
37
-		return out, ""
38
-	}
39
-
40
-	// Wait 10 seconds for the 'echo' to appear in the logs
41
-	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\n")), poll.WithTimeout(10*time.Second))
42
-
43
-	// Make sure the container has stopped before we restart it.
44
-	cli.WaitExited(c, cID, 20*time.Second)
45
-	cli.DockerCmd(c, "restart", cID)
46
-
47
-	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\nfoobar\n")), poll.WithTimeout(10*time.Second))
48
-}
49
-
50
-func (s *DockerCLIRestartSuite) TestRestartRunningContainer(c *testing.T) {
51
-	cID := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && sleep 30 && echo 'should not print this'").Stdout()
52
-	cID = strings.TrimSpace(cID)
53
-	cli.WaitRun(c, cID)
54
-
55
-	getLogs := func(t *testing.T) (any, string) {
56
-		out := cli.DockerCmd(t, "logs", cID).Combined()
57
-		return out, ""
58
-	}
59
-
60
-	// Wait 10 seconds for the 'echo' to appear in the logs
61
-	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\n")), poll.WithTimeout(10*time.Second))
62
-
63
-	cli.DockerCmd(c, "restart", "-t", "1", cID)
64
-	cli.WaitRun(c, cID)
65
-
66
-	// Wait 10 seconds for first 'echo' appear (again) in the logs
67
-	poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\nfoobar\n")), poll.WithTimeout(10*time.Second))
68
-}
69
-
70
-// Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819.
71
-func (s *DockerCLIRestartSuite) TestRestartWithVolumes(c *testing.T) {
72
-	cID := runSleepingContainer(c, "-d", "-v", dPath("/test"))
73
-	out, err := inspectFilter(cID, "len .Mounts")
74
-	assert.NilError(c, err, "failed to inspect %s: %s", cID, out)
75
-	out = strings.Trim(out, " \n\r")
76
-	assert.Equal(c, out, "1")
77
-
78
-	mnt, err := inspectMountPoint(cID, dPath("/test"))
79
-	assert.NilError(c, err)
80
-
81
-	cli.DockerCmd(c, "restart", cID)
82
-
83
-	out, err = inspectFilter(cID, "len .Mounts")
84
-	assert.NilError(c, err, "failed to inspect %s: %s", cID, out)
85
-	out = strings.Trim(out, " \n\r")
86
-	assert.Equal(c, out, "1")
87
-
88
-	mountAfterRestart, err := inspectMountPoint(cID, dPath("/test"))
89
-	assert.NilError(c, err)
90
-	assert.Equal(c, mnt.Source, mountAfterRestart.Source)
91
-}
92
-
93
-func (s *DockerCLIRestartSuite) TestRestartDisconnectedContainer(c *testing.T) {
94
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace)
95
-
96
-	// Run a container on the default bridge network
97
-	cID := cli.DockerCmd(c, "run", "-d", "--name", "c0", "busybox", "top").Stdout()
98
-	cID = strings.TrimSpace(cID)
99
-	cli.WaitRun(c, cID)
100
-
101
-	// Disconnect the container from the network
102
-	result := cli.DockerCmd(c, "network", "disconnect", "bridge", "c0")
103
-	assert.Assert(c, result.ExitCode == 0, result.Combined())
104
-
105
-	// Restart the container
106
-	result = cli.DockerCmd(c, "restart", "c0")
107
-	assert.Assert(c, result.ExitCode == 0, result.Combined())
108
-}
109
-
110
-func (s *DockerCLIRestartSuite) TestRestartPolicyNO(c *testing.T) {
111
-	cID := cli.DockerCmd(c, "create", "--restart=no", "busybox").Stdout()
112
-	cID = strings.TrimSpace(cID)
113
-	name := inspectField(c, cID, "HostConfig.RestartPolicy.Name")
114
-	assert.Equal(c, name, "no")
115
-}
116
-
117
-func (s *DockerCLIRestartSuite) TestRestartPolicyAlways(c *testing.T) {
118
-	id := cli.DockerCmd(c, "create", "--restart=always", "busybox").Stdout()
119
-	id = strings.TrimSpace(id)
120
-	name := inspectField(c, id, "HostConfig.RestartPolicy.Name")
121
-	assert.Equal(c, name, "always")
122
-
123
-	MaximumRetryCount := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
124
-
125
-	// MaximumRetryCount=0 if the restart policy is always
126
-	assert.Equal(c, MaximumRetryCount, "0")
127
-}
128
-
129
-func (s *DockerCLIRestartSuite) TestRestartPolicyOnFailure(c *testing.T) {
130
-	out, _, err := dockerCmdWithError("create", "--restart=on-failure:-1", "busybox")
131
-	assert.ErrorContains(c, err, "", out)
132
-	assert.Assert(c, is.Contains(out, "maximum retry count cannot be negative"))
133
-
134
-	id := cli.DockerCmd(c, "create", "--restart=on-failure:1", "busybox").Stdout()
135
-	id = strings.TrimSpace(id)
136
-	name := inspectField(c, id, "HostConfig.RestartPolicy.Name")
137
-	maxRetry := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
138
-	assert.Equal(c, name, "on-failure")
139
-	assert.Equal(c, maxRetry, "1")
140
-
141
-	id = cli.DockerCmd(c, "create", "--restart=on-failure:0", "busybox").Stdout()
142
-	id = strings.TrimSpace(id)
143
-	name = inspectField(c, id, "HostConfig.RestartPolicy.Name")
144
-	maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
145
-	assert.Equal(c, name, "on-failure")
146
-	assert.Equal(c, maxRetry, "0")
147
-
148
-	id = cli.DockerCmd(c, "create", "--restart=on-failure", "busybox").Stdout()
149
-	id = strings.TrimSpace(id)
150
-	name = inspectField(c, id, "HostConfig.RestartPolicy.Name")
151
-	maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount")
152
-	assert.Equal(c, name, "on-failure")
153
-	assert.Equal(c, maxRetry, "0")
154
-}
155
-
156
-func (s *DockerCLIRestartSuite) TestRestartContainerSuccess(c *testing.T) {
157
-	testRequires(c, testEnv.IsLocalDaemon)
158
-	// Skipped for Hyper-V isolated containers. Test is currently written
159
-	// such that it assumes there is a host process to kill. In Hyper-V
160
-	// containers, the process is inside the utility VM, not on the host.
161
-	if DaemonIsWindows() {
162
-		skip.If(c, testEnv.GitHubActions())
163
-		testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess)
164
-	}
165
-
166
-	id := runSleepingContainer(c, "-d", "--restart=always")
167
-	cli.WaitRun(c, id)
168
-
169
-	pidStr := inspectField(c, id, "State.Pid")
170
-
171
-	pid, err := strconv.Atoi(pidStr)
172
-	assert.NilError(c, err)
173
-
174
-	p, err := os.FindProcess(pid)
175
-	assert.NilError(c, err)
176
-	assert.Assert(c, p != nil)
177
-
178
-	err = p.Kill()
179
-	assert.NilError(c, err)
180
-
181
-	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
182
-	assert.NilError(c, err)
183
-
184
-	err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second)
185
-	assert.NilError(c, err)
186
-}
187
-
188
-func (s *DockerCLIRestartSuite) TestRestartWithPolicyUserDefinedNetwork(c *testing.T) {
189
-	// TODO Windows. This may be portable following HNS integration post TP5.
190
-	testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace)
191
-	cli.DockerCmd(c, "network", "create", "-d", "bridge", "udNet")
192
-
193
-	cli.DockerCmd(c, "run", "-d", "--net=udNet", "--name=first", "busybox", "top")
194
-	cli.WaitRun(c, "first")
195
-
196
-	cli.DockerCmd(c, "run", "-d", "--restart=always", "--net=udNet", "--name=second", "--link=first:foo", "busybox", "top")
197
-	cli.WaitRun(c, "second")
198
-
199
-	// ping to first and its alias foo must succeed
200
-	_, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
201
-	assert.NilError(c, err)
202
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
203
-	assert.NilError(c, err)
204
-
205
-	// Now kill the second container and let the restart policy kick in
206
-	pidStr := inspectField(c, "second", "State.Pid")
207
-
208
-	pid, err := strconv.Atoi(pidStr)
209
-	assert.NilError(c, err)
210
-
211
-	p, err := os.FindProcess(pid)
212
-	assert.NilError(c, err)
213
-	assert.Assert(c, p != nil)
214
-
215
-	err = p.Kill()
216
-	assert.NilError(c, err)
217
-
218
-	err = waitInspect("second", "{{.RestartCount}}", "1", 5*time.Second)
219
-	assert.NilError(c, err)
220
-
221
-	err = waitInspect("second", "{{.State.Status}}", "running", 5*time.Second)
222
-	assert.NilError(c, err)
223
-
224
-	// ping to first and its alias foo must still succeed
225
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first")
226
-	assert.NilError(c, err)
227
-	_, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo")
228
-	assert.NilError(c, err)
229
-}
230
-
231
-func (s *DockerCLIRestartSuite) TestRestartPolicyAfterRestart(c *testing.T) {
232
-	testRequires(c, testEnv.IsLocalDaemon)
233
-	// Skipped for Hyper-V isolated containers. Test is currently written
234
-	// such that it assumes there is a host process to kill. In Hyper-V
235
-	// containers, the process is inside the utility VM, not on the host.
236
-	if DaemonIsWindows() {
237
-		skip.If(c, testEnv.GitHubActions())
238
-		testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess)
239
-	}
240
-
241
-	id := runSleepingContainer(c, "-d", "--restart=always")
242
-	cli.WaitRun(c, id)
243
-
244
-	cli.DockerCmd(c, "restart", id)
245
-	cli.WaitRun(c, id)
246
-
247
-	pidStr := inspectField(c, id, "State.Pid")
248
-
249
-	pid, err := strconv.Atoi(pidStr)
250
-	assert.NilError(c, err)
251
-
252
-	p, err := os.FindProcess(pid)
253
-	assert.NilError(c, err)
254
-	assert.Assert(c, p != nil)
255
-
256
-	err = p.Kill()
257
-	assert.NilError(c, err)
258
-
259
-	err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second)
260
-	assert.NilError(c, err)
261
-
262
-	err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second)
263
-	assert.NilError(c, err)
264
-}
265
-
266
-func (s *DockerCLIRestartSuite) TestRestartAutoRemoveContainer(c *testing.T) {
267
-	id := runSleepingContainer(c, "--rm")
268
-	cli.DockerCmd(c, "restart", id)
269
-	err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second)
270
-	assert.NilError(c, err)
271
-
272
-	out := cli.DockerCmd(c, "ps").Stdout()
273
-	assert.Assert(c, is.Contains(out, id[:12]), "container should be restarted instead of removed: %v", out)
274
-
275
-	// Kill the container to make sure it will be removed
276
-	cli.DockerCmd(c, "kill", id)
277
-}
... ...
@@ -10,10 +10,12 @@ import (
10 10
 
11 11
 	"github.com/moby/moby/api/types/container"
12 12
 	"github.com/moby/moby/api/types/events"
13
+	networktypes "github.com/moby/moby/api/types/network"
13 14
 	"github.com/moby/moby/client"
14 15
 	testContainer "github.com/moby/moby/v2/integration/internal/container"
15 16
 	"github.com/moby/moby/v2/internal/testutil"
16 17
 	"github.com/moby/moby/v2/internal/testutil/daemon"
18
+	"github.com/moby/moby/v2/pkg/process"
17 19
 	"gotest.tools/v3/assert"
18 20
 	is "gotest.tools/v3/assert/cmp"
19 21
 	"gotest.tools/v3/poll"
... ...
@@ -162,6 +164,149 @@ func pollForNewHealthCheck(ctx context.Context, apiClient *client.Client, startT
162 162
 	}
163 163
 }
164 164
 
165
+func TestContainerRestartStoppedContainer(t *testing.T) {
166
+	ctx := setupTest(t)
167
+	apiClient := testEnv.APIClient()
168
+
169
+	cID := testContainer.Create(ctx, t, apiClient, testContainer.WithCmd("sh", "-c", "echo foobar && exit 0"))
170
+
171
+	waitTimeout := 10 * time.Second
172
+	if testEnv.DaemonInfo.OSType == "windows" {
173
+		waitTimeout = StopContainerWindowsPollTimeout
174
+	}
175
+
176
+	waitCtx, cancel := context.WithTimeout(ctx, waitTimeout)
177
+	defer cancel()
178
+	wait := apiClient.ContainerWait(waitCtx, cID, client.ContainerWaitOptions{Condition: container.WaitConditionNextExit})
179
+
180
+	_, err := apiClient.ContainerStart(ctx, cID, client.ContainerStartOptions{})
181
+	assert.NilError(t, err)
182
+	assertContainerExitCode(t, wait, 0, waitTimeout)
183
+	poll.WaitOn(t, logsContains(ctx, apiClient, cID, "foobar\n"), poll.WithTimeout(waitTimeout))
184
+
185
+	waitCtx, cancel = context.WithTimeout(ctx, waitTimeout)
186
+	defer cancel()
187
+	wait = apiClient.ContainerWait(waitCtx, cID, client.ContainerWaitOptions{Condition: container.WaitConditionNextExit})
188
+
189
+	_, err = apiClient.ContainerRestart(ctx, cID, client.ContainerRestartOptions{})
190
+	assert.NilError(t, err)
191
+	assertContainerExitCode(t, wait, 0, waitTimeout)
192
+	poll.WaitOn(t, logsContains(ctx, apiClient, cID, "foobar\nfoobar\n"), poll.WithTimeout(waitTimeout))
193
+}
194
+
195
+func TestContainerRestartWithVolumes(t *testing.T) {
196
+	ctx := setupTest(t)
197
+	apiClient := testEnv.APIClient()
198
+
199
+	cID := testContainer.Run(ctx, t, apiClient, testContainer.WithVolume(dPath("/test")))
200
+
201
+	inspect, err := apiClient.ContainerInspect(ctx, cID, client.ContainerInspectOptions{})
202
+	assert.NilError(t, err)
203
+	assert.Assert(t, is.Len(inspect.Container.Mounts, 1))
204
+	mountSource := inspect.Container.Mounts[0].Source
205
+
206
+	_, err = apiClient.ContainerRestart(ctx, cID, client.ContainerRestartOptions{})
207
+	assert.NilError(t, err)
208
+
209
+	inspect, err = apiClient.ContainerInspect(ctx, cID, client.ContainerInspectOptions{})
210
+	assert.NilError(t, err)
211
+	assert.Assert(t, is.Len(inspect.Container.Mounts, 1))
212
+	assert.Check(t, is.Equal(inspect.Container.Mounts[0].Source, mountSource))
213
+}
214
+
215
+func TestContainerRestartPolicyAfterProcessExit(t *testing.T) {
216
+	skip.If(t, testEnv.IsRemoteDaemon, "test requires daemon on the same host")
217
+	skip.If(t, testEnv.DaemonInfo.OSType == "windows" && testEnv.DaemonInfo.Isolation != "process")
218
+
219
+	ctx := setupTest(t)
220
+	apiClient := testEnv.APIClient()
221
+
222
+	for _, tc := range []struct {
223
+		name          string
224
+		manualRestart bool
225
+	}{
226
+		{name: "direct-process-exit"},
227
+		{name: "after-manual-restart", manualRestart: true},
228
+	} {
229
+		t.Run(tc.name, func(t *testing.T) {
230
+			ctx := testutil.StartSpan(ctx, t)
231
+
232
+			cID := testContainer.Run(ctx, t, apiClient, testContainer.WithRestartPolicy(container.RestartPolicyAlways))
233
+
234
+			if tc.manualRestart {
235
+				_, err := apiClient.ContainerRestart(ctx, cID, client.ContainerRestartOptions{})
236
+				assert.NilError(t, err)
237
+				poll.WaitOn(t, testContainer.IsInState(ctx, apiClient, cID, container.StateRunning), poll.WithTimeout(30*time.Second))
238
+			}
239
+
240
+			killContainerProcess(ctx, t, apiClient, cID)
241
+			poll.WaitOn(t, containerRestartCountIs(ctx, apiClient, cID, 1), poll.WithTimeout(30*time.Second))
242
+			poll.WaitOn(t, testContainer.IsInState(ctx, apiClient, cID, container.StateRunning), poll.WithTimeout(30*time.Second))
243
+		})
244
+	}
245
+}
246
+
247
+func TestContainerRestartPolicyUserDefinedNetwork(t *testing.T) {
248
+	skip.If(t, testEnv.DaemonInfo.OSType != "linux")
249
+	skip.If(t, testEnv.IsRemoteDaemon, "test requires daemon on the same host")
250
+	skip.If(t, testEnv.IsUserNamespace)
251
+
252
+	ctx := setupTest(t)
253
+	apiClient := testEnv.APIClient()
254
+
255
+	suffix := strconv.FormatInt(time.Now().UnixNano(), 10)
256
+	networkName := "restart-policy-network-" + suffix
257
+	firstName := "restart-first-" + suffix
258
+	secondName := "restart-second-" + suffix
259
+
260
+	_, err := apiClient.NetworkCreate(ctx, networkName, client.NetworkCreateOptions{Driver: "bridge"})
261
+	assert.NilError(t, err)
262
+
263
+	testContainer.Run(ctx, t, apiClient,
264
+		testContainer.WithName(firstName),
265
+		testContainer.WithNetworkMode(networkName),
266
+		testContainer.WithEndpointSettings(networkName, &networktypes.EndpointSettings{Aliases: []string{"foo"}}),
267
+	)
268
+
269
+	secondID := testContainer.Run(ctx, t, apiClient,
270
+		testContainer.WithName(secondName),
271
+		testContainer.WithNetworkMode(networkName),
272
+		testContainer.WithRestartPolicy(container.RestartPolicyAlways),
273
+	)
274
+
275
+	testContainer.ExecT(ctx, t, apiClient, secondID, []string{"ping", "-c", "1", firstName}).AssertSuccess(t)
276
+	testContainer.ExecT(ctx, t, apiClient, secondID, []string{"ping", "-c", "1", "foo"}).AssertSuccess(t)
277
+
278
+	killContainerProcess(ctx, t, apiClient, secondID)
279
+	poll.WaitOn(t, containerRestartCountIs(ctx, apiClient, secondID, 1), poll.WithTimeout(30*time.Second))
280
+	poll.WaitOn(t, testContainer.IsInState(ctx, apiClient, secondID, container.StateRunning), poll.WithTimeout(30*time.Second))
281
+
282
+	testContainer.ExecT(ctx, t, apiClient, secondID, []string{"ping", "-c", "1", firstName}).AssertSuccess(t)
283
+	testContainer.ExecT(ctx, t, apiClient, secondID, []string{"ping", "-c", "1", "foo"}).AssertSuccess(t)
284
+}
285
+
286
+func killContainerProcess(ctx context.Context, t *testing.T, apiClient client.APIClient, containerID string) {
287
+	t.Helper()
288
+
289
+	inspect, err := apiClient.ContainerInspect(ctx, containerID, client.ContainerInspectOptions{})
290
+	assert.NilError(t, err)
291
+
292
+	assert.NilError(t, process.Kill(inspect.Container.State.Pid))
293
+}
294
+
295
+func assertContainerExitCode(t *testing.T, wait client.ContainerWaitResult, expected int64, timeout time.Duration) {
296
+	t.Helper()
297
+
298
+	select {
299
+	case err := <-wait.Error:
300
+		assert.NilError(t, err)
301
+	case res := <-wait.Result:
302
+		assert.Check(t, is.Equal(res.StatusCode, expected))
303
+	case <-time.After(timeout):
304
+		t.Fatal("timeout waiting for container exit")
305
+	}
306
+}
307
+
165 308
 func TestContainerRestartPolicyOnFailure(t *testing.T) {
166 309
 	ctx := setupTest(t)
167 310
 	apiClient := testEnv.APIClient()