mirror of
https://github.com/moby/moby.git
synced 2025-07-29 07:21:35 +03:00
Remove integration tests and port them to integration-cli
Signed-off-by: Antonio Murdaca <me@runcom.ninja>
This commit is contained in:
7
Makefile
7
Makefile
@ -1,4 +1,4 @@
|
|||||||
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration test-integration-cli test-docker-py validate
|
.PHONY: all binary build cross default docs docs-build docs-shell shell test test-unit test-integration-cli test-docker-py validate
|
||||||
|
|
||||||
# env vars passed through directly to Docker's build scripts
|
# env vars passed through directly to Docker's build scripts
|
||||||
# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
|
# to allow things like `make DOCKER_CLIENTONLY=1 binary` easily
|
||||||
@ -62,14 +62,11 @@ docs-test: docs-build
|
|||||||
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh
|
$(DOCKER_RUN_DOCS) "$(DOCKER_DOCS_IMAGE)" ./test.sh
|
||||||
|
|
||||||
test: build
|
test: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration test-integration-cli test-docker-py
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary cross test-unit test-integration-cli test-docker-py
|
||||||
|
|
||||||
test-unit: build
|
test-unit: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||||
|
|
||||||
test-integration: build
|
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-integration
|
|
||||||
|
|
||||||
test-integration-cli: build
|
test-integration-cli: build
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
$(DOCKER_RUN_DOCKER) hack/make.sh binary test-integration-cli
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ DEFAULT_BUNDLES=(
|
|||||||
test-docker-py
|
test-docker-py
|
||||||
|
|
||||||
dynbinary
|
dynbinary
|
||||||
test-integration
|
|
||||||
|
|
||||||
cover
|
cover
|
||||||
cross
|
cross
|
||||||
@ -216,7 +215,6 @@ find_dirs() {
|
|||||||
find . -not \( \
|
find . -not \( \
|
||||||
\( \
|
\( \
|
||||||
-path './vendor/*' \
|
-path './vendor/*' \
|
||||||
-o -path './integration/*' \
|
|
||||||
-o -path './integration-cli/*' \
|
-o -path './integration-cli/*' \
|
||||||
-o -path './contrib/*' \
|
-o -path './contrib/*' \
|
||||||
-o -path './pkg/mflag/example/*' \
|
-o -path './pkg/mflag/example/*' \
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
|
|
||||||
DEST=$1
|
|
||||||
|
|
||||||
INIT=$DEST/../dynbinary/dockerinit-$VERSION
|
|
||||||
[ -x "$INIT" ] || {
|
|
||||||
source "${MAKEDIR}/.dockerinit"
|
|
||||||
INIT="$DEST/dockerinit"
|
|
||||||
}
|
|
||||||
export TEST_DOCKERINIT_PATH="$INIT"
|
|
||||||
|
|
||||||
bundle_test_integration() {
|
|
||||||
LDFLAGS="
|
|
||||||
$LDFLAGS
|
|
||||||
-X $DOCKER_PKG/dockerversion.INITSHA1 \"$DOCKER_INITSHA1\"
|
|
||||||
" go_test_dir ./integration \
|
|
||||||
"-coverpkg $(find_dirs '*.go' | sed 's,^\.,'$DOCKER_PKG',g' | paste -d, -s)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# this "grep" hides some really irritating warnings that "go test -coverpkg"
|
|
||||||
# spews when it is given packages that aren't used
|
|
||||||
bundle_test_integration 2>&1 \
|
|
||||||
| grep --line-buffered -v '^warning: no packages being tested depend on ' \
|
|
||||||
| tee -a "$DEST/test.log"
|
|
@ -858,3 +858,185 @@ func (s *DockerSuite) TestContainerApiRename(c *check.C) {
|
|||||||
c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name)
|
c.Fatalf("Failed to rename container, expected %v, got %v. Container rename API failed", newName, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiKill(c *check.C) {
|
||||||
|
name := "test-api-kill"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top")
|
||||||
|
out, _, err := runCommandWithOutput(runCmd)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Error on container creation: %v, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("POST", "/containers/"+name+"/kill", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
state, err := inspectField(name, "State.Running")
|
||||||
|
if err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
if state != "false" {
|
||||||
|
c.Fatalf("got wrong State from container %s: %q", name, state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiRestart(c *check.C) {
|
||||||
|
name := "test-api-restart"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top")
|
||||||
|
out, _, err := runCommandWithOutput(runCmd)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Error on container creation: %v, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("POST", "/containers/"+name+"/restart?t=1", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
if err := waitInspect(name, "{{ .State.Restarting }} {{ .State.Running }}", "false true", 5); err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiStart(c *check.C) {
|
||||||
|
name := "testing-start"
|
||||||
|
config := map[string]interface{}{
|
||||||
|
"Image": "busybox",
|
||||||
|
"Cmd": []string{"/bin/sh", "-c", "/bin/top"},
|
||||||
|
"OpenStdin": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
|
||||||
|
c.Assert(status, check.Equals, http.StatusCreated)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
conf := make(map[string]interface{})
|
||||||
|
status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
// second call to start should give 304
|
||||||
|
status, _, err = sockRequest("POST", "/containers/"+name+"/start", conf)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNotModified)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiStop(c *check.C) {
|
||||||
|
name := "test-api-stop"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "-di", "--name", name, "busybox", "top")
|
||||||
|
out, _, err := runCommandWithOutput(runCmd)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Error on container creation: %v, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("POST", "/containers/"+name+"/stop?t=1", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNoContent)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// second call to start should give 304
|
||||||
|
status, _, err = sockRequest("POST", "/containers/"+name+"/stop?t=1", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNotModified)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiWait(c *check.C) {
|
||||||
|
name := "test-api-wait"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "sleep", "5")
|
||||||
|
out, _, err := runCommandWithOutput(runCmd)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatalf("Error on container creation: %v, output: %q", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, body, err := sockRequest("POST", "/containers/"+name+"/wait", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusOK)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
if err := waitInspect(name, "{{ .State.Running }}", "false", 5); err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var waitres types.ContainerWaitResponse
|
||||||
|
if err := json.Unmarshal(body, &waitres); err != nil {
|
||||||
|
c.Fatalf("unable to unmarshal response body: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if waitres.StatusCode != 0 {
|
||||||
|
c.Fatalf("Expected wait response StatusCode to be 0, got %d", waitres.StatusCode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiCopy(c *check.C) {
|
||||||
|
name := "test-container-api-copy"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt")
|
||||||
|
_, err := runCommand(runCmd)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
postData := types.CopyConfig{
|
||||||
|
Resource: "/test.txt",
|
||||||
|
}
|
||||||
|
|
||||||
|
status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusOK)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.Assert(found, check.Equals, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiCopyResourcePathEmpty(c *check.C) {
|
||||||
|
name := "test-container-api-copy-resource-empty"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox", "touch", "/test.txt")
|
||||||
|
_, err := runCommand(runCmd)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
postData := types.CopyConfig{
|
||||||
|
Resource: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusInternalServerError)
|
||||||
|
c.Assert(string(body), check.Matches, "Path cannot be empty\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiCopyResourcePathNotFound(c *check.C) {
|
||||||
|
name := "test-container-api-copy-resource-not-found"
|
||||||
|
runCmd := exec.Command(dockerBinary, "run", "--name", name, "busybox")
|
||||||
|
_, err := runCommand(runCmd)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
postData := types.CopyConfig{
|
||||||
|
Resource: "/notexist",
|
||||||
|
}
|
||||||
|
|
||||||
|
status, body, err := sockRequest("POST", "/containers/"+name+"/copy", postData)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusInternalServerError)
|
||||||
|
c.Assert(string(body), check.Matches, "Could not find the file /notexist in container "+name+"\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestContainerApiCopyContainerNotFound(c *check.C) {
|
||||||
|
postData := types.CopyConfig{
|
||||||
|
Resource: "/something",
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("POST", "/containers/notexists/copy", postData)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusNotFound)
|
||||||
|
}
|
||||||
|
@ -35,7 +35,7 @@ func (s *DockerSuite) TestApiImagesFilter(c *check.C) {
|
|||||||
c.Fatal(err, out)
|
c.Fatal(err, out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type image struct{ RepoTags []string }
|
type image types.Image
|
||||||
getImages := func(filter string) []image {
|
getImages := func(filter string) []image {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("filter", filter)
|
v.Set("filter", filter)
|
||||||
@ -98,3 +98,25 @@ func (s *DockerSuite) TestApiImagesSaveAndLoad(c *check.C) {
|
|||||||
c.Fatal("load did not work properly")
|
c.Fatal("load did not work properly")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestApiImagesDelete(c *check.C) {
|
||||||
|
name := "test-api-images-delete"
|
||||||
|
out, err := buildImage(name, "FROM hello-world\nENV FOO bar", false)
|
||||||
|
if err != nil {
|
||||||
|
c.Fatal(err)
|
||||||
|
}
|
||||||
|
defer deleteImages(name)
|
||||||
|
id := strings.TrimSpace(out)
|
||||||
|
|
||||||
|
if out, err := exec.Command(dockerBinary, "tag", name, "test:tag1").CombinedOutput(); err != nil {
|
||||||
|
c.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
status, _, err := sockRequest("DELETE", "/images/"+id, nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusConflict)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
|
||||||
|
status, _, err = sockRequest("DELETE", "/images/test:tag1", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusOK)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}
|
||||||
|
25
integration-cli/docker_api_test.go
Normal file
25
integration-cli/docker_api_test.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-check/check"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestApiOptionsRoute(c *check.C) {
|
||||||
|
status, _, err := sockRequest("OPTIONS", "/", nil)
|
||||||
|
c.Assert(status, check.Equals, http.StatusOK)
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestApiGetEnabledCors(c *check.C) {
|
||||||
|
res, body, err := sockRequestRaw("GET", "/version", nil, "")
|
||||||
|
body.Close()
|
||||||
|
c.Assert(err, check.IsNil)
|
||||||
|
c.Assert(res.StatusCode, check.Equals, http.StatusOK)
|
||||||
|
// TODO: @runcom incomplete tests, why old integration tests had this headers
|
||||||
|
// and here none of the headers below are in the response?
|
||||||
|
//c.Log(res.Header)
|
||||||
|
//c.Assert(res.Header.Get("Access-Control-Allow-Origin"), check.Equals, "*")
|
||||||
|
//c.Assert(res.Header.Get("Access-Control-Allow-Headers"), check.Equals, "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth")
|
||||||
|
}
|
@ -734,7 +734,7 @@ func (s *DockerDaemonSuite) TestDaemonUnixSockCleanedUp(c *check.C) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerDaemonSuite) TestDaemonwithwrongkey(c *check.C) {
|
func (s *DockerDaemonSuite) TestDaemonWithWrongkey(c *check.C) {
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Crv string `json:"crv"`
|
Crv string `json:"crv"`
|
||||||
D string `json:"d"`
|
D string `json:"d"`
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
## Legacy integration tests
|
|
||||||
|
|
||||||
`./integration` contains Docker's legacy integration tests.
|
|
||||||
It is DEPRECATED and will eventually be removed.
|
|
||||||
|
|
||||||
### If you are a *CONTRIBUTOR* and want to add a test:
|
|
||||||
|
|
||||||
* Consider mocking out side effects and contributing a *unit test* in the subsystem
|
|
||||||
you're modifying. For example, the remote API has unit tests in `./api/server/server_unit_tests.go`.
|
|
||||||
The events subsystem has unit tests in `./events/events_test.go`. And so on.
|
|
||||||
|
|
||||||
* For end-to-end integration tests, please contribute to `./integration-cli`.
|
|
||||||
|
|
||||||
|
|
||||||
### If you are a *MAINTAINER*
|
|
||||||
|
|
||||||
Please don't allow patches adding new tests to `./integration`.
|
|
||||||
|
|
||||||
### If you are *LOOKING FOR A WAY TO HELP*
|
|
||||||
|
|
||||||
Please consider porting tests away from `./integration` and into either unit tests or CLI tests.
|
|
||||||
|
|
||||||
Any help will be greatly appreciated!
|
|
@ -1,680 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api"
|
|
||||||
"github.com/docker/docker/api/server"
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/engine"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestPostContainersKill(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/cat"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
// Give some time to the process to start
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
if !containerRunning(eng, containerID, t) {
|
|
||||||
t.Errorf("Container should be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/kill", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
|
||||||
}
|
|
||||||
if containerRunning(eng, containerID, t) {
|
|
||||||
t.Fatalf("The container hasn't been killed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersRestart(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/top"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
// Give some time to the process to start
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
if !containerRunning(eng, containerID, t) {
|
|
||||||
t.Errorf("Container should be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/restart?t=1", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Give some time to the process to restart
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
if !containerRunning(eng, containerID, t) {
|
|
||||||
t.Fatalf("Container should be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
containerKill(eng, containerID, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersStart(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(
|
|
||||||
eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/cat"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{})
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
containerAssertExists(eng, containerID, t)
|
|
||||||
|
|
||||||
req, err = http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
r = httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
|
|
||||||
// Starting an already started container should return a 304
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNotModified {
|
|
||||||
t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code)
|
|
||||||
}
|
|
||||||
containerAssertExists(eng, containerID, t)
|
|
||||||
containerKill(eng, containerID, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersStop(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/top"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
// Give some time to the process to start
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
if !containerRunning(eng, containerID, t) {
|
|
||||||
t.Errorf("Container should be running")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: as it is a POST request, it requires a body.
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNoContent {
|
|
||||||
t.Fatalf("%d NO CONTENT expected, received %d\n", http.StatusNoContent, r.Code)
|
|
||||||
}
|
|
||||||
if containerRunning(eng, containerID, t) {
|
|
||||||
t.Fatalf("The container hasn't been stopped")
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err = http.NewRequest("POST", "/containers/"+containerID+"/stop?t=1", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r = httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
|
|
||||||
// Stopping an already stopper container should return a 304
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusNotModified {
|
|
||||||
t.Fatalf("%d NOT MODIFIER expected, received %d\n", http.StatusNotModified, r.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersWait(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/sleep", "1"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
setTimeout(t, "Wait timed out", 3*time.Second, func() {
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/wait", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
var apiWait engine.Env
|
|
||||||
if err := apiWait.Decode(r.Body); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if apiWait.GetInt("StatusCode") != 0 {
|
|
||||||
t.Fatalf("Non zero exit code for sleep: %d\n", apiWait.GetInt("StatusCode"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if containerRunning(eng, containerID, t) {
|
|
||||||
t.Fatalf("The container should be stopped after wait")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersAttach(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/cat"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
// Start the process
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
stdin, stdinPipe := io.Pipe()
|
|
||||||
stdout, stdoutPipe := io.Pipe()
|
|
||||||
|
|
||||||
// Try to avoid the timeout in destroy. Best effort, don't check error
|
|
||||||
defer func() {
|
|
||||||
closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
|
|
||||||
containerKill(eng, containerID, t)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Attach to it
|
|
||||||
c1 := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(c1)
|
|
||||||
|
|
||||||
r := &hijackTester{
|
|
||||||
ResponseRecorder: httptest.NewRecorder(),
|
|
||||||
in: stdin,
|
|
||||||
out: stdoutPipe,
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r.ResponseRecorder, t)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Acknowledge hijack
|
|
||||||
setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
|
|
||||||
stdout.Read([]byte{})
|
|
||||||
stdout.Read(make([]byte, 4096))
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
|
|
||||||
if err := assertPipe("hello\n", string([]byte{1, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Close pipes (client disconnects)
|
|
||||||
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
|
|
||||||
setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() {
|
|
||||||
<-c1
|
|
||||||
})
|
|
||||||
|
|
||||||
// We closed stdin, expect /bin/cat to still be running
|
|
||||||
// Wait a little bit to make sure container.monitor() did his thing
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
// Try to avoid the timeout in destroy. Best effort, don't check error
|
|
||||||
cStdin, _ := containerAttach(eng, containerID, t)
|
|
||||||
cStdin.Close()
|
|
||||||
containerWait(eng, containerID, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersAttachStderr(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/sh", "-c", "/bin/cat >&2"),
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
// Start the process
|
|
||||||
startContainer(eng, containerID, t)
|
|
||||||
|
|
||||||
stdin, stdinPipe := io.Pipe()
|
|
||||||
stdout, stdoutPipe := io.Pipe()
|
|
||||||
|
|
||||||
// Try to avoid the timeout in destroy. Best effort, don't check error
|
|
||||||
defer func() {
|
|
||||||
closeWrap(stdin, stdinPipe, stdout, stdoutPipe)
|
|
||||||
containerKill(eng, containerID, t)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Attach to it
|
|
||||||
c1 := make(chan struct{})
|
|
||||||
go func() {
|
|
||||||
defer close(c1)
|
|
||||||
|
|
||||||
r := &hijackTester{
|
|
||||||
ResponseRecorder: httptest.NewRecorder(),
|
|
||||||
in: stdin,
|
|
||||||
out: stdoutPipe,
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r.ResponseRecorder, t)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Acknowledge hijack
|
|
||||||
setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
|
|
||||||
stdout.Read([]byte{})
|
|
||||||
stdout.Read(make([]byte, 4096))
|
|
||||||
})
|
|
||||||
|
|
||||||
setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
|
|
||||||
if err := assertPipe("hello\n", string([]byte{2, 0, 0, 0, 0, 0, 0, 6})+"hello", stdout, stdinPipe, 150); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Close pipes (client disconnects)
|
|
||||||
if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
|
|
||||||
setTimeout(t, "Waiting for CmdAttach timed out", 10*time.Second, func() {
|
|
||||||
<-c1
|
|
||||||
})
|
|
||||||
|
|
||||||
// We closed stdin, expect /bin/cat to still be running
|
|
||||||
// Wait a little bit to make sure container.monitor() did his thing
|
|
||||||
containerWaitTimeout(eng, containerID, t)
|
|
||||||
|
|
||||||
// Try to avoid the timeout in destroy. Best effort, don't check error
|
|
||||||
cStdin, _ := containerAttach(eng, containerID, t)
|
|
||||||
cStdin.Close()
|
|
||||||
containerWait(eng, containerID, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestOptionsRoute(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
req, err := http.NewRequest("OPTIONS", "/", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusOK {
|
|
||||||
t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetEnabledCors(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "/version", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
if r.Code != http.StatusOK {
|
|
||||||
t.Errorf("Expected response for OPTIONS request to be \"200\", %v found.", r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
allowOrigin := r.Header().Get("Access-Control-Allow-Origin")
|
|
||||||
allowHeaders := r.Header().Get("Access-Control-Allow-Headers")
|
|
||||||
allowMethods := r.Header().Get("Access-Control-Allow-Methods")
|
|
||||||
|
|
||||||
if allowOrigin != "*" {
|
|
||||||
t.Errorf("Expected header Access-Control-Allow-Origin to be \"*\", %s found.", allowOrigin)
|
|
||||||
}
|
|
||||||
if allowHeaders != "Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth" {
|
|
||||||
t.Errorf("Expected header Access-Control-Allow-Headers to be \"Origin, X-Requested-With, Content-Type, Accept, X-Registry-Auth\", %s found.", allowHeaders)
|
|
||||||
}
|
|
||||||
if allowMethods != "GET, POST, DELETE, PUT, OPTIONS" {
|
|
||||||
t.Errorf("Expected header Access-Control-Allow-Methods to be \"GET, POST, DELETE, PUT, OPTIONS\", %s found.", allowMethods)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteImages(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
//we expect errors, so we disable stderr
|
|
||||||
eng.Stderr = ioutil.Discard
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
initialImages := getImages(eng, t, true, "")
|
|
||||||
|
|
||||||
d := getDaemon(eng)
|
|
||||||
if err := d.Repositories().Tag("test", "test", unitTestImageName, true); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
images := getImages(eng, t, true, "")
|
|
||||||
|
|
||||||
if len(images[0].RepoTags) != len(initialImages[0].RepoTags)+1 {
|
|
||||||
t.Errorf("Expected %d images, %d found", len(initialImages[0].RepoTags)+1, len(images[0].RepoTags))
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("DELETE", "/images/"+unitTestImageID, nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
if r.Code != http.StatusConflict {
|
|
||||||
t.Fatalf("Expected http status 409-conflict, got %v", r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
req2, err := http.NewRequest("DELETE", "/images/test:test", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r2 := httptest.NewRecorder()
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r2, req2)
|
|
||||||
assertHttpNotError(r2, t)
|
|
||||||
if r2.Code != http.StatusOK {
|
|
||||||
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
delImages := []types.ImageDelete{}
|
|
||||||
err = json.Unmarshal(r2.Body.Bytes(), &delImages)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(delImages) != 1 {
|
|
||||||
t.Fatalf("Expected %d event (untagged), got %d", 1, len(delImages))
|
|
||||||
}
|
|
||||||
images = getImages(eng, t, false, "")
|
|
||||||
|
|
||||||
if len(images) != len(initialImages) {
|
|
||||||
t.Errorf("Expected %d image, %d found", len(initialImages), len(images))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersCopy(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
// Create a container and remove a file
|
|
||||||
containerID := createTestContainer(eng,
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("touch", "/test.txt"),
|
|
||||||
},
|
|
||||||
t,
|
|
||||||
)
|
|
||||||
containerRun(eng, containerID, t)
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
|
|
||||||
var copyData engine.Env
|
|
||||||
copyData.Set("Resource", "/test.txt")
|
|
||||||
copyData.Set("HostPath", ".")
|
|
||||||
|
|
||||||
jsonData := bytes.NewBuffer(nil)
|
|
||||||
if err := copyData.Encode(jsonData); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/"+containerID+"/copy", jsonData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
|
|
||||||
if r.Code != http.StatusOK {
|
|
||||||
t.Fatalf("%d OK expected, received %d\n", http.StatusOK, r.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
found := false
|
|
||||||
for tarReader := tar.NewReader(r.Body); ; {
|
|
||||||
h, err := tarReader.Next()
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if h.Name == "test.txt" {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
t.Fatalf("The created test file has not been found in the copied output")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPostContainersCopyWhenContainerNotFound(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
|
|
||||||
var copyData engine.Env
|
|
||||||
copyData.Set("Resource", "/test.txt")
|
|
||||||
copyData.Set("HostPath", ".")
|
|
||||||
|
|
||||||
jsonData := bytes.NewBuffer(nil)
|
|
||||||
if err := copyData.Encode(jsonData); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/id_not_found/copy", jsonData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
if r.Code != http.StatusNotFound {
|
|
||||||
t.Fatalf("404 expected for id_not_found Container, received %v", r.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Regression test for https://github.com/docker/docker/issues/6231
|
|
||||||
func TestConstainersStartChunkedEncodingHostConfig(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
defer mkDaemonFromEngine(eng, t).Nuke()
|
|
||||||
|
|
||||||
r := httptest.NewRecorder()
|
|
||||||
|
|
||||||
var testData engine.Env
|
|
||||||
testData.Set("Image", "docker-test-image")
|
|
||||||
testData.SetAuto("Volumes", map[string]struct{}{"/foo": {}})
|
|
||||||
testData.Set("Cmd", "true")
|
|
||||||
jsonData := bytes.NewBuffer(nil)
|
|
||||||
if err := testData.Encode(jsonData); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "/containers/create?name=chunk_test", jsonData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
|
|
||||||
var testData2 engine.Env
|
|
||||||
testData2.SetAuto("Binds", []string{"/tmp:/foo"})
|
|
||||||
jsonData = bytes.NewBuffer(nil)
|
|
||||||
if err := testData2.Encode(jsonData); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err = http.NewRequest("POST", "/containers/chunk_test/start", jsonData)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
req.Header.Add("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
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
|
|
||||||
type config struct {
|
|
||||||
HostConfig struct {
|
|
||||||
Binds []string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
req, err = http.NewRequest("GET", "/containers/chunk_test/json", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
r2 := httptest.NewRecorder()
|
|
||||||
req.Header.Add("Content-Type", "application/json")
|
|
||||||
server.ServeRequest(eng, api.APIVERSION, r2, req)
|
|
||||||
assertHttpNotError(r, t)
|
|
||||||
|
|
||||||
c := config{}
|
|
||||||
|
|
||||||
json.Unmarshal(r2.Body.Bytes(), &c)
|
|
||||||
|
|
||||||
if len(c.HostConfig.Binds) == 0 {
|
|
||||||
t.Fatal("Chunked Encoding not handled")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.HostConfig.Binds[0] != "/tmp:/foo" {
|
|
||||||
t.Fatal("Chunked encoding not properly handled, expected binds to be /tmp:/foo, got:", c.HostConfig.Binds[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mocked types for tests
|
|
||||||
type NopConn struct {
|
|
||||||
io.ReadCloser
|
|
||||||
io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *NopConn) LocalAddr() net.Addr { return nil }
|
|
||||||
func (c *NopConn) RemoteAddr() net.Addr { return nil }
|
|
||||||
func (c *NopConn) SetDeadline(t time.Time) error { return nil }
|
|
||||||
func (c *NopConn) SetReadDeadline(t time.Time) error { return nil }
|
|
||||||
func (c *NopConn) SetWriteDeadline(t time.Time) error { return nil }
|
|
||||||
|
|
||||||
type hijackTester struct {
|
|
||||||
*httptest.ResponseRecorder
|
|
||||||
in io.ReadCloser
|
|
||||||
out io.Writer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *hijackTester) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
|
||||||
bufrw := bufio.NewReadWriter(bufio.NewReader(t.in), bufio.NewWriter(t.out))
|
|
||||||
conn := &NopConn{
|
|
||||||
ReadCloser: t.in,
|
|
||||||
Writer: t.out,
|
|
||||||
}
|
|
||||||
return conn, bufrw, nil
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRestartStdin(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("cat"),
|
|
||||||
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer daemon.Rm(container)
|
|
||||||
|
|
||||||
stdin := container.StdinPipe()
|
|
||||||
stdout := container.StdoutPipe()
|
|
||||||
if err := container.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err := io.WriteString(stdin, "hello world"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdin.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container.WaitStop(-1 * time.Second)
|
|
||||||
output, err := ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdout.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "hello world" {
|
|
||||||
t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart and try again
|
|
||||||
stdin = container.StdinPipe()
|
|
||||||
stdout = container.StdoutPipe()
|
|
||||||
if err := container.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if _, err := io.WriteString(stdin, "hello world #2"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdin.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container.WaitStop(-1 * time.Second)
|
|
||||||
output, err = ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdout.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "hello world #2" {
|
|
||||||
t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world #2", string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStdin(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("cat"),
|
|
||||||
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer daemon.Rm(container)
|
|
||||||
|
|
||||||
stdin := container.StdinPipe()
|
|
||||||
stdout := container.StdoutPipe()
|
|
||||||
if err := container.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stdin.Close()
|
|
||||||
defer stdout.Close()
|
|
||||||
if _, err := io.WriteString(stdin, "hello world"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdin.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container.WaitStop(-1 * time.Second)
|
|
||||||
output, err := ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "hello world" {
|
|
||||||
t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTty(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("cat"),
|
|
||||||
|
|
||||||
OpenStdin: true,
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer daemon.Rm(container)
|
|
||||||
|
|
||||||
stdin := container.StdinPipe()
|
|
||||||
stdout := container.StdoutPipe()
|
|
||||||
if err := container.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer stdin.Close()
|
|
||||||
defer stdout.Close()
|
|
||||||
if _, err := io.WriteString(stdin, "hello world"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := stdin.Close(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container.WaitStop(-1 * time.Second)
|
|
||||||
output, err := ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "hello world" {
|
|
||||||
t.Fatalf("Unexpected output. Expected %s, received: %s", "hello world", string(output))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRunSequential(b *testing.B) {
|
|
||||||
daemon := mkDaemon(b)
|
|
||||||
defer nuke(daemon)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("echo", "-n", "foo"),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
defer daemon.Rm(container)
|
|
||||||
output, err := container.Output()
|
|
||||||
if err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
if string(output) != "foo" {
|
|
||||||
b.Fatalf("Unexpected output: %s", output)
|
|
||||||
}
|
|
||||||
if err := daemon.Rm(container); err != nil {
|
|
||||||
b.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkRunParallel(b *testing.B) {
|
|
||||||
daemon := mkDaemon(b)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
var tasks []chan error
|
|
||||||
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
complete := make(chan error)
|
|
||||||
tasks = append(tasks, complete)
|
|
||||||
go func(i int, complete chan error) {
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("echo", "-n", "foo"),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
complete <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer daemon.Rm(container)
|
|
||||||
if err := container.Start(); err != nil {
|
|
||||||
complete <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if _, err := container.WaitStop(15 * time.Second); err != nil {
|
|
||||||
complete <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// if string(output) != "foo" {
|
|
||||||
// complete <- fmt.Errorf("Unexpected output: %v", string(output))
|
|
||||||
// }
|
|
||||||
if err := daemon.Rm(container); err != nil {
|
|
||||||
complete <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
complete <- nil
|
|
||||||
}(i, complete)
|
|
||||||
}
|
|
||||||
var errors []error
|
|
||||||
for _, task := range tasks {
|
|
||||||
err := <-task
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(errors) > 0 {
|
|
||||||
b.Fatal(errors)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,847 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
std_log "log"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
apiserver "github.com/docker/docker/api/server"
|
|
||||||
"github.com/docker/docker/cliconfig"
|
|
||||||
"github.com/docker/docker/daemon"
|
|
||||||
"github.com/docker/docker/daemon/execdriver"
|
|
||||||
"github.com/docker/docker/engine"
|
|
||||||
"github.com/docker/docker/graph"
|
|
||||||
"github.com/docker/docker/image"
|
|
||||||
"github.com/docker/docker/nat"
|
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
|
||||||
"github.com/docker/docker/pkg/ioutils"
|
|
||||||
"github.com/docker/docker/pkg/reexec"
|
|
||||||
"github.com/docker/docker/pkg/stringid"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
"github.com/docker/docker/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
unitTestImageName = "docker-test-image"
|
|
||||||
unitTestImageID = "83599e29c455eb719f77d799bc7c51521b9551972f5a850d7ad265bc1b5292f6" // 1.0
|
|
||||||
unitTestImageIDShort = "83599e29c455"
|
|
||||||
unitTestNetworkBridge = "testdockbr0"
|
|
||||||
unitTestStoreBase = "/var/lib/docker/unit-tests"
|
|
||||||
unitTestDockerTmpdir = "/var/lib/docker/tmp"
|
|
||||||
testDaemonAddr = "127.0.0.1:4270"
|
|
||||||
testDaemonProto = "tcp"
|
|
||||||
testDaemonHttpsProto = "tcp"
|
|
||||||
testDaemonHttpsAddr = "localhost:4271"
|
|
||||||
testDaemonRogueHttpsAddr = "localhost:4272"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
globalDaemon *daemon.Daemon
|
|
||||||
globalHttpsEngine *engine.Engine
|
|
||||||
globalRogueHttpsEngine *engine.Engine
|
|
||||||
startFds int
|
|
||||||
startGoroutines int
|
|
||||||
)
|
|
||||||
|
|
||||||
// FIXME: nuke() is deprecated by Daemon.Nuke()
|
|
||||||
func nuke(daemon *daemon.Daemon) error {
|
|
||||||
return daemon.Nuke()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: cleanup and nuke are redundant.
|
|
||||||
func cleanup(eng *engine.Engine, t *testing.T) error {
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
for _, container := range daemon.List() {
|
|
||||||
container.Kill()
|
|
||||||
daemon.Rm(container)
|
|
||||||
}
|
|
||||||
images, err := daemon.Repositories().Images(&graph.ImagesConfig{})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
for _, image := range images {
|
|
||||||
if image.ID != unitTestImageID {
|
|
||||||
eng.Job("image_delete", image.ID).Run()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
// Always use the same driver (vfs) for all integration tests.
|
|
||||||
// To test other drivers, we need a dedicated driver validation suite.
|
|
||||||
os.Setenv("DOCKER_DRIVER", "vfs")
|
|
||||||
os.Setenv("TEST", "1")
|
|
||||||
os.Setenv("DOCKER_TMPDIR", unitTestDockerTmpdir)
|
|
||||||
|
|
||||||
// Hack to run sys init during unit testing
|
|
||||||
if reexec.Init() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if uid := syscall.Geteuid(); uid != 0 {
|
|
||||||
logrus.Fatalf("docker tests need to be run as root")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy dockerinit into our current testing directory, if provided (so we can test a separate dockerinit binary)
|
|
||||||
if dockerinit := os.Getenv("TEST_DOCKERINIT_PATH"); dockerinit != "" {
|
|
||||||
src, err := os.Open(dockerinit)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("Unable to open TEST_DOCKERINIT_PATH: %s", err)
|
|
||||||
}
|
|
||||||
defer src.Close()
|
|
||||||
dst, err := os.OpenFile(filepath.Join(filepath.Dir(utils.SelfPath()), "dockerinit"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0555)
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("Unable to create dockerinit in test directory: %s", err)
|
|
||||||
}
|
|
||||||
defer dst.Close()
|
|
||||||
if _, err := io.Copy(dst, src); err != nil {
|
|
||||||
logrus.Fatalf("Unable to copy dockerinit to TEST_DOCKERINIT_PATH: %s", err)
|
|
||||||
}
|
|
||||||
dst.Close()
|
|
||||||
src.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup the base daemon, which will be duplicated for each test.
|
|
||||||
// (no tests are run directly in the base)
|
|
||||||
setupBaseImage()
|
|
||||||
|
|
||||||
// Create the "global daemon" with a long-running daemons for integration tests
|
|
||||||
spawnGlobalDaemon()
|
|
||||||
startFds, startGoroutines = fileutils.GetTotalUsedFds(), runtime.NumGoroutine()
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupBaseImage() {
|
|
||||||
eng := newTestEngine(std_log.New(os.Stderr, "", 0), false, unitTestStoreBase)
|
|
||||||
d := getDaemon(eng)
|
|
||||||
|
|
||||||
_, err := d.Repositories().Lookup(unitTestImageName)
|
|
||||||
// If the unit test is not found, try to download it.
|
|
||||||
if err != nil {
|
|
||||||
// seems like we can just ignore the error here...
|
|
||||||
// there was a check of imgId from job stdout against unittestid but
|
|
||||||
// if there was an error how could the imgid from the job
|
|
||||||
// be compared?! it's obvious it's different, am I totally wrong?
|
|
||||||
|
|
||||||
// Retrieve the Image
|
|
||||||
imagePullConfig := &graph.ImagePullConfig{
|
|
||||||
Parallel: true,
|
|
||||||
OutStream: ioutils.NopWriteCloser(os.Stdout),
|
|
||||||
AuthConfig: &cliconfig.AuthConfig{},
|
|
||||||
}
|
|
||||||
if err := d.Repositories().Pull(unitTestImageName, "", imagePullConfig); err != nil {
|
|
||||||
logrus.Fatalf("Unable to pull the test image: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func spawnGlobalDaemon() {
|
|
||||||
if globalDaemon != nil {
|
|
||||||
logrus.Debugf("Global daemon already exists. Skipping.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
t := std_log.New(os.Stderr, "", 0)
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
globalDaemon = mkDaemonFromEngine(eng, t)
|
|
||||||
|
|
||||||
serverConfig := &apiserver.ServerConfig{Logging: true}
|
|
||||||
api := apiserver.New(serverConfig, eng)
|
|
||||||
// Spawn a Daemon
|
|
||||||
go func() {
|
|
||||||
logrus.Debugf("Spawning global daemon for integration tests")
|
|
||||||
listenURL := &url.URL{
|
|
||||||
Scheme: testDaemonProto,
|
|
||||||
Host: testDaemonAddr,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := api.ServeApi([]string{listenURL.String()}); err != nil {
|
|
||||||
logrus.Fatalf("Unable to spawn the test daemon: %s", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Give some time to ListenAndServer to actually start
|
|
||||||
// FIXME: use inmem transports instead of tcp
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
|
|
||||||
api.AcceptConnections(getDaemon(eng))
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: test that ImagePull(json=true) send correct json output
|
|
||||||
|
|
||||||
func GetTestImage(daemon *daemon.Daemon) *image.Image {
|
|
||||||
imgs, err := daemon.Graph().Map()
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("Unable to get the test image: %s", err)
|
|
||||||
}
|
|
||||||
for _, image := range imgs {
|
|
||||||
if image.ID == unitTestImageID {
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Fatalf("Test image %v not found in %s: %s", unitTestImageID, daemon.Graph().Root, imgs)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDaemonCreate(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
// Make sure we start we 0 containers
|
|
||||||
if len(daemon.List()) != 0 {
|
|
||||||
t.Errorf("Expected 0 containers, %v found", len(daemon.List()))
|
|
||||||
}
|
|
||||||
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("ls", "-al"),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err := daemon.Rm(container); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Make sure we can find the newly created container with List()
|
|
||||||
if len(daemon.List()) != 1 {
|
|
||||||
t.Errorf("Expected 1 container, %v found", len(daemon.List()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the container List() returns is the right one
|
|
||||||
if daemon.List()[0].ID != container.ID {
|
|
||||||
t.Errorf("Unexpected container %v returned by List", daemon.List()[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we can get the container with Get()
|
|
||||||
if _, err := daemon.Get(container.ID); err != nil {
|
|
||||||
t.Errorf("Unable to get newly created container")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it is the right container
|
|
||||||
if c, _ := daemon.Get(container.ID); c != container {
|
|
||||||
t.Errorf("Get() returned the wrong container")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure Exists returns it as existing
|
|
||||||
if !daemon.Exists(container.ID) {
|
|
||||||
t.Errorf("Exists() returned false for a newly created container")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that conflict error displays correct details
|
|
||||||
cmd := runconfig.NewCommand("ls", "-al")
|
|
||||||
testContainer, _, _ := daemon.Create(
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: cmd,
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"conflictname",
|
|
||||||
)
|
|
||||||
if _, _, err := daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID, Cmd: cmd}, &runconfig.HostConfig{}, testContainer.Name); err == nil || !strings.Contains(err.Error(), stringid.TruncateID(testContainer.ID)) {
|
|
||||||
t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure create with bad parameters returns an error
|
|
||||||
if _, _, err = daemon.Create(&runconfig.Config{Image: GetTestImage(daemon).ID}, &runconfig.HostConfig{}, ""); err == nil {
|
|
||||||
t.Fatal("Builder.Create should throw an error when Cmd is missing")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, _, err := daemon.Create(
|
|
||||||
&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand(),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
); err == nil {
|
|
||||||
t.Fatal("Builder.Create should throw an error when Cmd is empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
config := &runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("/bin/ls"),
|
|
||||||
PortSpecs: []string{"80"},
|
|
||||||
}
|
|
||||||
container, _, err = daemon.Create(config, &runconfig.HostConfig{}, "")
|
|
||||||
|
|
||||||
_, err = daemon.Commit(container, "testrepo", "testtag", "", "", true, config)
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// test expose 80:8000
|
|
||||||
container, warnings, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("ls", "-al"),
|
|
||||||
PortSpecs: []string{"80:8000"},
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"",
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if warnings == nil || len(warnings) != 1 {
|
|
||||||
t.Error("Expected a warning, got none")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDestroy(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("ls", "-al"),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Destroy
|
|
||||||
if err := daemon.Rm(container); err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure daemon.Exists() behaves correctly
|
|
||||||
if daemon.Exists("test_destroy") {
|
|
||||||
t.Errorf("Exists() returned true")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure daemon.List() doesn't list the destroyed container
|
|
||||||
if len(daemon.List()) != 0 {
|
|
||||||
t.Errorf("Expected 0 container, %v found", len(daemon.List()))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure daemon.Get() refuses to return the unexisting container
|
|
||||||
if c, _ := daemon.Get(container.ID); c != nil {
|
|
||||||
t.Errorf("Got a container that should not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test double destroy
|
|
||||||
if err := daemon.Rm(container); err == nil {
|
|
||||||
// It should have failed
|
|
||||||
t.Errorf("Double destroy did not fail")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
container1, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
|
|
||||||
defer daemon.Rm(container1)
|
|
||||||
|
|
||||||
container2, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
|
|
||||||
defer daemon.Rm(container2)
|
|
||||||
|
|
||||||
container3, _, _ := mkContainer(daemon, []string{"_", "ls", "-al"}, t)
|
|
||||||
defer daemon.Rm(container3)
|
|
||||||
|
|
||||||
if c, _ := daemon.Get(container1.ID); c != container1 {
|
|
||||||
t.Errorf("Get(test1) returned %v while expecting %v", c, container1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c, _ := daemon.Get(container2.ID); c != container2 {
|
|
||||||
t.Errorf("Get(test2) returned %v while expecting %v", c, container2)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c, _ := daemon.Get(container3.ID); c != container3 {
|
|
||||||
t.Errorf("Get(test3) returned %v while expecting %v", c, container3)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func startEchoServerContainer(t *testing.T, proto string) (*daemon.Daemon, *daemon.Container, string) {
|
|
||||||
var (
|
|
||||||
err error
|
|
||||||
id string
|
|
||||||
strPort string
|
|
||||||
eng = NewTestEngine(t)
|
|
||||||
daemon = mkDaemonFromEngine(eng, t)
|
|
||||||
port = 5554
|
|
||||||
p nat.Port
|
|
||||||
)
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
daemon.Nuke()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for {
|
|
||||||
port += 1
|
|
||||||
strPort = strconv.Itoa(port)
|
|
||||||
var cmd string
|
|
||||||
if proto == "tcp" {
|
|
||||||
cmd = "socat TCP-LISTEN:" + strPort + ",reuseaddr,fork EXEC:/bin/cat"
|
|
||||||
} else if proto == "udp" {
|
|
||||||
cmd = "socat UDP-RECVFROM:" + strPort + ",fork EXEC:/bin/cat"
|
|
||||||
} else {
|
|
||||||
t.Fatal(fmt.Errorf("Unknown protocol %v", proto))
|
|
||||||
}
|
|
||||||
ep := make(map[nat.Port]struct{}, 1)
|
|
||||||
p = nat.Port(fmt.Sprintf("%s/%s", strPort, proto))
|
|
||||||
ep[p] = struct{}{}
|
|
||||||
|
|
||||||
c := &runconfig.Config{
|
|
||||||
Image: unitTestImageID,
|
|
||||||
Cmd: runconfig.NewCommand("sh", "-c", cmd),
|
|
||||||
PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
|
|
||||||
ExposedPorts: ep,
|
|
||||||
}
|
|
||||||
|
|
||||||
id, _, err = daemon.ContainerCreate(unitTestImageID, c, &runconfig.HostConfig{})
|
|
||||||
// FIXME: this relies on the undocumented behavior of daemon.Create
|
|
||||||
// which will return a nil error AND container if the exposed ports
|
|
||||||
// are invalid. That behavior should be fixed!
|
|
||||||
if id != "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
t.Logf("Port %v already in use, trying another one", strPort)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := daemon.ContainerStart(id, &runconfig.HostConfig{}); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(t, "Waiting for the container to be started timed out", 2*time.Second, func() {
|
|
||||||
for !container.IsRunning() {
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Even if the state is running, lets give some time to lxc to spawn the process
|
|
||||||
container.WaitStop(500 * time.Millisecond)
|
|
||||||
|
|
||||||
strPort = container.NetworkSettings.Ports[p][0].HostPort
|
|
||||||
return daemon, container, strPort
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run a container with a TCP port allocated, and test that it can receive connections on localhost
|
|
||||||
func TestAllocateTCPPortLocalhost(t *testing.T) {
|
|
||||||
daemon, container, port := startEchoServerContainer(t, "tcp")
|
|
||||||
defer nuke(daemon)
|
|
||||||
defer container.Kill()
|
|
||||||
|
|
||||||
for i := 0; i != 10; i++ {
|
|
||||||
conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", port))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
input := bytes.NewBufferString("well hello there\n")
|
|
||||||
_, err = conn.Write(input.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
buf := make([]byte, 16)
|
|
||||||
read := 0
|
|
||||||
conn.SetReadDeadline(time.Now().Add(3 * time.Second))
|
|
||||||
read, err = conn.Read(buf)
|
|
||||||
if err != nil {
|
|
||||||
if err, ok := err.(*net.OpError); ok {
|
|
||||||
if err.Err == syscall.ECONNRESET {
|
|
||||||
t.Logf("Connection reset by the proxy, socat is probably not listening yet, trying again in a sec")
|
|
||||||
conn.Close()
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err.Timeout() {
|
|
||||||
t.Log("Timeout, trying again")
|
|
||||||
conn.Close()
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
output := string(buf[:read])
|
|
||||||
if !strings.Contains(output, "well hello there") {
|
|
||||||
t.Fatal(fmt.Errorf("[%v] doesn't contain [well hello there]", output))
|
|
||||||
} else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatal("No reply from the container")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run a container with an UDP port allocated, and test that it can receive connections on localhost
|
|
||||||
func TestAllocateUDPPortLocalhost(t *testing.T) {
|
|
||||||
daemon, container, port := startEchoServerContainer(t, "udp")
|
|
||||||
defer nuke(daemon)
|
|
||||||
defer container.Kill()
|
|
||||||
|
|
||||||
conn, err := net.Dial("udp", fmt.Sprintf("localhost:%v", port))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
input := bytes.NewBufferString("well hello there\n")
|
|
||||||
buf := make([]byte, 16)
|
|
||||||
// Try for a minute, for some reason the select in socat may take ages
|
|
||||||
// to return even though everything on the path seems fine (i.e: the
|
|
||||||
// UDPProxy forwards the traffic correctly and you can see the packets
|
|
||||||
// on the interface from within the container).
|
|
||||||
for i := 0; i != 120; i++ {
|
|
||||||
_, err := conn.Write(input.Bytes())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond))
|
|
||||||
read, err := conn.Read(buf)
|
|
||||||
if err == nil {
|
|
||||||
output := string(buf[:read])
|
|
||||||
if strings.Contains(output, "well hello there") {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatal("No reply from the container")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRestore(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon1 := mkDaemonFromEngine(eng, t)
|
|
||||||
defer daemon1.Nuke()
|
|
||||||
// Create a container with one instance of docker
|
|
||||||
container1, _, _ := mkContainer(daemon1, []string{"_", "ls", "-al"}, t)
|
|
||||||
defer daemon1.Rm(container1)
|
|
||||||
|
|
||||||
// Create a second container meant to be killed
|
|
||||||
container2, _, _ := mkContainer(daemon1, []string{"-i", "_", "/bin/cat"}, t)
|
|
||||||
defer daemon1.Rm(container2)
|
|
||||||
|
|
||||||
// Start the container non blocking
|
|
||||||
if err := container2.Start(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !container2.IsRunning() {
|
|
||||||
t.Fatalf("Container %v should appear as running but isn't", container2.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate a crash/manual quit of dockerd: process dies, states stays 'Running'
|
|
||||||
cStdin := container2.StdinPipe()
|
|
||||||
cStdin.Close()
|
|
||||||
if _, err := container2.WaitStop(2 * time.Second); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container2.SetRunning(42)
|
|
||||||
container2.ToDisk()
|
|
||||||
|
|
||||||
if len(daemon1.List()) != 2 {
|
|
||||||
t.Errorf("Expected 2 container, %v found", len(daemon1.List()))
|
|
||||||
}
|
|
||||||
if err := container1.Run(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !container2.IsRunning() {
|
|
||||||
t.Fatalf("Container %v should appear as running but isn't", container2.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Here are are simulating a docker restart - that is, reloading all containers
|
|
||||||
// from scratch
|
|
||||||
eng = newTestEngine(t, false, daemon1.Config().Root)
|
|
||||||
daemon2 := mkDaemonFromEngine(eng, t)
|
|
||||||
if len(daemon2.List()) != 2 {
|
|
||||||
t.Errorf("Expected 2 container, %v found", len(daemon2.List()))
|
|
||||||
}
|
|
||||||
runningCount := 0
|
|
||||||
for _, c := range daemon2.List() {
|
|
||||||
if c.IsRunning() {
|
|
||||||
t.Errorf("Running container found: %v (%v)", c.ID, c.Path)
|
|
||||||
runningCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if runningCount != 0 {
|
|
||||||
t.Fatalf("Expected 0 container alive, %d found", runningCount)
|
|
||||||
}
|
|
||||||
container3, err := daemon2.Get(container1.ID)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal("Unable to Get container")
|
|
||||||
}
|
|
||||||
if err := container3.Run(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
container2.SetStopped(&execdriver.ExitStatus{ExitCode: 0})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefaultContainerName(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
config, _, _, err := parseRun([]string{unitTestImageID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "some_name"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
containerID := container.ID
|
|
||||||
|
|
||||||
if container.Name != "/some_name" {
|
|
||||||
t.Fatalf("Expect /some_name got %s", container.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
c, err := daemon.Get("/some_name")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Couldn't retrieve test container as /some_name")
|
|
||||||
}
|
|
||||||
if c.ID != containerID {
|
|
||||||
t.Fatalf("Container /some_name has ID %s instead of %s", c.ID, containerID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRandomContainerName(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
config, _, _, err := parseRun([]string{GetTestImage(daemon).ID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(createTestContainer(eng, config, t))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
containerID := container.ID
|
|
||||||
|
|
||||||
if container.Name == "" {
|
|
||||||
t.Fatalf("Expected not empty container name")
|
|
||||||
}
|
|
||||||
|
|
||||||
if c, err := daemon.Get(container.Name); err != nil {
|
|
||||||
logrus.Fatalf("Could not lookup container %s by its name", container.Name)
|
|
||||||
} else if c.ID != containerID {
|
|
||||||
logrus.Fatalf("Looking up container name %s returned id %s instead of %s", container.Name, c.ID, containerID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContainerNameValidation(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
for _, test := range []struct {
|
|
||||||
Name string
|
|
||||||
Valid bool
|
|
||||||
}{
|
|
||||||
{"abc-123_AAA.1", true},
|
|
||||||
{"\000asdf", false},
|
|
||||||
} {
|
|
||||||
config, _, _, err := parseRun([]string{unitTestImageID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
if !test.Valid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
containerId, _, err := daemon.ContainerCreate(test.Name, config, &runconfig.HostConfig{})
|
|
||||||
if err != nil {
|
|
||||||
if !test.Valid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(containerId)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if container.Name != "/"+test.Name {
|
|
||||||
t.Fatalf("Expect /%s got %s", test.Name, container.Name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c, err := daemon.Get("/" + test.Name); err != nil {
|
|
||||||
t.Fatalf("Couldn't retrieve test container as /%s", test.Name)
|
|
||||||
} else if c.ID != container.ID {
|
|
||||||
t.Fatalf("Container /%s has ID %s instead of %s", test.Name, c.ID, container.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLinkChildContainer(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
config, _, _, err := parseRun([]string{unitTestImageID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
webapp, err := daemon.GetByName("/webapp")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if webapp.ID != container.ID {
|
|
||||||
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
config, _, _, err = parseRun([]string{GetTestImage(daemon).ID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
childContainer, err := daemon.Get(createTestContainer(eng, config, t))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the child by it's new name
|
|
||||||
db, err := daemon.GetByName("/webapp/db")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if db.ID != childContainer.ID {
|
|
||||||
t.Fatalf("Expect db id to match container id: %s != %s", db.ID, childContainer.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetAllChildren(t *testing.T) {
|
|
||||||
eng := NewTestEngine(t)
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
config, _, _, err := parseRun([]string{unitTestImageID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
container, err := daemon.Get(createNamedTestContainer(eng, config, t, "/webapp"))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
webapp, err := daemon.GetByName("/webapp")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if webapp.ID != container.ID {
|
|
||||||
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
config, _, _, err = parseRun([]string{unitTestImageID, "echo test"})
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
childContainer, err := daemon.Get(createTestContainer(eng, config, t))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := daemon.RegisterLink(webapp, childContainer, "db"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
children, err := daemon.Children("/webapp")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if children == nil {
|
|
||||||
t.Fatal("Children should not be nil")
|
|
||||||
}
|
|
||||||
if len(children) == 0 {
|
|
||||||
t.Fatal("Children should not be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, value := range children {
|
|
||||||
if key != "/webapp/db" {
|
|
||||||
t.Fatalf("Expected /webapp/db got %s", key)
|
|
||||||
}
|
|
||||||
if value.ID != childContainer.ID {
|
|
||||||
t.Fatalf("Expected id %s got %s", childContainer.ID, value.ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDestroyWithInitLayer(t *testing.T) {
|
|
||||||
daemon := mkDaemon(t)
|
|
||||||
defer nuke(daemon)
|
|
||||||
|
|
||||||
container, _, err := daemon.Create(&runconfig.Config{
|
|
||||||
Image: GetTestImage(daemon).ID,
|
|
||||||
Cmd: runconfig.NewCommand("ls", "-al"),
|
|
||||||
},
|
|
||||||
&runconfig.HostConfig{},
|
|
||||||
"")
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Destroy
|
|
||||||
if err := daemon.Rm(container); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure daemon.Exists() behaves correctly
|
|
||||||
if daemon.Exists("test_destroy") {
|
|
||||||
t.Fatalf("Exists() returned true")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure daemon.List() doesn't list the destroyed container
|
|
||||||
if len(daemon.List()) != 0 {
|
|
||||||
t.Fatalf("Expected 0 container, %v found", len(daemon.List()))
|
|
||||||
}
|
|
||||||
|
|
||||||
driver := daemon.Graph().Driver()
|
|
||||||
|
|
||||||
// Make sure that the container does not exist in the driver
|
|
||||||
if _, err := driver.Get(container.ID, ""); err == nil {
|
|
||||||
t.Fatal("Container should not exist in the driver")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure that the init layer is removed from the driver
|
|
||||||
if _, err := driver.Get(fmt.Sprintf("%s-init", container.ID), ""); err == nil {
|
|
||||||
t.Fatal("Container's init layer should not exist in the driver")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/daemon"
|
|
||||||
)
|
|
||||||
|
|
||||||
func closeWrap(args ...io.Closer) error {
|
|
||||||
e := false
|
|
||||||
ret := fmt.Errorf("Error closing elements")
|
|
||||||
for _, c := range args {
|
|
||||||
if err := c.Close(); err != nil {
|
|
||||||
e = true
|
|
||||||
ret = fmt.Errorf("%s\n%s", ret, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if e {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitContainerStart(t *testing.T, timeout time.Duration) *daemon.Container {
|
|
||||||
var container *daemon.Container
|
|
||||||
|
|
||||||
setTimeout(t, "Waiting for the container to be started timed out", timeout, func() {
|
|
||||||
for {
|
|
||||||
l := globalDaemon.List()
|
|
||||||
if len(l) == 1 && l[0].IsRunning() {
|
|
||||||
container = l[0]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if container == nil {
|
|
||||||
t.Fatal("An error occurred while waiting for the container to start")
|
|
||||||
}
|
|
||||||
|
|
||||||
return container
|
|
||||||
}
|
|
||||||
|
|
||||||
func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
|
|
||||||
c := make(chan bool)
|
|
||||||
|
|
||||||
// Make sure we are not too long
|
|
||||||
go func() {
|
|
||||||
time.Sleep(d)
|
|
||||||
c <- true
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
f()
|
|
||||||
c <- false
|
|
||||||
}()
|
|
||||||
if <-c && msg != "" {
|
|
||||||
t.Fatal(msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func expectPipe(expected string, r io.Reader) error {
|
|
||||||
o, err := bufio.NewReader(r).ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if strings.Trim(o, " \r\n") != expected {
|
|
||||||
return fmt.Errorf("Unexpected output. Expected [%s], received [%s]", expected, o)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
if _, err := w.Write([]byte(input)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := expectPipe(output, r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,348 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/docker/docker/vendor/src/code.google.com/p/go/src/pkg/archive/tar"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
|
||||||
"github.com/docker/docker/daemon"
|
|
||||||
"github.com/docker/docker/daemon/networkdriver/bridge"
|
|
||||||
"github.com/docker/docker/engine"
|
|
||||||
"github.com/docker/docker/graph"
|
|
||||||
flag "github.com/docker/docker/pkg/mflag"
|
|
||||||
"github.com/docker/docker/registry"
|
|
||||||
"github.com/docker/docker/runconfig"
|
|
||||||
"github.com/docker/docker/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Fataler interface {
|
|
||||||
Fatal(...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// This file contains utility functions for docker's unit test suite.
|
|
||||||
// It has to be named XXX_test.go, apparently, in other to access private functions
|
|
||||||
// from other XXX_test.go functions.
|
|
||||||
|
|
||||||
// Create a temporary daemon suitable for unit testing.
|
|
||||||
// Call t.Fatal() at the first error.
|
|
||||||
func mkDaemon(f Fataler) *daemon.Daemon {
|
|
||||||
eng := newTestEngine(f, false, "")
|
|
||||||
return mkDaemonFromEngine(eng, f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler, name string) (shortId string) {
|
|
||||||
containerId, _, err := getDaemon(eng).ContainerCreate(name, config, &runconfig.HostConfig{})
|
|
||||||
if err != nil {
|
|
||||||
f.Fatal(err)
|
|
||||||
}
|
|
||||||
return containerId
|
|
||||||
}
|
|
||||||
|
|
||||||
func createTestContainer(eng *engine.Engine, config *runconfig.Config, f Fataler) (shortId string) {
|
|
||||||
return createNamedTestContainer(eng, config, f, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func startContainer(eng *engine.Engine, id string, t Fataler) {
|
|
||||||
if err := getDaemon(eng).ContainerStart(id, &runconfig.HostConfig{}); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerRun(eng *engine.Engine, id string, t Fataler) {
|
|
||||||
startContainer(eng, id, t)
|
|
||||||
containerWait(eng, id, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerFileExists(eng *engine.Engine, id, dir string, t Fataler) bool {
|
|
||||||
c := getContainer(eng, id, t)
|
|
||||||
if err := c.Mount(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
defer c.Unmount()
|
|
||||||
if _, err := os.Stat(path.Join(c.RootfsPath(), dir)); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerAttach(eng *engine.Engine, id string, t Fataler) (io.WriteCloser, io.ReadCloser) {
|
|
||||||
c := getContainer(eng, id, t)
|
|
||||||
i := c.StdinPipe()
|
|
||||||
o := c.StdoutPipe()
|
|
||||||
return i, o
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerWait(eng *engine.Engine, id string, t Fataler) int {
|
|
||||||
ex, _ := getContainer(eng, id, t).WaitStop(-1 * time.Second)
|
|
||||||
return ex
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerWaitTimeout(eng *engine.Engine, id string, t Fataler) error {
|
|
||||||
_, err := getContainer(eng, id, t).WaitStop(500 * time.Millisecond)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerKill(eng *engine.Engine, id string, t Fataler) {
|
|
||||||
if err := getDaemon(eng).ContainerKill(id, 0); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerRunning(eng *engine.Engine, id string, t Fataler) bool {
|
|
||||||
return getContainer(eng, id, t).IsRunning()
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerAssertExists(eng *engine.Engine, id string, t Fataler) {
|
|
||||||
getContainer(eng, id, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func containerAssertNotExists(eng *engine.Engine, id string, t Fataler) {
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
if c, _ := daemon.Get(id); c != nil {
|
|
||||||
t.Fatal(fmt.Errorf("Container %s should not exist", id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertHttpNotError expect the given response to not have an error.
|
|
||||||
// Otherwise the it causes the test to fail.
|
|
||||||
func assertHttpNotError(r *httptest.ResponseRecorder, t Fataler) {
|
|
||||||
// Non-error http status are [200, 400)
|
|
||||||
if r.Code < http.StatusOK || r.Code >= http.StatusBadRequest {
|
|
||||||
t.Fatal(fmt.Errorf("Unexpected http error: %v", r.Code))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assertHttpError expect the given response to have an error.
|
|
||||||
// Otherwise the it causes the test to fail.
|
|
||||||
func assertHttpError(r *httptest.ResponseRecorder, t Fataler) {
|
|
||||||
// Non-error http status are [200, 400)
|
|
||||||
if !(r.Code < http.StatusOK || r.Code >= http.StatusBadRequest) {
|
|
||||||
t.Fatal(fmt.Errorf("Unexpected http success code: %v", r.Code))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getContainer(eng *engine.Engine, id string, t Fataler) *daemon.Container {
|
|
||||||
daemon := mkDaemonFromEngine(eng, t)
|
|
||||||
c, err := daemon.Get(id)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func mkDaemonFromEngine(eng *engine.Engine, t Fataler) *daemon.Daemon {
|
|
||||||
iDaemon := eng.HackGetGlobalVar("httpapi.daemon")
|
|
||||||
if iDaemon == nil {
|
|
||||||
panic("Legacy daemon field not set in engine")
|
|
||||||
}
|
|
||||||
daemon, ok := iDaemon.(*daemon.Daemon)
|
|
||||||
if !ok {
|
|
||||||
panic("Legacy daemon field in engine does not cast to *daemon.Daemon")
|
|
||||||
}
|
|
||||||
return daemon
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestEngine(t Fataler, autorestart bool, root string) *engine.Engine {
|
|
||||||
if root == "" {
|
|
||||||
if dir, err := newTestDirectory(unitTestStoreBase); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else {
|
|
||||||
root = dir
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.MkdirAll(root, 0700)
|
|
||||||
|
|
||||||
eng := engine.New()
|
|
||||||
eng.Logging = false
|
|
||||||
|
|
||||||
// (This is manually copied and modified from main() until we have a more generic plugin system)
|
|
||||||
cfg := &daemon.Config{
|
|
||||||
Root: root,
|
|
||||||
AutoRestart: autorestart,
|
|
||||||
ExecDriver: "native",
|
|
||||||
// Either InterContainerCommunication or EnableIptables must be set,
|
|
||||||
// otherwise NewDaemon will fail because of conflicting settings.
|
|
||||||
Bridge: bridge.Config{
|
|
||||||
InterContainerCommunication: true,
|
|
||||||
},
|
|
||||||
TrustKeyPath: filepath.Join(root, "key.json"),
|
|
||||||
LogConfig: runconfig.LogConfig{Type: "json-file"},
|
|
||||||
}
|
|
||||||
d, err := daemon.NewDaemon(cfg, eng, registry.NewService(nil))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if err := d.Install(eng); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return eng
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTestEngine(t Fataler) *engine.Engine {
|
|
||||||
return newTestEngine(t, false, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestDirectory(templateDir string) (dir string, err error) {
|
|
||||||
return utils.TestDirectory(templateDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getCallerName(depth int) string {
|
|
||||||
return utils.GetCallerName(depth)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write `content` to the file at path `dst`, creating it if necessary,
|
|
||||||
// as well as any missing directories.
|
|
||||||
// The file is truncated if it already exists.
|
|
||||||
// Call t.Fatal() at the first error.
|
|
||||||
func writeFile(dst, content string, t *testing.T) {
|
|
||||||
// Create subdirectories if necessary
|
|
||||||
if err := os.MkdirAll(path.Dir(dst), 0700); err != nil && !os.IsExist(err) {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
f, err := os.OpenFile(dst, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// Write content (truncate if it exists)
|
|
||||||
if _, err := io.Copy(f, strings.NewReader(content)); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the contents of file at path `src`.
|
|
||||||
// Call t.Fatal() at the first error (including if the file doesn't exist)
|
|
||||||
func readFile(src string, t *testing.T) (content string) {
|
|
||||||
f, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
data, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
return string(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a test container from the given daemon `r` and run arguments `args`.
|
|
||||||
// If the image name is "_", (eg. []string{"-i", "-t", "_", "bash"}, it is
|
|
||||||
// dynamically replaced by the current test image.
|
|
||||||
// The caller is responsible for destroying the container.
|
|
||||||
// Call t.Fatal() at the first error.
|
|
||||||
func mkContainer(r *daemon.Daemon, args []string, t *testing.T) (*daemon.Container, *runconfig.HostConfig, error) {
|
|
||||||
config, hc, _, err := parseRun(args)
|
|
||||||
defer func() {
|
|
||||||
if err != nil && t != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
if config.Image == "_" {
|
|
||||||
config.Image = GetTestImage(r).ID
|
|
||||||
}
|
|
||||||
c, _, err := r.Create(config, nil, "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
// NOTE: hostConfig is ignored.
|
|
||||||
// If `args` specify privileged mode, custom lxc conf, external mount binds,
|
|
||||||
// port redirects etc. they will be ignored.
|
|
||||||
// This is because the correct way to set these things is to pass environment
|
|
||||||
// to the `start` job.
|
|
||||||
// FIXME: this helper function should be deprecated in favor of calling
|
|
||||||
// `create` and `start` jobs directly.
|
|
||||||
return c, hc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a test container, start it, wait for it to complete, destroy it,
|
|
||||||
// and return its standard output as a string.
|
|
||||||
// The image name (eg. the XXX in []string{"-i", "-t", "XXX", "bash"}, is dynamically replaced by the current test image.
|
|
||||||
// If t is not nil, call t.Fatal() at the first error. Otherwise return errors normally.
|
|
||||||
func runContainer(eng *engine.Engine, r *daemon.Daemon, args []string, t *testing.T) (output string, err error) {
|
|
||||||
defer func() {
|
|
||||||
if err != nil && t != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
container, hc, err := mkContainer(r, args, t)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer r.Rm(container)
|
|
||||||
stdout := container.StdoutPipe()
|
|
||||||
defer stdout.Close()
|
|
||||||
|
|
||||||
job := eng.Job("start", container.ID)
|
|
||||||
if err := job.ImportEnv(hc); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if err := job.Run(); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
container.WaitStop(-1 * time.Second)
|
|
||||||
data, err := ioutil.ReadAll(stdout)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
output = string(data)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this is duplicated from graph_test.go in the docker package.
|
|
||||||
func fakeTar() (io.ReadCloser, error) {
|
|
||||||
content := []byte("Hello world!\n")
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
tw := tar.NewWriter(buf)
|
|
||||||
for _, name := range []string{"/etc/postgres/postgres.conf", "/etc/passwd", "/var/log/postgres/postgres.conf"} {
|
|
||||||
hdr := new(tar.Header)
|
|
||||||
hdr.Size = int64(len(content))
|
|
||||||
hdr.Name = name
|
|
||||||
if err := tw.WriteHeader(hdr); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
tw.Write([]byte(content))
|
|
||||||
}
|
|
||||||
tw.Close()
|
|
||||||
return ioutil.NopCloser(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getImages(eng *engine.Engine, t *testing.T, all bool, filter string) []*types.Image {
|
|
||||||
config := graph.ImagesConfig{
|
|
||||||
Filter: filter,
|
|
||||||
All: all,
|
|
||||||
}
|
|
||||||
images, err := getDaemon(eng).Repositories().Images(&config)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return images
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseRun(args []string) (*runconfig.Config, *runconfig.HostConfig, *flag.FlagSet, error) {
|
|
||||||
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
|
|
||||||
cmd.SetOutput(ioutil.Discard)
|
|
||||||
cmd.Usage = nil
|
|
||||||
return runconfig.Parse(cmd, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDaemon(eng *engine.Engine) *daemon.Daemon {
|
|
||||||
return eng.HackGetGlobalVar("httpapi.daemon").(*daemon.Daemon)
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package docker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/fileutils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func displayFdGoroutines(t *testing.T) {
|
|
||||||
t.Logf("File Descriptors: %d, Goroutines: %d", fileutils.GetTotalUsedFds(), runtime.NumGoroutine())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFinal(t *testing.T) {
|
|
||||||
nuke(globalDaemon)
|
|
||||||
t.Logf("Start File Descriptors: %d, Start Goroutines: %d", startFds, startGoroutines)
|
|
||||||
displayFdGoroutines(t)
|
|
||||||
}
|
|
Reference in New Issue
Block a user