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