gopkg.in/docker/docker.v1@v1.13.1/integration-cli/docker_api_containers_test.go (about)

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