github.com/rawahars/moby@v24.0.4+incompatible/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  				tc := tc
    80  				liveRestoreEnabled := liveRestoreEnabled
    81  				stopDaemon := stopDaemon
    82  				t.Run(fmt.Sprintf("live-restore=%v/%s/%s", liveRestoreEnabled, tc.desc, fnName), func(t *testing.T) {
    83  					t.Parallel()
    84  
    85  					d := daemon.New(t)
    86  					client := d.NewClientT(t)
    87  
    88  					args := []string{"--iptables=false"}
    89  					if liveRestoreEnabled {
    90  						args = append(args, "--live-restore")
    91  					}
    92  
    93  					d.StartWithBusybox(t, args...)
    94  					defer d.Stop(t)
    95  					ctx := context.Background()
    96  
    97  					resp, err := client.ContainerCreate(ctx, tc.config, tc.hostConfig, nil, nil, "")
    98  					assert.NilError(t, err)
    99  					defer client.ContainerRemove(ctx, resp.ID, types.ContainerRemoveOptions{Force: true})
   100  
   101  					if tc.xStart {
   102  						err = client.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{})
   103  						assert.NilError(t, err)
   104  					}
   105  
   106  					stopDaemon(t, d)
   107  					d.Start(t, args...)
   108  
   109  					expected := tc.xRunning
   110  					if liveRestoreEnabled {
   111  						expected = tc.xRunningLiveRestore
   112  					}
   113  
   114  					var running bool
   115  					for i := 0; i < 30; i++ {
   116  						inspect, err := client.ContainerInspect(ctx, resp.ID)
   117  						assert.NilError(t, err)
   118  
   119  						running = inspect.State.Running
   120  						if running == expected {
   121  							break
   122  						}
   123  						time.Sleep(2 * time.Second)
   124  					}
   125  					assert.Equal(t, expected, running, "got unexpected running state, expected %v, got: %v", expected, running)
   126  
   127  					if tc.xHealthCheck {
   128  						startTime := time.Now()
   129  						ctxPoll, cancel := context.WithTimeout(ctx, 30*time.Second)
   130  						defer cancel()
   131  						poll.WaitOn(t, pollForNewHealthCheck(ctxPoll, client, startTime, resp.ID), poll.WithDelay(100*time.Millisecond))
   132  					}
   133  					// TODO(cpuguy83): test pause states... this seems to be rather undefined currently
   134  				})
   135  			}
   136  		}
   137  	}
   138  }
   139  
   140  func pollForNewHealthCheck(ctx context.Context, client *client.Client, startTime time.Time, containerID string) func(log poll.LogT) poll.Result {
   141  	return func(log poll.LogT) poll.Result {
   142  		inspect, err := client.ContainerInspect(ctx, containerID)
   143  		if err != nil {
   144  			return poll.Error(err)
   145  		}
   146  		healthChecksTotal := len(inspect.State.Health.Log)
   147  		if healthChecksTotal > 0 {
   148  			if inspect.State.Health.Log[healthChecksTotal-1].Start.After(startTime) {
   149  				return poll.Success()
   150  			}
   151  		}
   152  		return poll.Continue("waiting for a new container healthcheck")
   153  	}
   154  }
   155  
   156  // Container started with --rm should be able to be restarted.
   157  // It should be removed only if killed or stopped
   158  func TestContainerWithAutoRemoveCanBeRestarted(t *testing.T) {
   159  	defer setupTest(t)()
   160  	cli := testEnv.APIClient()
   161  	ctx := context.Background()
   162  
   163  	noWaitTimeout := 0
   164  
   165  	for _, tc := range []struct {
   166  		desc  string
   167  		doSth func(ctx context.Context, containerID string) error
   168  	}{
   169  		{
   170  			desc: "kill",
   171  			doSth: func(ctx context.Context, containerID string) error {
   172  				return cli.ContainerKill(ctx, containerID, "SIGKILL")
   173  			},
   174  		},
   175  		{
   176  			desc: "stop",
   177  			doSth: func(ctx context.Context, containerID string) error {
   178  				return cli.ContainerStop(ctx, containerID, container.StopOptions{Timeout: &noWaitTimeout})
   179  			},
   180  		},
   181  	} {
   182  		tc := tc
   183  		t.Run(tc.desc, func(t *testing.T) {
   184  			cID := testContainer.Run(ctx, t, cli,
   185  				testContainer.WithName("autoremove-restart-and-"+tc.desc),
   186  				testContainer.WithAutoRemove,
   187  			)
   188  			defer func() {
   189  				err := cli.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true})
   190  				if t.Failed() && err != nil {
   191  					t.Logf("Cleaning up test container failed with error: %v", err)
   192  				}
   193  			}()
   194  
   195  			err := cli.ContainerRestart(ctx, cID, container.StopOptions{Timeout: &noWaitTimeout})
   196  			assert.NilError(t, err)
   197  
   198  			inspect, err := cli.ContainerInspect(ctx, cID)
   199  			assert.NilError(t, err)
   200  			assert.Assert(t, inspect.State.Status != "removing", "Container should not be removing yet")
   201  
   202  			poll.WaitOn(t, testContainer.IsInState(ctx, cli, cID, "running"))
   203  
   204  			err = tc.doSth(ctx, cID)
   205  			assert.NilError(t, err)
   206  
   207  			poll.WaitOn(t, testContainer.IsRemoved(ctx, cli, cID))
   208  		})
   209  	}
   210  }