github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/integration-cli/docker_api_containers_test.go (about)

     1  package main
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"context"
     7  	"encoding/json"
     8  	"fmt"
     9  	"io"
    10  	"io/ioutil"
    11  	"net/http"
    12  	"os"
    13  	"path/filepath"
    14  	"regexp"
    15  	"runtime"
    16  	"strings"
    17  	"testing"
    18  	"time"
    19  
    20  	"github.com/docker/docker/api/types"
    21  	containertypes "github.com/docker/docker/api/types/container"
    22  	mounttypes "github.com/docker/docker/api/types/mount"
    23  	networktypes "github.com/docker/docker/api/types/network"
    24  	"github.com/docker/docker/api/types/versions"
    25  	"github.com/docker/docker/client"
    26  	"github.com/docker/docker/integration-cli/cli"
    27  	"github.com/docker/docker/integration-cli/cli/build"
    28  	"github.com/docker/docker/pkg/ioutils"
    29  	"github.com/docker/docker/pkg/stringid"
    30  	"github.com/docker/docker/testutil/request"
    31  	"github.com/docker/docker/volume"
    32  	"github.com/docker/go-connections/nat"
    33  	"github.com/moby/sys/mount"
    34  	"gotest.tools/v3/assert"
    35  	is "gotest.tools/v3/assert/cmp"
    36  	"gotest.tools/v3/poll"
    37  )
    38  
    39  func (s *DockerSuite) TestContainerAPIGetAll(c *testing.T) {
    40  	startCount := getContainerCount(c)
    41  	name := "getall"
    42  	dockerCmd(c, "run", "--name", name, "busybox", "true")
    43  
    44  	cli, err := client.NewClientWithOpts(client.FromEnv)
    45  	assert.NilError(c, err)
    46  	defer cli.Close()
    47  
    48  	options := types.ContainerListOptions{
    49  		All: true,
    50  	}
    51  	containers, err := cli.ContainerList(context.Background(), options)
    52  	assert.NilError(c, err)
    53  	assert.Equal(c, len(containers), startCount+1)
    54  	actual := containers[0].Names[0]
    55  	assert.Equal(c, actual, "/"+name)
    56  }
    57  
    58  // regression test for empty json field being omitted #13691
    59  func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *testing.T) {
    60  	startCount := getContainerCount(c)
    61  	dockerCmd(c, "run", "busybox", "true")
    62  
    63  	cli, err := client.NewClientWithOpts(client.FromEnv)
    64  	assert.NilError(c, err)
    65  	defer cli.Close()
    66  
    67  	options := types.ContainerListOptions{
    68  		All: true,
    69  	}
    70  	containers, err := cli.ContainerList(context.Background(), options)
    71  	assert.NilError(c, err)
    72  	assert.Equal(c, len(containers), startCount+1)
    73  	actual := fmt.Sprintf("%+v", containers[0])
    74  
    75  	// empty Labels field triggered this bug, make sense to check for everything
    76  	// cause even Ports for instance can trigger this bug
    77  	// better safe than sorry..
    78  	fields := []string{
    79  		"ID",
    80  		"Names",
    81  		"Image",
    82  		"Command",
    83  		"Created",
    84  		"Ports",
    85  		"Labels",
    86  		"Status",
    87  		"NetworkSettings",
    88  	}
    89  
    90  	// decoding into types.Container do not work since it eventually unmarshal
    91  	// and empty field to an empty go map, so we just check for a string
    92  	for _, f := range fields {
    93  		if !strings.Contains(actual, f) {
    94  			c.Fatalf("Field %s is missing and it shouldn't", f)
    95  		}
    96  	}
    97  }
    98  
    99  func (s *DockerSuite) TestContainerAPIGetExport(c *testing.T) {
   100  	// Not supported on Windows as Windows does not support docker export
   101  	testRequires(c, DaemonIsLinux)
   102  	name := "exportcontainer"
   103  	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test")
   104  
   105  	cli, err := client.NewClientWithOpts(client.FromEnv)
   106  	assert.NilError(c, err)
   107  	defer cli.Close()
   108  
   109  	body, err := cli.ContainerExport(context.Background(), name)
   110  	assert.NilError(c, err)
   111  	defer body.Close()
   112  	found := false
   113  	for tarReader := tar.NewReader(body); ; {
   114  		h, err := tarReader.Next()
   115  		if err != nil && err == io.EOF {
   116  			break
   117  		}
   118  		if h.Name == "test" {
   119  			found = true
   120  			break
   121  		}
   122  	}
   123  	assert.Assert(c, found, "The created test file has not been found in the exported image")
   124  }
   125  
   126  func (s *DockerSuite) TestContainerAPIGetChanges(c *testing.T) {
   127  	// Not supported on Windows as Windows does not support docker diff (/containers/name/changes)
   128  	testRequires(c, DaemonIsLinux)
   129  	name := "changescontainer"
   130  	dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd")
   131  
   132  	cli, err := client.NewClientWithOpts(client.FromEnv)
   133  	assert.NilError(c, err)
   134  	defer cli.Close()
   135  
   136  	changes, err := cli.ContainerDiff(context.Background(), name)
   137  	assert.NilError(c, err)
   138  
   139  	// Check the changelog for removal of /etc/passwd
   140  	success := false
   141  	for _, elem := range changes {
   142  		if elem.Path == "/etc/passwd" && elem.Kind == 2 {
   143  			success = true
   144  		}
   145  	}
   146  	assert.Assert(c, success, "/etc/passwd has been removed but is not present in the diff")
   147  }
   148  
   149  func (s *DockerSuite) TestGetContainerStats(c *testing.T) {
   150  	var (
   151  		name = "statscontainer"
   152  	)
   153  	runSleepingContainer(c, "--name", name)
   154  
   155  	type b struct {
   156  		stats types.ContainerStats
   157  		err   error
   158  	}
   159  
   160  	bc := make(chan b, 1)
   161  	go func() {
   162  		cli, err := client.NewClientWithOpts(client.FromEnv)
   163  		assert.NilError(c, err)
   164  		defer cli.Close()
   165  
   166  		stats, err := cli.ContainerStats(context.Background(), name, true)
   167  		assert.NilError(c, err)
   168  		bc <- b{stats, err}
   169  	}()
   170  
   171  	// allow some time to stream the stats from the container
   172  	time.Sleep(4 * time.Second)
   173  	dockerCmd(c, "rm", "-f", name)
   174  
   175  	// collect the results from the stats stream or timeout and fail
   176  	// if the stream was not disconnected.
   177  	select {
   178  	case <-time.After(2 * time.Second):
   179  		c.Fatal("stream was not closed after container was removed")
   180  	case sr := <-bc:
   181  		dec := json.NewDecoder(sr.stats.Body)
   182  		defer sr.stats.Body.Close()
   183  		var s *types.Stats
   184  		// decode only one object from the stream
   185  		assert.NilError(c, dec.Decode(&s))
   186  	}
   187  }
   188  
   189  func (s *DockerSuite) TestGetContainerStatsRmRunning(c *testing.T) {
   190  	out := runSleepingContainer(c)
   191  	id := strings.TrimSpace(out)
   192  
   193  	buf := &ChannelBuffer{C: make(chan []byte, 1)}
   194  	defer buf.Close()
   195  
   196  	cli, err := client.NewClientWithOpts(client.FromEnv)
   197  	assert.NilError(c, err)
   198  	defer cli.Close()
   199  
   200  	stats, err := cli.ContainerStats(context.Background(), id, true)
   201  	assert.NilError(c, err)
   202  	defer stats.Body.Close()
   203  
   204  	chErr := make(chan error, 1)
   205  	go func() {
   206  		_, err = io.Copy(buf, stats.Body)
   207  		chErr <- err
   208  	}()
   209  
   210  	b := make([]byte, 32)
   211  	// make sure we've got some stats
   212  	_, err = buf.ReadTimeout(b, 2*time.Second)
   213  	assert.NilError(c, err)
   214  
   215  	// Now remove without `-f` and make sure we are still pulling stats
   216  	_, _, err = dockerCmdWithError("rm", id)
   217  	assert.Assert(c, err != nil, "rm should have failed but didn't")
   218  	_, err = buf.ReadTimeout(b, 2*time.Second)
   219  	assert.NilError(c, err)
   220  
   221  	dockerCmd(c, "rm", "-f", id)
   222  	assert.Assert(c, <-chErr == nil)
   223  }
   224  
   225  // ChannelBuffer holds a chan of byte array that can be populate in a goroutine.
   226  type ChannelBuffer struct {
   227  	C chan []byte
   228  }
   229  
   230  // Write implements Writer.
   231  func (c *ChannelBuffer) Write(b []byte) (int, error) {
   232  	c.C <- b
   233  	return len(b), nil
   234  }
   235  
   236  // Close closes the go channel.
   237  func (c *ChannelBuffer) Close() error {
   238  	close(c.C)
   239  	return nil
   240  }
   241  
   242  // ReadTimeout reads the content of the channel in the specified byte array with
   243  // the specified duration as timeout.
   244  func (c *ChannelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
   245  	select {
   246  	case b := <-c.C:
   247  		return copy(p[0:], b), nil
   248  	case <-time.After(n):
   249  		return -1, fmt.Errorf("timeout reading from channel")
   250  	}
   251  }
   252  
   253  // regression test for gh13421
   254  // previous test was just checking one stat entry so it didn't fail (stats with
   255  // stream false always return one stat)
   256  func (s *DockerSuite) TestGetContainerStatsStream(c *testing.T) {
   257  	name := "statscontainer"
   258  	runSleepingContainer(c, "--name", name)
   259  
   260  	type b struct {
   261  		stats types.ContainerStats
   262  		err   error
   263  	}
   264  
   265  	bc := make(chan b, 1)
   266  	go func() {
   267  		cli, err := client.NewClientWithOpts(client.FromEnv)
   268  		assert.NilError(c, err)
   269  		defer cli.Close()
   270  
   271  		stats, err := cli.ContainerStats(context.Background(), name, true)
   272  		assert.NilError(c, err)
   273  		bc <- b{stats, err}
   274  	}()
   275  
   276  	// allow some time to stream the stats from the container
   277  	time.Sleep(4 * time.Second)
   278  	dockerCmd(c, "rm", "-f", name)
   279  
   280  	// collect the results from the stats stream or timeout and fail
   281  	// if the stream was not disconnected.
   282  	select {
   283  	case <-time.After(2 * time.Second):
   284  		c.Fatal("stream was not closed after container was removed")
   285  	case sr := <-bc:
   286  		b, err := ioutil.ReadAll(sr.stats.Body)
   287  		defer sr.stats.Body.Close()
   288  		assert.NilError(c, err)
   289  		s := string(b)
   290  		// count occurrences of "read" of types.Stats
   291  		if l := strings.Count(s, "read"); l < 2 {
   292  			c.Fatalf("Expected more than one stat streamed, got %d", l)
   293  		}
   294  	}
   295  }
   296  
   297  func (s *DockerSuite) TestGetContainerStatsNoStream(c *testing.T) {
   298  	name := "statscontainer"
   299  	runSleepingContainer(c, "--name", name)
   300  
   301  	type b struct {
   302  		stats types.ContainerStats
   303  		err   error
   304  	}
   305  
   306  	bc := make(chan b, 1)
   307  
   308  	go func() {
   309  		cli, err := client.NewClientWithOpts(client.FromEnv)
   310  		assert.NilError(c, err)
   311  		defer cli.Close()
   312  
   313  		stats, err := cli.ContainerStats(context.Background(), name, false)
   314  		assert.NilError(c, err)
   315  		bc <- b{stats, err}
   316  	}()
   317  
   318  	// allow some time to stream the stats from the container
   319  	time.Sleep(4 * time.Second)
   320  	dockerCmd(c, "rm", "-f", name)
   321  
   322  	// collect the results from the stats stream or timeout and fail
   323  	// if the stream was not disconnected.
   324  	select {
   325  	case <-time.After(2 * time.Second):
   326  		c.Fatal("stream was not closed after container was removed")
   327  	case sr := <-bc:
   328  		b, err := ioutil.ReadAll(sr.stats.Body)
   329  		defer sr.stats.Body.Close()
   330  		assert.NilError(c, err)
   331  		s := string(b)
   332  		// count occurrences of `"read"` of types.Stats
   333  		assert.Assert(c, strings.Count(s, `"read"`) == 1, "Expected only one stat streamed, got %d", strings.Count(s, `"read"`))
   334  	}
   335  }
   336  
   337  func (s *DockerSuite) TestGetStoppedContainerStats(c *testing.T) {
   338  	name := "statscontainer"
   339  	dockerCmd(c, "create", "--name", name, "busybox", "ps")
   340  
   341  	chResp := make(chan error, 1)
   342  
   343  	// We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine
   344  	// below we'll check this on a timeout.
   345  	go func() {
   346  		cli, err := client.NewClientWithOpts(client.FromEnv)
   347  		assert.NilError(c, err)
   348  		defer cli.Close()
   349  
   350  		resp, err := cli.ContainerStats(context.Background(), name, false)
   351  		assert.NilError(c, err)
   352  		defer resp.Body.Close()
   353  		chResp <- err
   354  	}()
   355  
   356  	select {
   357  	case err := <-chResp:
   358  		assert.NilError(c, err)
   359  	case <-time.After(10 * time.Second):
   360  		c.Fatal("timeout waiting for stats response for stopped container")
   361  	}
   362  }
   363  
   364  func (s *DockerSuite) TestContainerAPIPause(c *testing.T) {
   365  	// Problematic on Windows as Windows does not support pause
   366  	testRequires(c, DaemonIsLinux)
   367  
   368  	getPaused := func(c *testing.T) []string {
   369  		return strings.Fields(cli.DockerCmd(c, "ps", "-f", "status=paused", "-q", "-a").Combined())
   370  	}
   371  
   372  	out := cli.DockerCmd(c, "run", "-d", "busybox", "sleep", "30").Combined()
   373  	ContainerID := strings.TrimSpace(out)
   374  
   375  	cli, err := client.NewClientWithOpts(client.FromEnv)
   376  	assert.NilError(c, err)
   377  	defer cli.Close()
   378  
   379  	err = cli.ContainerPause(context.Background(), ContainerID)
   380  	assert.NilError(c, err)
   381  
   382  	pausedContainers := getPaused(c)
   383  
   384  	if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] {
   385  		c.Fatalf("there should be one paused container and not %d", len(pausedContainers))
   386  	}
   387  
   388  	err = cli.ContainerUnpause(context.Background(), ContainerID)
   389  	assert.NilError(c, err)
   390  
   391  	pausedContainers = getPaused(c)
   392  	assert.Equal(c, len(pausedContainers), 0, "There should be no paused container.")
   393  }
   394  
   395  func (s *DockerSuite) TestContainerAPITop(c *testing.T) {
   396  	testRequires(c, DaemonIsLinux)
   397  	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top")
   398  	id := strings.TrimSpace(out)
   399  	assert.NilError(c, waitRun(id))
   400  
   401  	cli, err := client.NewClientWithOpts(client.FromEnv)
   402  	assert.NilError(c, err)
   403  	defer cli.Close()
   404  
   405  	// sort by comm[andline] to make sure order stays the same in case of PID rollover
   406  	top, err := cli.ContainerTop(context.Background(), id, []string{"aux", "--sort=comm"})
   407  	assert.NilError(c, err)
   408  	assert.Equal(c, len(top.Titles), 11, fmt.Sprintf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
   409  
   410  	if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" {
   411  		c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles)
   412  	}
   413  	assert.Equal(c, len(top.Processes), 2, fmt.Sprintf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes))
   414  	assert.Equal(c, top.Processes[0][10], "/bin/sh -c top")
   415  	assert.Equal(c, top.Processes[1][10], "top")
   416  }
   417  
   418  func (s *DockerSuite) TestContainerAPITopWindows(c *testing.T) {
   419  	testRequires(c, DaemonIsWindows)
   420  	out := runSleepingContainer(c, "-d")
   421  	id := strings.TrimSpace(out)
   422  	assert.NilError(c, waitRun(id))
   423  
   424  	cli, err := client.NewClientWithOpts(client.FromEnv)
   425  	assert.NilError(c, err)
   426  	defer cli.Close()
   427  
   428  	top, err := cli.ContainerTop(context.Background(), id, nil)
   429  	assert.NilError(c, err)
   430  	assert.Equal(c, len(top.Titles), 4, "expected 4 titles, found %d: %v", len(top.Titles), top.Titles)
   431  
   432  	if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" {
   433  		c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles)
   434  	}
   435  	assert.Assert(c, len(top.Processes) >= 2, "expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes)
   436  
   437  	foundProcess := false
   438  	expectedProcess := "busybox.exe"
   439  	for _, process := range top.Processes {
   440  		if process[0] == expectedProcess {
   441  			foundProcess = true
   442  			break
   443  		}
   444  	}
   445  
   446  	assert.Assert(c, foundProcess, "expected to find %s: %v", expectedProcess, top.Processes)
   447  }
   448  
   449  func (s *DockerSuite) TestContainerAPICommit(c *testing.T) {
   450  	cName := "testapicommit"
   451  	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
   452  
   453  	cli, err := client.NewClientWithOpts(client.FromEnv)
   454  	assert.NilError(c, err)
   455  	defer cli.Close()
   456  
   457  	options := types.ContainerCommitOptions{
   458  		Reference: "testcontainerapicommit:testtag",
   459  	}
   460  
   461  	img, err := cli.ContainerCommit(context.Background(), cName, options)
   462  	assert.NilError(c, err)
   463  
   464  	cmd := inspectField(c, img.ID, "Config.Cmd")
   465  	assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd))
   466  
   467  	// sanity check, make sure the image is what we think it is
   468  	dockerCmd(c, "run", img.ID, "ls", "/test")
   469  }
   470  
   471  func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *testing.T) {
   472  	cName := "testapicommitwithconfig"
   473  	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
   474  
   475  	cli, err := client.NewClientWithOpts(client.FromEnv)
   476  	assert.NilError(c, err)
   477  	defer cli.Close()
   478  
   479  	config := containertypes.Config{
   480  		Labels: map[string]string{"key1": "value1", "key2": "value2"}}
   481  
   482  	options := types.ContainerCommitOptions{
   483  		Reference: "testcontainerapicommitwithconfig",
   484  		Config:    &config,
   485  	}
   486  
   487  	img, err := cli.ContainerCommit(context.Background(), cName, options)
   488  	assert.NilError(c, err)
   489  
   490  	label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1")
   491  	assert.Equal(c, label1, "value1")
   492  
   493  	label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2")
   494  	assert.Equal(c, label2, "value2")
   495  
   496  	cmd := inspectField(c, img.ID, "Config.Cmd")
   497  	assert.Equal(c, cmd, "[/bin/sh -c touch /test]", fmt.Sprintf("got wrong Cmd from commit: %q", cmd))
   498  
   499  	// sanity check, make sure the image is what we think it is
   500  	dockerCmd(c, "run", img.ID, "ls", "/test")
   501  }
   502  
   503  func (s *DockerSuite) TestContainerAPIBadPort(c *testing.T) {
   504  	// TODO Windows to Windows CI - Port this test
   505  	testRequires(c, DaemonIsLinux)
   506  
   507  	config := containertypes.Config{
   508  		Image: "busybox",
   509  		Cmd:   []string{"/bin/sh", "-c", "echo test"},
   510  	}
   511  
   512  	hostConfig := containertypes.HostConfig{
   513  		PortBindings: nat.PortMap{
   514  			"8080/tcp": []nat.PortBinding{
   515  				{
   516  					HostIP:   "",
   517  					HostPort: "aa80"},
   518  			},
   519  		},
   520  	}
   521  
   522  	cli, err := client.NewClientWithOpts(client.FromEnv)
   523  	assert.NilError(c, err)
   524  	defer cli.Close()
   525  
   526  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
   527  	assert.ErrorContains(c, err, `invalid port specification: "aa80"`)
   528  }
   529  
   530  func (s *DockerSuite) TestContainerAPICreate(c *testing.T) {
   531  	config := containertypes.Config{
   532  		Image: "busybox",
   533  		Cmd:   []string{"/bin/sh", "-c", "touch /test && ls /test"},
   534  	}
   535  
   536  	cli, err := client.NewClientWithOpts(client.FromEnv)
   537  	assert.NilError(c, err)
   538  	defer cli.Close()
   539  
   540  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
   541  	assert.NilError(c, err)
   542  
   543  	out, _ := dockerCmd(c, "start", "-a", container.ID)
   544  	assert.Equal(c, strings.TrimSpace(out), "/test")
   545  }
   546  
   547  func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *testing.T) {
   548  
   549  	cli, err := client.NewClientWithOpts(client.FromEnv)
   550  	assert.NilError(c, err)
   551  	defer cli.Close()
   552  
   553  	_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
   554  
   555  	expected := "No command specified"
   556  	assert.ErrorContains(c, err, expected)
   557  }
   558  
   559  func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *testing.T) {
   560  	// Container creation must fail if client specified configurations for more than one network
   561  	config := containertypes.Config{
   562  		Image: "busybox",
   563  	}
   564  
   565  	networkingConfig := networktypes.NetworkingConfig{
   566  		EndpointsConfig: map[string]*networktypes.EndpointSettings{
   567  			"net1": {},
   568  			"net2": {},
   569  			"net3": {},
   570  		},
   571  	}
   572  
   573  	cli, err := client.NewClientWithOpts(client.FromEnv)
   574  	assert.NilError(c, err)
   575  	defer cli.Close()
   576  
   577  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networkingConfig, nil, "")
   578  	msg := err.Error()
   579  	// network name order in error message is not deterministic
   580  	assert.Assert(c, strings.Contains(msg, "Container cannot be connected to network endpoints"))
   581  	assert.Assert(c, strings.Contains(msg, "net1"))
   582  	assert.Assert(c, strings.Contains(msg, "net2"))
   583  	assert.Assert(c, strings.Contains(msg, "net3"))
   584  }
   585  
   586  func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *testing.T) {
   587  	// Windows does not support bridge
   588  	testRequires(c, DaemonIsLinux)
   589  	UtilCreateNetworkMode(c, "bridge")
   590  }
   591  
   592  func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *testing.T) {
   593  	// Windows does not support these network modes
   594  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   595  	UtilCreateNetworkMode(c, "host")
   596  	UtilCreateNetworkMode(c, "container:web1")
   597  }
   598  
   599  func UtilCreateNetworkMode(c *testing.T, networkMode containertypes.NetworkMode) {
   600  	config := containertypes.Config{
   601  		Image: "busybox",
   602  	}
   603  
   604  	hostConfig := containertypes.HostConfig{
   605  		NetworkMode: networkMode,
   606  	}
   607  
   608  	cli, err := client.NewClientWithOpts(client.FromEnv)
   609  	assert.NilError(c, err)
   610  	defer cli.Close()
   611  
   612  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
   613  	assert.NilError(c, err)
   614  
   615  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
   616  	assert.NilError(c, err)
   617  
   618  	assert.Equal(c, containerJSON.HostConfig.NetworkMode, networkMode, "Mismatched NetworkMode")
   619  }
   620  
   621  func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *testing.T) {
   622  	// TODO Windows to Windows CI. The CpuShares part could be ported.
   623  	testRequires(c, DaemonIsLinux)
   624  	config := containertypes.Config{
   625  		Image: "busybox",
   626  	}
   627  
   628  	hostConfig := containertypes.HostConfig{
   629  		Resources: containertypes.Resources{
   630  			CPUShares:  512,
   631  			CpusetCpus: "0",
   632  		},
   633  	}
   634  
   635  	cli, err := client.NewClientWithOpts(client.FromEnv)
   636  	assert.NilError(c, err)
   637  	defer cli.Close()
   638  
   639  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
   640  	assert.NilError(c, err)
   641  
   642  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
   643  	assert.NilError(c, err)
   644  
   645  	out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
   646  	assert.Equal(c, out, "512")
   647  
   648  	outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus")
   649  	assert.Equal(c, outCpuset, "0")
   650  }
   651  
   652  func (s *DockerSuite) TestContainerAPIVerifyHeader(c *testing.T) {
   653  	config := map[string]interface{}{
   654  		"Image": "busybox",
   655  	}
   656  
   657  	create := func(ct string) (*http.Response, io.ReadCloser, error) {
   658  		jsonData := bytes.NewBuffer(nil)
   659  		assert.Assert(c, json.NewEncoder(jsonData).Encode(config) == nil)
   660  		return request.Post("/containers/create", request.RawContent(ioutil.NopCloser(jsonData)), request.ContentType(ct))
   661  	}
   662  
   663  	// Try with no content-type
   664  	res, body, err := create("")
   665  	assert.NilError(c, err)
   666  	// todo: we need to figure out a better way to compare between dockerd versions
   667  	// comparing between daemon API version is not precise.
   668  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   669  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   670  	} else {
   671  		assert.Assert(c, res.StatusCode != http.StatusOK)
   672  	}
   673  	body.Close()
   674  
   675  	// Try with wrong content-type
   676  	res, body, err = create("application/xml")
   677  	assert.NilError(c, err)
   678  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   679  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   680  	} else {
   681  		assert.Assert(c, res.StatusCode != http.StatusOK)
   682  	}
   683  	body.Close()
   684  
   685  	// now application/json
   686  	res, body, err = create("application/json")
   687  	assert.NilError(c, err)
   688  	assert.Equal(c, res.StatusCode, http.StatusCreated)
   689  	body.Close()
   690  }
   691  
   692  // Issue 14230. daemon should return 500 for invalid port syntax
   693  func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *testing.T) {
   694  	config := `{
   695  				  "Image": "busybox",
   696  				  "HostConfig": {
   697  					"NetworkMode": "default",
   698  					"PortBindings": {
   699  					  "19039;1230": [
   700  						{}
   701  					  ]
   702  					}
   703  				  }
   704  				}`
   705  
   706  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   707  	assert.NilError(c, err)
   708  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   709  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   710  	} else {
   711  		assert.Assert(c, res.StatusCode != http.StatusOK)
   712  	}
   713  
   714  	b, err := request.ReadBody(body)
   715  	assert.NilError(c, err)
   716  	assert.Assert(c, strings.Contains(string(b[:]), "invalid port"))
   717  }
   718  
   719  func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *testing.T) {
   720  	config := `{
   721  		"Image": "busybox",
   722  		"HostConfig": {
   723  			"RestartPolicy": {
   724  				"Name": "something",
   725  				"MaximumRetryCount": 0
   726  			}
   727  		}
   728  	}`
   729  
   730  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   731  	assert.NilError(c, err)
   732  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   733  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   734  	} else {
   735  		assert.Assert(c, res.StatusCode != http.StatusOK)
   736  	}
   737  
   738  	b, err := request.ReadBody(body)
   739  	assert.NilError(c, err)
   740  	assert.Assert(c, strings.Contains(string(b[:]), "invalid restart policy"))
   741  }
   742  
   743  func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *testing.T) {
   744  	config := `{
   745  		"Image": "busybox",
   746  		"HostConfig": {
   747  			"RestartPolicy": {
   748  				"Name": "always",
   749  				"MaximumRetryCount": 2
   750  			}
   751  		}
   752  	}`
   753  
   754  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   755  	assert.NilError(c, err)
   756  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   757  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   758  	} else {
   759  		assert.Assert(c, res.StatusCode != http.StatusOK)
   760  	}
   761  
   762  	b, err := request.ReadBody(body)
   763  	assert.NilError(c, err)
   764  	assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be used with restart policy"))
   765  }
   766  
   767  func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *testing.T) {
   768  	config := `{
   769  		"Image": "busybox",
   770  		"HostConfig": {
   771  			"RestartPolicy": {
   772  				"Name": "on-failure",
   773  				"MaximumRetryCount": -2
   774  			}
   775  		}
   776  	}`
   777  
   778  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   779  	assert.NilError(c, err)
   780  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   781  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   782  	} else {
   783  		assert.Assert(c, res.StatusCode != http.StatusOK)
   784  	}
   785  
   786  	b, err := request.ReadBody(body)
   787  	assert.NilError(c, err)
   788  	assert.Assert(c, strings.Contains(string(b[:]), "maximum retry count cannot be negative"))
   789  }
   790  
   791  func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *testing.T) {
   792  	config := `{
   793  		"Image": "busybox",
   794  		"HostConfig": {
   795  			"RestartPolicy": {
   796  				"Name": "on-failure",
   797  				"MaximumRetryCount": 0
   798  			}
   799  		}
   800  	}`
   801  
   802  	res, _, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   803  	assert.NilError(c, err)
   804  	assert.Equal(c, res.StatusCode, http.StatusCreated)
   805  }
   806  
   807  // Issue 7941 - test to make sure a "null" in JSON is just ignored.
   808  // W/o this fix a null in JSON would be parsed into a string var as "null"
   809  func (s *DockerSuite) TestContainerAPIPostCreateNull(c *testing.T) {
   810  	config := `{
   811  		"Hostname":"",
   812  		"Domainname":"",
   813  		"Memory":0,
   814  		"MemorySwap":0,
   815  		"CpuShares":0,
   816  		"Cpuset":null,
   817  		"AttachStdin":true,
   818  		"AttachStdout":true,
   819  		"AttachStderr":true,
   820  		"ExposedPorts":{},
   821  		"Tty":true,
   822  		"OpenStdin":true,
   823  		"StdinOnce":true,
   824  		"Env":[],
   825  		"Cmd":"ls",
   826  		"Image":"busybox",
   827  		"Volumes":{},
   828  		"WorkingDir":"",
   829  		"Entrypoint":null,
   830  		"NetworkDisabled":false,
   831  		"OnBuild":null}`
   832  
   833  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   834  	assert.NilError(c, err)
   835  	assert.Equal(c, res.StatusCode, http.StatusCreated)
   836  
   837  	b, err := request.ReadBody(body)
   838  	assert.NilError(c, err)
   839  	type createResp struct {
   840  		ID string
   841  	}
   842  	var container createResp
   843  	assert.Assert(c, json.Unmarshal(b, &container) == nil)
   844  	out := inspectField(c, container.ID, "HostConfig.CpusetCpus")
   845  	assert.Equal(c, out, "")
   846  
   847  	outMemory := inspectField(c, container.ID, "HostConfig.Memory")
   848  	assert.Equal(c, outMemory, "0")
   849  	outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap")
   850  	assert.Equal(c, outMemorySwap, "0")
   851  }
   852  
   853  func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *testing.T) {
   854  	// TODO Windows: Port once memory is supported
   855  	testRequires(c, DaemonIsLinux)
   856  	config := `{
   857  		"Image":     "busybox",
   858  		"Cmd":       "ls",
   859  		"OpenStdin": true,
   860  		"CpuShares": 100,
   861  		"Memory":    524287
   862  	}`
   863  
   864  	res, body, err := request.Post("/containers/create", request.RawString(config), request.JSON)
   865  	assert.NilError(c, err)
   866  	b, err2 := request.ReadBody(body)
   867  	assert.Assert(c, err2 == nil)
   868  
   869  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   870  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
   871  	} else {
   872  		assert.Assert(c, res.StatusCode != http.StatusOK)
   873  	}
   874  	assert.Assert(c, strings.Contains(string(b), "Minimum memory limit allowed is 4MB"))
   875  }
   876  
   877  func (s *DockerSuite) TestContainerAPIRename(c *testing.T) {
   878  	out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh")
   879  
   880  	containerID := strings.TrimSpace(out)
   881  	newName := "TestContainerAPIRenameNew"
   882  
   883  	cli, err := client.NewClientWithOpts(client.FromEnv)
   884  	assert.NilError(c, err)
   885  	defer cli.Close()
   886  
   887  	err = cli.ContainerRename(context.Background(), containerID, newName)
   888  	assert.NilError(c, err)
   889  
   890  	name := inspectField(c, containerID, "Name")
   891  	assert.Equal(c, name, "/"+newName, "Failed to rename container")
   892  }
   893  
   894  func (s *DockerSuite) TestContainerAPIKill(c *testing.T) {
   895  	name := "test-api-kill"
   896  	runSleepingContainer(c, "-i", "--name", name)
   897  
   898  	cli, err := client.NewClientWithOpts(client.FromEnv)
   899  	assert.NilError(c, err)
   900  	defer cli.Close()
   901  
   902  	err = cli.ContainerKill(context.Background(), name, "SIGKILL")
   903  	assert.NilError(c, err)
   904  
   905  	state := inspectField(c, name, "State.Running")
   906  	assert.Equal(c, state, "false", fmt.Sprintf("got wrong State from container %s: %q", name, state))
   907  }
   908  
   909  func (s *DockerSuite) TestContainerAPIRestart(c *testing.T) {
   910  	name := "test-api-restart"
   911  	runSleepingContainer(c, "-di", "--name", name)
   912  	cli, err := client.NewClientWithOpts(client.FromEnv)
   913  	assert.NilError(c, err)
   914  	defer cli.Close()
   915  
   916  	timeout := 1 * time.Second
   917  	err = cli.ContainerRestart(context.Background(), name, &timeout)
   918  	assert.NilError(c, err)
   919  
   920  	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
   921  }
   922  
   923  func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *testing.T) {
   924  	name := "test-api-restart-no-timeout-param"
   925  	out := runSleepingContainer(c, "-di", "--name", name)
   926  	id := strings.TrimSpace(out)
   927  	assert.NilError(c, waitRun(id))
   928  
   929  	cli, err := client.NewClientWithOpts(client.FromEnv)
   930  	assert.NilError(c, err)
   931  	defer cli.Close()
   932  
   933  	err = cli.ContainerRestart(context.Background(), name, nil)
   934  	assert.NilError(c, err)
   935  
   936  	assert.Assert(c, waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second) == nil)
   937  }
   938  
   939  func (s *DockerSuite) TestContainerAPIStart(c *testing.T) {
   940  	name := "testing-start"
   941  	config := containertypes.Config{
   942  		Image:     "busybox",
   943  		Cmd:       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
   944  		OpenStdin: true,
   945  	}
   946  
   947  	cli, err := client.NewClientWithOpts(client.FromEnv)
   948  	assert.NilError(c, err)
   949  	defer cli.Close()
   950  
   951  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
   952  	assert.NilError(c, err)
   953  
   954  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
   955  	assert.NilError(c, err)
   956  
   957  	// second call to start should give 304
   958  	// maybe add ContainerStartWithRaw to test it
   959  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
   960  	assert.NilError(c, err)
   961  
   962  	// TODO(tibor): figure out why this doesn't work on windows
   963  }
   964  
   965  func (s *DockerSuite) TestContainerAPIStop(c *testing.T) {
   966  	name := "test-api-stop"
   967  	runSleepingContainer(c, "-i", "--name", name)
   968  	timeout := 30 * time.Second
   969  
   970  	cli, err := client.NewClientWithOpts(client.FromEnv)
   971  	assert.NilError(c, err)
   972  	defer cli.Close()
   973  
   974  	err = cli.ContainerStop(context.Background(), name, &timeout)
   975  	assert.NilError(c, err)
   976  	assert.Assert(c, waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
   977  
   978  	// second call to start should give 304
   979  	// maybe add ContainerStartWithRaw to test it
   980  	err = cli.ContainerStop(context.Background(), name, &timeout)
   981  	assert.NilError(c, err)
   982  }
   983  
   984  func (s *DockerSuite) TestContainerAPIWait(c *testing.T) {
   985  	name := "test-api-wait"
   986  
   987  	sleepCmd := "/bin/sleep"
   988  	if testEnv.OSType == "windows" {
   989  		sleepCmd = "sleep"
   990  	}
   991  	dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2")
   992  
   993  	cli, err := client.NewClientWithOpts(client.FromEnv)
   994  	assert.NilError(c, err)
   995  	defer cli.Close()
   996  
   997  	waitResC, errC := cli.ContainerWait(context.Background(), name, "")
   998  
   999  	select {
  1000  	case err = <-errC:
  1001  		assert.NilError(c, err)
  1002  	case waitRes := <-waitResC:
  1003  		assert.Equal(c, waitRes.StatusCode, int64(0))
  1004  	}
  1005  }
  1006  
  1007  func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *testing.T) {
  1008  	name := "test-container-api-copy"
  1009  	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  1010  
  1011  	postData := types.CopyConfig{
  1012  		Resource: "/test.txt",
  1013  	}
  1014  	// no copy in client/
  1015  	res, _, err := request.Post("/containers/"+name+"/copy", request.JSONBody(postData))
  1016  	assert.NilError(c, err)
  1017  	assert.Equal(c, res.StatusCode, http.StatusNotFound)
  1018  }
  1019  
  1020  func (s *DockerSuite) TestContainerAPICopyPre124(c *testing.T) {
  1021  	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
  1022  	name := "test-container-api-copy"
  1023  	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  1024  
  1025  	postData := types.CopyConfig{
  1026  		Resource: "/test.txt",
  1027  	}
  1028  
  1029  	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
  1030  	assert.NilError(c, err)
  1031  	assert.Equal(c, res.StatusCode, http.StatusOK)
  1032  
  1033  	found := false
  1034  	for tarReader := tar.NewReader(body); ; {
  1035  		h, err := tarReader.Next()
  1036  		if err != nil {
  1037  			if err == io.EOF {
  1038  				break
  1039  			}
  1040  			c.Fatal(err)
  1041  		}
  1042  		if h.Name == "test.txt" {
  1043  			found = true
  1044  			break
  1045  		}
  1046  	}
  1047  	assert.Assert(c, found)
  1048  }
  1049  
  1050  func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPre124(c *testing.T) {
  1051  	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
  1052  	name := "test-container-api-copy-resource-empty"
  1053  	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  1054  
  1055  	postData := types.CopyConfig{
  1056  		Resource: "",
  1057  	}
  1058  
  1059  	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
  1060  	assert.NilError(c, err)
  1061  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
  1062  		assert.Equal(c, res.StatusCode, http.StatusBadRequest)
  1063  	} else {
  1064  		assert.Assert(c, res.StatusCode != http.StatusOK)
  1065  	}
  1066  	b, err := request.ReadBody(body)
  1067  	assert.NilError(c, err)
  1068  	assert.Assert(c, is.Regexp("^Path cannot be empty\n$", string(b)))
  1069  
  1070  }
  1071  
  1072  func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *testing.T) {
  1073  	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
  1074  	name := "test-container-api-copy-resource-not-found"
  1075  	dockerCmd(c, "run", "--name", name, "busybox")
  1076  
  1077  	postData := types.CopyConfig{
  1078  		Resource: "/notexist",
  1079  	}
  1080  
  1081  	res, body, err := request.Post("/v1.23/containers/"+name+"/copy", request.JSONBody(postData))
  1082  	assert.NilError(c, err)
  1083  	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
  1084  		assert.Equal(c, res.StatusCode, http.StatusInternalServerError)
  1085  	} else {
  1086  		assert.Equal(c, res.StatusCode, http.StatusNotFound)
  1087  	}
  1088  	b, err := request.ReadBody(body)
  1089  	assert.NilError(c, err)
  1090  	assert.Assert(c, is.Regexp("^Could not find the file /notexist in container "+name+"\n$", string(b)))
  1091  
  1092  }
  1093  
  1094  func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *testing.T) {
  1095  	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
  1096  	postData := types.CopyConfig{
  1097  		Resource: "/something",
  1098  	}
  1099  
  1100  	res, _, err := request.Post("/v1.23/containers/notexists/copy", request.JSONBody(postData))
  1101  	assert.NilError(c, err)
  1102  	assert.Equal(c, res.StatusCode, http.StatusNotFound)
  1103  }
  1104  
  1105  func (s *DockerSuite) TestContainerAPIDelete(c *testing.T) {
  1106  	out := runSleepingContainer(c)
  1107  
  1108  	id := strings.TrimSpace(out)
  1109  	assert.NilError(c, waitRun(id))
  1110  
  1111  	dockerCmd(c, "stop", id)
  1112  
  1113  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1114  	assert.NilError(c, err)
  1115  	defer cli.Close()
  1116  
  1117  	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
  1118  	assert.NilError(c, err)
  1119  }
  1120  
  1121  func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *testing.T) {
  1122  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1123  	assert.NilError(c, err)
  1124  	defer cli.Close()
  1125  
  1126  	err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{})
  1127  	assert.ErrorContains(c, err, "No such container: doesnotexist")
  1128  }
  1129  
  1130  func (s *DockerSuite) TestContainerAPIDeleteForce(c *testing.T) {
  1131  	out := runSleepingContainer(c)
  1132  	id := strings.TrimSpace(out)
  1133  	assert.NilError(c, waitRun(id))
  1134  
  1135  	removeOptions := types.ContainerRemoveOptions{
  1136  		Force: true,
  1137  	}
  1138  
  1139  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1140  	assert.NilError(c, err)
  1141  	defer cli.Close()
  1142  
  1143  	err = cli.ContainerRemove(context.Background(), id, removeOptions)
  1144  	assert.NilError(c, err)
  1145  }
  1146  
  1147  func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *testing.T) {
  1148  	// Windows does not support links
  1149  	testRequires(c, DaemonIsLinux)
  1150  	out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top")
  1151  
  1152  	id := strings.TrimSpace(out)
  1153  	assert.NilError(c, waitRun(id))
  1154  
  1155  	out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top")
  1156  
  1157  	id2 := strings.TrimSpace(out)
  1158  	assert.Assert(c, waitRun(id2) == nil)
  1159  
  1160  	links := inspectFieldJSON(c, id2, "HostConfig.Links")
  1161  	assert.Equal(c, links, "[\"/tlink1:/tlink2/tlink1\"]", "expected to have links between containers")
  1162  
  1163  	removeOptions := types.ContainerRemoveOptions{
  1164  		RemoveLinks: true,
  1165  	}
  1166  
  1167  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1168  	assert.NilError(c, err)
  1169  	defer cli.Close()
  1170  
  1171  	err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions)
  1172  	assert.NilError(c, err)
  1173  
  1174  	linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links")
  1175  	assert.Equal(c, linksPostRm, "null", "call to api deleteContainer links should have removed the specified links")
  1176  }
  1177  
  1178  func (s *DockerSuite) TestContainerAPIDeleteConflict(c *testing.T) {
  1179  	out := runSleepingContainer(c)
  1180  
  1181  	id := strings.TrimSpace(out)
  1182  	assert.NilError(c, waitRun(id))
  1183  
  1184  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1185  	assert.NilError(c, err)
  1186  	defer cli.Close()
  1187  
  1188  	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
  1189  	expected := "cannot remove a running container"
  1190  	assert.ErrorContains(c, err, expected)
  1191  }
  1192  
  1193  func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *testing.T) {
  1194  	testRequires(c, testEnv.IsLocalDaemon)
  1195  
  1196  	vol := "/testvolume"
  1197  	if testEnv.OSType == "windows" {
  1198  		vol = `c:\testvolume`
  1199  	}
  1200  
  1201  	out := runSleepingContainer(c, "-v", vol)
  1202  
  1203  	id := strings.TrimSpace(out)
  1204  	assert.NilError(c, waitRun(id))
  1205  
  1206  	source, err := inspectMountSourceField(id, vol)
  1207  	assert.NilError(c, err)
  1208  	_, err = os.Stat(source)
  1209  	assert.NilError(c, err)
  1210  
  1211  	removeOptions := types.ContainerRemoveOptions{
  1212  		Force:         true,
  1213  		RemoveVolumes: true,
  1214  	}
  1215  
  1216  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1217  	assert.NilError(c, err)
  1218  	defer cli.Close()
  1219  
  1220  	err = cli.ContainerRemove(context.Background(), id, removeOptions)
  1221  	assert.NilError(c, err)
  1222  
  1223  	_, err = os.Stat(source)
  1224  	assert.Assert(c, os.IsNotExist(err), "expected to get ErrNotExist error, got %v", err)
  1225  }
  1226  
  1227  // Regression test for https://github.com/docker/docker/issues/6231
  1228  func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *testing.T) {
  1229  
  1230  	config := map[string]interface{}{
  1231  		"Image":     "busybox",
  1232  		"Cmd":       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
  1233  		"OpenStdin": true,
  1234  	}
  1235  
  1236  	resp, _, err := request.Post("/containers/create", request.JSONBody(config), request.With(func(req *http.Request) error {
  1237  		// This is a cheat to make the http request do chunked encoding
  1238  		// Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
  1239  		// https://golang.org/src/pkg/net/http/request.go?s=11980:12172
  1240  		req.ContentLength = -1
  1241  		return nil
  1242  	}))
  1243  	assert.Assert(c, err == nil, "error creating container with chunked encoding")
  1244  	defer resp.Body.Close()
  1245  	assert.Equal(c, resp.StatusCode, http.StatusCreated)
  1246  }
  1247  
  1248  func (s *DockerSuite) TestContainerAPIPostContainerStop(c *testing.T) {
  1249  	out := runSleepingContainer(c)
  1250  
  1251  	containerID := strings.TrimSpace(out)
  1252  	assert.Assert(c, waitRun(containerID) == nil)
  1253  
  1254  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1255  	assert.NilError(c, err)
  1256  	defer cli.Close()
  1257  
  1258  	err = cli.ContainerStop(context.Background(), containerID, nil)
  1259  	assert.NilError(c, err)
  1260  	assert.Assert(c, waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second) == nil)
  1261  }
  1262  
  1263  // #14170
  1264  func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *testing.T) {
  1265  	config := containertypes.Config{
  1266  		Image:      "busybox",
  1267  		Entrypoint: []string{"echo"},
  1268  		Cmd:        []string{"hello", "world"},
  1269  	}
  1270  
  1271  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1272  	assert.NilError(c, err)
  1273  	defer cli.Close()
  1274  
  1275  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
  1276  	assert.NilError(c, err)
  1277  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1278  	assert.Equal(c, strings.TrimSpace(out), "hello world")
  1279  
  1280  	config2 := struct {
  1281  		Image      string
  1282  		Entrypoint string
  1283  		Cmd        []string
  1284  	}{"busybox", "echo", []string{"hello", "world"}}
  1285  	_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2))
  1286  	assert.NilError(c, err)
  1287  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1288  	assert.Equal(c, strings.TrimSpace(out), "hello world")
  1289  }
  1290  
  1291  // #14170
  1292  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *testing.T) {
  1293  	config := containertypes.Config{
  1294  		Image: "busybox",
  1295  		Cmd:   []string{"echo", "hello", "world"},
  1296  	}
  1297  
  1298  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1299  	assert.NilError(c, err)
  1300  	defer cli.Close()
  1301  
  1302  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "echotest")
  1303  	assert.NilError(c, err)
  1304  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1305  	assert.Equal(c, strings.TrimSpace(out), "hello world")
  1306  
  1307  	config2 := struct {
  1308  		Image      string
  1309  		Entrypoint string
  1310  		Cmd        string
  1311  	}{"busybox", "echo", "hello world"}
  1312  	_, _, err = request.Post("/containers/create?name=echotest2", request.JSONBody(config2))
  1313  	assert.NilError(c, err)
  1314  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1315  	assert.Equal(c, strings.TrimSpace(out), "hello world")
  1316  }
  1317  
  1318  // regression #14318
  1319  // for backward compatibility testing with and without CAP_ prefix
  1320  // and with upper and lowercase
  1321  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *testing.T) {
  1322  	// Windows doesn't support CapAdd/CapDrop
  1323  	testRequires(c, DaemonIsLinux)
  1324  	config := struct {
  1325  		Image   string
  1326  		CapAdd  string
  1327  		CapDrop string
  1328  	}{"busybox", "NET_ADMIN", "cap_sys_admin"}
  1329  	res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config))
  1330  	assert.NilError(c, err)
  1331  	assert.Equal(c, res.StatusCode, http.StatusCreated)
  1332  
  1333  	config2 := containertypes.Config{
  1334  		Image: "busybox",
  1335  	}
  1336  	hostConfig := containertypes.HostConfig{
  1337  		CapAdd:  []string{"net_admin", "SYS_ADMIN"},
  1338  		CapDrop: []string{"SETGID", "CAP_SETPCAP"},
  1339  	}
  1340  
  1341  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1342  	assert.NilError(c, err)
  1343  	defer cli.Close()
  1344  
  1345  	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, nil, "capaddtest1")
  1346  	assert.NilError(c, err)
  1347  }
  1348  
  1349  // #14915
  1350  func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *testing.T) {
  1351  	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
  1352  	config := containertypes.Config{
  1353  		Image: "busybox",
  1354  	}
  1355  
  1356  	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
  1357  	assert.NilError(c, err)
  1358  
  1359  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
  1360  	assert.NilError(c, err)
  1361  }
  1362  
  1363  // Ensure an error occurs when you have a container read-only rootfs but you
  1364  // extract an archive to a symlink in a writable volume which points to a
  1365  // directory outside of the volume.
  1366  func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *testing.T) {
  1367  	// Windows does not support read-only rootfs
  1368  	// Requires local volume mount bind.
  1369  	// --read-only + userns has remount issues
  1370  	testRequires(c, testEnv.IsLocalDaemon, NotUserNamespace, DaemonIsLinux)
  1371  
  1372  	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1373  	defer os.RemoveAll(testVol)
  1374  
  1375  	makeTestContentInDir(c, testVol)
  1376  
  1377  	cID := makeTestContainer(c, testContainerOptions{
  1378  		readOnly: true,
  1379  		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
  1380  	})
  1381  
  1382  	// Attempt to extract to a symlink in the volume which points to a
  1383  	// directory outside the volume. This should cause an error because the
  1384  	// rootfs is read-only.
  1385  	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.20"))
  1386  	assert.NilError(c, err)
  1387  
  1388  	err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{})
  1389  	assert.ErrorContains(c, err, "container rootfs is marked read-only")
  1390  }
  1391  
  1392  func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *testing.T) {
  1393  	// Not supported on Windows
  1394  	testRequires(c, DaemonIsLinux)
  1395  
  1396  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1397  	assert.NilError(c, err)
  1398  	defer cli.Close()
  1399  
  1400  	config := containertypes.Config{
  1401  		Image: "busybox",
  1402  	}
  1403  	hostConfig1 := containertypes.HostConfig{
  1404  		Resources: containertypes.Resources{
  1405  			CpusetCpus: "1-42,,",
  1406  		},
  1407  	}
  1408  	name := "wrong-cpuset-cpus"
  1409  
  1410  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, nil, name)
  1411  	expected := "Invalid value 1-42,, for cpuset cpus"
  1412  	assert.ErrorContains(c, err, expected)
  1413  
  1414  	hostConfig2 := containertypes.HostConfig{
  1415  		Resources: containertypes.Resources{
  1416  			CpusetMems: "42-3,1--",
  1417  		},
  1418  	}
  1419  	name = "wrong-cpuset-mems"
  1420  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, nil, name)
  1421  	expected = "Invalid value 42-3,1-- for cpuset mems"
  1422  	assert.ErrorContains(c, err, expected)
  1423  }
  1424  
  1425  func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *testing.T) {
  1426  	// ShmSize is not supported on Windows
  1427  	testRequires(c, DaemonIsLinux)
  1428  	config := containertypes.Config{
  1429  		Image: "busybox",
  1430  	}
  1431  	hostConfig := containertypes.HostConfig{
  1432  		ShmSize: -1,
  1433  	}
  1434  
  1435  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1436  	assert.NilError(c, err)
  1437  	defer cli.Close()
  1438  
  1439  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
  1440  	assert.ErrorContains(c, err, "SHM size can not be less than 0")
  1441  }
  1442  
  1443  func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *testing.T) {
  1444  	// ShmSize is not supported on Windows
  1445  	testRequires(c, DaemonIsLinux)
  1446  	var defaultSHMSize int64 = 67108864
  1447  	config := containertypes.Config{
  1448  		Image: "busybox",
  1449  		Cmd:   []string{"mount"},
  1450  	}
  1451  
  1452  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1453  	assert.NilError(c, err)
  1454  	defer cli.Close()
  1455  
  1456  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
  1457  	assert.NilError(c, err)
  1458  
  1459  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1460  	assert.NilError(c, err)
  1461  
  1462  	assert.Equal(c, containerJSON.HostConfig.ShmSize, defaultSHMSize)
  1463  
  1464  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1465  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1466  	if !shmRegexp.MatchString(out) {
  1467  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1468  	}
  1469  }
  1470  
  1471  func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *testing.T) {
  1472  	// ShmSize is not supported on Windows
  1473  	testRequires(c, DaemonIsLinux)
  1474  	config := containertypes.Config{
  1475  		Image: "busybox",
  1476  		Cmd:   []string{"mount"},
  1477  	}
  1478  
  1479  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1480  	assert.NilError(c, err)
  1481  	defer cli.Close()
  1482  
  1483  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
  1484  	assert.NilError(c, err)
  1485  
  1486  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1487  	assert.NilError(c, err)
  1488  
  1489  	assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(67108864))
  1490  
  1491  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1492  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1493  	if !shmRegexp.MatchString(out) {
  1494  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1495  	}
  1496  }
  1497  
  1498  func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *testing.T) {
  1499  	// ShmSize is not supported on Windows
  1500  	testRequires(c, DaemonIsLinux)
  1501  	config := containertypes.Config{
  1502  		Image: "busybox",
  1503  		Cmd:   []string{"mount"},
  1504  	}
  1505  
  1506  	hostConfig := containertypes.HostConfig{
  1507  		ShmSize: 1073741824,
  1508  	}
  1509  
  1510  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1511  	assert.NilError(c, err)
  1512  	defer cli.Close()
  1513  
  1514  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "")
  1515  	assert.NilError(c, err)
  1516  
  1517  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1518  	assert.NilError(c, err)
  1519  
  1520  	assert.Equal(c, containerJSON.HostConfig.ShmSize, int64(1073741824))
  1521  
  1522  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1523  	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1524  	if !shmRegex.MatchString(out) {
  1525  		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1526  	}
  1527  }
  1528  
  1529  func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *testing.T) {
  1530  	// Swappiness is not supported on Windows
  1531  	testRequires(c, DaemonIsLinux)
  1532  	config := containertypes.Config{
  1533  		Image: "busybox",
  1534  	}
  1535  
  1536  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1537  	assert.NilError(c, err)
  1538  	defer cli.Close()
  1539  
  1540  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, "")
  1541  	assert.NilError(c, err)
  1542  
  1543  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1544  	assert.NilError(c, err)
  1545  
  1546  	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.31") {
  1547  		assert.Equal(c, *containerJSON.HostConfig.MemorySwappiness, int64(-1))
  1548  	} else {
  1549  		assert.Assert(c, containerJSON.HostConfig.MemorySwappiness == nil)
  1550  	}
  1551  }
  1552  
  1553  // check validation is done daemon side and not only in cli
  1554  func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *testing.T) {
  1555  	// OomScoreAdj is not supported on Windows
  1556  	testRequires(c, DaemonIsLinux)
  1557  
  1558  	config := containertypes.Config{
  1559  		Image: "busybox",
  1560  	}
  1561  
  1562  	hostConfig := containertypes.HostConfig{
  1563  		OomScoreAdj: 1001,
  1564  	}
  1565  
  1566  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1567  	assert.NilError(c, err)
  1568  	defer cli.Close()
  1569  
  1570  	name := "oomscoreadj-over"
  1571  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
  1572  
  1573  	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
  1574  	assert.ErrorContains(c, err, expected)
  1575  
  1576  	hostConfig = containertypes.HostConfig{
  1577  		OomScoreAdj: -1001,
  1578  	}
  1579  
  1580  	name = "oomscoreadj-low"
  1581  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, name)
  1582  
  1583  	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
  1584  	assert.ErrorContains(c, err, expected)
  1585  }
  1586  
  1587  // test case for #22210 where an empty container name caused panic.
  1588  func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *testing.T) {
  1589  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1590  	assert.NilError(c, err)
  1591  	defer cli.Close()
  1592  
  1593  	err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
  1594  	assert.ErrorContains(c, err, "No such container")
  1595  }
  1596  
  1597  func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *testing.T) {
  1598  	// Problematic on Windows as Windows does not support stats
  1599  	testRequires(c, DaemonIsLinux)
  1600  
  1601  	name := "testing-network-disabled"
  1602  
  1603  	config := containertypes.Config{
  1604  		Image:           "busybox",
  1605  		Cmd:             []string{"top"},
  1606  		NetworkDisabled: true,
  1607  	}
  1608  
  1609  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1610  	assert.NilError(c, err)
  1611  	defer cli.Close()
  1612  
  1613  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, nil, name)
  1614  	assert.NilError(c, err)
  1615  
  1616  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
  1617  	assert.NilError(c, err)
  1618  
  1619  	assert.Assert(c, waitRun(name) == nil)
  1620  
  1621  	type b struct {
  1622  		stats types.ContainerStats
  1623  		err   error
  1624  	}
  1625  	bc := make(chan b, 1)
  1626  	go func() {
  1627  		stats, err := cli.ContainerStats(context.Background(), name, false)
  1628  		bc <- b{stats, err}
  1629  	}()
  1630  
  1631  	// allow some time to stream the stats from the container
  1632  	time.Sleep(4 * time.Second)
  1633  	dockerCmd(c, "rm", "-f", name)
  1634  
  1635  	// collect the results from the stats stream or timeout and fail
  1636  	// if the stream was not disconnected.
  1637  	select {
  1638  	case <-time.After(2 * time.Second):
  1639  		c.Fatal("stream was not closed after container was removed")
  1640  	case sr := <-bc:
  1641  		assert.Assert(c, sr.err == nil)
  1642  		sr.stats.Body.Close()
  1643  	}
  1644  }
  1645  
  1646  func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *testing.T) {
  1647  	type testCase struct {
  1648  		config     containertypes.Config
  1649  		hostConfig containertypes.HostConfig
  1650  		msg        string
  1651  	}
  1652  
  1653  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1654  	destPath := prefix + slash + "foo"
  1655  	notExistPath := prefix + slash + "notexist"
  1656  
  1657  	cases := []testCase{
  1658  		{
  1659  			config: containertypes.Config{
  1660  				Image: "busybox",
  1661  			},
  1662  			hostConfig: containertypes.HostConfig{
  1663  				Mounts: []mounttypes.Mount{{
  1664  					Type:   "notreal",
  1665  					Target: destPath,
  1666  				},
  1667  				},
  1668  			},
  1669  
  1670  			msg: "mount type unknown",
  1671  		},
  1672  		{
  1673  			config: containertypes.Config{
  1674  				Image: "busybox",
  1675  			},
  1676  			hostConfig: containertypes.HostConfig{
  1677  				Mounts: []mounttypes.Mount{{
  1678  					Type: "bind"}}},
  1679  			msg: "Target must not be empty",
  1680  		},
  1681  		{
  1682  			config: containertypes.Config{
  1683  				Image: "busybox",
  1684  			},
  1685  			hostConfig: containertypes.HostConfig{
  1686  				Mounts: []mounttypes.Mount{{
  1687  					Type:   "bind",
  1688  					Target: destPath}}},
  1689  			msg: "Source must not be empty",
  1690  		},
  1691  		{
  1692  			config: containertypes.Config{
  1693  				Image: "busybox",
  1694  			},
  1695  			hostConfig: containertypes.HostConfig{
  1696  				Mounts: []mounttypes.Mount{{
  1697  					Type:   "bind",
  1698  					Source: notExistPath,
  1699  					Target: destPath}}},
  1700  			msg: "source path does not exist",
  1701  			// FIXME(vdemeester) fails into e2e, migrate to integration/container anyway
  1702  			// msg: "source path does not exist: " + notExistPath,
  1703  		},
  1704  		{
  1705  			config: containertypes.Config{
  1706  				Image: "busybox",
  1707  			},
  1708  			hostConfig: containertypes.HostConfig{
  1709  				Mounts: []mounttypes.Mount{{
  1710  					Type: "volume"}}},
  1711  			msg: "Target must not be empty",
  1712  		},
  1713  		{
  1714  			config: containertypes.Config{
  1715  				Image: "busybox",
  1716  			},
  1717  			hostConfig: containertypes.HostConfig{
  1718  				Mounts: []mounttypes.Mount{{
  1719  					Type:   "volume",
  1720  					Source: "hello",
  1721  					Target: destPath}}},
  1722  			msg: "",
  1723  		},
  1724  		{
  1725  			config: containertypes.Config{
  1726  				Image: "busybox",
  1727  			},
  1728  			hostConfig: containertypes.HostConfig{
  1729  				Mounts: []mounttypes.Mount{{
  1730  					Type:   "volume",
  1731  					Source: "hello2",
  1732  					Target: destPath,
  1733  					VolumeOptions: &mounttypes.VolumeOptions{
  1734  						DriverConfig: &mounttypes.Driver{
  1735  							Name: "local"}}}}},
  1736  			msg: "",
  1737  		},
  1738  	}
  1739  
  1740  	if testEnv.IsLocalDaemon() {
  1741  		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
  1742  		assert.NilError(c, err)
  1743  		defer os.RemoveAll(tmpDir)
  1744  		cases = append(cases, []testCase{
  1745  			{
  1746  				config: containertypes.Config{
  1747  					Image: "busybox",
  1748  				},
  1749  				hostConfig: containertypes.HostConfig{
  1750  					Mounts: []mounttypes.Mount{{
  1751  						Type:   "bind",
  1752  						Source: tmpDir,
  1753  						Target: destPath}}},
  1754  				msg: "",
  1755  			},
  1756  			{
  1757  				config: containertypes.Config{
  1758  					Image: "busybox",
  1759  				},
  1760  				hostConfig: containertypes.HostConfig{
  1761  					Mounts: []mounttypes.Mount{{
  1762  						Type:          "bind",
  1763  						Source:        tmpDir,
  1764  						Target:        destPath,
  1765  						VolumeOptions: &mounttypes.VolumeOptions{}}}},
  1766  				msg: "VolumeOptions must not be specified",
  1767  			},
  1768  		}...)
  1769  	}
  1770  
  1771  	if DaemonIsWindows() {
  1772  		cases = append(cases, []testCase{
  1773  			{
  1774  				config: containertypes.Config{
  1775  					Image: "busybox",
  1776  				},
  1777  				hostConfig: containertypes.HostConfig{
  1778  					Mounts: []mounttypes.Mount{
  1779  						{
  1780  							Type:   "volume",
  1781  							Source: "not-supported-on-windows",
  1782  							Target: destPath,
  1783  							VolumeOptions: &mounttypes.VolumeOptions{
  1784  								DriverConfig: &mounttypes.Driver{
  1785  									Name:    "local",
  1786  									Options: map[string]string{"type": "tmpfs"},
  1787  								},
  1788  							},
  1789  						},
  1790  					},
  1791  				},
  1792  				msg: `options are not supported on this platform`,
  1793  			},
  1794  		}...)
  1795  	}
  1796  
  1797  	if DaemonIsLinux() {
  1798  		cases = append(cases, []testCase{
  1799  			{
  1800  				config: containertypes.Config{
  1801  					Image: "busybox",
  1802  				},
  1803  				hostConfig: containertypes.HostConfig{
  1804  					Mounts: []mounttypes.Mount{
  1805  						{
  1806  							Type:   "volume",
  1807  							Source: "missing-device-opt",
  1808  							Target: destPath,
  1809  							VolumeOptions: &mounttypes.VolumeOptions{
  1810  								DriverConfig: &mounttypes.Driver{
  1811  									Name:    "local",
  1812  									Options: map[string]string{"foobar": "foobaz"},
  1813  								},
  1814  							},
  1815  						},
  1816  					},
  1817  				},
  1818  				msg: `invalid option: "foobar"`,
  1819  			},
  1820  			{
  1821  				config: containertypes.Config{
  1822  					Image: "busybox",
  1823  				},
  1824  				hostConfig: containertypes.HostConfig{
  1825  					Mounts: []mounttypes.Mount{
  1826  						{
  1827  							Type:   "volume",
  1828  							Source: "missing-device-opt",
  1829  							Target: destPath,
  1830  							VolumeOptions: &mounttypes.VolumeOptions{
  1831  								DriverConfig: &mounttypes.Driver{
  1832  									Name:    "local",
  1833  									Options: map[string]string{"type": "tmpfs"},
  1834  								},
  1835  							},
  1836  						},
  1837  					},
  1838  				},
  1839  				msg: `missing required option: "device"`,
  1840  			},
  1841  			{
  1842  				config: containertypes.Config{
  1843  					Image: "busybox",
  1844  				},
  1845  				hostConfig: containertypes.HostConfig{
  1846  					Mounts: []mounttypes.Mount{
  1847  						{
  1848  							Type:   "volume",
  1849  							Source: "missing-type-opt",
  1850  							Target: destPath,
  1851  							VolumeOptions: &mounttypes.VolumeOptions{
  1852  								DriverConfig: &mounttypes.Driver{
  1853  									Name:    "local",
  1854  									Options: map[string]string{"device": "tmpfs"},
  1855  								},
  1856  							},
  1857  						},
  1858  					},
  1859  				},
  1860  				msg: `missing required option: "type"`,
  1861  			},
  1862  			{
  1863  				config: containertypes.Config{
  1864  					Image: "busybox",
  1865  				},
  1866  				hostConfig: containertypes.HostConfig{
  1867  					Mounts: []mounttypes.Mount{
  1868  						{
  1869  							Type:   "volume",
  1870  							Source: "hello4",
  1871  							Target: destPath,
  1872  							VolumeOptions: &mounttypes.VolumeOptions{
  1873  								DriverConfig: &mounttypes.Driver{
  1874  									Name:    "local",
  1875  									Options: map[string]string{"o": "size=1", "type": "tmpfs", "device": "tmpfs"},
  1876  								},
  1877  							},
  1878  						},
  1879  					},
  1880  				},
  1881  				msg: "",
  1882  			},
  1883  			{
  1884  				config: containertypes.Config{
  1885  					Image: "busybox",
  1886  				},
  1887  				hostConfig: containertypes.HostConfig{
  1888  					Mounts: []mounttypes.Mount{{
  1889  						Type:   "tmpfs",
  1890  						Target: destPath}}},
  1891  				msg: "",
  1892  			},
  1893  			{
  1894  				config: containertypes.Config{
  1895  					Image: "busybox",
  1896  				},
  1897  				hostConfig: containertypes.HostConfig{
  1898  					Mounts: []mounttypes.Mount{{
  1899  						Type:   "tmpfs",
  1900  						Target: destPath,
  1901  						TmpfsOptions: &mounttypes.TmpfsOptions{
  1902  							SizeBytes: 4096 * 1024,
  1903  							Mode:      0700,
  1904  						}}}},
  1905  				msg: "",
  1906  			},
  1907  			{
  1908  				config: containertypes.Config{
  1909  					Image: "busybox",
  1910  				},
  1911  				hostConfig: containertypes.HostConfig{
  1912  					Mounts: []mounttypes.Mount{{
  1913  						Type:   "tmpfs",
  1914  						Source: "/shouldnotbespecified",
  1915  						Target: destPath}}},
  1916  				msg: "Source must not be specified",
  1917  			},
  1918  		}...)
  1919  
  1920  	}
  1921  	apiClient, err := client.NewClientWithOpts(client.FromEnv)
  1922  	assert.NilError(c, err)
  1923  	defer apiClient.Close()
  1924  
  1925  	// TODO add checks for statuscode returned by API
  1926  	for i, x := range cases {
  1927  		x := x
  1928  		c.Run(fmt.Sprintf("case %d", i), func(c *testing.T) {
  1929  			_, err = apiClient.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, nil, "")
  1930  			if len(x.msg) > 0 {
  1931  				assert.ErrorContains(c, err, x.msg, "%v", cases[i].config)
  1932  			} else {
  1933  				assert.NilError(c, err)
  1934  			}
  1935  		})
  1936  	}
  1937  }
  1938  
  1939  func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *testing.T) {
  1940  	testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon)
  1941  	// also with data in the host side
  1942  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1943  	destPath := prefix + slash + "foo"
  1944  	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
  1945  	assert.NilError(c, err)
  1946  	defer os.RemoveAll(tmpDir)
  1947  	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 0666)
  1948  	assert.NilError(c, err)
  1949  	config := containertypes.Config{
  1950  		Image: "busybox",
  1951  		Cmd:   []string{"/bin/sh", "-c", "cat /foo/bar"},
  1952  	}
  1953  	hostConfig := containertypes.HostConfig{
  1954  		Mounts: []mounttypes.Mount{
  1955  			{Type: "bind", Source: tmpDir, Target: destPath},
  1956  		},
  1957  	}
  1958  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1959  	assert.NilError(c, err)
  1960  	defer cli.Close()
  1961  
  1962  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, "test")
  1963  	assert.NilError(c, err)
  1964  
  1965  	out, _ := dockerCmd(c, "start", "-a", "test")
  1966  	assert.Equal(c, out, "hello")
  1967  }
  1968  
  1969  // Test Mounts comes out as expected for the MountPoint
  1970  func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *testing.T) {
  1971  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1972  	destPath := prefix + slash + "foo"
  1973  
  1974  	var (
  1975  		testImg string
  1976  	)
  1977  	if testEnv.OSType != "windows" {
  1978  		testImg = "test-mount-config"
  1979  		buildImageSuccessfully(c, testImg, build.WithDockerfile(`
  1980  	FROM busybox
  1981  	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
  1982  	CMD cat `+destPath+slash+`bar
  1983  	`))
  1984  	} else {
  1985  		testImg = "busybox"
  1986  	}
  1987  
  1988  	type testCase struct {
  1989  		spec     mounttypes.Mount
  1990  		expected types.MountPoint
  1991  	}
  1992  
  1993  	var selinuxSharedLabel string
  1994  	// this test label was added after a bug fix in 1.32, thus add requirements min API >= 1.32
  1995  	// for the sake of making test pass in earlier versions
  1996  	// bug fixed in https://github.com/moby/moby/pull/34684
  1997  	if !versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
  1998  		if runtime.GOOS == "linux" {
  1999  			selinuxSharedLabel = "z"
  2000  		}
  2001  	}
  2002  
  2003  	cases := []testCase{
  2004  		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
  2005  		// Validation of the actual `Mount` struct is done in another test is not needed here
  2006  		{
  2007  			spec:     mounttypes.Mount{Type: "volume", Target: destPath},
  2008  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2009  		},
  2010  		{
  2011  			spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash},
  2012  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2013  		},
  2014  		{
  2015  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
  2016  			expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2017  		},
  2018  		{
  2019  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
  2020  			expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  2021  		},
  2022  		{
  2023  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
  2024  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2025  		},
  2026  	}
  2027  
  2028  	if testEnv.IsLocalDaemon() {
  2029  		// setup temp dir for testing binds
  2030  		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
  2031  		assert.NilError(c, err)
  2032  		defer os.RemoveAll(tmpDir1)
  2033  		cases = append(cases, []testCase{
  2034  			{
  2035  				spec: mounttypes.Mount{
  2036  					Type:   "bind",
  2037  					Source: tmpDir1,
  2038  					Target: destPath,
  2039  				},
  2040  				expected: types.MountPoint{
  2041  					Type:        "bind",
  2042  					RW:          true,
  2043  					Destination: destPath,
  2044  					Source:      tmpDir1,
  2045  				},
  2046  			},
  2047  			{
  2048  				spec:     mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
  2049  				expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
  2050  			},
  2051  		}...)
  2052  
  2053  		// for modes only supported on Linux
  2054  		if DaemonIsLinux() {
  2055  			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
  2056  			assert.NilError(c, err)
  2057  			defer os.RemoveAll(tmpDir3)
  2058  
  2059  			assert.Assert(c, mount.Mount(tmpDir3, tmpDir3, "none", "bind,shared") == nil)
  2060  
  2061  			cases = append(cases, []testCase{
  2062  				{
  2063  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
  2064  					expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
  2065  				},
  2066  				{
  2067  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
  2068  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
  2069  				},
  2070  				{
  2071  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
  2072  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
  2073  				},
  2074  			}...)
  2075  		}
  2076  	}
  2077  
  2078  	if testEnv.OSType != "windows" { // Windows does not support volume populate
  2079  		cases = append(cases, []testCase{
  2080  			{
  2081  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2082  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2083  			},
  2084  			{
  2085  				spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2086  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2087  			},
  2088  			{
  2089  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2090  				expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2091  			},
  2092  			{
  2093  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2094  				expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  2095  			},
  2096  		}...)
  2097  	}
  2098  
  2099  	ctx := context.Background()
  2100  	apiclient := testEnv.APIClient()
  2101  	for i, x := range cases {
  2102  		x := x
  2103  		c.Run(fmt.Sprintf("%d config: %v", i, x.spec), func(c *testing.T) {
  2104  			container, err := apiclient.ContainerCreate(
  2105  				ctx,
  2106  				&containertypes.Config{Image: testImg},
  2107  				&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
  2108  				&networktypes.NetworkingConfig{},
  2109  				nil,
  2110  				"")
  2111  			assert.NilError(c, err)
  2112  
  2113  			containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
  2114  			assert.NilError(c, err)
  2115  			mps := containerInspect.Mounts
  2116  			assert.Assert(c, is.Len(mps, 1))
  2117  			mountPoint := mps[0]
  2118  
  2119  			if x.expected.Source != "" {
  2120  				assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source))
  2121  			}
  2122  			if x.expected.Name != "" {
  2123  				assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name))
  2124  			}
  2125  			if x.expected.Driver != "" {
  2126  				assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver))
  2127  			}
  2128  			if x.expected.Propagation != "" {
  2129  				assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation))
  2130  			}
  2131  			assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW))
  2132  			assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type))
  2133  			assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode))
  2134  			assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination))
  2135  
  2136  			err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
  2137  			assert.NilError(c, err)
  2138  			poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second))
  2139  
  2140  			err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
  2141  				RemoveVolumes: true,
  2142  				Force:         true,
  2143  			})
  2144  			assert.NilError(c, err)
  2145  
  2146  			switch {
  2147  
  2148  			// Named volumes still exist after the container is removed
  2149  			case x.spec.Type == "volume" && len(x.spec.Source) > 0:
  2150  				_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2151  				assert.NilError(c, err)
  2152  
  2153  			// Bind mounts are never removed with the container
  2154  			case x.spec.Type == "bind":
  2155  
  2156  			// anonymous volumes are removed
  2157  			default:
  2158  				_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2159  				assert.Check(c, client.IsErrNotFound(err))
  2160  			}
  2161  		})
  2162  	}
  2163  }
  2164  
  2165  func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result {
  2166  	return func(logT poll.LogT) poll.Result {
  2167  		container, err := apiclient.ContainerInspect(context.Background(), name)
  2168  		if err != nil {
  2169  			return poll.Error(err)
  2170  		}
  2171  		switch container.State.Status {
  2172  		case "created", "running":
  2173  			return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status)
  2174  		}
  2175  		return poll.Success()
  2176  	}
  2177  }
  2178  
  2179  func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *testing.T) {
  2180  	testRequires(c, DaemonIsLinux)
  2181  	type testCase struct {
  2182  		cfg             mounttypes.Mount
  2183  		expectedOptions []string
  2184  	}
  2185  	target := "/foo"
  2186  	cases := []testCase{
  2187  		{
  2188  			cfg: mounttypes.Mount{
  2189  				Type:   "tmpfs",
  2190  				Target: target},
  2191  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
  2192  		},
  2193  		{
  2194  			cfg: mounttypes.Mount{
  2195  				Type:   "tmpfs",
  2196  				Target: target,
  2197  				TmpfsOptions: &mounttypes.TmpfsOptions{
  2198  					SizeBytes: 4096 * 1024, Mode: 0700}},
  2199  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
  2200  		},
  2201  	}
  2202  
  2203  	cli, err := client.NewClientWithOpts(client.FromEnv)
  2204  	assert.NilError(c, err)
  2205  	defer cli.Close()
  2206  
  2207  	config := containertypes.Config{
  2208  		Image: "busybox",
  2209  		Cmd:   []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
  2210  	}
  2211  	for i, x := range cases {
  2212  		cName := fmt.Sprintf("test-tmpfs-%d", i)
  2213  		hostConfig := containertypes.HostConfig{
  2214  			Mounts: []mounttypes.Mount{x.cfg},
  2215  		}
  2216  
  2217  		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, nil, cName)
  2218  		assert.NilError(c, err)
  2219  		out, _ := dockerCmd(c, "start", "-a", cName)
  2220  		for _, option := range x.expectedOptions {
  2221  			assert.Assert(c, strings.Contains(out, option))
  2222  		}
  2223  	}
  2224  }
  2225  
  2226  // Regression test for #33334
  2227  // Makes sure that when a container which has a custom stop signal + restart=always
  2228  // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled.
  2229  func (s *DockerSuite) TestContainerKillCustomStopSignal(c *testing.T) {
  2230  	id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always"))
  2231  	res, _, err := request.Post("/containers/" + id + "/kill")
  2232  	assert.NilError(c, err)
  2233  	defer res.Body.Close()
  2234  
  2235  	b, err := ioutil.ReadAll(res.Body)
  2236  	assert.NilError(c, err)
  2237  	assert.Equal(c, res.StatusCode, http.StatusNoContent, string(b))
  2238  	err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second)
  2239  	assert.NilError(c, err)
  2240  }