github.com/kim0/docker@v0.6.2-0.20161130212042-4addda3f07e7/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 types.ContainerCreateResponse
   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 types.ContainerCreateResponse
   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 types.ContainerCreateResponse
   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 types.ContainerCreateResponse
   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 types.ContainerWaitResponse
   961  	c.Assert(json.Unmarshal(body, &waitres), checker.IsNil)
   962  	c.Assert(waitres.StatusCode, checker.Equals, 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  
   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  	name := "test-container-api-copy-resource-empty"
  1010  	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
  1011  
  1012  	postData := types.CopyConfig{
  1013  		Resource: "",
  1014  	}
  1015  
  1016  	status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
  1017  	c.Assert(err, checker.IsNil)
  1018  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1019  	c.Assert(string(body), checker.Matches, "Path cannot be empty\n")
  1020  }
  1021  
  1022  func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C) {
  1023  	name := "test-container-api-copy-resource-not-found"
  1024  	dockerCmd(c, "run", "--name", name, "busybox")
  1025  
  1026  	postData := types.CopyConfig{
  1027  		Resource: "/notexist",
  1028  	}
  1029  
  1030  	status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
  1031  	c.Assert(err, checker.IsNil)
  1032  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1033  	c.Assert(string(body), checker.Matches, "Could not find the file /notexist in container "+name+"\n")
  1034  }
  1035  
  1036  func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *check.C) {
  1037  	postData := types.CopyConfig{
  1038  		Resource: "/something",
  1039  	}
  1040  
  1041  	status, _, err := sockRequest("POST", "/v1.23/containers/notexists/copy", postData)
  1042  	c.Assert(err, checker.IsNil)
  1043  	c.Assert(status, checker.Equals, http.StatusNotFound)
  1044  }
  1045  
  1046  func (s *DockerSuite) TestContainerAPIDelete(c *check.C) {
  1047  	out, _ := runSleepingContainer(c)
  1048  
  1049  	id := strings.TrimSpace(out)
  1050  	c.Assert(waitRun(id), checker.IsNil)
  1051  
  1052  	dockerCmd(c, "stop", id)
  1053  
  1054  	status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
  1055  	c.Assert(err, checker.IsNil)
  1056  	c.Assert(status, checker.Equals, http.StatusNoContent)
  1057  }
  1058  
  1059  func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) {
  1060  	status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil)
  1061  	c.Assert(err, checker.IsNil)
  1062  	c.Assert(status, checker.Equals, http.StatusNotFound)
  1063  	c.Assert(getErrorMessage(c, body), checker.Matches, "No such container: doesnotexist")
  1064  }
  1065  
  1066  func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) {
  1067  	out, _ := runSleepingContainer(c)
  1068  
  1069  	id := strings.TrimSpace(out)
  1070  	c.Assert(waitRun(id), checker.IsNil)
  1071  
  1072  	status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil)
  1073  	c.Assert(err, checker.IsNil)
  1074  	c.Assert(status, checker.Equals, http.StatusNoContent)
  1075  }
  1076  
  1077  func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *check.C) {
  1078  	// Windows does not support links
  1079  	testRequires(c, DaemonIsLinux)
  1080  	out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top")
  1081  
  1082  	id := strings.TrimSpace(out)
  1083  	c.Assert(waitRun(id), checker.IsNil)
  1084  
  1085  	out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top")
  1086  
  1087  	id2 := strings.TrimSpace(out)
  1088  	c.Assert(waitRun(id2), checker.IsNil)
  1089  
  1090  	links := inspectFieldJSON(c, id2, "HostConfig.Links")
  1091  	c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers"))
  1092  
  1093  	status, b, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
  1094  	c.Assert(err, check.IsNil)
  1095  	c.Assert(status, check.Equals, http.StatusNoContent, check.Commentf(string(b)))
  1096  
  1097  	linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links")
  1098  	c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links"))
  1099  }
  1100  
  1101  func (s *DockerSuite) TestContainerAPIDeleteConflict(c *check.C) {
  1102  	out, _ := runSleepingContainer(c)
  1103  
  1104  	id := strings.TrimSpace(out)
  1105  	c.Assert(waitRun(id), checker.IsNil)
  1106  
  1107  	status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
  1108  	c.Assert(err, checker.IsNil)
  1109  	c.Assert(status, checker.Equals, http.StatusConflict)
  1110  }
  1111  
  1112  func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) {
  1113  	testRequires(c, SameHostDaemon)
  1114  
  1115  	vol := "/testvolume"
  1116  	if daemonPlatform == "windows" {
  1117  		vol = `c:\testvolume`
  1118  	}
  1119  
  1120  	out, _ := runSleepingContainer(c, "-v", vol)
  1121  
  1122  	id := strings.TrimSpace(out)
  1123  	c.Assert(waitRun(id), checker.IsNil)
  1124  
  1125  	source, err := inspectMountSourceField(id, vol)
  1126  	_, err = os.Stat(source)
  1127  	c.Assert(err, checker.IsNil)
  1128  
  1129  	status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
  1130  	c.Assert(err, checker.IsNil)
  1131  	c.Assert(status, checker.Equals, http.StatusNoContent)
  1132  	_, err = os.Stat(source)
  1133  	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err))
  1134  }
  1135  
  1136  // Regression test for https://github.com/docker/docker/issues/6231
  1137  func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) {
  1138  	conn, err := sockConn(time.Duration(10*time.Second), "")
  1139  	c.Assert(err, checker.IsNil)
  1140  	client := httputil.NewClientConn(conn, nil)
  1141  	defer client.Close()
  1142  
  1143  	config := map[string]interface{}{
  1144  		"Image":     "busybox",
  1145  		"Cmd":       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
  1146  		"OpenStdin": true,
  1147  	}
  1148  	b, err := json.Marshal(config)
  1149  	c.Assert(err, checker.IsNil)
  1150  
  1151  	req, err := http.NewRequest("POST", "/containers/create", bytes.NewBuffer(b))
  1152  	c.Assert(err, checker.IsNil)
  1153  	req.Header.Set("Content-Type", "application/json")
  1154  	// This is a cheat to make the http request do chunked encoding
  1155  	// Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
  1156  	// https://golang.org/src/pkg/net/http/request.go?s=11980:12172
  1157  	req.ContentLength = -1
  1158  
  1159  	resp, err := client.Do(req)
  1160  	c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding"))
  1161  	resp.Body.Close()
  1162  	c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated)
  1163  }
  1164  
  1165  func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) {
  1166  	out, _ := runSleepingContainer(c)
  1167  
  1168  	containerID := strings.TrimSpace(out)
  1169  	c.Assert(waitRun(containerID), checker.IsNil)
  1170  
  1171  	statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil)
  1172  	c.Assert(err, checker.IsNil)
  1173  	// 204 No Content is expected, not 200
  1174  	c.Assert(statusCode, checker.Equals, http.StatusNoContent)
  1175  	c.Assert(waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
  1176  }
  1177  
  1178  // #14170
  1179  func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) {
  1180  	config := struct {
  1181  		Image      string
  1182  		Entrypoint string
  1183  		Cmd        []string
  1184  	}{"busybox", "echo", []string{"hello", "world"}}
  1185  	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1186  	c.Assert(err, checker.IsNil)
  1187  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1188  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1189  
  1190  	config2 := struct {
  1191  		Image      string
  1192  		Entrypoint []string
  1193  		Cmd        []string
  1194  	}{"busybox", []string{"echo"}, []string{"hello", "world"}}
  1195  	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1196  	c.Assert(err, checker.IsNil)
  1197  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1198  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1199  }
  1200  
  1201  // #14170
  1202  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
  1203  	config := struct {
  1204  		Image      string
  1205  		Entrypoint string
  1206  		Cmd        string
  1207  	}{"busybox", "echo", "hello world"}
  1208  	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
  1209  	c.Assert(err, checker.IsNil)
  1210  	out, _ := dockerCmd(c, "start", "-a", "echotest")
  1211  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1212  
  1213  	config2 := struct {
  1214  		Image string
  1215  		Cmd   []string
  1216  	}{"busybox", []string{"echo", "hello", "world"}}
  1217  	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
  1218  	c.Assert(err, checker.IsNil)
  1219  	out, _ = dockerCmd(c, "start", "-a", "echotest2")
  1220  	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
  1221  }
  1222  
  1223  // regression #14318
  1224  func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
  1225  	// Windows doesn't support CapAdd/CapDrop
  1226  	testRequires(c, DaemonIsLinux)
  1227  	config := struct {
  1228  		Image   string
  1229  		CapAdd  string
  1230  		CapDrop string
  1231  	}{"busybox", "NET_ADMIN", "SYS_ADMIN"}
  1232  	status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config)
  1233  	c.Assert(err, checker.IsNil)
  1234  	c.Assert(status, checker.Equals, http.StatusCreated)
  1235  
  1236  	config2 := struct {
  1237  		Image   string
  1238  		CapAdd  []string
  1239  		CapDrop []string
  1240  	}{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}}
  1241  	status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2)
  1242  	c.Assert(err, checker.IsNil)
  1243  	c.Assert(status, checker.Equals, http.StatusCreated)
  1244  }
  1245  
  1246  // #14915
  1247  func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) {
  1248  	config := struct {
  1249  		Image string
  1250  	}{"busybox"}
  1251  	status, _, err := sockRequest("POST", "/v1.18/containers/create", config)
  1252  	c.Assert(err, checker.IsNil)
  1253  	c.Assert(status, checker.Equals, http.StatusCreated)
  1254  }
  1255  
  1256  // Ensure an error occurs when you have a container read-only rootfs but you
  1257  // extract an archive to a symlink in a writable volume which points to a
  1258  // directory outside of the volume.
  1259  func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
  1260  	// Windows does not support read-only rootfs
  1261  	// Requires local volume mount bind.
  1262  	// --read-only + userns has remount issues
  1263  	testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
  1264  
  1265  	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
  1266  	defer os.RemoveAll(testVol)
  1267  
  1268  	makeTestContentInDir(c, testVol)
  1269  
  1270  	cID := makeTestContainer(c, testContainerOptions{
  1271  		readOnly: true,
  1272  		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
  1273  	})
  1274  	defer deleteContainer(cID)
  1275  
  1276  	// Attempt to extract to a symlink in the volume which points to a
  1277  	// directory outside the volume. This should cause an error because the
  1278  	// rootfs is read-only.
  1279  	query := make(url.Values, 1)
  1280  	query.Set("path", "/vol2/symlinkToAbsDir")
  1281  	urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode())
  1282  
  1283  	statusCode, body, err := sockRequest("PUT", urlPath, nil)
  1284  	c.Assert(err, checker.IsNil)
  1285  
  1286  	if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) {
  1287  		c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body))
  1288  	}
  1289  }
  1290  
  1291  func (s *DockerSuite) TestContainerAPIGetContainersJSONEmpty(c *check.C) {
  1292  	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
  1293  	c.Assert(err, checker.IsNil)
  1294  	c.Assert(status, checker.Equals, http.StatusOK)
  1295  	c.Assert(string(body), checker.Equals, "[]\n")
  1296  }
  1297  
  1298  func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
  1299  	// Not supported on Windows
  1300  	testRequires(c, DaemonIsLinux)
  1301  
  1302  	c1 := struct {
  1303  		Image      string
  1304  		CpusetCpus string
  1305  	}{"busybox", "1-42,,"}
  1306  	name := "wrong-cpuset-cpus"
  1307  	status, body, err := sockRequest("POST", "/containers/create?name="+name, c1)
  1308  	c.Assert(err, checker.IsNil)
  1309  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1310  	expected := "Invalid value 1-42,, for cpuset cpus"
  1311  	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
  1312  
  1313  	c2 := struct {
  1314  		Image      string
  1315  		CpusetMems string
  1316  	}{"busybox", "42-3,1--"}
  1317  	name = "wrong-cpuset-mems"
  1318  	status, body, err = sockRequest("POST", "/containers/create?name="+name, c2)
  1319  	c.Assert(err, checker.IsNil)
  1320  	c.Assert(status, checker.Equals, http.StatusInternalServerError)
  1321  	expected = "Invalid value 42-3,1-- for cpuset mems"
  1322  	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
  1323  }
  1324  
  1325  func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
  1326  	// ShmSize is not supported on Windows
  1327  	testRequires(c, DaemonIsLinux)
  1328  	config := map[string]interface{}{
  1329  		"Image":      "busybox",
  1330  		"HostConfig": map[string]interface{}{"ShmSize": -1},
  1331  	}
  1332  
  1333  	status, body, err := sockRequest("POST", "/containers/create", config)
  1334  	c.Assert(err, check.IsNil)
  1335  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1336  	c.Assert(getErrorMessage(c, body), checker.Contains, "SHM size can not be less than 0")
  1337  }
  1338  
  1339  func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
  1340  	// ShmSize is not supported on Windows
  1341  	testRequires(c, DaemonIsLinux)
  1342  	var defaultSHMSize int64 = 67108864
  1343  	config := map[string]interface{}{
  1344  		"Image": "busybox",
  1345  		"Cmd":   "mount",
  1346  	}
  1347  
  1348  	status, body, err := sockRequest("POST", "/containers/create", config)
  1349  	c.Assert(err, check.IsNil)
  1350  	c.Assert(status, check.Equals, http.StatusCreated)
  1351  
  1352  	var container types.ContainerCreateResponse
  1353  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1354  
  1355  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1356  	c.Assert(err, check.IsNil)
  1357  	c.Assert(status, check.Equals, http.StatusOK)
  1358  
  1359  	var containerJSON types.ContainerJSON
  1360  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1361  
  1362  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
  1363  
  1364  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1365  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1366  	if !shmRegexp.MatchString(out) {
  1367  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1368  	}
  1369  }
  1370  
  1371  func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
  1372  	// ShmSize is not supported on Windows
  1373  	testRequires(c, DaemonIsLinux)
  1374  	config := map[string]interface{}{
  1375  		"Image":      "busybox",
  1376  		"HostConfig": map[string]interface{}{},
  1377  		"Cmd":        "mount",
  1378  	}
  1379  
  1380  	status, body, err := sockRequest("POST", "/containers/create", config)
  1381  	c.Assert(err, check.IsNil)
  1382  	c.Assert(status, check.Equals, http.StatusCreated)
  1383  
  1384  	var container types.ContainerCreateResponse
  1385  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1386  
  1387  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1388  	c.Assert(err, check.IsNil)
  1389  	c.Assert(status, check.Equals, http.StatusOK)
  1390  
  1391  	var containerJSON types.ContainerJSON
  1392  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1393  
  1394  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
  1395  
  1396  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1397  	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
  1398  	if !shmRegexp.MatchString(out) {
  1399  		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
  1400  	}
  1401  }
  1402  
  1403  func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
  1404  	// ShmSize is not supported on Windows
  1405  	testRequires(c, DaemonIsLinux)
  1406  	config := map[string]interface{}{
  1407  		"Image":      "busybox",
  1408  		"Cmd":        "mount",
  1409  		"HostConfig": map[string]interface{}{"ShmSize": 1073741824},
  1410  	}
  1411  
  1412  	status, body, err := sockRequest("POST", "/containers/create", config)
  1413  	c.Assert(err, check.IsNil)
  1414  	c.Assert(status, check.Equals, http.StatusCreated)
  1415  
  1416  	var container types.ContainerCreateResponse
  1417  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1418  
  1419  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1420  	c.Assert(err, check.IsNil)
  1421  	c.Assert(status, check.Equals, http.StatusOK)
  1422  
  1423  	var containerJSON types.ContainerJSON
  1424  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1425  
  1426  	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
  1427  
  1428  	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
  1429  	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
  1430  	if !shmRegex.MatchString(out) {
  1431  		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
  1432  	}
  1433  }
  1434  
  1435  func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
  1436  	// Swappiness is not supported on Windows
  1437  	testRequires(c, DaemonIsLinux)
  1438  	config := map[string]interface{}{
  1439  		"Image": "busybox",
  1440  	}
  1441  
  1442  	status, body, err := sockRequest("POST", "/containers/create", config)
  1443  	c.Assert(err, check.IsNil)
  1444  	c.Assert(status, check.Equals, http.StatusCreated)
  1445  
  1446  	var container types.ContainerCreateResponse
  1447  	c.Assert(json.Unmarshal(body, &container), check.IsNil)
  1448  
  1449  	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
  1450  	c.Assert(err, check.IsNil)
  1451  	c.Assert(status, check.Equals, http.StatusOK)
  1452  
  1453  	var containerJSON types.ContainerJSON
  1454  	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
  1455  
  1456  	c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
  1457  }
  1458  
  1459  // check validation is done daemon side and not only in cli
  1460  func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
  1461  	// OomScoreAdj is not supported on Windows
  1462  	testRequires(c, DaemonIsLinux)
  1463  
  1464  	config := struct {
  1465  		Image       string
  1466  		OomScoreAdj int
  1467  	}{"busybox", 1001}
  1468  	name := "oomscoreadj-over"
  1469  	status, b, err := sockRequest("POST", "/containers/create?name="+name, config)
  1470  	c.Assert(err, check.IsNil)
  1471  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1472  
  1473  	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
  1474  	msg := getErrorMessage(c, b)
  1475  	if !strings.Contains(msg, expected) {
  1476  		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
  1477  	}
  1478  
  1479  	config = struct {
  1480  		Image       string
  1481  		OomScoreAdj int
  1482  	}{"busybox", -1001}
  1483  	name = "oomscoreadj-low"
  1484  	status, b, err = sockRequest("POST", "/containers/create?name="+name, config)
  1485  	c.Assert(err, check.IsNil)
  1486  	c.Assert(status, check.Equals, http.StatusInternalServerError)
  1487  	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
  1488  	msg = getErrorMessage(c, b)
  1489  	if !strings.Contains(msg, expected) {
  1490  		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
  1491  	}
  1492  }
  1493  
  1494  // test case for #22210 where an empty container name caused panic.
  1495  func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
  1496  	status, out, err := sockRequest("DELETE", "/containers/", nil)
  1497  	c.Assert(err, checker.IsNil)
  1498  	c.Assert(status, checker.Equals, http.StatusBadRequest)
  1499  	c.Assert(string(out), checker.Contains, "No container name or ID supplied")
  1500  }
  1501  
  1502  func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
  1503  	// Problematic on Windows as Windows does not support stats
  1504  	testRequires(c, DaemonIsLinux)
  1505  
  1506  	name := "testing-network-disabled"
  1507  	config := map[string]interface{}{
  1508  		"Image":           "busybox",
  1509  		"Cmd":             []string{"top"},
  1510  		"NetworkDisabled": true,
  1511  	}
  1512  
  1513  	status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
  1514  	c.Assert(err, checker.IsNil)
  1515  	c.Assert(status, checker.Equals, http.StatusCreated)
  1516  
  1517  	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
  1518  	c.Assert(err, checker.IsNil)
  1519  	c.Assert(status, checker.Equals, http.StatusNoContent)
  1520  
  1521  	c.Assert(waitRun(name), check.IsNil)
  1522  
  1523  	type b struct {
  1524  		status int
  1525  		body   []byte
  1526  		err    error
  1527  	}
  1528  	bc := make(chan b, 1)
  1529  	go func() {
  1530  		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
  1531  		bc <- b{status, body, err}
  1532  	}()
  1533  
  1534  	// allow some time to stream the stats from the container
  1535  	time.Sleep(4 * time.Second)
  1536  	dockerCmd(c, "rm", "-f", name)
  1537  
  1538  	// collect the results from the stats stream or timeout and fail
  1539  	// if the stream was not disconnected.
  1540  	select {
  1541  	case <-time.After(2 * time.Second):
  1542  		c.Fatal("stream was not closed after container was removed")
  1543  	case sr := <-bc:
  1544  		c.Assert(sr.err, checker.IsNil)
  1545  		c.Assert(sr.status, checker.Equals, http.StatusOK)
  1546  
  1547  		// decode only one object from the stream
  1548  		var s *types.Stats
  1549  		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
  1550  		c.Assert(dec.Decode(&s), checker.IsNil)
  1551  	}
  1552  }
  1553  
  1554  func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
  1555  	type m mounttypes.Mount
  1556  	type hc struct{ Mounts []m }
  1557  	type cfg struct {
  1558  		Image      string
  1559  		HostConfig hc
  1560  	}
  1561  	type testCase struct {
  1562  		config cfg
  1563  		status int
  1564  		msg    string
  1565  	}
  1566  
  1567  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1568  	destPath := prefix + slash + "foo"
  1569  	notExistPath := prefix + slash + "notexist"
  1570  
  1571  	cases := []testCase{
  1572  		{
  1573  			config: cfg{
  1574  				Image: "busybox",
  1575  				HostConfig: hc{
  1576  					Mounts: []m{{
  1577  						Type:   "notreal",
  1578  						Target: destPath}}}},
  1579  			status: http.StatusBadRequest,
  1580  			msg:    "mount type unknown",
  1581  		},
  1582  		{
  1583  			config: cfg{
  1584  				Image: "busybox",
  1585  				HostConfig: hc{
  1586  					Mounts: []m{{
  1587  						Type: "bind"}}}},
  1588  			status: http.StatusBadRequest,
  1589  			msg:    "Target must not be empty",
  1590  		},
  1591  		{
  1592  			config: cfg{
  1593  				Image: "busybox",
  1594  				HostConfig: hc{
  1595  					Mounts: []m{{
  1596  						Type:   "bind",
  1597  						Target: destPath}}}},
  1598  			status: http.StatusBadRequest,
  1599  			msg:    "Source must not be empty",
  1600  		},
  1601  		{
  1602  			config: cfg{
  1603  				Image: "busybox",
  1604  				HostConfig: hc{
  1605  					Mounts: []m{{
  1606  						Type:   "bind",
  1607  						Source: notExistPath,
  1608  						Target: destPath}}}},
  1609  			status: http.StatusBadRequest,
  1610  			msg:    "bind source path does not exist",
  1611  		},
  1612  		{
  1613  			config: cfg{
  1614  				Image: "busybox",
  1615  				HostConfig: hc{
  1616  					Mounts: []m{{
  1617  						Type: "volume"}}}},
  1618  			status: http.StatusBadRequest,
  1619  			msg:    "Target must not be empty",
  1620  		},
  1621  		{
  1622  			config: cfg{
  1623  				Image: "busybox",
  1624  				HostConfig: hc{
  1625  					Mounts: []m{{
  1626  						Type:   "volume",
  1627  						Source: "hello",
  1628  						Target: destPath}}}},
  1629  			status: http.StatusCreated,
  1630  			msg:    "",
  1631  		},
  1632  		{
  1633  			config: cfg{
  1634  				Image: "busybox",
  1635  				HostConfig: hc{
  1636  					Mounts: []m{{
  1637  						Type:   "volume",
  1638  						Source: "hello2",
  1639  						Target: destPath,
  1640  						VolumeOptions: &mounttypes.VolumeOptions{
  1641  							DriverConfig: &mounttypes.Driver{
  1642  								Name: "local"}}}}}},
  1643  			status: http.StatusCreated,
  1644  			msg:    "",
  1645  		},
  1646  	}
  1647  
  1648  	if SameHostDaemon.Condition() {
  1649  		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
  1650  		c.Assert(err, checker.IsNil)
  1651  		defer os.RemoveAll(tmpDir)
  1652  		cases = append(cases, []testCase{
  1653  			{
  1654  				config: cfg{
  1655  					Image: "busybox",
  1656  					HostConfig: hc{
  1657  						Mounts: []m{{
  1658  							Type:   "bind",
  1659  							Source: tmpDir,
  1660  							Target: destPath}}}},
  1661  				status: http.StatusCreated,
  1662  				msg:    "",
  1663  			},
  1664  			{
  1665  				config: cfg{
  1666  					Image: "busybox",
  1667  					HostConfig: hc{
  1668  						Mounts: []m{{
  1669  							Type:          "bind",
  1670  							Source:        tmpDir,
  1671  							Target:        destPath,
  1672  							VolumeOptions: &mounttypes.VolumeOptions{}}}}},
  1673  				status: http.StatusBadRequest,
  1674  				msg:    "VolumeOptions must not be specified",
  1675  			},
  1676  		}...)
  1677  	}
  1678  
  1679  	if DaemonIsLinux.Condition() {
  1680  		cases = append(cases, []testCase{
  1681  			{
  1682  				config: cfg{
  1683  					Image: "busybox",
  1684  					HostConfig: hc{
  1685  						Mounts: []m{{
  1686  							Type:   "volume",
  1687  							Source: "hello3",
  1688  							Target: destPath,
  1689  							VolumeOptions: &mounttypes.VolumeOptions{
  1690  								DriverConfig: &mounttypes.Driver{
  1691  									Name:    "local",
  1692  									Options: map[string]string{"o": "size=1"}}}}}}},
  1693  				status: http.StatusCreated,
  1694  				msg:    "",
  1695  			},
  1696  			{
  1697  				config: cfg{
  1698  					Image: "busybox",
  1699  					HostConfig: hc{
  1700  						Mounts: []m{{
  1701  							Type:   "tmpfs",
  1702  							Target: destPath}}}},
  1703  				status: http.StatusCreated,
  1704  				msg:    "",
  1705  			},
  1706  			{
  1707  				config: cfg{
  1708  					Image: "busybox",
  1709  					HostConfig: hc{
  1710  						Mounts: []m{{
  1711  							Type:   "tmpfs",
  1712  							Target: destPath,
  1713  							TmpfsOptions: &mounttypes.TmpfsOptions{
  1714  								SizeBytes: 4096 * 1024,
  1715  								Mode:      0700,
  1716  							}}}}},
  1717  				status: http.StatusCreated,
  1718  				msg:    "",
  1719  			},
  1720  
  1721  			{
  1722  				config: cfg{
  1723  					Image: "busybox",
  1724  					HostConfig: hc{
  1725  						Mounts: []m{{
  1726  							Type:   "tmpfs",
  1727  							Source: "/shouldnotbespecified",
  1728  							Target: destPath}}}},
  1729  				status: http.StatusBadRequest,
  1730  				msg:    "Source must not be specified",
  1731  			},
  1732  		}...)
  1733  
  1734  	}
  1735  
  1736  	for i, x := range cases {
  1737  		c.Logf("case %d", i)
  1738  		status, b, err := sockRequest("POST", "/containers/create", x.config)
  1739  		c.Assert(err, checker.IsNil)
  1740  		c.Assert(status, checker.Equals, x.status, check.Commentf("%s\n%v", string(b), cases[i].config))
  1741  		if len(x.msg) > 0 {
  1742  			c.Assert(string(b), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
  1743  		}
  1744  	}
  1745  }
  1746  
  1747  func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
  1748  	testRequires(c, NotUserNamespace, SameHostDaemon)
  1749  	// also with data in the host side
  1750  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1751  	destPath := prefix + slash + "foo"
  1752  	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
  1753  	c.Assert(err, checker.IsNil)
  1754  	defer os.RemoveAll(tmpDir)
  1755  	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
  1756  	c.Assert(err, checker.IsNil)
  1757  
  1758  	data := map[string]interface{}{
  1759  		"Image":      "busybox",
  1760  		"Cmd":        []string{"/bin/sh", "-c", "cat /foo/bar"},
  1761  		"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{{"Type": "bind", "Source": tmpDir, "Target": destPath}}},
  1762  	}
  1763  	status, resp, err := sockRequest("POST", "/containers/create?name=test", data)
  1764  	c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
  1765  	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
  1766  
  1767  	out, _ := dockerCmd(c, "start", "-a", "test")
  1768  	c.Assert(out, checker.Equals, "hello")
  1769  }
  1770  
  1771  // Test Mounts comes out as expected for the MountPoint
  1772  func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
  1773  	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
  1774  	destPath := prefix + slash + "foo"
  1775  
  1776  	var (
  1777  		err     error
  1778  		testImg string
  1779  	)
  1780  	if daemonPlatform != "windows" {
  1781  		testImg, err = buildImage("test-mount-config", `
  1782  	FROM busybox
  1783  	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
  1784  	CMD cat `+destPath+slash+`bar
  1785  	`, true)
  1786  	} else {
  1787  		testImg = "busybox"
  1788  	}
  1789  	c.Assert(err, checker.IsNil)
  1790  
  1791  	type testCase struct {
  1792  		cfg      mounttypes.Mount
  1793  		expected types.MountPoint
  1794  	}
  1795  
  1796  	cases := []testCase{
  1797  		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
  1798  		// Validation of the actual `Mount` struct is done in another test is not needed here
  1799  		{mounttypes.Mount{Type: "volume", Target: destPath}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1800  		{mounttypes.Mount{Type: "volume", Target: destPath + slash}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1801  		{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath}},
  1802  		{mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath}},
  1803  		{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}},
  1804  	}
  1805  
  1806  	if SameHostDaemon.Condition() {
  1807  		// setup temp dir for testing binds
  1808  		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
  1809  		c.Assert(err, checker.IsNil)
  1810  		defer os.RemoveAll(tmpDir1)
  1811  		cases = append(cases, []testCase{
  1812  			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir1}},
  1813  			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}},
  1814  		}...)
  1815  
  1816  		// for modes only supported on Linux
  1817  		if DaemonIsLinux.Condition() {
  1818  			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
  1819  			c.Assert(err, checker.IsNil)
  1820  			defer os.RemoveAll(tmpDir3)
  1821  
  1822  			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
  1823  			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
  1824  
  1825  			cases = append(cases, []testCase{
  1826  				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}},
  1827  				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}},
  1828  				{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"}},
  1829  			}...)
  1830  		}
  1831  	}
  1832  
  1833  	if daemonPlatform != "windows" { // Windows does not support volume populate
  1834  		cases = append(cases, []testCase{
  1835  			{mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1836  			{mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
  1837  			{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath}},
  1838  			{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}},
  1839  		}...)
  1840  	}
  1841  
  1842  	type wrapper struct {
  1843  		containertypes.Config
  1844  		HostConfig containertypes.HostConfig
  1845  	}
  1846  	type createResp struct {
  1847  		ID string `json:"Id"`
  1848  	}
  1849  	for i, x := range cases {
  1850  		c.Logf("case %d - config: %v", i, x.cfg)
  1851  		status, data, err := sockRequest("POST", "/containers/create", wrapper{containertypes.Config{Image: testImg}, containertypes.HostConfig{Mounts: []mounttypes.Mount{x.cfg}}})
  1852  		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
  1853  		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(data)))
  1854  
  1855  		var resp createResp
  1856  		err = json.Unmarshal(data, &resp)
  1857  		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
  1858  		id := resp.ID
  1859  
  1860  		var mps []types.MountPoint
  1861  		err = json.NewDecoder(strings.NewReader(inspectFieldJSON(c, id, "Mounts"))).Decode(&mps)
  1862  		c.Assert(err, checker.IsNil)
  1863  		c.Assert(mps, checker.HasLen, 1)
  1864  		c.Assert(mps[0].Destination, checker.Equals, x.expected.Destination)
  1865  
  1866  		if len(x.expected.Source) > 0 {
  1867  			c.Assert(mps[0].Source, checker.Equals, x.expected.Source)
  1868  		}
  1869  		if len(x.expected.Name) > 0 {
  1870  			c.Assert(mps[0].Name, checker.Equals, x.expected.Name)
  1871  		}
  1872  		if len(x.expected.Driver) > 0 {
  1873  			c.Assert(mps[0].Driver, checker.Equals, x.expected.Driver)
  1874  		}
  1875  		c.Assert(mps[0].RW, checker.Equals, x.expected.RW)
  1876  		c.Assert(mps[0].Type, checker.Equals, x.expected.Type)
  1877  		c.Assert(mps[0].Mode, checker.Equals, x.expected.Mode)
  1878  		if len(x.expected.Propagation) > 0 {
  1879  			c.Assert(mps[0].Propagation, checker.Equals, x.expected.Propagation)
  1880  		}
  1881  
  1882  		out, _, err := dockerCmdWithError("start", "-a", id)
  1883  		if (x.cfg.Type != "volume" || (x.cfg.VolumeOptions != nil && x.cfg.VolumeOptions.NoCopy)) && daemonPlatform != "windows" {
  1884  			c.Assert(err, checker.NotNil, check.Commentf("%s\n%v", out, mps[0]))
  1885  		} else {
  1886  			c.Assert(err, checker.IsNil, check.Commentf("%s\n%v", out, mps[0]))
  1887  		}
  1888  
  1889  		dockerCmd(c, "rm", "-fv", id)
  1890  		if x.cfg.Type == "volume" && len(x.cfg.Source) > 0 {
  1891  			// This should still exist even though we removed the container
  1892  			dockerCmd(c, "volume", "inspect", mps[0].Name)
  1893  		} else {
  1894  			// This should be removed automatically when we removed the container
  1895  			out, _, err := dockerCmdWithError("volume", "inspect", mps[0].Name)
  1896  			c.Assert(err, checker.NotNil, check.Commentf(out))
  1897  		}
  1898  	}
  1899  }
  1900  
  1901  func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
  1902  	testRequires(c, DaemonIsLinux)
  1903  	type testCase struct {
  1904  		cfg             map[string]interface{}
  1905  		expectedOptions []string
  1906  	}
  1907  	target := "/foo"
  1908  	cases := []testCase{
  1909  		{
  1910  			cfg: map[string]interface{}{
  1911  				"Type":   "tmpfs",
  1912  				"Target": target},
  1913  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
  1914  		},
  1915  		{
  1916  			cfg: map[string]interface{}{
  1917  				"Type":   "tmpfs",
  1918  				"Target": target,
  1919  				"TmpfsOptions": map[string]interface{}{
  1920  					"SizeBytes": 4096 * 1024, "Mode": 0700}},
  1921  			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
  1922  		},
  1923  	}
  1924  
  1925  	for i, x := range cases {
  1926  		cName := fmt.Sprintf("test-tmpfs-%d", i)
  1927  		data := map[string]interface{}{
  1928  			"Image": "busybox",
  1929  			"Cmd": []string{"/bin/sh", "-c",
  1930  				fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
  1931  			"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{x.cfg}},
  1932  		}
  1933  		status, resp, err := sockRequest("POST", "/containers/create?name="+cName, data)
  1934  		c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
  1935  		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
  1936  		out, _ := dockerCmd(c, "start", "-a", cName)
  1937  		for _, option := range x.expectedOptions {
  1938  			c.Assert(out, checker.Contains, option)
  1939  		}
  1940  	}
  1941  }