github.com/jwhonce/docker@v0.6.7-0.20190327063223-da823cf3a5a3/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  	c.Assert(err, checker.IsNil)
    48  	defer cli.Close()
    49  
    50  	options := types.ContainerListOptions{
    51  		All: true,
    52  	}
    53  	containers, err := cli.ContainerList(context.Background(), options)
    54  	c.Assert(err, checker.IsNil)
    55  	c.Assert(containers, checker.HasLen, 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  	c.Assert(err, checker.IsNil)
    67  	defer cli.Close()
    68  
    69  	options := types.ContainerListOptions{
    70  		All: true,
    71  	}
    72  	containers, err := cli.ContainerList(context.Background(), options)
    73  	c.Assert(err, checker.IsNil)
    74  	c.Assert(containers, checker.HasLen, 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  	c.Assert(err, checker.IsNil)
   116  	defer cli.Close()
   117  
   118  	options := types.ContainerListOptions{
   119  		All: true,
   120  	}
   121  	containers, err := cli.ContainerList(context.Background(), options)
   122  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   148  	defer cli.Close()
   149  
   150  	body, err := cli.ContainerExport(context.Background(), name)
   151  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   175  	defer cli.Close()
   176  
   177  	changes, err := cli.ContainerDiff(context.Background(), name)
   178  	c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   205  		defer cli.Close()
   206  
   207  		stats, err := cli.ContainerStats(context.Background(), name, true)
   208  		c.Assert(err, checker.IsNil)
   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  		c.Assert(dec.Decode(&s), checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   239  	defer cli.Close()
   240  
   241  	stats, err := cli.ContainerStats(context.Background(), id, true)
   242  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   310  		defer cli.Close()
   311  
   312  		stats, err := cli.ContainerStats(context.Background(), name, true)
   313  		c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   352  		defer cli.Close()
   353  
   354  		stats, err := cli.ContainerStats(context.Background(), name, false)
   355  		c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   372  		s := string(b)
   373  		// count occurrences of `"read"` of types.Stats
   374  		c.Assert(strings.Count(s, `"read"`), checker.Equals, 1, check.Commentf("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  		c.Assert(err, checker.IsNil)
   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  		c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   417  	defer cli.Close()
   418  
   419  	err = cli.ContainerPause(context.Background(), ContainerID)
   420  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(waitRun(id), checker.IsNil)
   440  
   441  	cli, err := client.NewClientWithOpts(client.FromEnv)
   442  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(waitRun(id), checker.IsNil)
   463  
   464  	cli, err := client.NewClientWithOpts(client.FromEnv)
   465  	c.Assert(err, checker.IsNil)
   466  	defer cli.Close()
   467  
   468  	top, err := cli.ContainerTop(context.Background(), id, nil)
   469  	c.Assert(err, checker.IsNil)
   470  	c.Assert(top.Titles, checker.HasLen, 4, check.Commentf("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  	c.Assert(len(top.Processes), checker.GreaterOrEqualThan, 2, check.Commentf("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  	c.Assert(foundProcess, checker.Equals, true, check.Commentf("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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   564  	defer cli.Close()
   565  
   566  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
   567  	c.Assert(err.Error(), checker.Contains, `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  	c.Assert(err, checker.IsNil)
   578  	defer cli.Close()
   579  
   580  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
   581  	c.Assert(err, checker.IsNil)
   582  
   583  	out, _ := dockerCmd(c, "start", "-a", container.ID)
   584  	c.Assert(strings.TrimSpace(out), checker.Equals, "/test")
   585  }
   586  
   587  func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *check.C) {
   588  
   589  	cli, err := client.NewClientWithOpts(client.FromEnv)
   590  	c.Assert(err, checker.IsNil)
   591  	defer cli.Close()
   592  
   593  	_, err = cli.ContainerCreate(context.Background(), &containertypes.Config{}, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
   594  
   595  	expected := "No command specified"
   596  	c.Assert(err.Error(), checker.Contains, 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  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   637  	defer cli.Close()
   638  
   639  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
   640  	c.Assert(err, checker.IsNil)
   641  
   642  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
   643  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   673  	defer cli.Close()
   674  
   675  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
   676  	c.Assert(err, checker.IsNil)
   677  
   678  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
   679  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   700  	defer cli.Close()
   701  
   702  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
   703  	c.Assert(err, checker.IsNil)
   704  
   705  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
   706  	c.Assert(err, checker.IsNil)
   707  
   708  	out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
   709  	c.Assert(out, checker.Equals, "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  	c.Assert(err, checker.IsNil)
   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  		c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
   741  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   742  		c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
   751  	c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
   771  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   772  		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
   773  	} else {
   774  		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
   775  	}
   776  
   777  	b, err := request.ReadBody(body)
   778  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   795  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   796  		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
   797  	} else {
   798  		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
   799  	}
   800  
   801  	b, err := request.ReadBody(body)
   802  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   819  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   820  		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
   821  	} else {
   822  		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
   823  	}
   824  
   825  	b, err := request.ReadBody(body)
   826  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   843  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   844  		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
   845  	} else {
   846  		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
   847  	}
   848  
   849  	b, err := request.ReadBody(body)
   850  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   867  	c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
   898  	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
   899  
   900  	b, err := request.ReadBody(body)
   901  	c.Assert(err, checker.IsNil)
   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  	c.Assert(out, checker.Equals, "")
   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  	c.Assert(err, checker.IsNil)
   929  	b, err2 := request.ReadBody(body)
   930  	c.Assert(err2, checker.IsNil)
   931  
   932  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
   933  		c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
   948  	defer cli.Close()
   949  
   950  	err = cli.ContainerRename(context.Background(), containerID, newName)
   951  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   963  	defer cli.Close()
   964  
   965  	err = cli.ContainerKill(context.Background(), name, "SIGKILL")
   966  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
   977  	defer cli.Close()
   978  
   979  	timeout := 1 * time.Second
   980  	err = cli.ContainerRestart(context.Background(), name, &timeout)
   981  	c.Assert(err, checker.IsNil)
   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  	c.Assert(waitRun(id), checker.IsNil)
   991  
   992  	cli, err := client.NewClientWithOpts(client.FromEnv)
   993  	c.Assert(err, checker.IsNil)
   994  	defer cli.Close()
   995  
   996  	err = cli.ContainerRestart(context.Background(), name, nil)
   997  	c.Assert(err, checker.IsNil)
   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  	c.Assert(err, checker.IsNil)
  1012  	defer cli.Close()
  1013  
  1014  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
  1015  	c.Assert(err, checker.IsNil)
  1016  
  1017  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
  1018  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1035  	defer cli.Close()
  1036  
  1037  	err = cli.ContainerStop(context.Background(), name, &timeout)
  1038  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1058  	defer cli.Close()
  1059  
  1060  	waitresC, errC := cli.ContainerWait(context.Background(), name, "")
  1061  
  1062  	select {
  1063  	case err = <-errC:
  1064  		c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1080  	c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
  1094  	c.Assert(res.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
  1124  	if versions.GreaterThanOrEqualTo(testEnv.DaemonAPIVersion(), "1.32") {
  1125  		c.Assert(res.StatusCode, checker.Equals, http.StatusBadRequest)
  1126  	} else {
  1127  		c.Assert(res.StatusCode, checker.Not(checker.Equals), http.StatusOK)
  1128  	}
  1129  	b, err := request.ReadBody(body)
  1130  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1145  	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
  1146  		c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
  1147  	} else {
  1148  		c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
  1149  	}
  1150  	b, err := request.ReadBody(body)
  1151  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1163  	c.Assert(res.StatusCode, checker.Equals, http.StatusNotFound)
  1164  }
  1165  
  1166  func (s *DockerSuite) TestContainerAPIDelete(c *check.C) {
  1167  	out := runSleepingContainer(c)
  1168  
  1169  	id := strings.TrimSpace(out)
  1170  	c.Assert(waitRun(id), checker.IsNil)
  1171  
  1172  	dockerCmd(c, "stop", id)
  1173  
  1174  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1175  	c.Assert(err, checker.IsNil)
  1176  	defer cli.Close()
  1177  
  1178  	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
  1179  	c.Assert(err, checker.IsNil)
  1180  }
  1181  
  1182  func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) {
  1183  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1184  	c.Assert(err, checker.IsNil)
  1185  	defer cli.Close()
  1186  
  1187  	err = cli.ContainerRemove(context.Background(), "doesnotexist", types.ContainerRemoveOptions{})
  1188  	c.Assert(err.Error(), checker.Contains, "No such container: doesnotexist")
  1189  }
  1190  
  1191  func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) {
  1192  	out := runSleepingContainer(c)
  1193  	id := strings.TrimSpace(out)
  1194  	c.Assert(waitRun(id), checker.IsNil)
  1195  
  1196  	removeOptions := types.ContainerRemoveOptions{
  1197  		Force: true,
  1198  	}
  1199  
  1200  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1201  	c.Assert(err, checker.IsNil)
  1202  	defer cli.Close()
  1203  
  1204  	err = cli.ContainerRemove(context.Background(), id, removeOptions)
  1205  	c.Assert(err, checker.IsNil)
  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  	c.Assert(waitRun(id), checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1230  	defer cli.Close()
  1231  
  1232  	err = cli.ContainerRemove(context.Background(), "tlink2/tlink1", removeOptions)
  1233  	c.Assert(err, check.IsNil)
  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  	c.Assert(waitRun(id), checker.IsNil)
  1244  
  1245  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1246  	c.Assert(err, checker.IsNil)
  1247  	defer cli.Close()
  1248  
  1249  	err = cli.ContainerRemove(context.Background(), id, types.ContainerRemoveOptions{})
  1250  	expected := "cannot remove a running container"
  1251  	c.Assert(err.Error(), checker.Contains, 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  	c.Assert(waitRun(id), checker.IsNil)
  1266  
  1267  	source, err := inspectMountSourceField(id, vol)
  1268  	c.Assert(err, checker.IsNil)
  1269  	_, err = os.Stat(source)
  1270  	c.Assert(err, checker.IsNil)
  1271  
  1272  	removeOptions := types.ContainerRemoveOptions{
  1273  		Force:         true,
  1274  		RemoveVolumes: true,
  1275  	}
  1276  
  1277  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1278  	c.Assert(err, checker.IsNil)
  1279  	defer cli.Close()
  1280  
  1281  	err = cli.ContainerRemove(context.Background(), id, removeOptions)
  1282  	c.Assert(err, check.IsNil)
  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  	c.Assert(resp.StatusCode, checker.Equals, 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  	c.Assert(err, checker.IsNil)
  1317  	defer cli.Close()
  1318  
  1319  	err = cli.ContainerStop(context.Background(), containerID, nil)
  1320  	c.Assert(err, checker.IsNil)
  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  	c.Assert(err, checker.IsNil)
  1334  	defer cli.Close()
  1335  
  1336  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
  1337  	c.Assert(err, checker.IsNil)
  1338  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1339  	c.Assert(strings.TrimSpace(out), checker.Equals, "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  	c.Assert(err, checker.IsNil)
  1348  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1349  	c.Assert(strings.TrimSpace(out), checker.Equals, "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  	c.Assert(err, checker.IsNil)
  1361  	defer cli.Close()
  1362  
  1363  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "echotest")
  1364  	c.Assert(err, checker.IsNil)
  1365  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1366  	c.Assert(strings.TrimSpace(out), checker.Equals, "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  	c.Assert(err, checker.IsNil)
  1375  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1376  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1377  }
  1378  
  1379  // regression #14318
  1380  // for backward compatibility testing with and without CAP_ prefix
  1381  // and with upper and lowercase
  1382  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
  1383  	// Windows doesn't support CapAdd/CapDrop
  1384  	testRequires(c, DaemonIsLinux)
  1385  	config := struct {
  1386  		Image   string
  1387  		CapAdd  string
  1388  		CapDrop string
  1389  	}{"busybox", "NET_ADMIN", "cap_sys_admin"}
  1390  	res, _, err := request.Post("/containers/create?name=capaddtest0", request.JSONBody(config))
  1391  	c.Assert(err, checker.IsNil)
  1392  	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
  1393  
  1394  	config2 := containertypes.Config{
  1395  		Image: "busybox",
  1396  	}
  1397  	hostConfig := containertypes.HostConfig{
  1398  		CapAdd:  []string{"net_admin", "SYS_ADMIN"},
  1399  		CapDrop: []string{"SETGID", "CAP_SETPCAP"},
  1400  	}
  1401  
  1402  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1403  	c.Assert(err, checker.IsNil)
  1404  	defer cli.Close()
  1405  
  1406  	_, err = cli.ContainerCreate(context.Background(), &config2, &hostConfig, &networktypes.NetworkingConfig{}, "capaddtest1")
  1407  	c.Assert(err, checker.IsNil)
  1408  }
  1409  
  1410  // #14915
  1411  func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) {
  1412  	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
  1413  	config := containertypes.Config{
  1414  		Image: "busybox",
  1415  	}
  1416  
  1417  	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.18"))
  1418  	c.Assert(err, checker.IsNil)
  1419  
  1420  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1421  	c.Assert(err, checker.IsNil)
  1422  }
  1423  
  1424  // Ensure an error occurs when you have a container read-only rootfs but you
  1425  // extract an archive to a symlink in a writable volume which points to a
  1426  // directory outside of the volume.
  1427  func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
  1428  	// Windows does not support read-only rootfs
  1429  	// Requires local volume mount bind.
  1430  	// --read-only + userns has remount issues
  1431  	testRequires(c, testEnv.IsLocalDaemon, NotUserNamespace, DaemonIsLinux)
  1432  
  1433  	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1434  	defer os.RemoveAll(testVol)
  1435  
  1436  	makeTestContentInDir(c, testVol)
  1437  
  1438  	cID := makeTestContainer(c, testContainerOptions{
  1439  		readOnly: true,
  1440  		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
  1441  	})
  1442  
  1443  	// Attempt to extract to a symlink in the volume which points to a
  1444  	// directory outside the volume. This should cause an error because the
  1445  	// rootfs is read-only.
  1446  	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithVersion("v1.20"))
  1447  	c.Assert(err, checker.IsNil)
  1448  
  1449  	err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{})
  1450  	c.Assert(err.Error(), checker.Contains, "container rootfs is marked read-only")
  1451  }
  1452  
  1453  func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
  1454  	// Not supported on Windows
  1455  	testRequires(c, DaemonIsLinux)
  1456  
  1457  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1458  	c.Assert(err, checker.IsNil)
  1459  	defer cli.Close()
  1460  
  1461  	config := containertypes.Config{
  1462  		Image: "busybox",
  1463  	}
  1464  	hostConfig1 := containertypes.HostConfig{
  1465  		Resources: containertypes.Resources{
  1466  			CpusetCpus: "1-42,,",
  1467  		},
  1468  	}
  1469  	name := "wrong-cpuset-cpus"
  1470  
  1471  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name)
  1472  	expected := "Invalid value 1-42,, for cpuset cpus"
  1473  	c.Assert(err.Error(), checker.Contains, expected)
  1474  
  1475  	hostConfig2 := containertypes.HostConfig{
  1476  		Resources: containertypes.Resources{
  1477  			CpusetMems: "42-3,1--",
  1478  		},
  1479  	}
  1480  	name = "wrong-cpuset-mems"
  1481  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name)
  1482  	expected = "Invalid value 42-3,1-- for cpuset mems"
  1483  	c.Assert(err.Error(), checker.Contains, expected)
  1484  }
  1485  
  1486  func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
  1487  	// ShmSize is not supported on Windows
  1488  	testRequires(c, DaemonIsLinux)
  1489  	config := containertypes.Config{
  1490  		Image: "busybox",
  1491  	}
  1492  	hostConfig := containertypes.HostConfig{
  1493  		ShmSize: -1,
  1494  	}
  1495  
  1496  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1497  	c.Assert(err, checker.IsNil)
  1498  	defer cli.Close()
  1499  
  1500  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
  1501  	c.Assert(err.Error(), checker.Contains, "SHM size can not be less than 0")
  1502  }
  1503  
  1504  func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
  1505  	// ShmSize is not supported on Windows
  1506  	testRequires(c, DaemonIsLinux)
  1507  	var defaultSHMSize int64 = 67108864
  1508  	config := containertypes.Config{
  1509  		Image: "busybox",
  1510  		Cmd:   []string{"mount"},
  1511  	}
  1512  
  1513  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1514  	c.Assert(err, checker.IsNil)
  1515  	defer cli.Close()
  1516  
  1517  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1518  	c.Assert(err, check.IsNil)
  1519  
  1520  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1521  	c.Assert(err, check.IsNil)
  1522  
  1523  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
  1524  
  1525  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1526  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1527  	if !shmRegexp.MatchString(out) {
  1528  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1529  	}
  1530  }
  1531  
  1532  func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
  1533  	// ShmSize is not supported on Windows
  1534  	testRequires(c, DaemonIsLinux)
  1535  	config := containertypes.Config{
  1536  		Image: "busybox",
  1537  		Cmd:   []string{"mount"},
  1538  	}
  1539  
  1540  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1541  	c.Assert(err, checker.IsNil)
  1542  	defer cli.Close()
  1543  
  1544  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1545  	c.Assert(err, check.IsNil)
  1546  
  1547  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1548  	c.Assert(err, check.IsNil)
  1549  
  1550  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
  1551  
  1552  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1553  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1554  	if !shmRegexp.MatchString(out) {
  1555  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1556  	}
  1557  }
  1558  
  1559  func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
  1560  	// ShmSize is not supported on Windows
  1561  	testRequires(c, DaemonIsLinux)
  1562  	config := containertypes.Config{
  1563  		Image: "busybox",
  1564  		Cmd:   []string{"mount"},
  1565  	}
  1566  
  1567  	hostConfig := containertypes.HostConfig{
  1568  		ShmSize: 1073741824,
  1569  	}
  1570  
  1571  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1572  	c.Assert(err, checker.IsNil)
  1573  	defer cli.Close()
  1574  
  1575  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
  1576  	c.Assert(err, check.IsNil)
  1577  
  1578  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1579  	c.Assert(err, check.IsNil)
  1580  
  1581  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
  1582  
  1583  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1584  	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1585  	if !shmRegex.MatchString(out) {
  1586  		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1587  	}
  1588  }
  1589  
  1590  func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
  1591  	// Swappiness is not supported on Windows
  1592  	testRequires(c, DaemonIsLinux)
  1593  	config := containertypes.Config{
  1594  		Image: "busybox",
  1595  	}
  1596  
  1597  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1598  	c.Assert(err, checker.IsNil)
  1599  	defer cli.Close()
  1600  
  1601  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1602  	c.Assert(err, check.IsNil)
  1603  
  1604  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1605  	c.Assert(err, check.IsNil)
  1606  
  1607  	if versions.LessThan(testEnv.DaemonAPIVersion(), "1.31") {
  1608  		c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
  1609  	} else {
  1610  		c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil)
  1611  	}
  1612  }
  1613  
  1614  // check validation is done daemon side and not only in cli
  1615  func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
  1616  	// OomScoreAdj is not supported on Windows
  1617  	testRequires(c, DaemonIsLinux)
  1618  
  1619  	config := containertypes.Config{
  1620  		Image: "busybox",
  1621  	}
  1622  
  1623  	hostConfig := containertypes.HostConfig{
  1624  		OomScoreAdj: 1001,
  1625  	}
  1626  
  1627  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1628  	c.Assert(err, checker.IsNil)
  1629  	defer cli.Close()
  1630  
  1631  	name := "oomscoreadj-over"
  1632  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
  1633  
  1634  	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
  1635  	c.Assert(err.Error(), checker.Contains, expected)
  1636  
  1637  	hostConfig = containertypes.HostConfig{
  1638  		OomScoreAdj: -1001,
  1639  	}
  1640  
  1641  	name = "oomscoreadj-low"
  1642  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
  1643  
  1644  	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
  1645  	c.Assert(err.Error(), checker.Contains, expected)
  1646  }
  1647  
  1648  // test case for #22210 where an empty container name caused panic.
  1649  func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
  1650  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1651  	c.Assert(err, checker.IsNil)
  1652  	defer cli.Close()
  1653  
  1654  	err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
  1655  	c.Assert(err.Error(), checker.Contains, "No such container")
  1656  }
  1657  
  1658  func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
  1659  	// Problematic on Windows as Windows does not support stats
  1660  	testRequires(c, DaemonIsLinux)
  1661  
  1662  	name := "testing-network-disabled"
  1663  
  1664  	config := containertypes.Config{
  1665  		Image:           "busybox",
  1666  		Cmd:             []string{"top"},
  1667  		NetworkDisabled: true,
  1668  	}
  1669  
  1670  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1671  	c.Assert(err, checker.IsNil)
  1672  	defer cli.Close()
  1673  
  1674  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
  1675  	c.Assert(err, checker.IsNil)
  1676  
  1677  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
  1678  	c.Assert(err, checker.IsNil)
  1679  
  1680  	c.Assert(waitRun(name), check.IsNil)
  1681  
  1682  	type b struct {
  1683  		stats types.ContainerStats
  1684  		err   error
  1685  	}
  1686  	bc := make(chan b, 1)
  1687  	go func() {
  1688  		stats, err := cli.ContainerStats(context.Background(), name, false)
  1689  		bc <- b{stats, err}
  1690  	}()
  1691  
  1692  	// allow some time to stream the stats from the container
  1693  	time.Sleep(4 * time.Second)
  1694  	dockerCmd(c, "rm", "-f", name)
  1695  
  1696  	// collect the results from the stats stream or timeout and fail
  1697  	// if the stream was not disconnected.
  1698  	select {
  1699  	case <-time.After(2 * time.Second):
  1700  		c.Fatal("stream was not closed after container was removed")
  1701  	case sr := <-bc:
  1702  		c.Assert(sr.err, checker.IsNil)
  1703  		sr.stats.Body.Close()
  1704  	}
  1705  }
  1706  
  1707  func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
  1708  	type testCase struct {
  1709  		config     containertypes.Config
  1710  		hostConfig containertypes.HostConfig
  1711  		msg        string
  1712  	}
  1713  
  1714  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1715  	destPath := prefix + slash + "foo"
  1716  	notExistPath := prefix + slash + "notexist"
  1717  
  1718  	cases := []testCase{
  1719  		{
  1720  			config: containertypes.Config{
  1721  				Image: "busybox",
  1722  			},
  1723  			hostConfig: containertypes.HostConfig{
  1724  				Mounts: []mounttypes.Mount{{
  1725  					Type:   "notreal",
  1726  					Target: destPath,
  1727  				},
  1728  				},
  1729  			},
  1730  
  1731  			msg: "mount type unknown",
  1732  		},
  1733  		{
  1734  			config: containertypes.Config{
  1735  				Image: "busybox",
  1736  			},
  1737  			hostConfig: containertypes.HostConfig{
  1738  				Mounts: []mounttypes.Mount{{
  1739  					Type: "bind"}}},
  1740  			msg: "Target must not be empty",
  1741  		},
  1742  		{
  1743  			config: containertypes.Config{
  1744  				Image: "busybox",
  1745  			},
  1746  			hostConfig: containertypes.HostConfig{
  1747  				Mounts: []mounttypes.Mount{{
  1748  					Type:   "bind",
  1749  					Target: destPath}}},
  1750  			msg: "Source must not be empty",
  1751  		},
  1752  		{
  1753  			config: containertypes.Config{
  1754  				Image: "busybox",
  1755  			},
  1756  			hostConfig: containertypes.HostConfig{
  1757  				Mounts: []mounttypes.Mount{{
  1758  					Type:   "bind",
  1759  					Source: notExistPath,
  1760  					Target: destPath}}},
  1761  			msg: "source path does not exist",
  1762  			// FIXME(vdemeester) fails into e2e, migrate to integration/container anyway
  1763  			// msg: "source path does not exist: " + notExistPath,
  1764  		},
  1765  		{
  1766  			config: containertypes.Config{
  1767  				Image: "busybox",
  1768  			},
  1769  			hostConfig: containertypes.HostConfig{
  1770  				Mounts: []mounttypes.Mount{{
  1771  					Type: "volume"}}},
  1772  			msg: "Target must not be empty",
  1773  		},
  1774  		{
  1775  			config: containertypes.Config{
  1776  				Image: "busybox",
  1777  			},
  1778  			hostConfig: containertypes.HostConfig{
  1779  				Mounts: []mounttypes.Mount{{
  1780  					Type:   "volume",
  1781  					Source: "hello",
  1782  					Target: destPath}}},
  1783  			msg: "",
  1784  		},
  1785  		{
  1786  			config: containertypes.Config{
  1787  				Image: "busybox",
  1788  			},
  1789  			hostConfig: containertypes.HostConfig{
  1790  				Mounts: []mounttypes.Mount{{
  1791  					Type:   "volume",
  1792  					Source: "hello2",
  1793  					Target: destPath,
  1794  					VolumeOptions: &mounttypes.VolumeOptions{
  1795  						DriverConfig: &mounttypes.Driver{
  1796  							Name: "local"}}}}},
  1797  			msg: "",
  1798  		},
  1799  	}
  1800  
  1801  	if testEnv.IsLocalDaemon() {
  1802  		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
  1803  		c.Assert(err, checker.IsNil)
  1804  		defer os.RemoveAll(tmpDir)
  1805  		cases = append(cases, []testCase{
  1806  			{
  1807  				config: containertypes.Config{
  1808  					Image: "busybox",
  1809  				},
  1810  				hostConfig: containertypes.HostConfig{
  1811  					Mounts: []mounttypes.Mount{{
  1812  						Type:   "bind",
  1813  						Source: tmpDir,
  1814  						Target: destPath}}},
  1815  				msg: "",
  1816  			},
  1817  			{
  1818  				config: containertypes.Config{
  1819  					Image: "busybox",
  1820  				},
  1821  				hostConfig: containertypes.HostConfig{
  1822  					Mounts: []mounttypes.Mount{{
  1823  						Type:          "bind",
  1824  						Source:        tmpDir,
  1825  						Target:        destPath,
  1826  						VolumeOptions: &mounttypes.VolumeOptions{}}}},
  1827  				msg: "VolumeOptions must not be specified",
  1828  			},
  1829  		}...)
  1830  	}
  1831  
  1832  	if DaemonIsWindows() {
  1833  		cases = append(cases, []testCase{
  1834  			{
  1835  				config: containertypes.Config{
  1836  					Image: "busybox",
  1837  				},
  1838  				hostConfig: containertypes.HostConfig{
  1839  					Mounts: []mounttypes.Mount{
  1840  						{
  1841  							Type:   "volume",
  1842  							Source: "not-supported-on-windows",
  1843  							Target: destPath,
  1844  							VolumeOptions: &mounttypes.VolumeOptions{
  1845  								DriverConfig: &mounttypes.Driver{
  1846  									Name:    "local",
  1847  									Options: map[string]string{"type": "tmpfs"},
  1848  								},
  1849  							},
  1850  						},
  1851  					},
  1852  				},
  1853  				msg: `options are not supported on this platform`,
  1854  			},
  1855  		}...)
  1856  	}
  1857  
  1858  	if DaemonIsLinux() {
  1859  		cases = append(cases, []testCase{
  1860  			{
  1861  				config: containertypes.Config{
  1862  					Image: "busybox",
  1863  				},
  1864  				hostConfig: containertypes.HostConfig{
  1865  					Mounts: []mounttypes.Mount{
  1866  						{
  1867  							Type:   "volume",
  1868  							Source: "missing-device-opt",
  1869  							Target: destPath,
  1870  							VolumeOptions: &mounttypes.VolumeOptions{
  1871  								DriverConfig: &mounttypes.Driver{
  1872  									Name:    "local",
  1873  									Options: map[string]string{"foobar": "foobaz"},
  1874  								},
  1875  							},
  1876  						},
  1877  					},
  1878  				},
  1879  				msg: `invalid option: "foobar"`,
  1880  			},
  1881  			{
  1882  				config: containertypes.Config{
  1883  					Image: "busybox",
  1884  				},
  1885  				hostConfig: containertypes.HostConfig{
  1886  					Mounts: []mounttypes.Mount{
  1887  						{
  1888  							Type:   "volume",
  1889  							Source: "missing-device-opt",
  1890  							Target: destPath,
  1891  							VolumeOptions: &mounttypes.VolumeOptions{
  1892  								DriverConfig: &mounttypes.Driver{
  1893  									Name:    "local",
  1894  									Options: map[string]string{"type": "tmpfs"},
  1895  								},
  1896  							},
  1897  						},
  1898  					},
  1899  				},
  1900  				msg: `missing required option: "device"`,
  1901  			},
  1902  			{
  1903  				config: containertypes.Config{
  1904  					Image: "busybox",
  1905  				},
  1906  				hostConfig: containertypes.HostConfig{
  1907  					Mounts: []mounttypes.Mount{
  1908  						{
  1909  							Type:   "volume",
  1910  							Source: "missing-type-opt",
  1911  							Target: destPath,
  1912  							VolumeOptions: &mounttypes.VolumeOptions{
  1913  								DriverConfig: &mounttypes.Driver{
  1914  									Name:    "local",
  1915  									Options: map[string]string{"device": "tmpfs"},
  1916  								},
  1917  							},
  1918  						},
  1919  					},
  1920  				},
  1921  				msg: `missing required option: "type"`,
  1922  			},
  1923  			{
  1924  				config: containertypes.Config{
  1925  					Image: "busybox",
  1926  				},
  1927  				hostConfig: containertypes.HostConfig{
  1928  					Mounts: []mounttypes.Mount{
  1929  						{
  1930  							Type:   "volume",
  1931  							Source: "hello4",
  1932  							Target: destPath,
  1933  							VolumeOptions: &mounttypes.VolumeOptions{
  1934  								DriverConfig: &mounttypes.Driver{
  1935  									Name:    "local",
  1936  									Options: map[string]string{"o": "size=1", "type": "tmpfs", "device": "tmpfs"},
  1937  								},
  1938  							},
  1939  						},
  1940  					},
  1941  				},
  1942  				msg: "",
  1943  			},
  1944  			{
  1945  				config: containertypes.Config{
  1946  					Image: "busybox",
  1947  				},
  1948  				hostConfig: containertypes.HostConfig{
  1949  					Mounts: []mounttypes.Mount{{
  1950  						Type:   "tmpfs",
  1951  						Target: destPath}}},
  1952  				msg: "",
  1953  			},
  1954  			{
  1955  				config: containertypes.Config{
  1956  					Image: "busybox",
  1957  				},
  1958  				hostConfig: containertypes.HostConfig{
  1959  					Mounts: []mounttypes.Mount{{
  1960  						Type:   "tmpfs",
  1961  						Target: destPath,
  1962  						TmpfsOptions: &mounttypes.TmpfsOptions{
  1963  							SizeBytes: 4096 * 1024,
  1964  							Mode:      0700,
  1965  						}}}},
  1966  				msg: "",
  1967  			},
  1968  			{
  1969  				config: containertypes.Config{
  1970  					Image: "busybox",
  1971  				},
  1972  				hostConfig: containertypes.HostConfig{
  1973  					Mounts: []mounttypes.Mount{{
  1974  						Type:   "tmpfs",
  1975  						Source: "/shouldnotbespecified",
  1976  						Target: destPath}}},
  1977  				msg: "Source must not be specified",
  1978  			},
  1979  		}...)
  1980  
  1981  	}
  1982  	cli, err := client.NewClientWithOpts(client.FromEnv)
  1983  	c.Assert(err, checker.IsNil)
  1984  	defer cli.Close()
  1985  
  1986  	// TODO add checks for statuscode returned by API
  1987  	for i, x := range cases {
  1988  		c.Logf("case %d", i)
  1989  		_, err = cli.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "")
  1990  		if len(x.msg) > 0 {
  1991  			c.Assert(err.Error(), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
  1992  		} else {
  1993  			c.Assert(err, checker.IsNil)
  1994  		}
  1995  	}
  1996  }
  1997  
  1998  func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
  1999  	testRequires(c, NotUserNamespace, testEnv.IsLocalDaemon)
  2000  	// also with data in the host side
  2001  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  2002  	destPath := prefix + slash + "foo"
  2003  	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
  2004  	c.Assert(err, checker.IsNil)
  2005  	defer os.RemoveAll(tmpDir)
  2006  	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
  2007  	c.Assert(err, checker.IsNil)
  2008  	config := containertypes.Config{
  2009  		Image: "busybox",
  2010  		Cmd:   []string{"/bin/sh", "-c", "cat /foo/bar"},
  2011  	}
  2012  	hostConfig := containertypes.HostConfig{
  2013  		Mounts: []mounttypes.Mount{
  2014  			{Type: "bind", Source: tmpDir, Target: destPath},
  2015  		},
  2016  	}
  2017  	cli, err := client.NewClientWithOpts(client.FromEnv)
  2018  	c.Assert(err, checker.IsNil)
  2019  	defer cli.Close()
  2020  
  2021  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test")
  2022  	c.Assert(err, checker.IsNil)
  2023  
  2024  	out, _ := dockerCmd(c, "start", "-a", "test")
  2025  	c.Assert(out, checker.Equals, "hello")
  2026  }
  2027  
  2028  // Test Mounts comes out as expected for the MountPoint
  2029  func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
  2030  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  2031  	destPath := prefix + slash + "foo"
  2032  
  2033  	var (
  2034  		testImg string
  2035  	)
  2036  	if testEnv.OSType != "windows" {
  2037  		testImg = "test-mount-config"
  2038  		buildImageSuccessfully(c, testImg, build.WithDockerfile(`
  2039  	FROM busybox
  2040  	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
  2041  	CMD cat `+destPath+slash+`bar
  2042  	`))
  2043  	} else {
  2044  		testImg = "busybox"
  2045  	}
  2046  
  2047  	type testCase struct {
  2048  		spec     mounttypes.Mount
  2049  		expected types.MountPoint
  2050  	}
  2051  
  2052  	var selinuxSharedLabel string
  2053  	// this test label was added after a bug fix in 1.32, thus add requirements min API >= 1.32
  2054  	// for the sake of making test pass in earlier versions
  2055  	// bug fixed in https://github.com/moby/moby/pull/34684
  2056  	if !versions.LessThan(testEnv.DaemonAPIVersion(), "1.32") {
  2057  		if runtime.GOOS == "linux" {
  2058  			selinuxSharedLabel = "z"
  2059  		}
  2060  	}
  2061  
  2062  	cases := []testCase{
  2063  		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
  2064  		// Validation of the actual `Mount` struct is done in another test is not needed here
  2065  		{
  2066  			spec:     mounttypes.Mount{Type: "volume", Target: destPath},
  2067  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2068  		},
  2069  		{
  2070  			spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash},
  2071  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2072  		},
  2073  		{
  2074  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
  2075  			expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2076  		},
  2077  		{
  2078  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
  2079  			expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  2080  		},
  2081  		{
  2082  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
  2083  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2084  		},
  2085  	}
  2086  
  2087  	if testEnv.IsLocalDaemon() {
  2088  		// setup temp dir for testing binds
  2089  		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
  2090  		c.Assert(err, checker.IsNil)
  2091  		defer os.RemoveAll(tmpDir1)
  2092  		cases = append(cases, []testCase{
  2093  			{
  2094  				spec: mounttypes.Mount{
  2095  					Type:   "bind",
  2096  					Source: tmpDir1,
  2097  					Target: destPath,
  2098  				},
  2099  				expected: types.MountPoint{
  2100  					Type:        "bind",
  2101  					RW:          true,
  2102  					Destination: destPath,
  2103  					Source:      tmpDir1,
  2104  				},
  2105  			},
  2106  			{
  2107  				spec:     mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
  2108  				expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
  2109  			},
  2110  		}...)
  2111  
  2112  		// for modes only supported on Linux
  2113  		if DaemonIsLinux() {
  2114  			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
  2115  			c.Assert(err, checker.IsNil)
  2116  			defer os.RemoveAll(tmpDir3)
  2117  
  2118  			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
  2119  			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
  2120  
  2121  			cases = append(cases, []testCase{
  2122  				{
  2123  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
  2124  					expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
  2125  				},
  2126  				{
  2127  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
  2128  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
  2129  				},
  2130  				{
  2131  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
  2132  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
  2133  				},
  2134  			}...)
  2135  		}
  2136  	}
  2137  
  2138  	if testEnv.OSType != "windows" { // Windows does not support volume populate
  2139  		cases = append(cases, []testCase{
  2140  			{
  2141  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2142  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2143  			},
  2144  			{
  2145  				spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2146  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2147  			},
  2148  			{
  2149  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2150  				expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2151  			},
  2152  			{
  2153  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2154  				expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  2155  			},
  2156  		}...)
  2157  	}
  2158  
  2159  	type wrapper struct {
  2160  		containertypes.Config
  2161  		HostConfig containertypes.HostConfig
  2162  	}
  2163  	type createResp struct {
  2164  		ID string `json:"Id"`
  2165  	}
  2166  
  2167  	ctx := context.Background()
  2168  	apiclient := testEnv.APIClient()
  2169  	for i, x := range cases {
  2170  		c.Logf("case %d - config: %v", i, x.spec)
  2171  		container, err := apiclient.ContainerCreate(
  2172  			ctx,
  2173  			&containertypes.Config{Image: testImg},
  2174  			&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
  2175  			&networktypes.NetworkingConfig{},
  2176  			"")
  2177  		assert.NilError(c, err)
  2178  
  2179  		containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
  2180  		assert.NilError(c, err)
  2181  		mps := containerInspect.Mounts
  2182  		assert.Assert(c, is.Len(mps, 1))
  2183  		mountPoint := mps[0]
  2184  
  2185  		if x.expected.Source != "" {
  2186  			assert.Check(c, is.Equal(x.expected.Source, mountPoint.Source))
  2187  		}
  2188  		if x.expected.Name != "" {
  2189  			assert.Check(c, is.Equal(x.expected.Name, mountPoint.Name))
  2190  		}
  2191  		if x.expected.Driver != "" {
  2192  			assert.Check(c, is.Equal(x.expected.Driver, mountPoint.Driver))
  2193  		}
  2194  		if x.expected.Propagation != "" {
  2195  			assert.Check(c, is.Equal(x.expected.Propagation, mountPoint.Propagation))
  2196  		}
  2197  		assert.Check(c, is.Equal(x.expected.RW, mountPoint.RW))
  2198  		assert.Check(c, is.Equal(x.expected.Type, mountPoint.Type))
  2199  		assert.Check(c, is.Equal(x.expected.Mode, mountPoint.Mode))
  2200  		assert.Check(c, is.Equal(x.expected.Destination, mountPoint.Destination))
  2201  
  2202  		err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
  2203  		assert.NilError(c, err)
  2204  		poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second))
  2205  
  2206  		err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
  2207  			RemoveVolumes: true,
  2208  			Force:         true,
  2209  		})
  2210  		assert.NilError(c, err)
  2211  
  2212  		switch {
  2213  
  2214  		// Named volumes still exist after the container is removed
  2215  		case x.spec.Type == "volume" && len(x.spec.Source) > 0:
  2216  			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2217  			assert.NilError(c, err)
  2218  
  2219  		// Bind mounts are never removed with the container
  2220  		case x.spec.Type == "bind":
  2221  
  2222  		// anonymous volumes are removed
  2223  		default:
  2224  			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2225  			assert.Check(c, client.IsErrNotFound(err))
  2226  		}
  2227  	}
  2228  }
  2229  
  2230  func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result {
  2231  	return func(logT poll.LogT) poll.Result {
  2232  		container, err := apiclient.ContainerInspect(context.Background(), name)
  2233  		if err != nil {
  2234  			return poll.Error(err)
  2235  		}
  2236  		switch container.State.Status {
  2237  		case "created", "running":
  2238  			return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status)
  2239  		}
  2240  		return poll.Success()
  2241  	}
  2242  }
  2243  
  2244  func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
  2245  	testRequires(c, DaemonIsLinux)
  2246  	type testCase struct {
  2247  		cfg             mounttypes.Mount
  2248  		expectedOptions []string
  2249  	}
  2250  	target := "/foo"
  2251  	cases := []testCase{
  2252  		{
  2253  			cfg: mounttypes.Mount{
  2254  				Type:   "tmpfs",
  2255  				Target: target},
  2256  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
  2257  		},
  2258  		{
  2259  			cfg: mounttypes.Mount{
  2260  				Type:   "tmpfs",
  2261  				Target: target,
  2262  				TmpfsOptions: &mounttypes.TmpfsOptions{
  2263  					SizeBytes: 4096 * 1024, Mode: 0700}},
  2264  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
  2265  		},
  2266  	}
  2267  
  2268  	cli, err := client.NewClientWithOpts(client.FromEnv)
  2269  	c.Assert(err, checker.IsNil)
  2270  	defer cli.Close()
  2271  
  2272  	config := containertypes.Config{
  2273  		Image: "busybox",
  2274  		Cmd:   []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
  2275  	}
  2276  	for i, x := range cases {
  2277  		cName := fmt.Sprintf("test-tmpfs-%d", i)
  2278  		hostConfig := containertypes.HostConfig{
  2279  			Mounts: []mounttypes.Mount{x.cfg},
  2280  		}
  2281  
  2282  		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName)
  2283  		c.Assert(err, checker.IsNil)
  2284  		out, _ := dockerCmd(c, "start", "-a", cName)
  2285  		for _, option := range x.expectedOptions {
  2286  			c.Assert(out, checker.Contains, option)
  2287  		}
  2288  	}
  2289  }
  2290  
  2291  // Regression test for #33334
  2292  // Makes sure that when a container which has a custom stop signal + restart=always
  2293  // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled.
  2294  func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) {
  2295  	id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always"))
  2296  	res, _, err := request.Post("/containers/" + id + "/kill")
  2297  	c.Assert(err, checker.IsNil)
  2298  	defer res.Body.Close()
  2299  
  2300  	b, err := ioutil.ReadAll(res.Body)
  2301  	c.Assert(err, checker.IsNil)
  2302  	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b)))
  2303  	err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second)
  2304  	c.Assert(err, checker.IsNil)
  2305  }