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