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