github.com/moby/docker@v26.1.3+incompatible/integration-cli/docker_cli_restart_test.go (about) 1 package main 2 3 import ( 4 "context" 5 "os" 6 "strconv" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/docker/docker/integration-cli/checker" 12 "github.com/docker/docker/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, c *testing.T) { 24 s.ds.TearDownTest(ctx, c) 25 } 26 27 func (s *DockerCLIRestartSuite) OnTimeout(c *testing.T) { 28 s.ds.OnTimeout(c) 29 } 30 31 func (s *DockerCLIRestartSuite) TestRestartStoppedContainer(c *testing.T) { 32 cli.DockerCmd(c, "run", "--name=test", "busybox", "echo", "foobar") 33 cID := getIDByName(c, "test") 34 35 out := cli.DockerCmd(c, "logs", cID).Combined() 36 assert.Equal(c, out, "foobar\n") 37 38 cli.DockerCmd(c, "restart", cID) 39 40 // Wait until the container has stopped 41 err := waitInspect(cID, "{{.State.Running}}", "false", 20*time.Second) 42 assert.NilError(c, err) 43 44 out = cli.DockerCmd(c, "logs", cID).Combined() 45 assert.Equal(c, out, "foobar\nfoobar\n") 46 } 47 48 func (s *DockerCLIRestartSuite) TestRestartRunningContainer(c *testing.T) { 49 cID := cli.DockerCmd(c, "run", "-d", "busybox", "sh", "-c", "echo foobar && sleep 30 && echo 'should not print this'").Stdout() 50 cID = strings.TrimSpace(cID) 51 cli.WaitRun(c, cID) 52 53 getLogs := func(c *testing.T) (interface{}, string) { 54 out := cli.DockerCmd(c, "logs", cID).Combined() 55 return out, "" 56 } 57 58 // Wait 10 seconds for the 'echo' to appear in the logs 59 poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\n")), poll.WithTimeout(10*time.Second)) 60 61 cli.DockerCmd(c, "restart", "-t", "1", cID) 62 cli.WaitRun(c, cID) 63 64 // Wait 10 seconds for first 'echo' appear (again) in the logs 65 poll.WaitOn(c, pollCheck(c, getLogs, checker.Equals("foobar\nfoobar\n")), poll.WithTimeout(10*time.Second)) 66 } 67 68 // Test that restarting a container with a volume does not create a new volume on restart. Regression test for #819. 69 func (s *DockerCLIRestartSuite) TestRestartWithVolumes(c *testing.T) { 70 prefix, slash := getPrefixAndSlashFromDaemonPlatform() 71 cID := runSleepingContainer(c, "-d", "-v", prefix+slash+"test") 72 out, err := inspectFilter(cID, "len .Mounts") 73 assert.NilError(c, err, "failed to inspect %s: %s", cID, out) 74 out = strings.Trim(out, " \n\r") 75 assert.Equal(c, out, "1") 76 77 source, err := inspectMountSourceField(cID, prefix+slash+"test") 78 assert.NilError(c, err) 79 80 cli.DockerCmd(c, "restart", cID) 81 82 out, err = inspectFilter(cID, "len .Mounts") 83 assert.NilError(c, err, "failed to inspect %s: %s", cID, out) 84 out = strings.Trim(out, " \n\r") 85 assert.Equal(c, out, "1") 86 87 sourceAfterRestart, err := inspectMountSourceField(cID, prefix+slash+"test") 88 assert.NilError(c, err) 89 assert.Equal(c, source, sourceAfterRestart) 90 } 91 92 func (s *DockerCLIRestartSuite) TestRestartDisconnectedContainer(c *testing.T) { 93 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace) 94 95 // Run a container on the default bridge network 96 cID := cli.DockerCmd(c, "run", "-d", "--name", "c0", "busybox", "top").Stdout() 97 cID = strings.TrimSpace(cID) 98 cli.WaitRun(c, cID) 99 100 // Disconnect the container from the network 101 result := cli.DockerCmd(c, "network", "disconnect", "bridge", "c0") 102 assert.Assert(c, result.ExitCode == 0, result.Combined()) 103 104 // Restart the container 105 result = cli.DockerCmd(c, "restart", "c0") 106 assert.Assert(c, result.ExitCode == 0, result.Combined()) 107 } 108 109 func (s *DockerCLIRestartSuite) TestRestartPolicyNO(c *testing.T) { 110 cID := cli.DockerCmd(c, "create", "--restart=no", "busybox").Stdout() 111 cID = strings.TrimSpace(cID) 112 name := inspectField(c, cID, "HostConfig.RestartPolicy.Name") 113 assert.Equal(c, name, "no") 114 } 115 116 func (s *DockerCLIRestartSuite) TestRestartPolicyAlways(c *testing.T) { 117 id := cli.DockerCmd(c, "create", "--restart=always", "busybox").Stdout() 118 id = strings.TrimSpace(id) 119 name := inspectField(c, id, "HostConfig.RestartPolicy.Name") 120 assert.Equal(c, name, "always") 121 122 MaximumRetryCount := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount") 123 124 // MaximumRetryCount=0 if the restart policy is always 125 assert.Equal(c, MaximumRetryCount, "0") 126 } 127 128 func (s *DockerCLIRestartSuite) TestRestartPolicyOnFailure(c *testing.T) { 129 out, _, err := dockerCmdWithError("create", "--restart=on-failure:-1", "busybox") 130 assert.ErrorContains(c, err, "", out) 131 assert.Assert(c, strings.Contains(out, "maximum retry count cannot be negative")) 132 133 id := cli.DockerCmd(c, "create", "--restart=on-failure:1", "busybox").Stdout() 134 id = strings.TrimSpace(id) 135 name := inspectField(c, id, "HostConfig.RestartPolicy.Name") 136 maxRetry := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount") 137 assert.Equal(c, name, "on-failure") 138 assert.Equal(c, maxRetry, "1") 139 140 id = cli.DockerCmd(c, "create", "--restart=on-failure:0", "busybox").Stdout() 141 id = strings.TrimSpace(id) 142 name = inspectField(c, id, "HostConfig.RestartPolicy.Name") 143 maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount") 144 assert.Equal(c, name, "on-failure") 145 assert.Equal(c, maxRetry, "0") 146 147 id = cli.DockerCmd(c, "create", "--restart=on-failure", "busybox").Stdout() 148 id = strings.TrimSpace(id) 149 name = inspectField(c, id, "HostConfig.RestartPolicy.Name") 150 maxRetry = inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount") 151 assert.Equal(c, name, "on-failure") 152 assert.Equal(c, maxRetry, "0") 153 } 154 155 // a good container with --restart=on-failure:3 156 // MaximumRetryCount!=0; RestartCount=0 157 func (s *DockerCLIRestartSuite) TestRestartContainerwithGoodContainer(c *testing.T) { 158 id := cli.DockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "true").Stdout() 159 id = strings.TrimSpace(id) 160 err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false false", 30*time.Second) 161 assert.NilError(c, err) 162 163 count := inspectField(c, id, "RestartCount") 164 assert.Equal(c, count, "0") 165 166 MaximumRetryCount := inspectField(c, id, "HostConfig.RestartPolicy.MaximumRetryCount") 167 assert.Equal(c, MaximumRetryCount, "3") 168 } 169 170 func (s *DockerCLIRestartSuite) TestRestartContainerSuccess(c *testing.T) { 171 testRequires(c, testEnv.IsLocalDaemon) 172 // Skipped for Hyper-V isolated containers. Test is currently written 173 // such that it assumes there is a host process to kill. In Hyper-V 174 // containers, the process is inside the utility VM, not on the host. 175 if DaemonIsWindows() { 176 skip.If(c, testEnv.GitHubActions()) 177 testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess) 178 } 179 180 id := runSleepingContainer(c, "-d", "--restart=always") 181 cli.WaitRun(c, id) 182 183 pidStr := inspectField(c, id, "State.Pid") 184 185 pid, err := strconv.Atoi(pidStr) 186 assert.NilError(c, err) 187 188 p, err := os.FindProcess(pid) 189 assert.NilError(c, err) 190 assert.Assert(c, p != nil) 191 192 err = p.Kill() 193 assert.NilError(c, err) 194 195 err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second) 196 assert.NilError(c, err) 197 198 err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second) 199 assert.NilError(c, err) 200 } 201 202 func (s *DockerCLIRestartSuite) TestRestartWithPolicyUserDefinedNetwork(c *testing.T) { 203 // TODO Windows. This may be portable following HNS integration post TP5. 204 testRequires(c, DaemonIsLinux, testEnv.IsLocalDaemon, NotUserNamespace) 205 cli.DockerCmd(c, "network", "create", "-d", "bridge", "udNet") 206 207 cli.DockerCmd(c, "run", "-d", "--net=udNet", "--name=first", "busybox", "top") 208 cli.WaitRun(c, "first") 209 210 cli.DockerCmd(c, "run", "-d", "--restart=always", "--net=udNet", "--name=second", "--link=first:foo", "busybox", "top") 211 cli.WaitRun(c, "second") 212 213 // ping to first and its alias foo must succeed 214 _, _, err := dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 215 assert.NilError(c, err) 216 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 217 assert.NilError(c, err) 218 219 // Now kill the second container and let the restart policy kick in 220 pidStr := inspectField(c, "second", "State.Pid") 221 222 pid, err := strconv.Atoi(pidStr) 223 assert.NilError(c, err) 224 225 p, err := os.FindProcess(pid) 226 assert.NilError(c, err) 227 assert.Assert(c, p != nil) 228 229 err = p.Kill() 230 assert.NilError(c, err) 231 232 err = waitInspect("second", "{{.RestartCount}}", "1", 5*time.Second) 233 assert.NilError(c, err) 234 235 err = waitInspect("second", "{{.State.Status}}", "running", 5*time.Second) 236 assert.NilError(c, err) 237 238 // ping to first and its alias foo must still succeed 239 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "first") 240 assert.NilError(c, err) 241 _, _, err = dockerCmdWithError("exec", "second", "ping", "-c", "1", "foo") 242 assert.NilError(c, err) 243 } 244 245 func (s *DockerCLIRestartSuite) TestRestartPolicyAfterRestart(c *testing.T) { 246 testRequires(c, testEnv.IsLocalDaemon) 247 // Skipped for Hyper-V isolated containers. Test is currently written 248 // such that it assumes there is a host process to kill. In Hyper-V 249 // containers, the process is inside the utility VM, not on the host. 250 if DaemonIsWindows() { 251 skip.If(c, testEnv.GitHubActions()) 252 testRequires(c, testEnv.DaemonInfo.Isolation.IsProcess) 253 } 254 255 id := runSleepingContainer(c, "-d", "--restart=always") 256 cli.WaitRun(c, id) 257 258 cli.DockerCmd(c, "restart", id) 259 cli.WaitRun(c, id) 260 261 pidStr := inspectField(c, id, "State.Pid") 262 263 pid, err := strconv.Atoi(pidStr) 264 assert.NilError(c, err) 265 266 p, err := os.FindProcess(pid) 267 assert.NilError(c, err) 268 assert.Assert(c, p != nil) 269 270 err = p.Kill() 271 assert.NilError(c, err) 272 273 err = waitInspect(id, "{{.RestartCount}}", "1", 30*time.Second) 274 assert.NilError(c, err) 275 276 err = waitInspect(id, "{{.State.Status}}", "running", 30*time.Second) 277 assert.NilError(c, err) 278 } 279 280 func (s *DockerCLIRestartSuite) TestRestartContainerwithRestartPolicy(c *testing.T) { 281 id1 := cli.DockerCmd(c, "run", "-d", "--restart=on-failure:3", "busybox", "false").Stdout() 282 id1 = strings.TrimSpace(id1) 283 id2 := cli.DockerCmd(c, "run", "-d", "--restart=always", "busybox", "false").Stdout() 284 id2 = strings.TrimSpace(id2) 285 286 waitTimeout := 15 * time.Second 287 if testEnv.DaemonInfo.OSType == "windows" { 288 waitTimeout = 150 * time.Second 289 } 290 err := waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout) 291 assert.NilError(c, err) 292 293 cli.DockerCmd(c, "restart", id1) 294 cli.DockerCmd(c, "restart", id2) 295 296 // Make sure we can stop/start (regression test from a705e166cf3bcca62543150c2b3f9bfeae45ecfa) 297 cli.DockerCmd(c, "stop", id1) 298 cli.DockerCmd(c, "stop", id2) 299 cli.DockerCmd(c, "start", id1) 300 cli.DockerCmd(c, "start", id2) 301 302 // Kill the containers, making sure they are stopped at the end of the test 303 cli.DockerCmd(c, "kill", id1) 304 cli.DockerCmd(c, "kill", id2) 305 err = waitInspect(id1, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout) 306 assert.NilError(c, err) 307 err = waitInspect(id2, "{{ .State.Restarting }} {{ .State.Running }}", "false false", waitTimeout) 308 assert.NilError(c, err) 309 } 310 311 func (s *DockerCLIRestartSuite) TestRestartAutoRemoveContainer(c *testing.T) { 312 id := runSleepingContainer(c, "--rm") 313 cli.DockerCmd(c, "restart", id) 314 err := waitInspect(id, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 15*time.Second) 315 assert.NilError(c, err) 316 317 out := cli.DockerCmd(c, "ps").Stdout() 318 assert.Assert(c, is.Contains(out, id[:12]), "container should be restarted instead of removed: %v", out) 319 320 // Kill the container to make sure it will be removed 321 cli.DockerCmd(c, "kill", id) 322 }