Introduce test-integration
target (and deprecate/freeze test-integration-cli
)
This adds a new package `integration` where `engine` integration tests should live. Those integration tests should not depends on any `cli` components (except from the `dockerd` daemon for now — to actually start a daemon). Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
parent
187cd25517
commit
6b025a8b66
8 changed files with 201 additions and 78 deletions
|
@ -160,6 +160,10 @@ it! Take a look at existing tests for inspiration. [Run the full test
|
||||||
suite](https://docs.docker.com/opensource/project/test-and-docs/) on your branch before
|
suite](https://docs.docker.com/opensource/project/test-and-docs/) on your branch before
|
||||||
submitting a pull request.
|
submitting a pull request.
|
||||||
|
|
||||||
|
If your changes need integration tests, write them against the API. The `cli`
|
||||||
|
integration tests are slowly either migrated to API tests or moved away as unit
|
||||||
|
tests in `docker/cli` and end-to-end tests for docker.
|
||||||
|
|
||||||
Update the documentation when creating or modifying features. Test your
|
Update the documentation when creating or modifying features. Test your
|
||||||
documentation changes for clarity, concision, and correctness, as well as a
|
documentation changes for clarity, concision, and correctness, as well as a
|
||||||
clean documentation build. See our contributors guide for [our style
|
clean documentation build. See our contributors guide for [our style
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -154,8 +154,11 @@ test: build ## run the unit, integration and docker-py tests
|
||||||
test-docker-py: build ## run the docker-py tests
|
test-docker-py: build ## run the docker-py tests
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-docker-py
|
||||||
|
|
||||||
test-integration-cli: build ## run the integration tests
|
test-integration-cli: build ## (DEPRECATED) use test-integration
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh build-integration-test-binary dynbinary test-integration-cli
|
$(DOCKER_RUN_DOCKER) hack/make.sh build-integration-test-cli-binary dynbinary test-integration
|
||||||
|
|
||||||
|
test-integration: build ## run the integration tests
|
||||||
|
$(DOCKER_RUN_DOCKER) hack/make.sh dynbinary test-integration
|
||||||
|
|
||||||
test-unit: build ## run the unit tests
|
test-unit: build ## run the unit tests
|
||||||
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
$(DOCKER_RUN_DOCKER) hack/make.sh test-unit
|
||||||
|
|
|
@ -60,6 +60,7 @@ DEFAULT_BUNDLES=(
|
||||||
dynbinary
|
dynbinary
|
||||||
|
|
||||||
test-unit
|
test-unit
|
||||||
|
test-integration
|
||||||
test-integration-cli
|
test-integration-cli
|
||||||
test-docker-py
|
test-docker-py
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,25 @@ bundle_test_integration_cli() {
|
||||||
go_test_dir integration-cli $DOCKER_INTEGRATION_TESTS_VERIFIED
|
go_test_dir integration-cli $DOCKER_INTEGRATION_TESTS_VERIFIED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bundle_test_integration() {
|
||||||
|
TESTFLAGS="$TESTFLAGS -v -test.timeout=60m"
|
||||||
|
(
|
||||||
|
set -e
|
||||||
|
cd integration
|
||||||
|
INCBUILD="-i"
|
||||||
|
count=0
|
||||||
|
for flag in "${BUILDFLAGS[@]}"; do
|
||||||
|
if [ "${flag}" == ${INCBUILD} ]; then
|
||||||
|
unset BUILDFLAGS[${count}]
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
count=$[ ${count} + 1 ]
|
||||||
|
done
|
||||||
|
echo go test -ldflags "$LDFLAGS" "${BUILDFLAGS[@]}" $TESTFLAGS ./...
|
||||||
|
go test -ldflags "$LDFLAGS" "${BUILDFLAGS[@]}" $TESTFLAGS ./...
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
# If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
# If $TESTFLAGS is set in the environment, it is passed as extra arguments to 'go test'.
|
||||||
# You can use this to select certain tests to run, e.g.
|
# You can use this to select certain tests to run, e.g.
|
||||||
#
|
#
|
||||||
|
|
28
hack/make/test-integration
Executable file
28
hack/make/test-integration
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
source hack/make/.integration-test-helpers
|
||||||
|
|
||||||
|
# subshell so that we can export PATH without breaking other things
|
||||||
|
(
|
||||||
|
bundle .integration-daemon-start
|
||||||
|
|
||||||
|
bundle .integration-daemon-setup
|
||||||
|
|
||||||
|
bundle_test_integration
|
||||||
|
|
||||||
|
bundle .integration-daemon-stop
|
||||||
|
|
||||||
|
if [ "$(go env GOOS)" != 'windows' ]
|
||||||
|
then
|
||||||
|
leftovers=$(ps -ax -o pid,cmd | awk '$2 == "docker-containerd-shim" && $4 ~ /.*\/bundles\/.*\/test-integration-cli/ { print $1 }')
|
||||||
|
if [ -n "$leftovers" ]
|
||||||
|
then
|
||||||
|
ps aux
|
||||||
|
kill -9 $leftovers 2> /dev/null
|
||||||
|
echo "!!!! WARNING you have left over shim(s), Cleanup your test !!!!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
) 2>&1 | tee -a "$DEST/test.log"
|
|
@ -11,82 +11,6 @@ import (
|
||||||
"github.com/go-check/check"
|
"github.com/go-check/check"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *DockerSuite) TestAPICreateWithNotExistImage(c *check.C) {
|
|
||||||
name := "test"
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Image": "test456:v1",
|
|
||||||
"Volumes": map[string]struct{}{"/tmp": {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
status, body, err := request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusNotFound)
|
|
||||||
expected := "No such image: test456:v1"
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
|
|
||||||
|
|
||||||
config2 := map[string]interface{}{
|
|
||||||
"Image": "test456",
|
|
||||||
"Volumes": map[string]struct{}{"/tmp": {}},
|
|
||||||
}
|
|
||||||
|
|
||||||
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config2, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusNotFound)
|
|
||||||
expected = "No such image: test456:latest"
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
|
|
||||||
|
|
||||||
config3 := map[string]interface{}{
|
|
||||||
"Image": "sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
|
|
||||||
}
|
|
||||||
|
|
||||||
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config3, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusNotFound)
|
|
||||||
expected = "No such image: sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa"
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Equals, expected)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test for #25099
|
|
||||||
func (s *DockerSuite) TestAPICreateEmptyEnv(c *check.C) {
|
|
||||||
name := "test1"
|
|
||||||
config := map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Env": []string{"", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
|
||||||
"Cmd": []string{"true"},
|
|
||||||
}
|
|
||||||
|
|
||||||
status, body, err := request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusInternalServerError)
|
|
||||||
expected := "invalid environment variable:"
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
|
|
||||||
|
|
||||||
name = "test2"
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Env": []string{"=", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
|
||||||
"Cmd": []string{"true"},
|
|
||||||
}
|
|
||||||
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusInternalServerError)
|
|
||||||
expected = "invalid environment variable: ="
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
|
|
||||||
|
|
||||||
name = "test3"
|
|
||||||
config = map[string]interface{}{
|
|
||||||
"Image": "busybox",
|
|
||||||
"Env": []string{"=foo", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
|
|
||||||
"Cmd": []string{"true"},
|
|
||||||
}
|
|
||||||
status, body, err = request.SockRequest("POST", "/containers/create?name="+name, config, daemonHost())
|
|
||||||
c.Assert(err, check.IsNil)
|
|
||||||
c.Assert(status, check.Equals, http.StatusInternalServerError)
|
|
||||||
expected = "invalid environment variable: =foo"
|
|
||||||
c.Assert(getErrorMessage(c, body), checker.Contains, expected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
func (s *DockerSuite) TestAPICreateWithInvalidHealthcheckParams(c *check.C) {
|
||||||
// test invalid Interval in Healthcheck: less than 0s
|
// test invalid Interval in Healthcheck: less than 0s
|
||||||
name := "test1"
|
name := "test1"
|
||||||
|
|
141
integration/container/create_test.go
Normal file
141
integration/container/create_test.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
|
"github.com/docker/docker/api/types/network"
|
||||||
|
"github.com/docker/docker/client"
|
||||||
|
"github.com/docker/docker/integration-cli/environment"
|
||||||
|
"github.com/docker/docker/integration-cli/fixtures/load"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testEnv *environment.Execution
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
var err error
|
||||||
|
testEnv, err = environment.New()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if testEnv.LocalDaemon() {
|
||||||
|
fmt.Println("INFO: Testing against a local daemon")
|
||||||
|
} else {
|
||||||
|
fmt.Println("INFO: Testing against a remote daemon")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: ensure and protect images
|
||||||
|
res := m.Run()
|
||||||
|
os.Exit(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPICreateWithNotExistImage(t *testing.T) {
|
||||||
|
defer setupTest(t)()
|
||||||
|
clt := createClient(t)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
image string
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
image: "test456:v1",
|
||||||
|
expectedError: "No such image: test456:v1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: "test456",
|
||||||
|
expectedError: "No such image: test456",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
image: "sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
|
||||||
|
expectedError: "No such image: sha256:0cb40641836c461bc97c793971d84d758371ed682042457523e4ae701efeaaaa",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(strconv.Itoa(index), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := clt.ContainerCreate(context.Background(),
|
||||||
|
&container.Config{
|
||||||
|
Image: tc.image,
|
||||||
|
},
|
||||||
|
&container.HostConfig{},
|
||||||
|
&network.NetworkingConfig{},
|
||||||
|
"foo",
|
||||||
|
)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), tc.expectedError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPICreateEmptyEnv(t *testing.T) {
|
||||||
|
defer setupTest(t)()
|
||||||
|
clt := createClient(t)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
env string
|
||||||
|
expectedError string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
env: "",
|
||||||
|
expectedError: "invalid environment variable:",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env: "=",
|
||||||
|
expectedError: "invalid environment variable: =",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
env: "=foo",
|
||||||
|
expectedError: "invalid environment variable: =foo",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for index, tc := range testCases {
|
||||||
|
tc := tc
|
||||||
|
t.Run(strconv.Itoa(index), func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
_, err := clt.ContainerCreate(context.Background(),
|
||||||
|
&container.Config{
|
||||||
|
Image: "busybox",
|
||||||
|
Env: []string{tc.env},
|
||||||
|
},
|
||||||
|
&container.HostConfig{},
|
||||||
|
&network.NetworkingConfig{},
|
||||||
|
"foo",
|
||||||
|
)
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Contains(t, err.Error(), tc.expectedError)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createClient(t *testing.T) client.APIClient {
|
||||||
|
clt, err := client.NewEnvClient()
|
||||||
|
require.NoError(t, err)
|
||||||
|
return clt
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupTest(t *testing.T) func() {
|
||||||
|
if testEnv.DaemonPlatform() == "linux" {
|
||||||
|
images := []string{"busybox:latest", "hello-world:frozen", "debian:jessie"}
|
||||||
|
err := load.FrozenImagesLinux(testEnv.DockerBinary(), images...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%+v", err)
|
||||||
|
}
|
||||||
|
defer testEnv.ProtectImage(t, images...)
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
testEnv.Clean(t, testEnv.DockerBinary())
|
||||||
|
}
|
||||||
|
}
|
3
integration/doc.go
Normal file
3
integration/doc.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package integration provides integrations tests for Moby (API).
|
||||||
|
// These tests require a daemon (dockerd for now) to run.
|
||||||
|
package integration
|
Loading…
Reference in a new issue