integration-cli/docker_api_containers_test.go
d8cd8165
 package main
 
 import (
576985a1
 	"archive/tar"
d8cd8165
 	"bytes"
 	"encoding/json"
75f6929b
 	"fmt"
d8cd8165
 	"io"
fc7b904d
 	"io/ioutil"
531433e7
 	"net/http"
7c574b9e
 	"net/http/httputil"
75f6929b
 	"net/url"
8771cafa
 	"os"
fc7b904d
 	"path/filepath"
ef1d410b
 	"regexp"
09de92b8
 	"strconv"
d44c9f91
 	"strings"
76141a00
 	"time"
91bfed60
 
91e197d6
 	"github.com/docker/docker/api/types"
 	containertypes "github.com/docker/docker/api/types/container"
fc7b904d
 	mounttypes "github.com/docker/docker/api/types/mount"
91e197d6
 	networktypes "github.com/docker/docker/api/types/network"
51090717
 	"github.com/docker/docker/pkg/integration"
4bdf957c
 	"github.com/docker/docker/pkg/integration/checker"
fc7b904d
 	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/mount"
91bfed60
 	"github.com/docker/docker/pkg/stringid"
fc7b904d
 	"github.com/docker/docker/volume"
dc944ea7
 	"github.com/go-check/check"
d8cd8165
 )
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIGetAll(c *check.C) {
d8cd8165
 	startCount, err := getContainerCount()
4bdf957c
 	c.Assert(err, checker.IsNil, check.Commentf("Cannot query container count"))
d8cd8165
 
ba5370c1
 	name := "getall"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox", "true")
d8cd8165
 
c7845e27
 	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
d8cd8165
 
ba5370c1
 	var inspectJSON []struct {
 		Names []string
 	}
4bdf957c
 	err = json.Unmarshal(body, &inspectJSON)
 	c.Assert(err, checker.IsNil, check.Commentf("unable to unmarshal response body"))
d8cd8165
 
4bdf957c
 	c.Assert(inspectJSON, checker.HasLen, startCount+1)
ba5370c1
 
4bdf957c
 	actual := inspectJSON[0].Names[0]
 	c.Assert(actual, checker.Equals, "/"+name)
d8cd8165
 }
 
725f3415
 // regression test for empty json field being omitted #13691
7fb7a477
 func (s *DockerSuite) TestContainerAPIGetJSONNoFieldsOmitted(c *check.C) {
5c295460
 	dockerCmd(c, "run", "busybox", "true")
725f3415
 
 	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
725f3415
 
 	// empty Labels field triggered this bug, make sense to check for everything
 	// cause even Ports for instance can trigger this bug
 	// better safe than sorry..
 	fields := []string{
 		"Id",
 		"Names",
 		"Image",
 		"Command",
 		"Created",
 		"Ports",
 		"Labels",
 		"Status",
755f8609
 		"NetworkSettings",
725f3415
 	}
 
 	// decoding into types.Container do not work since it eventually unmarshal
 	// and empty field to an empty go map, so we just check for a string
 	for _, f := range fields {
 		if !strings.Contains(string(body), f) {
 			c.Fatalf("Field %s is missing and it shouldn't", f)
 		}
 	}
 }
 
09de92b8
 type containerPs struct {
 	Names []string
 	Ports []map[string]interface{}
 }
 
 // regression test for non-empty fields from #13901
