github.com/lazyboychen7/engine@v17.12.1-ce-rc2+incompatible/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/poll"
    35  	"github.com/stretchr/testify/assert"
    36  	"github.com/stretchr/testify/require"
    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.DaemonPlatform() == "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.DaemonPlatform() == "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 := request.NewEnvClientWithVersion("v1.18")
  1376  
  1377  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1378  	c.Assert(err, checker.IsNil)
  1379  }
  1380  
  1381  // Ensure an error occurs when you have a container read-only rootfs but you
  1382  // extract an archive to a symlink in a writable volume which points to a
  1383  // directory outside of the volume.
  1384  func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
  1385  	// Windows does not support read-only rootfs
  1386  	// Requires local volume mount bind.
  1387  	// --read-only + userns has remount issues
  1388  	testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
  1389  
  1390  	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1391  	defer os.RemoveAll(testVol)
  1392  
  1393  	makeTestContentInDir(c, testVol)
  1394  
  1395  	cID := makeTestContainer(c, testContainerOptions{
  1396  		readOnly: true,
  1397  		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
  1398  	})
  1399  
  1400  	// Attempt to extract to a symlink in the volume which points to a
  1401  	// directory outside the volume. This should cause an error because the
  1402  	// rootfs is read-only.
  1403  	var httpClient *http.Client
  1404  	cli, err := client.NewClient(daemonHost(), "v1.20", httpClient, map[string]string{})
  1405  	c.Assert(err, checker.IsNil)
  1406  
  1407  	err = cli.CopyToContainer(context.Background(), cID, "/vol2/symlinkToAbsDir", nil, types.CopyToContainerOptions{})
  1408  	c.Assert(err.Error(), checker.Contains, "container rootfs is marked read-only")
  1409  }
  1410  
  1411  func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
  1412  	// Not supported on Windows
  1413  	testRequires(c, DaemonIsLinux)
  1414  
  1415  	cli, err := client.NewEnvClient()
  1416  	c.Assert(err, checker.IsNil)
  1417  	defer cli.Close()
  1418  
  1419  	config := containertypes.Config{
  1420  		Image: "busybox",
  1421  	}
  1422  	hostConfig1 := containertypes.HostConfig{
  1423  		Resources: containertypes.Resources{
  1424  			CpusetCpus: "1-42,,",
  1425  		},
  1426  	}
  1427  	name := "wrong-cpuset-cpus"
  1428  
  1429  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig1, &networktypes.NetworkingConfig{}, name)
  1430  	expected := "Invalid value 1-42,, for cpuset cpus"
  1431  	c.Assert(err.Error(), checker.Contains, expected)
  1432  
  1433  	hostConfig2 := containertypes.HostConfig{
  1434  		Resources: containertypes.Resources{
  1435  			CpusetMems: "42-3,1--",
  1436  		},
  1437  	}
  1438  	name = "wrong-cpuset-mems"
  1439  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig2, &networktypes.NetworkingConfig{}, name)
  1440  	expected = "Invalid value 42-3,1-- for cpuset mems"
  1441  	c.Assert(err.Error(), checker.Contains, expected)
  1442  }
  1443  
  1444  func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
  1445  	// ShmSize is not supported on Windows
  1446  	testRequires(c, DaemonIsLinux)
  1447  	config := containertypes.Config{
  1448  		Image: "busybox",
  1449  	}
  1450  	hostConfig := containertypes.HostConfig{
  1451  		ShmSize: -1,
  1452  	}
  1453  
  1454  	cli, err := client.NewEnvClient()
  1455  	c.Assert(err, checker.IsNil)
  1456  	defer cli.Close()
  1457  
  1458  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
  1459  	c.Assert(err.Error(), checker.Contains, "SHM size can not be less than 0")
  1460  }
  1461  
  1462  func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
  1463  	// ShmSize is not supported on Windows
  1464  	testRequires(c, DaemonIsLinux)
  1465  	var defaultSHMSize int64 = 67108864
  1466  	config := containertypes.Config{
  1467  		Image: "busybox",
  1468  		Cmd:   []string{"mount"},
  1469  	}
  1470  
  1471  	cli, err := client.NewEnvClient()
  1472  	c.Assert(err, checker.IsNil)
  1473  	defer cli.Close()
  1474  
  1475  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1476  	c.Assert(err, check.IsNil)
  1477  
  1478  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1479  	c.Assert(err, check.IsNil)
  1480  
  1481  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
  1482  
  1483  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1484  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1485  	if !shmRegexp.MatchString(out) {
  1486  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1487  	}
  1488  }
  1489  
  1490  func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
  1491  	// ShmSize is not supported on Windows
  1492  	testRequires(c, DaemonIsLinux)
  1493  	config := containertypes.Config{
  1494  		Image: "busybox",
  1495  		Cmd:   []string{"mount"},
  1496  	}
  1497  
  1498  	cli, err := client.NewEnvClient()
  1499  	c.Assert(err, checker.IsNil)
  1500  	defer cli.Close()
  1501  
  1502  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1503  	c.Assert(err, check.IsNil)
  1504  
  1505  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1506  	c.Assert(err, check.IsNil)
  1507  
  1508  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
  1509  
  1510  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1511  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1512  	if !shmRegexp.MatchString(out) {
  1513  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1514  	}
  1515  }
  1516  
  1517  func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
  1518  	// ShmSize is not supported on Windows
  1519  	testRequires(c, DaemonIsLinux)
  1520  	config := containertypes.Config{
  1521  		Image: "busybox",
  1522  		Cmd:   []string{"mount"},
  1523  	}
  1524  
  1525  	hostConfig := containertypes.HostConfig{
  1526  		ShmSize: 1073741824,
  1527  	}
  1528  
  1529  	cli, err := client.NewEnvClient()
  1530  	c.Assert(err, checker.IsNil)
  1531  	defer cli.Close()
  1532  
  1533  	container, err := cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "")
  1534  	c.Assert(err, check.IsNil)
  1535  
  1536  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1537  	c.Assert(err, check.IsNil)
  1538  
  1539  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
  1540  
  1541  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1542  	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1543  	if !shmRegex.MatchString(out) {
  1544  		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1545  	}
  1546  }
  1547  
  1548  func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
  1549  	// Swappiness is not supported on Windows
  1550  	testRequires(c, DaemonIsLinux)
  1551  	config := containertypes.Config{
  1552  		Image: "busybox",
  1553  	}
  1554  
  1555  	cli, err := client.NewEnvClient()
  1556  	c.Assert(err, checker.IsNil)
  1557  	defer cli.Close()
  1558  
  1559  	container, err := cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, "")
  1560  	c.Assert(err, check.IsNil)
  1561  
  1562  	containerJSON, err := cli.ContainerInspect(context.Background(), container.ID)
  1563  	c.Assert(err, check.IsNil)
  1564  
  1565  	c.Assert(containerJSON.HostConfig.MemorySwappiness, check.IsNil)
  1566  }
  1567  
  1568  // check validation is done daemon side and not only in cli
  1569  func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
  1570  	// OomScoreAdj is not supported on Windows
  1571  	testRequires(c, DaemonIsLinux)
  1572  
  1573  	config := containertypes.Config{
  1574  		Image: "busybox",
  1575  	}
  1576  
  1577  	hostConfig := containertypes.HostConfig{
  1578  		OomScoreAdj: 1001,
  1579  	}
  1580  
  1581  	cli, err := client.NewEnvClient()
  1582  	c.Assert(err, checker.IsNil)
  1583  	defer cli.Close()
  1584  
  1585  	name := "oomscoreadj-over"
  1586  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
  1587  
  1588  	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
  1589  	c.Assert(err.Error(), checker.Contains, expected)
  1590  
  1591  	hostConfig = containertypes.HostConfig{
  1592  		OomScoreAdj: -1001,
  1593  	}
  1594  
  1595  	name = "oomscoreadj-low"
  1596  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, name)
  1597  
  1598  	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
  1599  	c.Assert(err.Error(), checker.Contains, expected)
  1600  }
  1601  
  1602  // test case for #22210 where an empty container name caused panic.
  1603  func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
  1604  	cli, err := client.NewEnvClient()
  1605  	c.Assert(err, checker.IsNil)
  1606  	defer cli.Close()
  1607  
  1608  	err = cli.ContainerRemove(context.Background(), "", types.ContainerRemoveOptions{})
  1609  	c.Assert(err.Error(), checker.Contains, "No such container")
  1610  }
  1611  
  1612  func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
  1613  	// Problematic on Windows as Windows does not support stats
  1614  	testRequires(c, DaemonIsLinux)
  1615  
  1616  	name := "testing-network-disabled"
  1617  
  1618  	config := containertypes.Config{
  1619  		Image:           "busybox",
  1620  		Cmd:             []string{"top"},
  1621  		NetworkDisabled: true,
  1622  	}
  1623  
  1624  	cli, err := client.NewEnvClient()
  1625  	c.Assert(err, checker.IsNil)
  1626  	defer cli.Close()
  1627  
  1628  	_, err = cli.ContainerCreate(context.Background(), &config, &containertypes.HostConfig{}, &networktypes.NetworkingConfig{}, name)
  1629  	c.Assert(err, checker.IsNil)
  1630  
  1631  	err = cli.ContainerStart(context.Background(), name, types.ContainerStartOptions{})
  1632  	c.Assert(err, checker.IsNil)
  1633  
  1634  	c.Assert(waitRun(name), check.IsNil)
  1635  
  1636  	type b struct {
  1637  		stats types.ContainerStats
  1638  		err   error
  1639  	}
  1640  	bc := make(chan b, 1)
  1641  	go func() {
  1642  		stats, err := cli.ContainerStats(context.Background(), name, false)
  1643  		bc <- b{stats, err}
  1644  	}()
  1645  
  1646  	// allow some time to stream the stats from the container
  1647  	time.Sleep(4 * time.Second)
  1648  	dockerCmd(c, "rm", "-f", name)
  1649  
  1650  	// collect the results from the stats stream or timeout and fail
  1651  	// if the stream was not disconnected.
  1652  	select {
  1653  	case <-time.After(2 * time.Second):
  1654  		c.Fatal("stream was not closed after container was removed")
  1655  	case sr := <-bc:
  1656  		c.Assert(sr.err, checker.IsNil)
  1657  		sr.stats.Body.Close()
  1658  	}
  1659  }
  1660  
  1661  func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
  1662  	type testCase struct {
  1663  		config     containertypes.Config
  1664  		hostConfig containertypes.HostConfig
  1665  		msg        string
  1666  	}
  1667  
  1668  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1669  	destPath := prefix + slash + "foo"
  1670  	notExistPath := prefix + slash + "notexist"
  1671  
  1672  	cases := []testCase{
  1673  		{
  1674  			config: containertypes.Config{
  1675  				Image: "busybox",
  1676  			},
  1677  			hostConfig: containertypes.HostConfig{
  1678  				Mounts: []mounttypes.Mount{{
  1679  					Type:   "notreal",
  1680  					Target: destPath,
  1681  				},
  1682  				},
  1683  			},
  1684  
  1685  			msg: "mount type unknown",
  1686  		},
  1687  		{
  1688  			config: containertypes.Config{
  1689  				Image: "busybox",
  1690  			},
  1691  			hostConfig: containertypes.HostConfig{
  1692  				Mounts: []mounttypes.Mount{{
  1693  					Type: "bind"}}},
  1694  			msg: "Target must not be empty",
  1695  		},
  1696  		{
  1697  			config: containertypes.Config{
  1698  				Image: "busybox",
  1699  			},
  1700  			hostConfig: containertypes.HostConfig{
  1701  				Mounts: []mounttypes.Mount{{
  1702  					Type:   "bind",
  1703  					Target: destPath}}},
  1704  			msg: "Source must not be empty",
  1705  		},
  1706  		{
  1707  			config: containertypes.Config{
  1708  				Image: "busybox",
  1709  			},
  1710  			hostConfig: containertypes.HostConfig{
  1711  				Mounts: []mounttypes.Mount{{
  1712  					Type:   "bind",
  1713  					Source: notExistPath,
  1714  					Target: destPath}}},
  1715  			msg: "bind source path does not exist",
  1716  		},
  1717  		{
  1718  			config: containertypes.Config{
  1719  				Image: "busybox",
  1720  			},
  1721  			hostConfig: containertypes.HostConfig{
  1722  				Mounts: []mounttypes.Mount{{
  1723  					Type: "volume"}}},
  1724  			msg: "Target must not be empty",
  1725  		},
  1726  		{
  1727  			config: containertypes.Config{
  1728  				Image: "busybox",
  1729  			},
  1730  			hostConfig: containertypes.HostConfig{
  1731  				Mounts: []mounttypes.Mount{{
  1732  					Type:   "volume",
  1733  					Source: "hello",
  1734  					Target: destPath}}},
  1735  			msg: "",
  1736  		},
  1737  		{
  1738  			config: containertypes.Config{
  1739  				Image: "busybox",
  1740  			},
  1741  			hostConfig: containertypes.HostConfig{
  1742  				Mounts: []mounttypes.Mount{{
  1743  					Type:   "volume",
  1744  					Source: "hello2",
  1745  					Target: destPath,
  1746  					VolumeOptions: &mounttypes.VolumeOptions{
  1747  						DriverConfig: &mounttypes.Driver{
  1748  							Name: "local"}}}}},
  1749  			msg: "",
  1750  		},
  1751  	}
  1752  
  1753  	if SameHostDaemon() {
  1754  		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
  1755  		c.Assert(err, checker.IsNil)
  1756  		defer os.RemoveAll(tmpDir)
  1757  		cases = append(cases, []testCase{
  1758  			{
  1759  				config: containertypes.Config{
  1760  					Image: "busybox",
  1761  				},
  1762  				hostConfig: containertypes.HostConfig{
  1763  					Mounts: []mounttypes.Mount{{
  1764  						Type:   "bind",
  1765  						Source: tmpDir,
  1766  						Target: destPath}}},
  1767  				msg: "",
  1768  			},
  1769  			{
  1770  				config: containertypes.Config{
  1771  					Image: "busybox",
  1772  				},
  1773  				hostConfig: containertypes.HostConfig{
  1774  					Mounts: []mounttypes.Mount{{
  1775  						Type:          "bind",
  1776  						Source:        tmpDir,
  1777  						Target:        destPath,
  1778  						VolumeOptions: &mounttypes.VolumeOptions{}}}},
  1779  				msg: "VolumeOptions must not be specified",
  1780  			},
  1781  		}...)
  1782  	}
  1783  
  1784  	if DaemonIsLinux() {
  1785  		cases = append(cases, []testCase{
  1786  			{
  1787  				config: containertypes.Config{
  1788  					Image: "busybox",
  1789  				},
  1790  				hostConfig: containertypes.HostConfig{
  1791  					Mounts: []mounttypes.Mount{{
  1792  						Type:   "volume",
  1793  						Source: "hello3",
  1794  						Target: destPath,
  1795  						VolumeOptions: &mounttypes.VolumeOptions{
  1796  							DriverConfig: &mounttypes.Driver{
  1797  								Name:    "local",
  1798  								Options: map[string]string{"o": "size=1"}}}}}},
  1799  				msg: "",
  1800  			},
  1801  			{
  1802  				config: containertypes.Config{
  1803  					Image: "busybox",
  1804  				},
  1805  				hostConfig: containertypes.HostConfig{
  1806  					Mounts: []mounttypes.Mount{{
  1807  						Type:   "tmpfs",
  1808  						Target: destPath}}},
  1809  				msg: "",
  1810  			},
  1811  			{
  1812  				config: containertypes.Config{
  1813  					Image: "busybox",
  1814  				},
  1815  				hostConfig: containertypes.HostConfig{
  1816  					Mounts: []mounttypes.Mount{{
  1817  						Type:   "tmpfs",
  1818  						Target: destPath,
  1819  						TmpfsOptions: &mounttypes.TmpfsOptions{
  1820  							SizeBytes: 4096 * 1024,
  1821  							Mode:      0700,
  1822  						}}}},
  1823  				msg: "",
  1824  			},
  1825  
  1826  			{
  1827  				config: containertypes.Config{
  1828  					Image: "busybox",
  1829  				},
  1830  				hostConfig: containertypes.HostConfig{
  1831  					Mounts: []mounttypes.Mount{{
  1832  						Type:   "tmpfs",
  1833  						Source: "/shouldnotbespecified",
  1834  						Target: destPath}}},
  1835  				msg: "Source must not be specified",
  1836  			},
  1837  		}...)
  1838  
  1839  	}
  1840  	cli, err := client.NewEnvClient()
  1841  	c.Assert(err, checker.IsNil)
  1842  	defer cli.Close()
  1843  
  1844  	for i, x := range cases {
  1845  		c.Logf("case %d", i)
  1846  		_, err = cli.ContainerCreate(context.Background(), &x.config, &x.hostConfig, &networktypes.NetworkingConfig{}, "")
  1847  		if len(x.msg) > 0 {
  1848  			c.Assert(err.Error(), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
  1849  		} else {
  1850  			c.Assert(err, checker.IsNil)
  1851  		}
  1852  	}
  1853  }
  1854  
  1855  func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
  1856  	testRequires(c, NotUserNamespace, SameHostDaemon)
  1857  	// also with data in the host side
  1858  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1859  	destPath := prefix + slash + "foo"
  1860  	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
  1861  	c.Assert(err, checker.IsNil)
  1862  	defer os.RemoveAll(tmpDir)
  1863  	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
  1864  	c.Assert(err, checker.IsNil)
  1865  	config := containertypes.Config{
  1866  		Image: "busybox",
  1867  		Cmd:   []string{"/bin/sh", "-c", "cat /foo/bar"},
  1868  	}
  1869  	hostConfig := containertypes.HostConfig{
  1870  		Mounts: []mounttypes.Mount{
  1871  			{Type: "bind", Source: tmpDir, Target: destPath},
  1872  		},
  1873  	}
  1874  	cli, err := client.NewEnvClient()
  1875  	c.Assert(err, checker.IsNil)
  1876  	defer cli.Close()
  1877  
  1878  	_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, "test")
  1879  	c.Assert(err, checker.IsNil)
  1880  
  1881  	out, _ := dockerCmd(c, "start", "-a", "test")
  1882  	c.Assert(out, checker.Equals, "hello")
  1883  }
  1884  
  1885  // Test Mounts comes out as expected for the MountPoint
  1886  func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
  1887  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1888  	destPath := prefix + slash + "foo"
  1889  
  1890  	var (
  1891  		testImg string
  1892  	)
  1893  	if testEnv.DaemonPlatform() != "windows" {
  1894  		testImg = "test-mount-config"
  1895  		buildImageSuccessfully(c, testImg, build.WithDockerfile(`
  1896  	FROM busybox
  1897  	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
  1898  	CMD cat `+destPath+slash+`bar
  1899  	`))
  1900  	} else {
  1901  		testImg = "busybox"
  1902  	}
  1903  
  1904  	type testCase struct {
  1905  		spec     mounttypes.Mount
  1906  		expected types.MountPoint
  1907  	}
  1908  
  1909  	var selinuxSharedLabel string
  1910  	if runtime.GOOS == "linux" {
  1911  		selinuxSharedLabel = "z"
  1912  	}
  1913  
  1914  	cases := []testCase{
  1915  		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
  1916  		// Validation of the actual `Mount` struct is done in another test is not needed here
  1917  		{
  1918  			spec:     mounttypes.Mount{Type: "volume", Target: destPath},
  1919  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1920  		},
  1921  		{
  1922  			spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash},
  1923  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1924  		},
  1925  		{
  1926  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"},
  1927  			expected: types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1928  		},
  1929  		{
  1930  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"},
  1931  			expected: types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  1932  		},
  1933  		{
  1934  			spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test3", VolumeOptions: &mounttypes.VolumeOptions{DriverConfig: &mounttypes.Driver{Name: volume.DefaultDriverName}}},
  1935  			expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", Name: "test3", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1936  		},
  1937  	}
  1938  
  1939  	if SameHostDaemon() {
  1940  		// setup temp dir for testing binds
  1941  		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
  1942  		c.Assert(err, checker.IsNil)
  1943  		defer os.RemoveAll(tmpDir1)
  1944  		cases = append(cases, []testCase{
  1945  			{
  1946  				spec: mounttypes.Mount{
  1947  					Type:   "bind",
  1948  					Source: tmpDir1,
  1949  					Target: destPath,
  1950  				},
  1951  				expected: types.MountPoint{
  1952  					Type:        "bind",
  1953  					RW:          true,
  1954  					Destination: destPath,
  1955  					Source:      tmpDir1,
  1956  				},
  1957  			},
  1958  			{
  1959  				spec:     mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true},
  1960  				expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1},
  1961  			},
  1962  		}...)
  1963  
  1964  		// for modes only supported on Linux
  1965  		if DaemonIsLinux() {
  1966  			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
  1967  			c.Assert(err, checker.IsNil)
  1968  			defer os.RemoveAll(tmpDir3)
  1969  
  1970  			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
  1971  			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
  1972  
  1973  			cases = append(cases, []testCase{
  1974  				{
  1975  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath},
  1976  					expected: types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3},
  1977  				},
  1978  				{
  1979  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true},
  1980  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3},
  1981  				},
  1982  				{
  1983  					spec:     mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true, BindOptions: &mounttypes.BindOptions{Propagation: "shared"}},
  1984  					expected: types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3, Propagation: "shared"},
  1985  				},
  1986  			}...)
  1987  		}
  1988  	}
  1989  
  1990  	if testEnv.DaemonPlatform() != "windows" { // Windows does not support volume populate
  1991  		cases = append(cases, []testCase{
  1992  			{
  1993  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  1994  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1995  			},
  1996  			{
  1997  				spec:     mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  1998  				expected: types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  1999  			},
  2000  			{
  2001  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2002  				expected: types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath, Mode: selinuxSharedLabel},
  2003  			},
  2004  			{
  2005  				spec:     mounttypes.Mount{Type: "volume", Target: destPath, Source: "test5", ReadOnly: true, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}},
  2006  				expected: types.MountPoint{Type: "volume", Name: "test5", RW: false, Destination: destPath, Mode: selinuxSharedLabel},
  2007  			},
  2008  		}...)
  2009  	}
  2010  
  2011  	type wrapper struct {
  2012  		containertypes.Config
  2013  		HostConfig containertypes.HostConfig
  2014  	}
  2015  	type createResp struct {
  2016  		ID string `json:"Id"`
  2017  	}
  2018  
  2019  	ctx := context.Background()
  2020  	apiclient := testEnv.APIClient()
  2021  	for i, x := range cases {
  2022  		c.Logf("case %d - config: %v", i, x.spec)
  2023  		container, err := apiclient.ContainerCreate(
  2024  			ctx,
  2025  			&containertypes.Config{Image: testImg},
  2026  			&containertypes.HostConfig{Mounts: []mounttypes.Mount{x.spec}},
  2027  			&networktypes.NetworkingConfig{},
  2028  			"")
  2029  		require.NoError(c, err)
  2030  
  2031  		containerInspect, err := apiclient.ContainerInspect(ctx, container.ID)
  2032  		require.NoError(c, err)
  2033  		mps := containerInspect.Mounts
  2034  		require.Len(c, mps, 1)
  2035  		mountPoint := mps[0]
  2036  
  2037  		if x.expected.Source != "" {
  2038  			assert.Equal(c, x.expected.Source, mountPoint.Source)
  2039  		}
  2040  		if x.expected.Name != "" {
  2041  			assert.Equal(c, x.expected.Name, mountPoint.Name)
  2042  		}
  2043  		if x.expected.Driver != "" {
  2044  			assert.Equal(c, x.expected.Driver, mountPoint.Driver)
  2045  		}
  2046  		if x.expected.Propagation != "" {
  2047  			assert.Equal(c, x.expected.Propagation, mountPoint.Propagation)
  2048  		}
  2049  		assert.Equal(c, x.expected.RW, mountPoint.RW)
  2050  		assert.Equal(c, x.expected.Type, mountPoint.Type)
  2051  		assert.Equal(c, x.expected.Mode, mountPoint.Mode)
  2052  		assert.Equal(c, x.expected.Destination, mountPoint.Destination)
  2053  
  2054  		err = apiclient.ContainerStart(ctx, container.ID, types.ContainerStartOptions{})
  2055  		require.NoError(c, err)
  2056  		poll.WaitOn(c, containerExit(apiclient, container.ID), poll.WithDelay(time.Second))
  2057  
  2058  		err = apiclient.ContainerRemove(ctx, container.ID, types.ContainerRemoveOptions{
  2059  			RemoveVolumes: true,
  2060  			Force:         true,
  2061  		})
  2062  		require.NoError(c, err)
  2063  
  2064  		switch {
  2065  
  2066  		// Named volumes still exist after the container is removed
  2067  		case x.spec.Type == "volume" && len(x.spec.Source) > 0:
  2068  			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2069  			require.NoError(c, err)
  2070  
  2071  		// Bind mounts are never removed with the container
  2072  		case x.spec.Type == "bind":
  2073  
  2074  		// anonymous volumes are removed
  2075  		default:
  2076  			_, err := apiclient.VolumeInspect(ctx, mountPoint.Name)
  2077  			assert.True(c, client.IsErrNotFound(err))
  2078  		}
  2079  	}
  2080  }
  2081  
  2082  func containerExit(apiclient client.APIClient, name string) func(poll.LogT) poll.Result {
  2083  	return func(logT poll.LogT) poll.Result {
  2084  		container, err := apiclient.ContainerInspect(context.Background(), name)
  2085  		if err != nil {
  2086  			return poll.Error(err)
  2087  		}
  2088  		switch container.State.Status {
  2089  		case "created", "running":
  2090  			return poll.Continue("container %s is %s, waiting for exit", name, container.State.Status)
  2091  		}
  2092  		return poll.Success()
  2093  	}
  2094  }
  2095  
  2096  func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
  2097  	testRequires(c, DaemonIsLinux)
  2098  	type testCase struct {
  2099  		cfg             mounttypes.Mount
  2100  		expectedOptions []string
  2101  	}
  2102  	target := "/foo"
  2103  	cases := []testCase{
  2104  		{
  2105  			cfg: mounttypes.Mount{
  2106  				Type:   "tmpfs",
  2107  				Target: target},
  2108  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
  2109  		},
  2110  		{
  2111  			cfg: mounttypes.Mount{
  2112  				Type:   "tmpfs",
  2113  				Target: target,
  2114  				TmpfsOptions: &mounttypes.TmpfsOptions{
  2115  					SizeBytes: 4096 * 1024, Mode: 0700}},
  2116  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
  2117  		},
  2118  	}
  2119  
  2120  	cli, err := client.NewEnvClient()
  2121  	c.Assert(err, checker.IsNil)
  2122  	defer cli.Close()
  2123  
  2124  	config := containertypes.Config{
  2125  		Image: "busybox",
  2126  		Cmd:   []string{"/bin/sh", "-c", fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
  2127  	}
  2128  	for i, x := range cases {
  2129  		cName := fmt.Sprintf("test-tmpfs-%d", i)
  2130  		hostConfig := containertypes.HostConfig{
  2131  			Mounts: []mounttypes.Mount{x.cfg},
  2132  		}
  2133  
  2134  		_, err = cli.ContainerCreate(context.Background(), &config, &hostConfig, &networktypes.NetworkingConfig{}, cName)
  2135  		c.Assert(err, checker.IsNil)
  2136  		out, _ := dockerCmd(c, "start", "-a", cName)
  2137  		for _, option := range x.expectedOptions {
  2138  			c.Assert(out, checker.Contains, option)
  2139  		}
  2140  	}
  2141  }
  2142  
  2143  // Regression test for #33334
  2144  // Makes sure that when a container which has a custom stop signal + restart=always
  2145  // gets killed (with SIGKILL) by the kill API, that the restart policy is cancelled.
  2146  func (s *DockerSuite) TestContainerKillCustomStopSignal(c *check.C) {
  2147  	id := strings.TrimSpace(runSleepingContainer(c, "--stop-signal=SIGTERM", "--restart=always"))
  2148  	res, _, err := request.Post("/containers/" + id + "/kill")
  2149  	c.Assert(err, checker.IsNil)
  2150  	defer res.Body.Close()
  2151  
  2152  	b, err := ioutil.ReadAll(res.Body)
  2153  	c.Assert(res.StatusCode, checker.Equals, http.StatusNoContent, check.Commentf(string(b)))
  2154  	err = waitInspect(id, "{{.State.Running}} {{.State.Restarting}}", "false false", 30*time.Second)
  2155  	c.Assert(err, checker.IsNil)
  2156  }