github.com/rita33cool1/iot-system-gateway@v0.0.0-20200911033302-e65bde238cc5/docker-engine/integration-cli/docker_api_containers_test.go (about)

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