7fb7a477
 func (s *DockerSuite) TestContainerAPIPsOmitFields(c *check.C) {
2b0a7422
 	// Problematic for Windows porting due to networking not yet being passed back
f9a3558a
 	testRequires(c, DaemonIsLinux)
09de92b8
 	name := "pstest"
 	port := 80
777ee34b
 	runSleepingContainer(c, "--name", name, "--expose", strconv.Itoa(port))
09de92b8
 
 	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
09de92b8
 
 	var resp []containerPs
 	err = json.Unmarshal(body, &resp)
4bdf957c
 	c.Assert(err, checker.IsNil)
09de92b8
 
 	var foundContainer *containerPs
 	for _, container := range resp {
 		for _, testName := range container.Names {
 			if "/"+name == testName {
 				foundContainer = &container
 				break
 			}
 		}
 	}
 
4bdf957c
 	c.Assert(foundContainer.Ports, checker.HasLen, 1)
 	c.Assert(foundContainer.Ports[0]["PrivatePort"], checker.Equals, float64(port))
09de92b8
 	_, ok := foundContainer.Ports[0]["PublicPort"]
4bdf957c
 	c.Assert(ok, checker.Not(checker.Equals), true)
09de92b8
 	_, ok = foundContainer.Ports[0]["IP"]
4bdf957c
 	c.Assert(ok, checker.Not(checker.Equals), true)
09de92b8
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIGetExport(c *check.C) {
6482410c
 	// Not supported on Windows as Windows does not support docker export
f9a3558a
 	testRequires(c, DaemonIsLinux)
ba5370c1
 	name := "exportcontainer"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test")
d8cd8165
 
c7845e27
 	status, body, err := sockRequest("GET", "/containers/"+name+"/export", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
d8cd8165
 
 	found := false
 	for tarReader := tar.NewReader(bytes.NewReader(body)); ; {
 		h, err := tarReader.Next()
4bdf957c
 		if err != nil && err == io.EOF {
 			break
d8cd8165
 		}
 		if h.Name == "test" {
 			found = true
 			break
 		}
 	}
4bdf957c
 	c.Assert(found, checker.True, check.Commentf("The created test file has not been found in the exported image"))
d8cd8165
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIGetChanges(c *check.C) {
2b0a7422
 	// Not supported on Windows as Windows does not support docker diff (/containers/name/changes)
f9a3558a
 	testRequires(c, DaemonIsLinux)
ba5370c1
 	name := "changescontainer"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox", "rm", "/etc/passwd")
d8cd8165
 
c7845e27
 	status, body, err := sockRequest("GET", "/containers/"+name+"/changes", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
d8cd8165
 
 	changes := []struct {
 		Kind int
 		Path string
 	}{}
4bdf957c
 	c.Assert(json.Unmarshal(body, &changes), checker.IsNil, check.Commentf("unable to unmarshal response body"))
d8cd8165
 
 	// Check the changelog for removal of /etc/passwd
 	success := false
 	for _, elem := range changes {
 		if elem.Path == "/etc/passwd" && elem.Kind == 2 {
 			success = true
 		}
 	}
4bdf957c
 	c.Assert(success, checker.True, check.Commentf("/etc/passwd has been removed but is not present in the diff"))
d8cd8165
 }
d44c9f91
 
dc944ea7
 func (s *DockerSuite) TestGetContainerStats(c *check.C) {
4d7707e1
 	var (
5c295460
 		name = "statscontainer"
4d7707e1
 	)
340e5233
 	runSleepingContainer(c, "--name", name)
5c295460
 
4d7707e1
 	type b struct {
4203230c
 		status int
 		body   []byte
 		err    error
4d7707e1
 	}
 	bc := make(chan b, 1)
76141a00
 	go func() {
c7845e27
 		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
4203230c
 		bc <- b{status, body, err}
76141a00
 	}()
 
4d7707e1
 	// allow some time to stream the stats from the container
 	time.Sleep(4 * time.Second)
5c295460
 	dockerCmd(c, "rm", "-f", name)
76141a00
 
4d7707e1
 	// collect the results from the stats stream or timeout and fail
 	// if the stream was not disconnected.
 	select {
 	case <-time.After(2 * time.Second):
dc944ea7
 		c.Fatal("stream was not closed after container was removed")
4d7707e1
 	case sr := <-bc:
4bdf957c
 		c.Assert(sr.err, checker.IsNil)
 		c.Assert(sr.status, checker.Equals, http.StatusOK)
4d7707e1
 
 		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
7fed7d7e
 		var s *types.Stats
4d7707e1
 		// decode only one object from the stream
4bdf957c
 		c.Assert(dec.Decode(&s), checker.IsNil)
76141a00
 	}
 }
198ff76d
 
ec97f414
 func (s *DockerSuite) TestGetContainerStatsRmRunning(c *check.C) {
340e5233
 	out, _ := runSleepingContainer(c)
b3e8ab30
 	id := strings.TrimSpace(out)
 
51090717
 	buf := &integration.ChannelBuffer{make(chan []byte, 1)}
b3e8ab30
 	defer buf.Close()
f5fb3c69
 
 	_, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json")
 	c.Assert(err, checker.IsNil)
 	defer body.Close()
 
9a9ce80a
 	chErr := make(chan error, 1)
b3e8ab30
 	go func() {
 		_, err = io.Copy(buf, body)
 		chErr <- err
 	}()
 
 	b := make([]byte, 32)
 	// make sure we've got some stats
f5fb3c69
 	_, err = buf.ReadTimeout(b, 2*time.Second)
4bdf957c
 	c.Assert(err, checker.IsNil)
b3e8ab30
 
 	// Now remove without `-f` and make sure we are still pulling stats
693ba98c
 	_, _, err = dockerCmdWithError("rm", id)
4bdf957c
 	c.Assert(err, checker.Not(checker.IsNil), check.Commentf("rm should have failed but didn't"))
b3e8ab30
 	_, err = buf.ReadTimeout(b, 2*time.Second)
4bdf957c
 	c.Assert(err, checker.IsNil)
b3e8ab30
 
f5fb3c69
 	dockerCmd(c, "rm", "-f", id)
 	c.Assert(<-chErr, checker.IsNil)
b3e8ab30
 }
 
ec97f414
 // regression test for gh13421
 // previous test was just checking one stat entry so it didn't fail (stats with
 // stream false always return one stat)
 func (s *DockerSuite) TestGetContainerStatsStream(c *check.C) {
 	name := "statscontainer"
340e5233
 	runSleepingContainer(c, "--name", name)
ec97f414
 
 	type b struct {
 		status int
 		body   []byte
 		err    error
 	}
 	bc := make(chan b, 1)
 	go func() {
 		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
 		bc <- b{status, body, err}
 	}()
 
 	// allow some time to stream the stats from the container
 	time.Sleep(4 * time.Second)
5c295460
 	dockerCmd(c, "rm", "-f", name)
ec97f414
 
 	// collect the results from the stats stream or timeout and fail
 	// if the stream was not disconnected.
 	select {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
4bdf957c
 		c.Assert(sr.err, checker.IsNil)
 		c.Assert(sr.status, checker.Equals, http.StatusOK)
ec97f414
 
 		s := string(sr.body)
 		// count occurrences of "read" of types.Stats
 		if l := strings.Count(s, "read"); l < 2 {
 			c.Fatalf("Expected more than one stat streamed, got %d", l)
 		}
 	}
 }
 
 func (s *DockerSuite) TestGetContainerStatsNoStream(c *check.C) {
 	name := "statscontainer"
340e5233
 	runSleepingContainer(c, "--name", name)
ec97f414
 
 	type b struct {
 		status int
 		body   []byte
 		err    error
 	}
 	bc := make(chan b, 1)
 	go func() {
 		status, body, err := sockRequest("GET", "/containers/"+name+"/stats?stream=0", nil)
 		bc <- b{status, body, err}
 	}()
 
 	// allow some time to stream the stats from the container
 	time.Sleep(4 * time.Second)
5c295460
 	dockerCmd(c, "rm", "-f", name)
ec97f414
 
 	// collect the results from the stats stream or timeout and fail
 	// if the stream was not disconnected.
 	select {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
4bdf957c
 		c.Assert(sr.err, checker.IsNil)
 		c.Assert(sr.status, checker.Equals, http.StatusOK)
ec97f414
 
 		s := string(sr.body)
340e5233
 		// count occurrences of `"read"` of types.Stats
 		c.Assert(strings.Count(s, `"read"`), checker.Equals, 1, check.Commentf("Expected only one stat streamed, got %d", strings.Count(s, `"read"`)))
ec97f414
 	}
 }
 
dc944ea7
 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) {
3434860d
 	name := "statscontainer"
340e5233
 	dockerCmd(c, "create", "--name", name, "busybox", "ps")
8dc5791f
 
3434860d
 	type stats struct {
 		status int
 		err    error
 	}
 	chResp := make(chan stats)
 
 	// We expect an immediate response, but if it's not immediate, the test would hang, so put it in a goroutine
 	// below we'll check this on a timeout.
8dc5791f
 	go func() {
3434860d
 		resp, body, err := sockRequestRaw("GET", "/containers/"+name+"/stats", nil, "")
 		body.Close()
 		chResp <- stats{resp.StatusCode, err}
8dc5791f
 	}()
 
3434860d
 	select {
 	case r := <-chResp:
 		c.Assert(r.err, checker.IsNil)
 		c.Assert(r.status, checker.Equals, http.StatusOK)
 	case <-time.After(10 * time.Second):
2eee6133
 		c.Fatal("timeout waiting for stats response for stopped container")
3434860d
 	}
8dc5791f
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIPause(c *check.C) {
2b0a7422
 	// Problematic on Windows as Windows does not support pause
f9a3558a
 	testRequires(c, DaemonIsLinux)
8636a219
 	defer unpauseAllContainers()
5c295460
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "sleep", "30")
8636a219
 	ContainerID := strings.TrimSpace(out)
 
c7845e27
 	status, _, err := sockRequest("POST", "/containers/"+ContainerID+"/pause", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
8636a219
 
 	pausedContainers, err := getSliceOfPausedContainers()
4bdf957c
 	c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused"))
8636a219
 
 	if len(pausedContainers) != 1 || stringid.TruncateID(ContainerID) != pausedContainers[0] {
dc944ea7
 		c.Fatalf("there should be one paused container and not %d", len(pausedContainers))
8636a219
 	}
 
c7845e27
 	status, _, err = sockRequest("POST", "/containers/"+ContainerID+"/unpause", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
8636a219
 
 	pausedContainers, err = getSliceOfPausedContainers()
4bdf957c
 	c.Assert(err, checker.IsNil, check.Commentf("error thrown while checking if containers were paused"))
 	c.Assert(pausedContainers, checker.IsNil, check.Commentf("There should be no paused container."))
8636a219
 }
d9e4b143
 
7fb7a477
 func (s *DockerSuite) TestContainerAPITop(c *check.C) {
f9a3558a
 	testRequires(c, DaemonIsLinux)
5c295460
 	out, _ := dockerCmd(c, "run", "-d", "busybox", "/bin/sh", "-c", "top")
dc944ea7
 	id := strings.TrimSpace(string(out))
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
d9e4b143
 
 	type topResp struct {
 		Titles    []string
 		Processes [][]string
 	}
 	var top topResp
c7845e27
 	status, b, err := sockRequest("GET", "/containers/"+id+"/top?ps_args=aux", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
 	c.Assert(json.Unmarshal(b, &top), checker.IsNil)
 	c.Assert(top.Titles, checker.HasLen, 11, check.Commentf("expected 11 titles, found %d: %v", len(top.Titles), top.Titles))
d9e4b143
 
 	if top.Titles[0] != "USER" || top.Titles[10] != "COMMAND" {
dc944ea7
 		c.Fatalf("expected `USER` at `Titles[0]` and `COMMAND` at Titles[10]: %v", top.Titles)
d9e4b143
 	}
4bdf957c
 	c.Assert(top.Processes, checker.HasLen, 2, check.Commentf("expected 2 processes, found %d: %v", len(top.Processes), top.Processes))
 	c.Assert(top.Processes[0][10], checker.Equals, "/bin/sh -c top")
 	c.Assert(top.Processes[1][10], checker.Equals, "top")
d9e4b143
 }
f19061cc
 
6482410c
 func (s *DockerSuite) TestContainerAPITopWindows(c *check.C) {
 	testRequires(c, DaemonIsWindows)
 	out, _ := runSleepingContainer(c, "-d")
 	id := strings.TrimSpace(string(out))
 	c.Assert(waitRun(id), checker.IsNil)
 
 	type topResp struct {
 		Titles    []string
 		Processes [][]string
 	}
 	var top topResp
 	status, b, err := sockRequest("GET", "/containers/"+id+"/top", nil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
 	c.Assert(json.Unmarshal(b, &top), checker.IsNil)
 	c.Assert(top.Titles, checker.HasLen, 4, check.Commentf("expected 4 titles, found %d: %v", len(top.Titles), top.Titles))
 
 	if top.Titles[0] != "Name" || top.Titles[3] != "Private Working Set" {
 		c.Fatalf("expected `Name` at `Titles[0]` and `Private Working Set` at Titles[3]: %v", top.Titles)
 	}
 	c.Assert(len(top.Processes), checker.GreaterOrEqualThan, 2, check.Commentf("expected at least 2 processes, found %d: %v", len(top.Processes), top.Processes))
 
 	foundProcess := false
 	expectedProcess := "busybox.exe"
 	for _, process := range top.Processes {
 		if process[0] == expectedProcess {
 			foundProcess = true
 			break
 		}
 	}
 
 	c.Assert(foundProcess, checker.Equals, true, check.Commentf("expected to find %s: %v", expectedProcess, top.Processes))
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICommit(c *check.C) {
cd4f507b
 	cName := "testapicommit"
5c295460
 	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
f19061cc
 
4352da78
 	name := "testcontainerapicommit"
cd4f507b
 	status, b, err := sockRequest("POST", "/commit?repo="+name+"&testtag=tag&container="+cName, nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
f19061cc
 
 	type resp struct {
6b3c9281
 		ID string
f19061cc
 	}
 	var img resp
4bdf957c
 	c.Assert(json.Unmarshal(b, &img), checker.IsNil)
f19061cc
 
62a856e9
 	cmd := inspectField(c, img.ID, "Config.Cmd")
53b0d626
 	c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd))
4bdf957c
 
f19061cc
 	// sanity check, make sure the image is what we think it is
6b3c9281
 	dockerCmd(c, "run", img.ID, "ls", "/test")
c61fa847
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICommitWithLabelInConfig(c *check.C) {
c61fa847
 	cName := "testapicommitwithconfig"
5c295460
 	dockerCmd(c, "run", "--name="+cName, "busybox", "/bin/sh", "-c", "touch /test")
c61fa847
 
 	config := map[string]interface{}{
 		"Labels": map[string]string{"key1": "value1", "key2": "value2"},
 	}
 
4352da78
 	name := "testcontainerapicommitwithconfig"
c61fa847
 	status, b, err := sockRequest("POST", "/commit?repo="+name+"&container="+cName, config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
c61fa847
 
 	type resp struct {
6b3c9281
 		ID string
c61fa847
 	}
 	var img resp
4bdf957c
 	c.Assert(json.Unmarshal(b, &img), checker.IsNil)
c61fa847
 
62a856e9
 	label1 := inspectFieldMap(c, img.ID, "Config.Labels", "key1")
4bdf957c
 	c.Assert(label1, checker.Equals, "value1")
c61fa847
 
62a856e9
 	label2 := inspectFieldMap(c, img.ID, "Config.Labels", "key2")
4bdf957c
 	c.Assert(label2, checker.Equals, "value2")
c61fa847
 
62a856e9
 	cmd := inspectField(c, img.ID, "Config.Cmd")
53b0d626
 	c.Assert(cmd, checker.Equals, "[/bin/sh -c touch /test]", check.Commentf("got wrong Cmd from commit: %q", cmd))
c61fa847
 
 	// sanity check, make sure the image is what we think it is
6b3c9281
 	dockerCmd(c, "run", img.ID, "ls", "/test")
f19061cc
 }
23fa7d41
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIBadPort(c *check.C) {
2b0a7422
 	// TODO Windows to Windows CI - Port this test
f9a3558a
 	testRequires(c, DaemonIsLinux)
12b6083c
 	config := map[string]interface{}{
 		"Image": "busybox",
 		"Cmd":   []string{"/bin/sh", "-c", "echo test"},
 		"PortBindings": map[string]interface{}{
 			"8080/tcp": []map[string]interface{}{
 				{
15d01d6e
 					"HostIP":   "",
12b6083c
 					"HostPort": "aa80",
 				},
 			},
 		},
 	}
 
 	jsonData := bytes.NewBuffer(nil)
 	json.NewEncoder(jsonData).Encode(config)
 
322e2a7d
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
6dd8e10d
 	c.Assert(getErrorMessage(c, body), checker.Equals, `invalid port specification: "aa80"`, check.Commentf("Incorrect error msg: %s", body))
12b6083c
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreate(c *check.C) {
23fa7d41
 	config := map[string]interface{}{
 		"Image": "busybox",
 		"Cmd":   []string{"/bin/sh", "-c", "touch /test && ls /test"},
 	}
 
c7845e27
 	status, b, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
c7845e27
 
23fa7d41
 	type createResp struct {
6b3c9281
 		ID string
23fa7d41
 	}
 	var container createResp
4bdf957c
 	c.Assert(json.Unmarshal(b, &container), checker.IsNil)
23fa7d41
 
6b3c9281
 	out, _ := dockerCmd(c, "start", "-a", container.ID)
4bdf957c
 	c.Assert(strings.TrimSpace(out), checker.Equals, "/test")
23fa7d41
 }
5dc02a2f
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateEmptyConfig(c *check.C) {
4ce81779
 	config := map[string]interface{}{}
 
322e2a7d
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
4ce81779
 
322e2a7d
 	expected := "Config cannot be empty in order to create a container"
 	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
4ce81779
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateMultipleNetworksConfig(c *check.C) {
cfa515fd
 	// Container creation must fail if client specified configurations for more than one network
 	config := map[string]interface{}{
 		"Image": "busybox",
 		"NetworkingConfig": networktypes.NetworkingConfig{
 			EndpointsConfig: map[string]*networktypes.EndpointSettings{
 				"net1": {},
 				"net2": {},
 				"net3": {},
 			},
 		},
 	}
 
322e2a7d
 	status, body, err := sockRequest("POST", "/containers/create", config)
cfa515fd
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusBadRequest)
322e2a7d
 	msg := getErrorMessage(c, body)
cfa515fd
 	// network name order in error message is not deterministic
322e2a7d
 	c.Assert(msg, checker.Contains, "Container cannot be connected to network endpoints")
 	c.Assert(msg, checker.Contains, "net1")
 	c.Assert(msg, checker.Contains, "net2")
 	c.Assert(msg, checker.Contains, "net3")
cfa515fd
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateWithHostName(c *check.C) {
80d73c83
 	hostName := "test-host"
fca4aea0
 	config := map[string]interface{}{
 		"Image":    "busybox",
 		"Hostname": hostName,
 	}
 
80d73c83
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
80d73c83
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
4bdf957c
 	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
80d73c83
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
80d73c83
 
 	var containerJSON types.ContainerJSON
4bdf957c
 	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
 	c.Assert(containerJSON.Config.Hostname, checker.Equals, hostName, check.Commentf("Mismatched Hostname"))
80d73c83
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateWithDomainName(c *check.C) {
80d73c83
 	domainName := "test-domain"
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"Domainname": domainName,
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
80d73c83
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
4bdf957c
 	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
80d73c83
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
80d73c83
 
 	var containerJSON types.ContainerJSON
4bdf957c
 	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
 	c.Assert(containerJSON.Config.Domainname, checker.Equals, domainName, check.Commentf("Mismatched Domainname"))
80d73c83
 }
fca4aea0
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateBridgeNetworkMode(c *check.C) {
2b0a7422
 	// Windows does not support bridge
f9a3558a
 	testRequires(c, DaemonIsLinux)
80d73c83
 	UtilCreateNetworkMode(c, "bridge")
557c7cb8
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateOtherNetworkModes(c *check.C) {
2b0a7422
 	// Windows does not support these network modes
557c7cb8
 	testRequires(c, DaemonIsLinux, NotUserNamespace)
 	UtilCreateNetworkMode(c, "host")
80d73c83
 	UtilCreateNetworkMode(c, "container:web1")
 }
fca4aea0
 
80d73c83
 func UtilCreateNetworkMode(c *check.C, networkMode string) {
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"HostConfig": map[string]interface{}{"NetworkMode": networkMode},
fca4aea0
 	}
80d73c83
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
80d73c83
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
4bdf957c
 	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
fca4aea0
 
80d73c83
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
80d73c83
 
 	var containerJSON types.ContainerJSON
4bdf957c
 	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
7ac4232e
 	c.Assert(containerJSON.HostConfig.NetworkMode, checker.Equals, containertypes.NetworkMode(networkMode), check.Commentf("Mismatched NetworkMode"))
fca4aea0
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateWithCpuSharesCpuset(c *check.C) {
2b0a7422
 	// TODO Windows to Windows CI. The CpuShares part could be ported.
f9a3558a
 	testRequires(c, DaemonIsLinux)
ebe344d3
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"CpuShares":  512,
a9afebae
 		"CpusetCpus": "0",
ebe344d3
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
ebe344d3
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
4bdf957c
 	c.Assert(json.Unmarshal(body, &container), checker.IsNil)
ebe344d3
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
ebe344d3
 
6b3c9281
 	var containerJSON types.ContainerJSON
ebe344d3
 
4bdf957c
 	c.Assert(json.Unmarshal(body, &containerJSON), checker.IsNil)
ebe344d3
 
62a856e9
 	out := inspectField(c, containerJSON.ID, "HostConfig.CpuShares")
4bdf957c
 	c.Assert(out, checker.Equals, "512")
ebe344d3
 
62a856e9
 	outCpuset := inspectField(c, containerJSON.ID, "HostConfig.CpusetCpus")
4bdf957c
 	c.Assert(outCpuset, checker.Equals, "0")
ebe344d3
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIVerifyHeader(c *check.C) {
5dc02a2f
 	config := map[string]interface{}{
 		"Image": "busybox",
 	}
 
bb1c576e
 	create := func(ct string) (*http.Response, io.ReadCloser, error) {
5dc02a2f
 		jsonData := bytes.NewBuffer(nil)
4bdf957c
 		c.Assert(json.NewEncoder(jsonData).Encode(config), checker.IsNil)
5dc02a2f
 		return sockRequestRaw("POST", "/containers/create", jsonData, ct)
 	}
 
 	// Try with no content-type
bb1c576e
 	res, body, err := create("")
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
5dc02a2f
 	body.Close()
c7845e27
 
5dc02a2f
 	// Try with wrong content-type
bb1c576e
 	res, body, err = create("application/xml")
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
5dc02a2f
 	body.Close()
 
 	// now application/json
bb1c576e
 	res, body, err = create("application/json")
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
5dc02a2f
 	body.Close()
 }
308a2302
 
477201a2
 //Issue 14230. daemon should return 500 for invalid port syntax
7fb7a477
 func (s *DockerSuite) TestContainerAPIInvalidPortSyntax(c *check.C) {
477201a2
 	config := `{
 				  "Image": "busybox",
 				  "HostConfig": {
2b0a7422
 					"NetworkMode": "default",
477201a2
 					"PortBindings": {
 					  "19039;1230": [
 						{}
 					  ]
 					}
 				  }
 				}`
 
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
477201a2
 
 	b, err := readBody(body)
4bdf957c
 	c.Assert(err, checker.IsNil)
6dd8e10d
 	c.Assert(string(b[:]), checker.Contains, "invalid port")
477201a2
 }
 
ecf88981
 func (s *DockerSuite) TestContainerAPIRestartPolicyInvalidPolicyName(c *check.C) {
94e95e47
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
 			"RestartPolicy": {
 				"Name": "something",
 				"MaximumRetryCount": 0
 			}
 		}
 	}`
 
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
 
 	b, err := readBody(body)
 	c.Assert(err, checker.IsNil)
 	c.Assert(string(b[:]), checker.Contains, "invalid restart policy")
 }
 
ecf88981
 func (s *DockerSuite) TestContainerAPIRestartPolicyRetryMismatch(c *check.C) {
94e95e47
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
 			"RestartPolicy": {
 				"Name": "always",
 				"MaximumRetryCount": 2
 			}
 		}
 	}`
 
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
 
 	b, err := readBody(body)
 	c.Assert(err, checker.IsNil)
ecf88981
 	c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be used with restart policy")
94e95e47
 }
 
ecf88981
 func (s *DockerSuite) TestContainerAPIRestartPolicyNegativeRetryCount(c *check.C) {
94e95e47
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
 			"RestartPolicy": {
 				"Name": "on-failure",
 				"MaximumRetryCount": -2
 			}
 		}
 	}`
 
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
 
 	b, err := readBody(body)
 	c.Assert(err, checker.IsNil)
ecf88981
 	c.Assert(string(b[:]), checker.Contains, "maximum retry count cannot be negative")
 }
 
 func (s *DockerSuite) TestContainerAPIRestartPolicyDefaultRetryCount(c *check.C) {
 	config := `{
 		"Image": "busybox",
 		"HostConfig": {
 			"RestartPolicy": {
 				"Name": "on-failure",
 				"MaximumRetryCount": 0
 			}
 		}
 	}`
 
 	res, _, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
94e95e47
 }
 
308a2302
 // Issue 7941 - test to make sure a "null" in JSON is just ignored.
 // W/o this fix a null in JSON would be parsed into a string var as "null"
7fb7a477
 func (s *DockerSuite) TestContainerAPIPostCreateNull(c *check.C) {
308a2302
 	config := `{
 		"Hostname":"",
 		"Domainname":"",
 		"Memory":0,
 		"MemorySwap":0,
 		"CpuShares":0,
 		"Cpuset":null,
 		"AttachStdin":true,
 		"AttachStdout":true,
 		"AttachStderr":true,
 		"ExposedPorts":{},
 		"Tty":true,
 		"OpenStdin":true,
 		"StdinOnce":true,
 		"Env":[],
 		"Cmd":"ls",
 		"Image":"busybox",
 		"Volumes":{},
 		"WorkingDir":"",
 		"Entrypoint":null,
 		"NetworkDisabled":false,
 		"OnBuild":null}`
 
bb1c576e
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(res.StatusCode, checker.Equals, http.StatusCreated)
308a2302
 
 	b, err := readBody(body)
4bdf957c
 	c.Assert(err, checker.IsNil)
308a2302
 	type createResp struct {
6b3c9281
 		ID string
308a2302
 	}
 	var container createResp
4bdf957c
 	c.Assert(json.Unmarshal(b, &container), checker.IsNil)
62a856e9
 	out := inspectField(c, container.ID, "HostConfig.CpusetCpus")
4bdf957c
 	c.Assert(out, checker.Equals, "")
1eea2c58
 
62a856e9
 	outMemory := inspectField(c, container.ID, "HostConfig.Memory")
4bdf957c
 	c.Assert(outMemory, checker.Equals, "0")
62a856e9
 	outMemorySwap := inspectField(c, container.ID, "HostConfig.MemorySwap")
4bdf957c
 	c.Assert(outMemorySwap, checker.Equals, "0")
308a2302
 }
1a35b16b
 
dc944ea7
 func (s *DockerSuite) TestCreateWithTooLowMemoryLimit(c *check.C) {
2b0a7422
 	// TODO Windows: Port once memory is supported
f9a3558a
 	testRequires(c, DaemonIsLinux)
1a35b16b
 	config := `{
 		"Image":     "busybox",
 		"Cmd":       "ls",
 		"OpenStdin": true,
 		"CpuShares": 100,
 		"Memory":    524287
 	}`
 
fa6925aa
 	res, body, err := sockRequestRaw("POST", "/containers/create", strings.NewReader(config), "application/json")
4bdf957c
 	c.Assert(err, checker.IsNil)
1a35b16b
 	b, err2 := readBody(body)
4bdf957c
 	c.Assert(err2, checker.IsNil)
1a35b16b
 
4bdf957c
 	c.Assert(res.StatusCode, checker.Equals, http.StatusInternalServerError)
 	c.Assert(string(b), checker.Contains, "Minimum memory limit allowed is 4MB")
1a35b16b
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIRename(c *check.C) {
 	out, _ := dockerCmd(c, "run", "--name", "TestContainerAPIRename", "-d", "busybox", "sh")
8f752ffe
 
 	containerID := strings.TrimSpace(out)
7fb7a477
 	newName := "TestContainerAPIRenameNew"
8f752ffe
 	statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/rename?name="+newName, nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
8f752ffe
 	// 204 No Content is expected, not 200
4bdf957c
 	c.Assert(statusCode, checker.Equals, http.StatusNoContent)
8f752ffe
 
62a856e9
 	name := inspectField(c, containerID, "Name")
4bdf957c
 	c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container"))
8f752ffe
 }
f7e417ea
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIKill(c *check.C) {
f7e417ea
 	name := "test-api-kill"
777ee34b
 	runSleepingContainer(c, "-i", "--name", name)
f7e417ea
 
 	status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
f7e417ea
 
62a856e9
 	state := inspectField(c, name, "State.Running")
4bdf957c
 	c.Assert(state, checker.Equals, "false", check.Commentf("got wrong State from container %s: %q", name, state))
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIRestart(c *check.C) {
f7e417ea
 	name := "test-api-restart"
6482410c
 	runSleepingContainer(c, "-di", "--name", name)
f7e417ea
 
 	status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
6482410c
 	c.Assert(waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second), checker.IsNil)
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIRestartNotimeoutParam(c *check.C) {
74121a42
 	name := "test-api-restart-no-timeout-param"
6482410c
 	out, _ := runSleepingContainer(c, "-di", "--name", name)
74121a42
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
74121a42
 
 	status, _, err := sockRequest("POST", "/containers/"+name+"/restart", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
6482410c
 	c.Assert(waitInspect(name, "{{ .State.Restarting  }} {{ .State.Running  }}", "false true", 15*time.Second), checker.IsNil)
74121a42
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIStart(c *check.C) {
f7e417ea
 	name := "testing-start"
 	config := map[string]interface{}{
 		"Image":     "busybox",
52f04748
 		"Cmd":       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
f7e417ea
 		"OpenStdin": true,
 	}
 
 	status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
f7e417ea
 
0a8386c8
 	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
f7e417ea
 
 	// second call to start should give 304
0a8386c8
 	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
f4a1e3db
 
 	// TODO(tibor): figure out why this doesn't work on windows
 	if isLocalDaemon {
 		c.Assert(status, checker.Equals, http.StatusNotModified)
 	}
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIStop(c *check.C) {
f7e417ea
 	name := "test-api-stop"
777ee34b
 	runSleepingContainer(c, "-i", "--name", name)
f7e417ea
 
2b0a7422
 	status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
2b0a7422
 	c.Assert(waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
f7e417ea
 
 	// second call to start should give 304
2b0a7422
 	status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=30", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNotModified)
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIWait(c *check.C) {
f7e417ea
 	name := "test-api-wait"
777ee34b
 
 	sleepCmd := "/bin/sleep"
 	if daemonPlatform == "windows" {
 		sleepCmd = "sleep"
 	}
94b1bb82
 	dockerCmd(c, "run", "--name", name, "busybox", sleepCmd, "2")
f7e417ea
 
 	status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
2b0a7422
 	c.Assert(waitInspect(name, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
f7e417ea
 
181562c2
 	var waitres containertypes.ContainerWaitOKBody
4bdf957c
 	c.Assert(json.Unmarshal(body, &waitres), checker.IsNil)
181562c2
 	c.Assert(waitres.StatusCode, checker.Equals, int64(0))
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICopyNotExistsAnyMore(c *check.C) {
f7e417ea
 	name := "test-container-api-copy"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
f7e417ea
 
 	postData := types.CopyConfig{
 		Resource: "/test.txt",
 	}
 
42832890
 	status, _, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNotFound)
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICopyPre124(c *check.C) {
f811d5b1
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
42832890
 	name := "test-container-api-copy"
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
 
 	postData := types.CopyConfig{
 		Resource: "/test.txt",
 	}
 
 	status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
f7e417ea
 
 	found := false
 	for tarReader := tar.NewReader(bytes.NewReader(body)); ; {
 		h, err := tarReader.Next()
 		if err != nil {
 			if err == io.EOF {
 				break
 			}
 			c.Fatal(err)
 		}
 		if h.Name == "test.txt" {
 			found = true
 			break
 		}
 	}
4bdf957c
 	c.Assert(found, checker.True)
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICopyResourcePathEmptyPr124(c *check.C) {
f811d5b1
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
f7e417ea
 	name := "test-container-api-copy-resource-empty"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox", "touch", "/test.txt")
f7e417ea
 
 	postData := types.CopyConfig{
 		Resource: "",
 	}
 
42832890
 	status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
 	c.Assert(string(body), checker.Matches, "Path cannot be empty\n")
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICopyResourcePathNotFoundPre124(c *check.C) {
f811d5b1
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
f7e417ea
 	name := "test-container-api-copy-resource-not-found"
5c295460
 	dockerCmd(c, "run", "--name", name, "busybox")
f7e417ea
 
 	postData := types.CopyConfig{
 		Resource: "/notexist",
 	}
 
42832890
 	status, body, err := sockRequest("POST", "/v1.23/containers/"+name+"/copy", postData)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
 	c.Assert(string(body), checker.Matches, "Could not find the file /notexist in container "+name+"\n")
f7e417ea
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICopyContainerNotFoundPr124(c *check.C) {
f811d5b1
 	testRequires(c, DaemonIsLinux) // Windows only supports 1.25 or later
f7e417ea
 	postData := types.CopyConfig{
 		Resource: "/something",
 	}
 
42832890
 	status, _, err := sockRequest("POST", "/v1.23/containers/notexists/copy", postData)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNotFound)
f7e417ea
 }
8771cafa
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDelete(c *check.C) {
777ee34b
 	out, _ := runSleepingContainer(c)
8771cafa
 
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
8771cafa
 
5c295460
 	dockerCmd(c, "stop", id)
8771cafa
 
 	status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
8771cafa
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteNotExist(c *check.C) {
8771cafa
 	status, body, err := sockRequest("DELETE", "/containers/doesnotexist", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNotFound)
322e2a7d
 	c.Assert(getErrorMessage(c, body), checker.Matches, "No such container: doesnotexist")
8771cafa
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteForce(c *check.C) {
777ee34b
 	out, _ := runSleepingContainer(c)
8771cafa
 
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
8771cafa
 
 	status, _, err := sockRequest("DELETE", "/containers/"+id+"?force=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
8771cafa
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteRemoveLinks(c *check.C) {
2b0a7422
 	// Windows does not support links
f9a3558a
 	testRequires(c, DaemonIsLinux)
5c295460
 	out, _ := dockerCmd(c, "run", "-d", "--name", "tlink1", "busybox", "top")
8771cafa
 
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
8771cafa
 
5c295460
 	out, _ = dockerCmd(c, "run", "--link", "tlink1:tlink1", "--name", "tlink2", "-d", "busybox", "top")
8771cafa
 
 	id2 := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id2), checker.IsNil)
8771cafa
 
62a856e9
 	links := inspectFieldJSON(c, id2, "HostConfig.Links")
4bdf957c
 	c.Assert(links, checker.Equals, "[\"/tlink1:/tlink2/tlink1\"]", check.Commentf("expected to have links between containers"))
8771cafa
 
0f9f9950
 	status, b, err := sockRequest("DELETE", "/containers/tlink2/tlink1?link=1", nil)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusNoContent, check.Commentf(string(b)))
8771cafa
 
62a856e9
 	linksPostRm := inspectFieldJSON(c, id2, "HostConfig.Links")
4bdf957c
 	c.Assert(linksPostRm, checker.Equals, "null", check.Commentf("call to api deleteContainer links should have removed the specified links"))
8771cafa
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteConflict(c *check.C) {
777ee34b
 	out, _ := runSleepingContainer(c)
8771cafa
 
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
8771cafa
 
 	status, _, err := sockRequest("DELETE", "/containers/"+id, nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusConflict)
8771cafa
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteRemoveVolume(c *check.C) {
8771cafa
 	testRequires(c, SameHostDaemon)
 
2b0a7422
 	vol := "/testvolume"
 	if daemonPlatform == "windows" {
 		vol = `c:\testvolume`
 	}
 
777ee34b
 	out, _ := runSleepingContainer(c, "-v", vol)
8771cafa
 
 	id := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(id), checker.IsNil)
8771cafa
 
2b0a7422
 	source, err := inspectMountSourceField(id, vol)
1c3cb2d3
 	_, err = os.Stat(source)
4bdf957c
 	c.Assert(err, checker.IsNil)
8771cafa
 
 	status, _, err := sockRequest("DELETE", "/containers/"+id+"?v=1&force=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
 	_, err = os.Stat(source)
 	c.Assert(os.IsNotExist(err), checker.True, check.Commentf("expected to get ErrNotExist error, got %v", err))
8771cafa
 }
7c574b9e
 
 // Regression test for https://github.com/docker/docker/issues/6231
7fb7a477
 func (s *DockerSuite) TestContainerAPIChunkedEncoding(c *check.C) {
0d88d5b6
 	conn, err := sockConn(time.Duration(10*time.Second), "")
4bdf957c
 	c.Assert(err, checker.IsNil)
7c574b9e
 	client := httputil.NewClientConn(conn, nil)
 	defer client.Close()
 
0a8386c8
 	config := map[string]interface{}{
 		"Image":     "busybox",
52f04748
 		"Cmd":       append([]string{"/bin/sh", "-c"}, sleepCommandForDaemonPlatform()...),
0a8386c8
 		"OpenStdin": true,
 	}
 	b, err := json.Marshal(config)
 	c.Assert(err, checker.IsNil)
 
 	req, err := http.NewRequest("POST", "/containers/create", bytes.NewBuffer(b))
4bdf957c
 	c.Assert(err, checker.IsNil)
7c574b9e
 	req.Header.Set("Content-Type", "application/json")
 	// This is a cheat to make the http request do chunked encoding
 	// Otherwise (just setting the Content-Encoding to chunked) net/http will overwrite
 	// https://golang.org/src/pkg/net/http/request.go?s=11980:12172
 	req.ContentLength = -1
 
 	resp, err := client.Do(req)
0a8386c8
 	c.Assert(err, checker.IsNil, check.Commentf("error creating container with chunked encoding"))
7c574b9e
 	resp.Body.Close()
0a8386c8
 	c.Assert(resp.StatusCode, checker.Equals, http.StatusCreated)
7c574b9e
 }
68e9c078
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIPostContainerStop(c *check.C) {
777ee34b
 	out, _ := runSleepingContainer(c)
68e9c078
 
 	containerID := strings.TrimSpace(out)
4bdf957c
 	c.Assert(waitRun(containerID), checker.IsNil)
68e9c078
 
 	statusCode, _, err := sockRequest("POST", "/containers/"+containerID+"/stop", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
68e9c078
 	// 204 No Content is expected, not 200
4bdf957c
 	c.Assert(statusCode, checker.Equals, http.StatusNoContent)
d691b4af
 	c.Assert(waitInspect(containerID, "{{ .State.Running  }}", "false", 60*time.Second), checker.IsNil)
68e9c078
 }
17d6f00e
 
 // #14170
7fb7a477
 func (s *DockerSuite) TestPostContainerAPICreateWithStringOrSliceEntrypoint(c *check.C) {
17d6f00e
 	config := struct {
 		Image      string
 		Entrypoint string
 		Cmd        []string
 	}{"busybox", "echo", []string{"hello", "world"}}
 	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
17d6f00e
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
4bdf957c
 	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
17d6f00e
 
 	config2 := struct {
 		Image      string
 		Entrypoint []string
 		Cmd        []string
 	}{"busybox", []string{"echo"}, []string{"hello", "world"}}
 	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
4bdf957c
 	c.Assert(err, checker.IsNil)
17d6f00e
 	out, _ = dockerCmd(c, "start", "-a", "echotest2")
4bdf957c
 	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
17d6f00e
 }
 
 // #14170
 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCmd(c *check.C) {
 	config := struct {
 		Image      string
 		Entrypoint string
 		Cmd        string
 	}{"busybox", "echo", "hello world"}
 	_, _, err := sockRequest("POST", "/containers/create?name=echotest", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
17d6f00e
 	out, _ := dockerCmd(c, "start", "-a", "echotest")
4bdf957c
 	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
17d6f00e
 
 	config2 := struct {
 		Image string
 		Cmd   []string
 	}{"busybox", []string{"echo", "hello", "world"}}
 	_, _, err = sockRequest("POST", "/containers/create?name=echotest2", config2)
4bdf957c
 	c.Assert(err, checker.IsNil)
17d6f00e
 	out, _ = dockerCmd(c, "start", "-a", "echotest2")
4bdf957c
 	c.Assert(strings.TrimSpace(out), checker.Equals, "hello world")
17d6f00e
 }
10a3061c
 
 // regression #14318
 func (s *DockerSuite) TestPostContainersCreateWithStringOrSliceCapAddDrop(c *check.C) {
2b0a7422
 	// Windows doesn't support CapAdd/CapDrop
f9a3558a
 	testRequires(c, DaemonIsLinux)
10a3061c
 	config := struct {
 		Image   string
 		CapAdd  string
 		CapDrop string
 	}{"busybox", "NET_ADMIN", "SYS_ADMIN"}
 	status, _, err := sockRequest("POST", "/containers/create?name=capaddtest0", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
10a3061c
 
 	config2 := struct {
 		Image   string
 		CapAdd  []string
 		CapDrop []string
 	}{"busybox", []string{"NET_ADMIN", "SYS_ADMIN"}, []string{"SETGID"}}
 	status, _, err = sockRequest("POST", "/containers/create?name=capaddtest1", config2)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
10a3061c
 }
65121e5f
 
c358a4cd
 // #14915
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateNoHostConfig118(c *check.C) {
f811d5b1
 	testRequires(c, DaemonIsLinux) // Windows only support 1.25 or later
c358a4cd
 	config := struct {
 		Image string
 	}{"busybox"}
 	status, _, err := sockRequest("POST", "/v1.18/containers/create", config)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
c358a4cd
 }
75f6929b
 
 // Ensure an error occurs when you have a container read-only rootfs but you
 // extract an archive to a symlink in a writable volume which points to a
 // directory outside of the volume.
 func (s *DockerSuite) TestPutContainerArchiveErrSymlinkInVolumeToReadOnlyRootfs(c *check.C) {
2b0a7422
 	// Windows does not support read-only rootfs
ea3afdad
 	// Requires local volume mount bind.
 	// --read-only + userns has remount issues
25c38339
 	testRequires(c, SameHostDaemon, NotUserNamespace, DaemonIsLinux)
75f6929b
 
 	testVol := getTestDir(c, "test-put-container-archive-err-symlink-in-volume-to-read-only-rootfs-")
 	defer os.RemoveAll(testVol)
 
 	makeTestContentInDir(c, testVol)
 
 	cID := makeTestContainer(c, testContainerOptions{
 		readOnly: true,
 		volumes:  defaultVolumes(testVol), // Our bind mount is at /vol2
 	})
 	defer deleteContainer(cID)
 
 	// Attempt to extract to a symlink in the volume which points to a
 	// directory outside the volume. This should cause an error because the
 	// rootfs is read-only.
 	query := make(url.Values, 1)
 	query.Set("path", "/vol2/symlinkToAbsDir")
 	urlPath := fmt.Sprintf("/v1.20/containers/%s/archive?%s", cID, query.Encode())
 
 	statusCode, body, err := sockRequest("PUT", urlPath, nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
75f6929b
 
 	if !isCpCannotCopyReadOnly(fmt.Errorf(string(body))) {
 		c.Fatalf("expected ErrContainerRootfsReadonly error, but got %d: %s", statusCode, string(body))
 	}
 }
26bd5e3a
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIGetContainersJSONEmpty(c *check.C) {
26bd5e3a
 	status, body, err := sockRequest("GET", "/containers/json?all=1", nil)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusOK)
 	c.Assert(string(body), checker.Equals, "[]\n")
26bd5e3a
 }
94464e3a
 
 func (s *DockerSuite) TestPostContainersCreateWithWrongCpusetValues(c *check.C) {
2b0a7422
 	// Not supported on Windows
94464e3a
 	testRequires(c, DaemonIsLinux)
 
 	c1 := struct {
 		Image      string
 		CpusetCpus string
 	}{"busybox", "1-42,,"}
 	name := "wrong-cpuset-cpus"
 	status, body, err := sockRequest("POST", "/containers/create?name="+name, c1)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
322e2a7d
 	expected := "Invalid value 1-42,, for cpuset cpus"
 	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
94464e3a
 
 	c2 := struct {
 		Image      string
 		CpusetMems string
 	}{"busybox", "42-3,1--"}
 	name = "wrong-cpuset-mems"
 	status, body, err = sockRequest("POST", "/containers/create?name="+name, c2)
4bdf957c
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusInternalServerError)
322e2a7d
 	expected = "Invalid value 42-3,1-- for cpuset mems"
 	c.Assert(getErrorMessage(c, body), checker.Equals, expected)
94464e3a
 }
d7117a1b
 
ef1d410b
 func (s *DockerSuite) TestPostContainersCreateShmSizeNegative(c *check.C) {
2b0a7422
 	// ShmSize is not supported on Windows
25c38339
 	testRequires(c, DaemonIsLinux)
ef1d410b
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"HostConfig": map[string]interface{}{"ShmSize": -1},
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusInternalServerError)
e5bed175
 	c.Assert(getErrorMessage(c, body), checker.Contains, "SHM size can not be less than 0")
ef1d410b
 }
 
 func (s *DockerSuite) TestPostContainersCreateShmSizeHostConfigOmitted(c *check.C) {
2b0a7422
 	// ShmSize is not supported on Windows
25c38339
 	testRequires(c, DaemonIsLinux)
4354b348
 	var defaultSHMSize int64 = 67108864
ef1d410b
 	config := map[string]interface{}{
 		"Image": "busybox",
 		"Cmd":   "mount",
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusCreated)
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
ef1d410b
 	c.Assert(json.Unmarshal(body, &container), check.IsNil)
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusOK)
 
 	var containerJSON types.ContainerJSON
 	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
 
5190794f
 	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, defaultSHMSize)
ef1d410b
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
 	if !shmRegexp.MatchString(out) {
 		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
 	}
 }
 
 func (s *DockerSuite) TestPostContainersCreateShmSizeOmitted(c *check.C) {
2b0a7422
 	// ShmSize is not supported on Windows
25c38339
 	testRequires(c, DaemonIsLinux)
ef1d410b
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"HostConfig": map[string]interface{}{},
 		"Cmd":        "mount",
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusCreated)
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
ef1d410b
 	c.Assert(json.Unmarshal(body, &container), check.IsNil)
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusOK)
 
 	var containerJSON types.ContainerJSON
 	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
 
5190794f
 	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(67108864))
ef1d410b
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegexp := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=65536k`)
 	if !shmRegexp.MatchString(out) {
 		c.Fatalf("Expected shm of 64MB in mount command, got %v", out)
 	}
 }
 
 func (s *DockerSuite) TestPostContainersCreateWithShmSize(c *check.C) {
2b0a7422
 	// ShmSize is not supported on Windows
25c38339
 	testRequires(c, DaemonIsLinux)
ef1d410b
 	config := map[string]interface{}{
 		"Image":      "busybox",
 		"Cmd":        "mount",
 		"HostConfig": map[string]interface{}{"ShmSize": 1073741824},
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusCreated)
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
ef1d410b
 	c.Assert(json.Unmarshal(body, &container), check.IsNil)
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusOK)
 
 	var containerJSON types.ContainerJSON
 	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
 
5190794f
 	c.Assert(containerJSON.HostConfig.ShmSize, check.Equals, int64(1073741824))
ef1d410b
 
 	out, _ := dockerCmd(c, "start", "-i", containerJSON.ID)
 	shmRegex := regexp.MustCompile(`shm on /dev/shm type tmpfs(.*)size=1048576k`)
 	if !shmRegex.MatchString(out) {
 		c.Fatalf("Expected shm of 1GB in mount command, got %v", out)
 	}
 }
 
 func (s *DockerSuite) TestPostContainersCreateMemorySwappinessHostConfigOmitted(c *check.C) {
2b0a7422
 	// Swappiness is not supported on Windows
25c38339
 	testRequires(c, DaemonIsLinux)
ef1d410b
 	config := map[string]interface{}{
 		"Image": "busybox",
 	}
 
 	status, body, err := sockRequest("POST", "/containers/create", config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusCreated)
 
bad849fc
 	var container containertypes.ContainerCreateCreatedBody
ef1d410b
 	c.Assert(json.Unmarshal(body, &container), check.IsNil)
 
 	status, body, err = sockRequest("GET", "/containers/"+container.ID+"/json", nil)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusOK)
 
 	var containerJSON types.ContainerJSON
 	c.Assert(json.Unmarshal(body, &containerJSON), check.IsNil)
 
4089b4e4
 	c.Assert(*containerJSON.HostConfig.MemorySwappiness, check.Equals, int64(-1))
ef1d410b
 }
d3af7f28
 
 // check validation is done daemon side and not only in cli
 func (s *DockerSuite) TestPostContainersCreateWithOomScoreAdjInvalidRange(c *check.C) {
2b0a7422
 	// OomScoreAdj is not supported on Windows
d3af7f28
 	testRequires(c, DaemonIsLinux)
 
 	config := struct {
 		Image       string
 		OomScoreAdj int
 	}{"busybox", 1001}
 	name := "oomscoreadj-over"
 	status, b, err := sockRequest("POST", "/containers/create?name="+name, config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusInternalServerError)
322e2a7d
 
aae4bcf7
 	expected := "Invalid value 1001, range for oom score adj is [-1000, 1000]"
322e2a7d
 	msg := getErrorMessage(c, b)
 	if !strings.Contains(msg, expected) {
 		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
d3af7f28
 	}
 
 	config = struct {
 		Image       string
 		OomScoreAdj int
 	}{"busybox", -1001}
 	name = "oomscoreadj-low"
 	status, b, err = sockRequest("POST", "/containers/create?name="+name, config)
 	c.Assert(err, check.IsNil)
 	c.Assert(status, check.Equals, http.StatusInternalServerError)
aae4bcf7
 	expected = "Invalid value -1001, range for oom score adj is [-1000, 1000]"
322e2a7d
 	msg = getErrorMessage(c, b)
 	if !strings.Contains(msg, expected) {
 		c.Fatalf("Expected output to contain %q, got %q", expected, msg)
d3af7f28
 	}
 }
9d8071a7
 
644a7426
 // test case for #22210 where an empty container name caused panic.
7fb7a477
 func (s *DockerSuite) TestContainerAPIDeleteWithEmptyName(c *check.C) {
9d8071a7
 	status, out, err := sockRequest("DELETE", "/containers/", nil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusBadRequest)
 	c.Assert(string(out), checker.Contains, "No container name or ID supplied")
 }
7bb9c539
 
7fb7a477
 func (s *DockerSuite) TestContainerAPIStatsWithNetworkDisabled(c *check.C) {
7bb9c539
 	// Problematic on Windows as Windows does not support stats
 	testRequires(c, DaemonIsLinux)
 
 	name := "testing-network-disabled"
 	config := map[string]interface{}{
 		"Image":           "busybox",
 		"Cmd":             []string{"top"},
 		"NetworkDisabled": true,
 	}
 
 	status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusCreated)
 
 	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
 	c.Assert(err, checker.IsNil)
 	c.Assert(status, checker.Equals, http.StatusNoContent)
 
 	c.Assert(waitRun(name), check.IsNil)
 
 	type b struct {
 		status int
 		body   []byte
 		err    error
 	}
 	bc := make(chan b, 1)
 	go func() {
 		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
 		bc <- b{status, body, err}
 	}()
 
 	// allow some time to stream the stats from the container
 	time.Sleep(4 * time.Second)
 	dockerCmd(c, "rm", "-f", name)
 
 	// collect the results from the stats stream or timeout and fail
 	// if the stream was not disconnected.
 	select {
 	case <-time.After(2 * time.Second):
 		c.Fatal("stream was not closed after container was removed")
 	case sr := <-bc:
 		c.Assert(sr.err, checker.IsNil)
 		c.Assert(sr.status, checker.Equals, http.StatusOK)
 
 		// decode only one object from the stream
 		var s *types.Stats
 		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
 		c.Assert(dec.Decode(&s), checker.IsNil)
 	}
 }
fc7b904d
 
7fb7a477
 func (s *DockerSuite) TestContainersAPICreateMountsValidation(c *check.C) {
fc7b904d
 	type m mounttypes.Mount
 	type hc struct{ Mounts []m }
 	type cfg struct {
 		Image      string
 		HostConfig hc
 	}
 	type testCase struct {
 		config cfg
 		status int
 		msg    string
 	}
 
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	destPath := prefix + slash + "foo"
 	notExistPath := prefix + slash + "notexist"
 
 	cases := []testCase{
18768fdc
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type:   "notreal",
 						Target: destPath}}}},
 			status: http.StatusBadRequest,
 			msg:    "mount type unknown",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type: "bind"}}}},
 			status: http.StatusBadRequest,
 			msg:    "Target must not be empty",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type:   "bind",
 						Target: destPath}}}},
 			status: http.StatusBadRequest,
 			msg:    "Source must not be empty",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type:   "bind",
 						Source: notExistPath,
 						Target: destPath}}}},
 			status: http.StatusBadRequest,
 			msg:    "bind source path does not exist",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type: "volume"}}}},
 			status: http.StatusBadRequest,
 			msg:    "Target must not be empty",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type:   "volume",
 						Source: "hello",
 						Target: destPath}}}},
 			status: http.StatusCreated,
 			msg:    "",
 		},
 		{
 			config: cfg{
 				Image: "busybox",
 				HostConfig: hc{
 					Mounts: []m{{
 						Type:   "volume",
 						Source: "hello2",
 						Target: destPath,
 						VolumeOptions: &mounttypes.VolumeOptions{
 							DriverConfig: &mounttypes.Driver{
 								Name: "local"}}}}}},
 			status: http.StatusCreated,
 			msg:    "",
 		},
