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