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  }