fc7b904d
 	}
 
 	if SameHostDaemon.Condition() {
 		tmpDir, err := ioutils.TempDir("", "test-mounts-api")
 		c.Assert(err, checker.IsNil)
 		defer os.RemoveAll(tmpDir)
 		cases = append(cases, []testCase{
18768fdc
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:   "bind",
 							Source: tmpDir,
 							Target: destPath}}}},
 				status: http.StatusCreated,
 				msg:    "",
 			},
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:          "bind",
 							Source:        tmpDir,
 							Target:        destPath,
 							VolumeOptions: &mounttypes.VolumeOptions{}}}}},
 				status: http.StatusBadRequest,
 				msg:    "VolumeOptions must not be specified",
 			},
fc7b904d
 		}...)
 	}
 
 	if DaemonIsLinux.Condition() {
 		cases = append(cases, []testCase{
18768fdc
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:   "volume",
 							Source: "hello3",
 							Target: destPath,
 							VolumeOptions: &mounttypes.VolumeOptions{
 								DriverConfig: &mounttypes.Driver{
 									Name:    "local",
 									Options: map[string]string{"o": "size=1"}}}}}}},
 				status: http.StatusCreated,
 				msg:    "",
 			},
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:   "tmpfs",
 							Target: destPath}}}},
 				status: http.StatusCreated,
 				msg:    "",
 			},
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:   "tmpfs",
 							Target: destPath,
 							TmpfsOptions: &mounttypes.TmpfsOptions{
 								SizeBytes: 4096 * 1024,
 								Mode:      0700,
 							}}}}},
 				status: http.StatusCreated,
 				msg:    "",
 			},
 
 			{
 				config: cfg{
 					Image: "busybox",
 					HostConfig: hc{
 						Mounts: []m{{
 							Type:   "tmpfs",
 							Source: "/shouldnotbespecified",
 							Target: destPath}}}},
 				status: http.StatusBadRequest,
 				msg:    "Source must not be specified",
 			},
