github.com/nullne/docker@v1.13.0-rc1/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/http/httputil"
    12  	"net/url"
    13  	"os"
    14  	"path/filepath"
    15  	"regexp"
    16  	"strconv"
    17  	"strings"
    18  	"time"
    19  
    20  	"github.com/docker/docker/api/types"
    21  	containertypes "github.com/docker/docker/api/types/container"
    22  	mounttypes "github.com/docker/docker/api/types/mount"
    23  	networktypes "github.com/docker/docker/api/types/network"
    24  	"github.com/docker/docker/pkg/integration"
    25  	"github.com/docker/docker/pkg/integration/checker"
    26  	"github.com/docker/docker/pkg/ioutils"
    27  	"github.com/docker/docker/pkg/mount"
    28  	"github.com/docker/docker/pkg/stringid"
    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 := sockRequest("GET", "/containers/json?all=1", nil)
    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 := sockRequest("GET", "/containers/json?all=1", nil)
    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 := sockRequest("GET", "/containers/json?all=1", nil)
   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 := sockRequest("GET", "/containers/"+name+"/export", nil)
   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 := sockRequest("GET", "/containers/"+name+"/changes", nil)
   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 := sockRequest("GET", "/containers/"+name+"/stats", nil)
   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 := &integration.ChannelBuffer{make(chan []byte, 1)}
   219  	defer buf.Close()
   220  
   221  	_, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/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   []byte
   256  		err    error
   257  	}
   258  	bc := make(chan b, 1)
   259  	go func() {
   260  		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
   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 := sockRequest("GET", "/containers/"+name+"/stats?stream=0", nil)
   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 := sockRequestRaw("GET", "/containers/"+name+"/stats", nil, "")
   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()
   350  	out, _ := dockerCmd(c, "run", "-d", "busybox", "sleep", "30")
   351  	ContainerID := strings.TrimSpace(out)
   352  
   353  	status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil)
   354  	c.Assert(err, checker.IsNil)
   355  	c.Assert(status, checker.Equals, http.StatusNoContent)
   356  
   357  	pausedContainers, err := getSliceOfPausedContainers()
   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 = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil)
   365  	c.Assert(err, checker.IsNil)
   366  	c.Assert(status, checker.Equals, http.StatusNoContent)
   367  
   368  	pausedContainers, err = getSliceOfPausedContainers()
   369  	c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused"))
   370  	c.Assert(pausedContainers, checker.IsNil, 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 := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil)
   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 := sockRequest("GET", "/containers/"+id+"/top", nil)
   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 := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil)
   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 := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config)
   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 := sockRequest("POST", "/containers/create", config)
   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 := sockRequest("POST", "/containers/create", config)
   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 := sockRequest("POST", "/containers/create", config)
   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 := sockRequest("POST", "/containers/create", config)
   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  	hostName := "test-host"
   568  	config := map[string]interface{}{
   569  		"Image":    "busybox",
   570  		"Hostname": hostName,
   571  	}
   572  
   573  	status, body, err := sockRequest("POST", "/containers/create", config)
   574  	c.Assert(err, checker.IsNil)
   575  	c.Assert(status, checker.Equals, http.StatusCreated)
   576  
   577  	var container containertypes.ContainerCreateCreatedBody
   578  	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
   579  
   580  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
   581  	c.Assert(err, checker.IsNil)
   582  	c.Assert(status, checker.Equals, http.StatusOK)
   583  
   584  	var containerJSON types.ContainerJSON
   585  	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
   586  	c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname"))
   587  }
   588  
   589  func (s *DockerSuite) TestContainerAPICreateWithDomainName(c *check.C) {
   590  	domainName := "test-domain"
   591  	config := map[string]interface{}{
   592  		"Image":      "busybox",
   593  		"Domainname": domainName,
   594  	}
   595  
   596  	status, body, err := sockRequest("POST", "/containers/create", config)
   597  	c.Assert(err, checker.IsNil)
   598  	c.Assert(status, checker.Equals, http.StatusCreated)
   599  
   600  	var container containertypes.ContainerCreateCreatedBody
   601  	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
   602  
   603  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
   604  	c.Assert(err, checker.IsNil)
   605  	c.Assert(status, checker.Equals, http.StatusOK)
   606  
   607  	var containerJSON types.ContainerJSON
   608  	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
   609  	c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname"))
   610  }
   611  
   612  func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *check.C) {
   613  	// Windows does not support bridge
   614  	testRequires(c, DaemonIsLinux)
   615  	UtilCreateNetworkMode(c, "bridge")
   616  }
   617  
   618  func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *check.C) {
   619  	// Windows does not support these network modes
   620  	testRequires(c, DaemonIsLinux, NotUserNamespace)
   621  	UtilCreateNetworkMode(c, "host")
   622  	UtilCreateNetworkMode(c, "container:web1")
   623  }
   624  
   625  func UtilCreateNetworkMode(c *check.C, networkMode string) {
   626  	config := map[string]interface{}{
   627  		"Image":      "busybox",
   628  		"HostConfig": map[string]interface{}{"NetworkMode": networkMode},
   629  	}
   630  
   631  	status, body, err := sockRequest("POST", "/containers/create", config)
   632  	c.Assert(err, checker.IsNil)
   633  	c.Assert(status, checker.Equals, http.StatusCreated)
   634  
   635  	var container containertypes.ContainerCreateCreatedBody
   636  	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
   637  
   638  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
   639  	c.Assert(err, checker.IsNil)
   640  	c.Assert(status, checker.Equals, http.StatusOK)
   641  
   642  	var containerJSON types.ContainerJSON
   643  	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
   644  	c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode"))
   645  }
   646  
   647  func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) {
   648  	// TODO Windows to Windows CI. The CpuShares part could be ported.
   649  	testRequires(c, DaemonIsLinux)
   650  	config := map[string]interface{}{
   651  		"Image":      "busybox",
   652  		"CpuShares":  512,
   653  		"CpusetCpus": "0",
   654  	}
   655  
   656  	status, body, err := sockRequest("POST", "/containers/create", config)
   657  	c.Assert(err, checker.IsNil)
   658  	c.Assert(status, checker.Equals, http.StatusCreated)
   659  
   660  	var container containertypes.ContainerCreateCreatedBody
   661  	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
   662  
   663  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
   664  	c.Assert(err, checker.IsNil)
   665  	c.Assert(status, checker.Equals, http.StatusOK)
   666  
   667  	var containerJSON types.ContainerJSON
   668  
   669  	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
   670  
   671  	out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
   672  	c.Assert(out, checker.Equals, "512")
   673  
   674  	outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus")
   675  	c.Assert(outCpuset, checker.Equals, "0")
   676  }
   677  
   678  func (s *DockerSuite) TestContainerAPIVerifyHeader(c *check.C) {
   679  	config := map[string]interface{}{
   680  		"Image": "busybox",
   681  	}
   682  
   683  	create := func(ct string) (*http.Response, io.ReadCloser, error) {
   684  		jsonData := bytes.NewBuffer(nil)
   685  		c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil)
   686  		return sockRequestRaw("POST", "/containers/create", jsonData, ct)
   687  	}
   688  
   689  	// Try with no content-type
   690  	res, body, err := create("")
   691  	c.Assert(err, checker.IsNil)
   692  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   693  	body.Close()
   694  
   695  	// Try with wrong content-type
   696  	res, body, err = create("application/xml")
   697  	c.Assert(err, checker.IsNil)
   698  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   699  	body.Close()
   700  
   701  	// now application/json
   702  	res, body, err = create("application/json")
   703  	c.Assert(err, checker.IsNil)
   704  	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
   705  	body.Close()
   706  }
   707  
   708  //Issue 14230. daemon should return 500 for invalid port syntax
   709  func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) {
   710  	config := `{
   711  				  "Image": "busybox",
   712  				  "HostConfig": {
   713  					"NetworkMode": "default",
   714  					"PortBindings": {
   715  					  "19039;1230": [
   716  						{}
   717  					  ]
   718  					}
   719  				  }
   720  				}`
   721  
   722  	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   723  	c.Assert(err, checker.IsNil)
   724  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   725  
   726  	b, err := readBody(body)
   727  	c.Assert(err, checker.IsNil)
   728  	c.Assert(string(b[:]), checker.Contains, "invalid port")
   729  }
   730  
   731  func (s *DockerSuite) TestContainerAPIInvalidRestartPolicyName(c *check.C) {
   732  	config := `{
   733  		"Image": "busybox",
   734  		"HostConfig": {
   735  			"RestartPolicy": {
   736  				"Name": "something",
   737  				"MaximumRetryCount": 0
   738  			}
   739  		}
   740  	}`
   741  
   742  	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   743  	c.Assert(err, checker.IsNil)
   744  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   745  
   746  	b, err := readBody(body)
   747  	c.Assert(err, checker.IsNil)
   748  	c.Assert(string(b[:]), checker.Contains, "invalid restart policy")
   749  }
   750  
   751  func (s *DockerSuite) TestContainerAPIInvalidRestartPolicyRetryMismatch(c *check.C) {
   752  	config := `{
   753  		"Image": "busybox",
   754  		"HostConfig": {
   755  			"RestartPolicy": {
   756  				"Name": "always",
   757  				"MaximumRetryCount": 2
   758  			}
   759  		}
   760  	}`
   761  
   762  	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   763  	c.Assert(err, checker.IsNil)
   764  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   765  
   766  	b, err := readBody(body)
   767  	c.Assert(err, checker.IsNil)
   768  	c.Assert(string(b[:]), checker.Contains, "maximum restart count not valid with restart policy")
   769  }
   770  
   771  func (s *DockerSuite) TestContainerAPIInvalidRestartPolicyPositiveRetryCount(c *check.C) {
   772  	config := `{
   773  		"Image": "busybox",
   774  		"HostConfig": {
   775  			"RestartPolicy": {
   776  				"Name": "on-failure",
   777  				"MaximumRetryCount": -2
   778  			}
   779  		}
   780  	}`
   781  
   782  	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   783  	c.Assert(err, checker.IsNil)
   784  	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
   785  
   786  	b, err := readBody(body)
   787  	c.Assert(err, checker.IsNil)
   788  	c.Assert(string(b[:]), checker.Contains, "maximum restart count must be a positive integer")
   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 := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   818  	c.Assert(err, checker.IsNil)
   819  	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
   820  
   821  	b, err := 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 := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
   849  	c.Assert(err, checker.IsNil)
   850  	b, err2 := 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 := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil)
   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 := sockRequest("POST", "/containers/"+name+"/kill", nil)
   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 := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil)
   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 := sockRequest("POST", "/containers/"+name+"/restart", nil)
   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 := sockRequest("POST", "/containers/create?name="+name, config)
   914  	c.Assert(err, checker.IsNil)
   915  	c.Assert(status, checker.Equals, http.StatusCreated)
   916  
   917  	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
   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 = sockRequest("POST", "/containers/"+name+"/start", nil)
   923  	c.Assert(err, checker.IsNil)
   924  
   925  	// TODO(tibor): figure out why this doesn't work on windows
   926  	if isLocalDaemon {
   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 := sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
   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 = sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
   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 daemonPlatform == "windows" {
   951  		sleepCmd = "sleep"
   952  	}
   953  	dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2")
   954  
   955  	status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil)
   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 := sockRequest("POST", "/containers/"+name+"/copy", postData)
   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 := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
   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 := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
  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 := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
  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 := sockRequest("POST", "/v1.23/containers/notexists/copy", postData)
  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 := sockRequest("DELETE", "/containers/"+id, nil)
  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 := sockRequest("DELETE", "/containers/doesnotexist", nil)
  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 := sockRequest("DELETE", "/containers/"+id+"?force=1", nil)
  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 := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
  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 := sockRequest("DELETE", "/containers/"+id, nil)
  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 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 := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
  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  	conn, err := sockConn(time.Duration(10*time.Second), "")
  1142  	c.Assert(err, checker.IsNil)
  1143  	client := httputil.NewClientConn(conn, nil)
  1144  	defer client.Close()
  1145  
  1146  	config := map[string]interface{}{
  1147  		"Image":     "busybox",
  1148  		"Cmd":       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
  1149  		"OpenStdin": true,
  1150  	}
  1151  	b, err := json.Marshal(config)
  1152  	c.Assert(err, checker.IsNil)
  1153  
  1154  	req, err := http.NewRequest("POST", "/containers/create", bytes.NewBuffer(b))
  1155  	c.Assert(err, checker.IsNil)
  1156  	req.Header.Set("Content-Type", "application/json")
  1157  	// This is a cheat to make the http request do chunked encoding
  1158  	// Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
  1159  	// https://golang.org/src/pkg/net/http/request.go?s=11980:12172
  1160  	req.ContentLength = -1
  1161  
  1162  	resp, err := client.Do(req)
  1163  	c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding"))
  1164  	resp.Body.Close()
  1165  	c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated)
  1166  }
  1167  
  1168  func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) {
  1169  	out, _ := runSleepingContainer(c)
  1170  
  1171  	containerID := strings.TrimSpace(out)
  1172  	c.Assert(waitRun(containerID), checker.IsNil)
  1173  
  1174  	statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil)
  1175  	c.Assert(err, checker.IsNil)
  1176  	// 204 No Content is expected, not 200
  1177  	c.Assert(statusCode, checker.Equals, http.StatusNoContent)
  1178  	c.Assert(waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
  1179  }
  1180  
  1181  // #14170
  1182  func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) {
  1183  	config := struct {
  1184  		Image      string
  1185  		Entrypoint string
  1186  		Cmd        []string
  1187  	}{"busybox", "echo", []string{"hello", "world"}}
  1188  	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1189  	c.Assert(err, checker.IsNil)
  1190  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1191  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1192  
  1193  	config2 := struct {
  1194  		Image      string
  1195  		Entrypoint []string
  1196  		Cmd        []string
  1197  	}{"busybox", []string{"echo"}, []string{"hello", "world"}}
  1198  	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1199  	c.Assert(err, checker.IsNil)
  1200  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1201  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1202  }
  1203  
  1204  // #14170
  1205  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
  1206  	config := struct {
  1207  		Image      string
  1208  		Entrypoint string
  1209  		Cmd        string
  1210  	}{"busybox", "echo", "hello world"}
  1211  	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1212  	c.Assert(err, checker.IsNil)
  1213  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1214  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1215  
  1216  	config2 := struct {
  1217  		Image string
  1218  		Cmd   []string
  1219  	}{"busybox", []string{"echo", "hello", "world"}}
  1220  	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1221  	c.Assert(err, checker.IsNil)
  1222  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1223  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1224  }
  1225  
  1226  // regression #14318
  1227  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
  1228  	// Windows doesn't support CapAdd/CapDrop
  1229  	testRequires(c, DaemonIsLinux)
  1230  	config := struct {
  1231  		Image   string
  1232  		CapAdd  string
  1233  		CapDrop string
  1234  	}{"busybox", "NET_ADMIN", "SYS_ADMIN"}
  1235  	status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config)
  1236  	c.Assert(err, checker.IsNil)
  1237  	c.Assert(status, checker.Equals, http.StatusCreated)
  1238  
  1239  	config2 := struct {
  1240  		Image   string
  1241  		CapAdd  []string
  1242  		CapDrop []string
  1243  	}{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}}
  1244  	status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2)
  1245  	c.Assert(err, checker.IsNil)
  1246  	c.Assert(status, checker.Equals, http.StatusCreated)
  1247  }
  1248  
  1249  // #14915
  1250  func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) {
  1251  	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
  1252  	config := struct {
  1253  		Image string
  1254  	}{"busybox"}
  1255  	status, _, err := sockRequest("POST", "/v1.18/containers/create", config)
  1256  	c.Assert(err, checker.IsNil)
  1257  	c.Assert(status, checker.Equals, http.StatusCreated)
  1258  }
  1259  
  1260  // Ensure an error occurs when you have a container read-only rootfs but you
  1261  // extract an archive to a symlink in a writable volume which points to a
  1262  // directory outside of the volume.
  1263  func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
  1264  	// Windows does not support read-only rootfs
  1265  	// Requires local volume mount bind.
  1266  	// --read-only + userns has remount issues
  1267  	testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
  1268  
  1269  	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1270  	defer os.RemoveAll(testVol)
  1271  
  1272  	makeTestContentInDir(c, testVol)
  1273  
  1274  	cID := makeTestContainer(c, testContainerOptions{
  1275  		readOnly: true,
  1276  		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
  1277  	})
  1278  	defer deleteContainer(cID)
  1279  
  1280  	// Attempt to extract to a symlink in the volume which points to a
  1281  	// directory outside the volume. This should cause an error because the
  1282  	// rootfs is read-only.
  1283  	query := make(url.Values, 1)
  1284  	query.Set("path", "/vol2/symlinkToAbsDir")
  1285  	urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode())
  1286  
  1287  	statusCode, body, err := sockRequest("PUT", urlPath, nil)
  1288  	c.Assert(err, checker.IsNil)
  1289  
  1290  	if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) {
  1291  		c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body))
  1292  	}
  1293  }
  1294  
  1295  func (s *DockerSuite) TestContainerAPIGetContainersJSONEmpty(c *check.C) {
  1296  	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  1297  	c.Assert(err, checker.IsNil)
  1298  	c.Assert(status, checker.Equals, http.StatusOK)
  1299  	c.Assert(string(body), checker.Equals, "[]\n")
  1300  }
  1301  
  1302  func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
  1303  	// Not supported on Windows
  1304  	testRequires(c, DaemonIsLinux)
  1305  
  1306  	c1 := struct {
  1307  		Image      string
  1308  		CpusetCpus string
  1309  	}{"busybox", "1-42,,"}
  1310  	name := "wrong-cpuset-cpus"
  1311  	status, body, err := sockRequest("POST", "/containers/create?name="+name, c1)
  1312  	c.Assert(err, checker.IsNil)
  1313  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1314  	expected := "Invalid value 1-42,, for cpuset cpus"
  1315  	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
  1316  
  1317  	c2 := struct {
  1318  		Image      string
  1319  		CpusetMems string
  1320  	}{"busybox", "42-3,1--"}
  1321  	name = "wrong-cpuset-mems"
  1322  	status, body, err = sockRequest("POST", "/containers/create?name="+name, c2)
  1323  	c.Assert(err, checker.IsNil)
  1324  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1325  	expected = "Invalid value 42-3,1-- for cpuset mems"
  1326  	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
  1327  }
  1328  
  1329  func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
  1330  	// ShmSize is not supported on Windows
  1331  	testRequires(c, DaemonIsLinux)
  1332  	config := map[string]interface{}{
  1333  		"Image":      "busybox",
  1334  		"HostConfig": map[string]interface{}{"ShmSize": -1},
  1335  	}
  1336  
  1337  	status, body, err := sockRequest("POST", "/containers/create", config)
  1338  	c.Assert(err, check.IsNil)
  1339  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1340  	c.Assert(getErrorMessage(c, body), checker.Contains, "SHM size can not be less than 0")
  1341  }
  1342  
  1343  func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
  1344  	// ShmSize is not supported on Windows
  1345  	testRequires(c, DaemonIsLinux)
  1346  	var defaultSHMSize int64 = 67108864
  1347  	config := map[string]interface{}{
  1348  		"Image": "busybox",
  1349  		"Cmd":   "mount",
  1350  	}
  1351  
  1352  	status, body, err := sockRequest("POST", "/containers/create", config)
  1353  	c.Assert(err, check.IsNil)
  1354  	c.Assert(status, check.Equals, http.StatusCreated)
  1355  
  1356  	var container containertypes.ContainerCreateCreatedBody
  1357  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1358  
  1359  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1360  	c.Assert(err, check.IsNil)
  1361  	c.Assert(status, check.Equals, http.StatusOK)
  1362  
  1363  	var containerJSON types.ContainerJSON
  1364  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1365  
  1366  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
  1367  
  1368  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1369  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1370  	if !shmRegexp.MatchString(out) {
  1371  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1372  	}
  1373  }
  1374  
  1375  func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
  1376  	// ShmSize is not supported on Windows
  1377  	testRequires(c, DaemonIsLinux)
  1378  	config := map[string]interface{}{
  1379  		"Image":      "busybox",
  1380  		"HostConfig": map[string]interface{}{},
  1381  		"Cmd":        "mount",
  1382  	}
  1383  
  1384  	status, body, err := sockRequest("POST", "/containers/create", config)
  1385  	c.Assert(err, check.IsNil)
  1386  	c.Assert(status, check.Equals, http.StatusCreated)
  1387  
  1388  	var container containertypes.ContainerCreateCreatedBody
  1389  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1390  
  1391  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1392  	c.Assert(err, check.IsNil)
  1393  	c.Assert(status, check.Equals, http.StatusOK)
  1394  
  1395  	var containerJSON types.ContainerJSON
  1396  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1397  
  1398  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
  1399  
  1400  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1401  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1402  	if !shmRegexp.MatchString(out) {
  1403  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1404  	}
  1405  }
  1406  
  1407  func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
  1408  	// ShmSize is not supported on Windows
  1409  	testRequires(c, DaemonIsLinux)
  1410  	config := map[string]interface{}{
  1411  		"Image":      "busybox",
  1412  		"Cmd":        "mount",
  1413  		"HostConfig": map[string]interface{}{"ShmSize": 1073741824},
  1414  	}
  1415  
  1416  	status, body, err := sockRequest("POST", "/containers/create", config)
  1417  	c.Assert(err, check.IsNil)
  1418  	c.Assert(status, check.Equals, http.StatusCreated)
  1419  
  1420  	var container containertypes.ContainerCreateCreatedBody
  1421  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1422  
  1423  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1424  	c.Assert(err, check.IsNil)
  1425  	c.Assert(status, check.Equals, http.StatusOK)
  1426  
  1427  	var containerJSON types.ContainerJSON
  1428  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1429  
  1430  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
  1431  
  1432  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1433  	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1434  	if !shmRegex.MatchString(out) {
  1435  		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1436  	}
  1437  }
  1438  
  1439  func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
  1440  	// Swappiness is not supported on Windows
  1441  	testRequires(c, DaemonIsLinux)
  1442  	config := map[string]interface{}{
  1443  		"Image": "busybox",
  1444  	}
  1445  
  1446  	status, body, err := sockRequest("POST", "/containers/create", config)
  1447  	c.Assert(err, check.IsNil)
  1448  	c.Assert(status, check.Equals, http.StatusCreated)
  1449  
  1450  	var container containertypes.ContainerCreateCreatedBody
  1451  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1452  
  1453  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1454  	c.Assert(err, check.IsNil)
  1455  	c.Assert(status, check.Equals, http.StatusOK)
  1456  
  1457  	var containerJSON types.ContainerJSON
  1458  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1459  
  1460  	c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
  1461  }
  1462  
  1463  // check validation is done daemon side and not only in cli
  1464  func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
  1465  	// OomScoreAdj is not supported on Windows
  1466  	testRequires(c, DaemonIsLinux)
  1467  
  1468  	config := struct {
  1469  		Image       string
  1470  		OomScoreAdj int
  1471  	}{"busybox", 1001}
  1472  	name := "oomscoreadj-over"
  1473  	status, b, err := sockRequest("POST", "/containers/create?name="+name, config)
  1474  	c.Assert(err, check.IsNil)
  1475  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1476  
  1477  	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
  1478  	msg := getErrorMessage(c, b)
  1479  	if !strings.Contains(msg, expected) {
  1480  		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
  1481  	}
  1482  
  1483  	config = struct {
  1484  		Image       string
  1485  		OomScoreAdj int
  1486  	}{"busybox", -1001}
  1487  	name = "oomscoreadj-low"
  1488  	status, b, err = sockRequest("POST", "/containers/create?name="+name, config)
  1489  	c.Assert(err, check.IsNil)
  1490  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1491  	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
  1492  	msg = getErrorMessage(c, b)
  1493  	if !strings.Contains(msg, expected) {
  1494  		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
  1495  	}
  1496  }
  1497  
  1498  // test case for #22210 where an empty container name caused panic.
  1499  func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
  1500  	status, out, err := sockRequest("DELETE", "/containers/", nil)
  1501  	c.Assert(err, checker.IsNil)
  1502  	c.Assert(status, checker.Equals, http.StatusBadRequest)
  1503  	c.Assert(string(out), checker.Contains, "No container name or ID supplied")
  1504  }
  1505  
  1506  func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
  1507  	// Problematic on Windows as Windows does not support stats
  1508  	testRequires(c, DaemonIsLinux)
  1509  
  1510  	name := "testing-network-disabled"
  1511  	config := map[string]interface{}{
  1512  		"Image":           "busybox",
  1513  		"Cmd":             []string{"top"},
  1514  		"NetworkDisabled": true,
  1515  	}
  1516  
  1517  	status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  1518  	c.Assert(err, checker.IsNil)
  1519  	c.Assert(status, checker.Equals, http.StatusCreated)
  1520  
  1521  	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
  1522  	c.Assert(err, checker.IsNil)
  1523  	c.Assert(status, checker.Equals, http.StatusNoContent)
  1524  
  1525  	c.Assert(waitRun(name), check.IsNil)
  1526  
  1527  	type b struct {
  1528  		status int
  1529  		body   []byte
  1530  		err    error
  1531  	}
  1532  	bc := make(chan b, 1)
  1533  	go func() {
  1534  		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
  1535  		bc <- b{status, body, err}
  1536  	}()
  1537  
  1538  	// allow some time to stream the stats from the container
  1539  	time.Sleep(4 * time.Second)
  1540  	dockerCmd(c, "rm", "-f", name)
  1541  
  1542  	// collect the results from the stats stream or timeout and fail
  1543  	// if the stream was not disconnected.
  1544  	select {
  1545  	case <-time.After(2 * time.Second):
  1546  		c.Fatal("stream was not closed after container was removed")
  1547  	case sr := <-bc:
  1548  		c.Assert(sr.err, checker.IsNil)
  1549  		c.Assert(sr.status, checker.Equals, http.StatusOK)
  1550  
  1551  		// decode only one object from the stream
  1552  		var s *types.Stats
  1553  		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
  1554  		c.Assert(dec.Decode(&s), checker.IsNil)
  1555  	}
  1556  }
  1557  
  1558  func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
  1559  	type m mounttypes.Mount
  1560  	type hc struct{ Mounts []m }
  1561  	type cfg struct {
  1562  		Image      string
  1563  		HostConfig hc
  1564  	}
  1565  	type testCase struct {
  1566  		config cfg
  1567  		status int
  1568  		msg    string
  1569  	}
  1570  
  1571  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1572  	destPath := prefix + slash + "foo"
  1573  	notExistPath := prefix + slash + "notexist"
  1574  
  1575  	cases := []testCase{
  1576  		{
  1577  			config: cfg{
  1578  				Image: "busybox",
  1579  				HostConfig: hc{
  1580  					Mounts: []m{{
  1581  						Type:   "notreal",
  1582  						Target: destPath}}}},
  1583  			status: http.StatusBadRequest,
  1584  			msg:    "mount type unknown",
  1585  		},
  1586  		{
  1587  			config: cfg{
  1588  				Image: "busybox",
  1589  				HostConfig: hc{
  1590  					Mounts: []m{{
  1591  						Type: "bind"}}}},
  1592  			status: http.StatusBadRequest,
  1593  			msg:    "Target must not be empty",
  1594  		},
  1595  		{
  1596  			config: cfg{
  1597  				Image: "busybox",
  1598  				HostConfig: hc{
  1599  					Mounts: []m{{
  1600  						Type:   "bind",
  1601  						Target: destPath}}}},
  1602  			status: http.StatusBadRequest,
  1603  			msg:    "Source must not be empty",
  1604  		},
  1605  		{
  1606  			config: cfg{
  1607  				Image: "busybox",
  1608  				HostConfig: hc{
  1609  					Mounts: []m{{
  1610  						Type:   "bind",
  1611  						Source: notExistPath,
  1612  						Target: destPath}}}},
  1613  			status: http.StatusBadRequest,
  1614  			msg:    "bind source path does not exist",
  1615  		},
  1616  		{
  1617  			config: cfg{
  1618  				Image: "busybox",
  1619  				HostConfig: hc{
  1620  					Mounts: []m{{
  1621  						Type: "volume"}}}},
  1622  			status: http.StatusBadRequest,
  1623  			msg:    "Target must not be empty",
  1624  		},
  1625  		{
  1626  			config: cfg{
  1627  				Image: "busybox",
  1628  				HostConfig: hc{
  1629  					Mounts: []m{{
  1630  						Type:   "volume",
  1631  						Source: "hello",
  1632  						Target: destPath}}}},
  1633  			status: http.StatusCreated,
  1634  			msg:    "",
  1635  		},
  1636  		{
  1637  			config: cfg{
  1638  				Image: "busybox",
  1639  				HostConfig: hc{
  1640  					Mounts: []m{{
  1641  						Type:   "volume",
  1642  						Source: "hello2",
  1643  						Target: destPath,
  1644  						VolumeOptions: &mounttypes.VolumeOptions{
  1645  							DriverConfig: &mounttypes.Driver{
  1646  								Name: "local"}}}}}},
  1647  			status: http.StatusCreated,
  1648  			msg:    "",
  1649  		},
  1650  	}
  1651  
  1652  	if SameHostDaemon.Condition() {
  1653  		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
  1654  		c.Assert(err, checker.IsNil)
  1655  		defer os.RemoveAll(tmpDir)
  1656  		cases = append(cases, []testCase{
  1657  			{
  1658  				config: cfg{
  1659  					Image: "busybox",
  1660  					HostConfig: hc{
  1661  						Mounts: []m{{
  1662  							Type:   "bind",
  1663  							Source: tmpDir,
  1664  							Target: destPath}}}},
  1665  				status: http.StatusCreated,
  1666  				msg:    "",
  1667  			},
  1668  			{
  1669  				config: cfg{
  1670  					Image: "busybox",
  1671  					HostConfig: hc{
  1672  						Mounts: []m{{
  1673  							Type:          "bind",
  1674  							Source:        tmpDir,
  1675  							Target:        destPath,
  1676  							VolumeOptions: &mounttypes.VolumeOptions{}}}}},
  1677  				status: http.StatusBadRequest,
  1678  				msg:    "VolumeOptions must not be specified",
  1679  			},
  1680  		}...)
  1681  	}
  1682  
  1683  	if DaemonIsLinux.Condition() {
  1684  		cases = append(cases, []testCase{
  1685  			{
  1686  				config: cfg{
  1687  					Image: "busybox",
  1688  					HostConfig: hc{
  1689  						Mounts: []m{{
  1690  							Type:   "volume",
  1691  							Source: "hello3",
  1692  							Target: destPath,
  1693  							VolumeOptions: &mounttypes.VolumeOptions{
  1694  								DriverConfig: &mounttypes.Driver{
  1695  									Name:    "local",
  1696  									Options: map[string]string{"o": "size=1"}}}}}}},
  1697  				status: http.StatusCreated,
  1698  				msg:    "",
  1699  			},
  1700  			{
  1701  				config: cfg{
  1702  					Image: "busybox",
  1703  					HostConfig: hc{
  1704  						Mounts: []m{{
  1705  							Type:   "tmpfs",
  1706  							Target: destPath}}}},
  1707  				status: http.StatusCreated,
  1708  				msg:    "",
  1709  			},
  1710  			{
  1711  				config: cfg{
  1712  					Image: "busybox",
  1713  					HostConfig: hc{
  1714  						Mounts: []m{{
  1715  							Type:   "tmpfs",
  1716  							Target: destPath,
  1717  							TmpfsOptions: &mounttypes.TmpfsOptions{
  1718  								SizeBytes: 4096 * 1024,
  1719  								Mode:      0700,
  1720  							}}}}},
  1721  				status: http.StatusCreated,
  1722  				msg:    "",
  1723  			},
  1724  
  1725  			{
  1726  				config: cfg{
  1727  					Image: "busybox",
  1728  					HostConfig: hc{
  1729  						Mounts: []m{{
  1730  							Type:   "tmpfs",
  1731  							Source: "/shouldnotbespecified",
  1732  							Target: destPath}}}},
  1733  				status: http.StatusBadRequest,
  1734  				msg:    "Source must not be specified",
  1735  			},
  1736  		}...)
  1737  
  1738  	}
  1739  
  1740  	for i, x := range cases {
  1741  		c.Logf("case %d", i)
  1742  		status, b, err := sockRequest("POST", "/containers/create", x.config)
  1743  		c.Assert(err, checker.IsNil)
  1744  		c.Assert(status, checker.Equals, x.status, check.Commentf("%s\n%v", string(b), cases[i].config))
  1745  		if len(x.msg) > 0 {
  1746  			c.Assert(string(b), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
  1747  		}
  1748  	}
  1749  }
  1750  
  1751  func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
  1752  	testRequires(c, NotUserNamespace, SameHostDaemon)
  1753  	// also with data in the host side
  1754  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1755  	destPath := prefix + slash + "foo"
  1756  	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
  1757  	c.Assert(err, checker.IsNil)
  1758  	defer os.RemoveAll(tmpDir)
  1759  	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
  1760  	c.Assert(err, checker.IsNil)
  1761  
  1762  	data := map[string]interface{}{
  1763  		"Image":      "busybox",
  1764  		"Cmd":        []string{"/bin/sh", "-c", "cat /foo/bar"},
  1765  		"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{{"Type": "bind", "Source": tmpDir, "Target": destPath}}},
  1766  	}
  1767  	status, resp, err := sockRequest("POST", "/containers/create?name=test", data)
  1768  	c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
  1769  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
  1770  
  1771  	out, _ := dockerCmd(c, "start", "-a", "test")
  1772  	c.Assert(out, checker.Equals, "hello")
  1773  }
  1774  
  1775  // Test Mounts comes out as expected for the MountPoint
  1776  func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
  1777  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1778  	destPath := prefix + slash + "foo"
  1779  
  1780  	var (
  1781  		err     error
  1782  		testImg string
  1783  	)
  1784  	if daemonPlatform != "windows" {
  1785  		testImg, err = buildImage("test-mount-config", `
  1786  	FROM busybox
  1787  	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
  1788  	CMD cat `+destPath+slash+`bar
  1789  	`, true)
  1790  	} else {
  1791  		testImg = "busybox"
  1792  	}
  1793  	c.Assert(err, checker.IsNil)
  1794  
  1795  	type testCase struct {
  1796  		cfg      mounttypes.Mount
  1797  		expected types.MountPoint
  1798  	}
  1799  
  1800  	cases := []testCase{
  1801  		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
  1802  		// Validation of the actual `Mount` struct is done in another test is not needed here
  1803  		{mounttypes.Mount{Type: "volume", Target: destPath}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1804  		{mounttypes.Mount{Type: "volume", Target: destPath + slash}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1805  		{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath}},
  1806  		{mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath}},
  1807  		{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}},
  1808  	}
  1809  
  1810  	if SameHostDaemon.Condition() {
  1811  		// setup temp dir for testing binds
  1812  		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
  1813  		c.Assert(err, checker.IsNil)
  1814  		defer os.RemoveAll(tmpDir1)
  1815  		cases = append(cases, []testCase{
  1816  			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir1}},
  1817  			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}},
  1818  		}...)
  1819  
  1820  		// for modes only supported on Linux
  1821  		if DaemonIsLinux.Condition() {
  1822  			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
  1823  			c.Assert(err, checker.IsNil)
  1824  			defer os.RemoveAll(tmpDir3)
  1825  
  1826  			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
  1827  			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
  1828  
  1829  			cases = append(cases, []testCase{
  1830  				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}},
  1831  				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}},
  1832  				{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"}},
  1833  			}...)
  1834  		}
  1835  	}
  1836  
  1837  	if daemonPlatform != "windows" { // Windows does not support volume populate
  1838  		cases = append(cases, []testCase{
  1839  			{mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1840  			{mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1841  			{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath}},
  1842  			{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}},
  1843  		}...)
  1844  	}
  1845  
  1846  	type wrapper struct {
  1847  		containertypes.Config
  1848  		HostConfig containertypes.HostConfig
  1849  	}
  1850  	type createResp struct {
  1851  		ID string `json:"Id"`
  1852  	}
  1853  	for i, x := range cases {
  1854  		c.Logf("case %d - config: %v", i, x.cfg)
  1855  		status, data, err := sockRequest("POST", "/containers/create", wrapper{containertypes.Config{Image: testImg}, containertypes.HostConfig{Mounts: []mounttypes.Mount{x.cfg}}})
  1856  		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
  1857  		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(data)))
  1858  
  1859  		var resp createResp
  1860  		err = json.Unmarshal(data, &resp)
  1861  		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
  1862  		id := resp.ID
  1863  
  1864  		var mps []types.MountPoint
  1865  		err = json.NewDecoder(strings.NewReader(inspectFieldJSON(c, id, "Mounts"))).Decode(&mps)
  1866  		c.Assert(err, checker.IsNil)
  1867  		c.Assert(mps, checker.HasLen, 1)
  1868  		c.Assert(mps[0].Destination, checker.Equals, x.expected.Destination)
  1869  
  1870  		if len(x.expected.Source) > 0 {
  1871  			c.Assert(mps[0].Source, checker.Equals, x.expected.Source)
  1872  		}
  1873  		if len(x.expected.Name) > 0 {
  1874  			c.Assert(mps[0].Name, checker.Equals, x.expected.Name)
  1875  		}
  1876  		if len(x.expected.Driver) > 0 {
  1877  			c.Assert(mps[0].Driver, checker.Equals, x.expected.Driver)
  1878  		}
  1879  		c.Assert(mps[0].RW, checker.Equals, x.expected.RW)
  1880  		c.Assert(mps[0].Type, checker.Equals, x.expected.Type)
  1881  		c.Assert(mps[0].Mode, checker.Equals, x.expected.Mode)
  1882  		if len(x.expected.Propagation) > 0 {
  1883  			c.Assert(mps[0].Propagation, checker.Equals, x.expected.Propagation)
  1884  		}
  1885  
  1886  		out, _, err := dockerCmdWithError("start", "-a", id)
  1887  		if (x.cfg.Type != "volume" || (x.cfg.VolumeOptions != nil && x.cfg.VolumeOptions.NoCopy)) && daemonPlatform != "windows" {
  1888  			c.Assert(err, checker.NotNil, check.Commentf("%s\n%v", out, mps[0]))
  1889  		} else {
  1890  			c.Assert(err, checker.IsNil, check.Commentf("%s\n%v", out, mps[0]))
  1891  		}
  1892  
  1893  		dockerCmd(c, "rm", "-fv", id)
  1894  		if x.cfg.Type == "volume" && len(x.cfg.Source) > 0 {
  1895  			// This should still exist even though we removed the container
  1896  			dockerCmd(c, "volume", "inspect", mps[0].Name)
  1897  		} else {
  1898  			// This should be removed automatically when we removed the container
  1899  			out, _, err := dockerCmdWithError("volume", "inspect", mps[0].Name)
  1900  			c.Assert(err, checker.NotNil, check.Commentf(out))
  1901  		}
  1902  	}
  1903  }
  1904  
  1905  func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
  1906  	testRequires(c, DaemonIsLinux)
  1907  	type testCase struct {
  1908  		cfg             map[string]interface{}
  1909  		expectedOptions []string
  1910  	}
  1911  	target := "/foo"
  1912  	cases := []testCase{
  1913  		{
  1914  			cfg: map[string]interface{}{
  1915  				"Type":   "tmpfs",
  1916  				"Target": target},
  1917  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
  1918  		},
  1919  		{
  1920  			cfg: map[string]interface{}{
  1921  				"Type":   "tmpfs",
  1922  				"Target": target,
  1923  				"TmpfsOptions": map[string]interface{}{
  1924  					"SizeBytes": 4096 * 1024, "Mode": 0700}},
  1925  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
  1926  		},
  1927  	}
  1928  
  1929  	for i, x := range cases {
  1930  		cName := fmt.Sprintf("test-tmpfs-%d", i)
  1931  		data := map[string]interface{}{
  1932  			"Image": "busybox",
  1933  			"Cmd": []string{"/bin/sh", "-c",
  1934  				fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
  1935  			"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{x.cfg}},
  1936  		}
  1937  		status, resp, err := sockRequest("POST", "/containers/create?name="+cName, data)
  1938  		c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
  1939  		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
  1940  		out, _ := dockerCmd(c, "start", "-a", cName)
  1941  		for _, option := range x.expectedOptions {
  1942  			c.Assert(out, checker.Contains, option)
  1943  		}
  1944  	}
  1945  }