github.com/fabiokung/docker@v0.11.2-0.20170222101415-4534dcd49497/integration-cli/docker_api_containers_test.go (about)

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