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