fc7b904d
 		}...)
 
 	}
 
 	for i, x := range cases {
 		c.Logf("case %d", i)
 		status, b, err := sockRequest("POST", "/containers/create", x.config)
 		c.Assert(err, checker.IsNil)
 		c.Assert(status, checker.Equals, x.status, check.Commentf("%s\n%v", string(b), cases[i].config))
 		if len(x.msg) > 0 {
 			c.Assert(string(b), checker.Contains, x.msg, check.Commentf("%v", cases[i].config))
 		}
 	}
 }
 
7fb7a477
 func (s *DockerSuite) TestContainerAPICreateMountsBindRead(c *check.C) {
fc7b904d
 	testRequires(c, NotUserNamespace, SameHostDaemon)
 	// also with data in the host side
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	destPath := prefix + slash + "foo"
 	tmpDir, err := ioutil.TempDir("", "test-mounts-api-bind")
 	c.Assert(err, checker.IsNil)
 	defer os.RemoveAll(tmpDir)
 	err = ioutil.WriteFile(filepath.Join(tmpDir, "bar"), []byte("hello"), 666)
 	c.Assert(err, checker.IsNil)
 
 	data := map[string]interface{}{
 		"Image":      "busybox",
 		"Cmd":        []string{"/bin/sh", "-c", "cat /foo/bar"},
 		"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{{"Type": "bind", "Source": tmpDir, "Target": destPath}}},
 	}
 	status, resp, err := sockRequest("POST", "/containers/create?name=test", data)
 	c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
 	c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
 
 	out, _ := dockerCmd(c, "start", "-a", "test")
 	c.Assert(out, checker.Equals, "hello")
 }
 
 // Test Mounts comes out as expected for the MountPoint
