github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/integration/container/restart_test.go (about) 1 package container // import "github.com/docker/docker/integration/container" 2 3 import ( 4 "context" 5 "fmt" 6 "testing" 7 "time" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/client" 12 testContainer "github.com/docker/docker/integration/internal/container" 13 "github.com/docker/docker/testutil/daemon" 14 "gotest.tools/v3/assert" 15 "gotest.tools/v3/poll" 16 "gotest.tools/v3/skip" 17 ) 18 19 func TestDaemonRestartKillContainers(t *testing.T) { 20 skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run") 21 skip.If(t, testEnv.DaemonInfo.OSType == "windows") 22 skip.If(t, testEnv.IsRootless, "rootless mode doesn't support live-restore") 23 type testCase struct { 24 desc string 25 config *container.Config 26 hostConfig *container.HostConfig 27 28 xRunning bool 29 xRunningLiveRestore bool 30 xStart bool 31 xHealthCheck bool 32 } 33 34 for _, tc := range []testCase{ 35 { 36 desc: "container without restart policy", 37 config: &container.Config{Image: "busybox", Cmd: []string{"top"}}, 38 xRunningLiveRestore: true, 39 xStart: true, 40 }, 41 { 42 desc: "container with restart=always", 43 config: &container.Config{Image: "busybox", Cmd: []string{"top"}}, 44 hostConfig: &container.HostConfig{RestartPolicy: container.RestartPolicy{Name: "always"}}, 45 xRunning: true, 46 xRunningLiveRestore: true, 47 xStart: true, 48 }, 49 { 50 desc: "container with restart=always and with healthcheck", 51 config: &container.Config{Image: "busybox", Cmd: []string{"top"}, 52 Healthcheck: &container.HealthConfig{ 53 Test: []string{"CMD-SHELL", "sleep 1"}, 54 Interval: time.Second, 55 }, 56 }, 57 hostConfig: &container.HostConfig{RestartPolicy: container.RestartPolicy{Name: "always"}}, 58 xRunning: true, 59 xRunningLiveRestore: true, 60 xStart: true, 61 xHealthCheck: true, 62 }, 63 { 64 desc: "container created should not be restarted", 65 config: &container.Config{Image: "busybox", Cmd: []string{"top"}}, 66 hostConfig: &container.HostConfig{RestartPolicy: container.RestartPolicy{Name: "always"}}, 67 }, 68 } { 69 for _, liveRestoreEnabled := range []bool{false, true} { 70 for fnName, stopDaemon := range map[string]func(*testing.T, *daemon.Daemon){ 71 "kill-daemon": func(t *testing.T, d *daemon.Daemon) { 72 err := d.Kill() 73 assert.NilError(t, err) 74 }, 75 "stop-daemon": func(t *testing.T, d *daemon.Daemon) { 76 d.Stop(t) 77 }, 78 } { 79 t.Run(fmt.Sprintf("live-restore=%v/%s/%s", liveRestoreEnabled, tc.desc, fnName), func(t *testing.T) { 80 c := tc 81 liveRestoreEnabled := liveRestoreEnabled 82 stopDaemon := stopDaemon 83 84 t.Parallel() 85 86 d := daemon.New(t) 87 client := d.NewClientT(t) 88 89 args := []string{"--iptables=false"} 90 if liveRestoreEnabled { 91 args = append(args, "--live-restore") 92 } 93 94 d.StartWithBusybox(t, args...) 95 defer d.Stop(t) 96 ctx := context.Background() 97 98 resp, err := client.ContainerCreate(ctx, c.config, c.hostConfig, nil, nil, "") 99 assert.NilError(t, err) 100 defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true}) 101 102 if c.xStart { 103 err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}) 104 assert.NilError(t, err) 105 } 106 107 stopDaemon(t, d) 108 d.Start(t, args...) 109 110 expected := c.xRunning 111 if liveRestoreEnabled { 112 expected = c.xRunningLiveRestore 113 } 114 115 var running bool 116 for i := 0; i < 30; i++ { 117 inspect, err := client.ContainerInspect(ctx, resp.ID) 118 assert.NilError(t, err) 119 120 running = inspect.State.Running 121 if running == expected { 122 break 123 } 124 time.Sleep(2 * time.Second) 125 } 126 assert.Equal(t, expected, running, "got unexpected running state, expected %v, got: %v", expected, running) 127 128 if c.xHealthCheck { 129 startTime := time.Now() 130 ctxPoll, cancel := context.WithTimeout(ctx, 30*time.Second) 131 defer cancel() 132 poll.WaitOn(t, pollForNewHealthCheck(ctxPoll, client, startTime, resp.ID), poll.WithDelay(100*time.Millisecond)) 133 } 134 // TODO(cpuguy83): test pause states... this seems to be rather undefined currently 135 }) 136 } 137 } 138 } 139 } 140 141 func pollForNewHealthCheck(ctx context.Context, client *client.Client, startTime time.Time, containerID string) func(log poll.LogT) poll.Result { 142 return func(log poll.LogT) poll.Result { 143 inspect, err := client.ContainerInspect(ctx, containerID) 144 if err != nil { 145 return poll.Error(err) 146 } 147 healthChecksTotal := len(inspect.State.Health.Log) 148 if healthChecksTotal > 0 { 149 if inspect.State.Health.Log[healthChecksTotal-1].Start.After(startTime) { 150 return poll.Success() 151 } 152 } 153 return poll.Continue("waiting for a new container healthcheck") 154 } 155 } 156 157 // Container started with --rm should be able to be restarted. 158 // It should be removed only if killed or stopped 159 func TestContainerWithAutoRemoveCanBeRestarted(t *testing.T) { 160 defer setupTest(t)() 161 cli := testEnv.APIClient() 162 ctx := context.Background() 163 164 noWaitTimeout := 0 165 166 for _, tc := range []struct { 167 desc string 168 doSth func(ctx context.Context, containerID string) error 169 }{ 170 { 171 desc: "kill", 172 doSth: func(ctx context.Context, containerID string) error { 173 return cli.ContainerKill(ctx, containerID, "SIGKILL") 174 }, 175 }, 176 { 177 desc: "stop", 178 doSth: func(ctx context.Context, containerID string) error { 179 return cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: &noWaitTimeout}) 180 }, 181 }, 182 } { 183 tc := tc 184 t.Run(tc.desc, func(t *testing.T) { 185 cID := testContainer.Run(ctx, t, cli, 186 testContainer.WithName("autoremove-restart-and-"+tc.desc), 187 testContainer.WithAutoRemove, 188 ) 189 defer func() { 190 err := cli.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true}) 191 if t.Failed() && err != nil { 192 t.Logf("Cleaning up test container failed with error: %v", err) 193 } 194 }() 195 196 err := cli.ContainerRestart(ctx, cID, container.StopOptions{Timeout: &noWaitTimeout}) 197 assert.NilError(t, err) 198 199 inspect, err := cli.ContainerInspect(ctx, cID) 200 assert.NilError(t, err) 201 assert.Assert(t, inspect.State.Status != "removing", "Container should not be removing yet") 202 203 poll.WaitOn(t, testContainer.IsInState(ctx, cli, cID, "running")) 204 205 err = tc.doSth(ctx, cID) 206 assert.NilError(t, err) 207 208 poll.WaitOn(t, testContainer.IsRemoved(ctx, cli, cID)) 209 }) 210 } 211 }