7fb7a477
 func (s *DockerSuite) TestContainersAPICreateMountsCreate(c *check.C) {
fc7b904d
 	prefix, slash := getPrefixAndSlashFromDaemonPlatform()
 	destPath := prefix + slash + "foo"
 
 	var (
 		err     error
 		testImg string
 	)
 	if daemonPlatform != "windows" {
 		testImg, err = buildImage("test-mount-config", `
 	FROM busybox
 	RUN mkdir `+destPath+` && touch `+destPath+slash+`bar
 	CMD cat `+destPath+slash+`bar
 	`, true)
 	} else {
 		testImg = "busybox"
 	}
 	c.Assert(err, checker.IsNil)
 
 	type testCase struct {
 		cfg      mounttypes.Mount
 		expected types.MountPoint
 	}
 
 	cases := []testCase{
 		// use literal strings here for `Type` instead of the defined constants in the volume package to keep this honest
 		// Validation of the actual `Mount` struct is done in another test is not needed here
 		{mounttypes.Mount{Type: "volume", Target: destPath}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
 		{mounttypes.Mount{Type: "volume", Target: destPath + slash}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
 		{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test1"}, types.MountPoint{Type: "volume", Name: "test1", RW: true, Destination: destPath}},
 		{mounttypes.Mount{Type: "volume", Target: destPath, ReadOnly: true, Source: "test2"}, types.MountPoint{Type: "volume", Name: "test2", RW: false, Destination: destPath}},
 		{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}},
 	}
 
 	if SameHostDaemon.Condition() {
 		// setup temp dir for testing binds
 		tmpDir1, err := ioutil.TempDir("", "test-mounts-api-1")
 		c.Assert(err, checker.IsNil)
 		defer os.RemoveAll(tmpDir1)
 		cases = append(cases, []testCase{
 			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir1}},
 			{mounttypes.Mount{Type: "bind", Source: tmpDir1, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir1}},
 		}...)
 
 		// for modes only supported on Linux
 		if DaemonIsLinux.Condition() {
 			tmpDir3, err := ioutils.TempDir("", "test-mounts-api-3")
 			c.Assert(err, checker.IsNil)
 			defer os.RemoveAll(tmpDir3)
 
 			c.Assert(mount.Mount(tmpDir3, tmpDir3, "none", "bind,rw"), checker.IsNil)
 			c.Assert(mount.ForceMount("", tmpDir3, "none", "shared"), checker.IsNil)
 
 			cases = append(cases, []testCase{
 				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath}, types.MountPoint{Type: "bind", RW: true, Destination: destPath, Source: tmpDir3}},
 				{mounttypes.Mount{Type: "bind", Source: tmpDir3, Target: destPath, ReadOnly: true}, types.MountPoint{Type: "bind", RW: false, Destination: destPath, Source: tmpDir3}},
 				{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"}},
 			}...)
 		}
 	}
 
 	if daemonPlatform != "windows" { // Windows does not support volume populate
 		cases = append(cases, []testCase{
 			{mounttypes.Mount{Type: "volume", Target: destPath, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
 			{mounttypes.Mount{Type: "volume", Target: destPath + slash, VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Driver: volume.DefaultDriverName, Type: "volume", RW: true, Destination: destPath}},
 			{mounttypes.Mount{Type: "volume", Target: destPath, Source: "test4", VolumeOptions: &mounttypes.VolumeOptions{NoCopy: true}}, types.MountPoint{Type: "volume", Name: "test4", RW: true, Destination: destPath}},
 			{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}},
 		}...)
 	}
 
 	type wrapper struct {
 		containertypes.Config
 		HostConfig containertypes.HostConfig
 	}
 	type createResp struct {
 		ID string `json:"Id"`
 	}
 	for i, x := range cases {
 		c.Logf("case %d - config: %v", i, x.cfg)
 		status, data, err := sockRequest("POST", "/containers/create", wrapper{containertypes.Config{Image: testImg}, containertypes.HostConfig{Mounts: []mounttypes.Mount{x.cfg}}})
 		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
 		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(data)))
 
 		var resp createResp
 		err = json.Unmarshal(data, &resp)
 		c.Assert(err, checker.IsNil, check.Commentf(string(data)))
 		id := resp.ID
 
 		var mps []types.MountPoint
 		err = json.NewDecoder(strings.NewReader(inspectFieldJSON(c, id, "Mounts"))).Decode(&mps)
 		c.Assert(err, checker.IsNil)
 		c.Assert(mps, checker.HasLen, 1)
 		c.Assert(mps[0].Destination, checker.Equals, x.expected.Destination)
 
 		if len(x.expected.Source) > 0 {
 			c.Assert(mps[0].Source, checker.Equals, x.expected.Source)
 		}
 		if len(x.expected.Name) > 0 {
 			c.Assert(mps[0].Name, checker.Equals, x.expected.Name)
 		}
 		if len(x.expected.Driver) > 0 {
 			c.Assert(mps[0].Driver, checker.Equals, x.expected.Driver)
 		}
 		c.Assert(mps[0].RW, checker.Equals, x.expected.RW)
 		c.Assert(mps[0].Type, checker.Equals, x.expected.Type)
 		c.Assert(mps[0].Mode, checker.Equals, x.expected.Mode)
 		if len(x.expected.Propagation) > 0 {
 			c.Assert(mps[0].Propagation, checker.Equals, x.expected.Propagation)
 		}
 
 		out, _, err := dockerCmdWithError("start", "-a", id)
 		if (x.cfg.Type != "volume" || (x.cfg.VolumeOptions != nil && x.cfg.VolumeOptions.NoCopy)) && daemonPlatform != "windows" {
 			c.Assert(err, checker.NotNil, check.Commentf("%s\n%v", out, mps[0]))
 		} else {
 			c.Assert(err, checker.IsNil, check.Commentf("%s\n%v", out, mps[0]))
 		}
 
 		dockerCmd(c, "rm", "-fv", id)
 		if x.cfg.Type == "volume" && len(x.cfg.Source) > 0 {
 			// This should still exist even though we removed the container
 			dockerCmd(c, "volume", "inspect", mps[0].Name)
 		} else {
 			// This should be removed automatically when we removed the container
 			out, _, err := dockerCmdWithError("volume", "inspect", mps[0].Name)
 			c.Assert(err, checker.NotNil, check.Commentf(out))
 		}
 	}
 }
18768fdc
 
 func (s *DockerSuite) TestContainersAPICreateMountsTmpfs(c *check.C) {
 	testRequires(c, DaemonIsLinux)
 	type testCase struct {
 		cfg             map[string]interface{}
 		expectedOptions []string
 	}
 	target := "/foo"
 	cases := []testCase{
 		{
 			cfg: map[string]interface{}{
 				"Type":   "tmpfs",
 				"Target": target},
 			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime"},
 		},
 		{
 			cfg: map[string]interface{}{
 				"Type":   "tmpfs",
 				"Target": target,
 				"TmpfsOptions": map[string]interface{}{
 					"SizeBytes": 4096 * 1024, "Mode": 0700}},
 			expectedOptions: []string{"rw", "nosuid", "nodev", "noexec", "relatime", "size=4096k", "mode=700"},
 		},
 	}
 
 	for i, x := range cases {
 		cName := fmt.Sprintf("test-tmpfs-%d", i)
 		data := map[string]interface{}{
 			"Image": "busybox",
 			"Cmd": []string{"/bin/sh", "-c",
 				fmt.Sprintf("mount | grep 'tmpfs on %s'", target)},
 			"HostConfig": map[string]interface{}{"Mounts": []map[string]interface{}{x.cfg}},
 		}
 		status, resp, err := sockRequest("POST", "/containers/create?name="+cName, data)
 		c.Assert(err, checker.IsNil, check.Commentf(string(resp)))
 		c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(resp)))
 		out, _ := dockerCmd(c, "start", "-a", cName)
 		for _, option := range x.expectedOptions {
 			c.Assert(out, checker.Contains, option)
 		}
 	}
 }