diff --git a/components/engine/Dockerfile b/components/engine/Dockerfile index 9b4033ef6fc..18a88e13417 100644 --- a/components/engine/Dockerfile +++ b/components/engine/Dockerfile @@ -142,7 +142,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 5e28dcaace5f7b70cbe44c313b7a3b288fa38916 +ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.aarch64 b/components/engine/Dockerfile.aarch64 index 23a2f46d161..389b00f109e 100644 --- a/components/engine/Dockerfile.aarch64 +++ b/components/engine/Dockerfile.aarch64 @@ -106,7 +106,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 5e28dcaace5f7b70cbe44c313b7a3b288fa38916 +ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.armhf b/components/engine/Dockerfile.armhf index 1bd2db6014d..b640396aee2 100644 --- a/components/engine/Dockerfile.armhf +++ b/components/engine/Dockerfile.armhf @@ -104,7 +104,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 5e28dcaace5f7b70cbe44c313b7a3b288fa38916 +ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.ppc64le b/components/engine/Dockerfile.ppc64le index ea75224fea7..97a2fa995f8 100644 --- a/components/engine/Dockerfile.ppc64le +++ b/components/engine/Dockerfile.ppc64le @@ -102,7 +102,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 5e28dcaace5f7b70cbe44c313b7a3b288fa38916 +ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/Dockerfile.s390x b/components/engine/Dockerfile.s390x index 16aa1fdc6d5..2302750c9cc 100644 --- a/components/engine/Dockerfile.s390x +++ b/components/engine/Dockerfile.s390x @@ -96,7 +96,7 @@ RUN set -x \ && rm -rf "$GOPATH" # Get the "docker-py" source so we can run their integration tests -ENV DOCKER_PY_COMMIT 5e28dcaace5f7b70cbe44c313b7a3b288fa38916 +ENV DOCKER_PY_COMMIT 8b246db271a85d6541dc458838627e89c683e42f # To run integration tests docker-pycreds is required. RUN git clone https://github.com/docker/docker-py.git /docker-py \ && cd /docker-py \ diff --git a/components/engine/container/container.go b/components/engine/container/container.go index 46e592ab45f..461139b4355 100644 --- a/components/engine/container/container.go +++ b/components/engine/container/container.go @@ -38,7 +38,7 @@ import ( "github.com/docker/docker/runconfig" "github.com/docker/docker/volume" "github.com/docker/go-connections/nat" - "github.com/docker/go-units" + units "github.com/docker/go-units" "github.com/docker/libnetwork" "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/options" @@ -391,6 +391,8 @@ func (container *Container) StartLogger() (logger.Logger, error) { if err != nil { return nil, err } + + container.LogPath = info.LogPath } l, err := initDriver(info) @@ -974,11 +976,6 @@ func (container *Container) startLogging() error { copier.Run() container.LogDriver = l - // set LogPath field only for json-file logdriver - if jl, ok := l.(*jsonfilelog.JSONFileLogger); ok { - container.LogPath = jl.LogPath() - } - return nil } diff --git a/components/engine/container/container_unit_test.go b/components/engine/container/container_unit_test.go index f6ded8fa9dc..863a47a1f22 100644 --- a/components/engine/container/container_unit_test.go +++ b/components/engine/container/container_unit_test.go @@ -1,12 +1,17 @@ package container // import "github.com/docker/docker/container" import ( + "fmt" + "io/ioutil" + "os" "path/filepath" "testing" "github.com/docker/docker/api/types/container" swarmtypes "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/daemon/logger/jsonfilelog" "github.com/docker/docker/pkg/signal" + "github.com/stretchr/testify/require" ) func TestContainerStopSignal(t *testing.T) { @@ -66,3 +71,56 @@ func TestContainerSecretReferenceDestTarget(t *testing.T) { t.Fatalf("expected secret dest %q; received %q", expected, d) } } + +func TestContainerLogPathSetForJSONFileLogger(t *testing.T) { + containerRoot, err := ioutil.TempDir("", "TestContainerLogPathSetForJSONFileLogger") + require.NoError(t, err) + defer os.RemoveAll(containerRoot) + + c := &Container{ + Config: &container.Config{}, + HostConfig: &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: jsonfilelog.Name, + }, + }, + ID: "TestContainerLogPathSetForJSONFileLogger", + Root: containerRoot, + } + + logger, err := c.StartLogger() + require.NoError(t, err) + defer logger.Close() + + expectedLogPath, err := filepath.Abs(filepath.Join(containerRoot, fmt.Sprintf("%s-json.log", c.ID))) + require.NoError(t, err) + require.Equal(t, c.LogPath, expectedLogPath) +} + +func TestContainerLogPathSetForRingLogger(t *testing.T) { + containerRoot, err := ioutil.TempDir("", "TestContainerLogPathSetForRingLogger") + require.NoError(t, err) + defer os.RemoveAll(containerRoot) + + c := &Container{ + Config: &container.Config{}, + HostConfig: &container.HostConfig{ + LogConfig: container.LogConfig{ + Type: jsonfilelog.Name, + Config: map[string]string{ + "mode": string(container.LogModeNonBlock), + }, + }, + }, + ID: "TestContainerLogPathSetForRingLogger", + Root: containerRoot, + } + + logger, err := c.StartLogger() + require.NoError(t, err) + defer logger.Close() + + expectedLogPath, err := filepath.Abs(filepath.Join(containerRoot, fmt.Sprintf("%s-json.log", c.ID))) + require.NoError(t, err) + require.Equal(t, c.LogPath, expectedLogPath) +} diff --git a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go index 92484697d52..7d637f85736 100644 --- a/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go +++ b/components/engine/daemon/logger/jsonfilelog/jsonfilelog.go @@ -150,11 +150,6 @@ func ValidateLogOpt(cfg map[string]string) error { return nil } -// LogPath returns the location the given json logger logs to. -func (l *JSONFileLogger) LogPath() string { - return l.writer.LogPath() -} - // Close closes underlying file and signals all readers to stop. func (l *JSONFileLogger) Close() error { l.mu.Lock() diff --git a/components/engine/daemon/logger/loggerutils/logfile.go b/components/engine/daemon/logger/loggerutils/logfile.go index a9d9d633db7..b533726c420 100644 --- a/components/engine/daemon/logger/loggerutils/logfile.go +++ b/components/engine/daemon/logger/loggerutils/logfile.go @@ -130,13 +130,6 @@ func rotate(name string, maxFiles int) error { return nil } -// LogPath returns the location the given writer logs to. -func (w *LogFile) LogPath() string { - w.mu.Lock() - defer w.mu.Unlock() - return w.f.Name() -} - // MaxFiles return maximum number of files func (w *LogFile) MaxFiles() int { return w.maxFiles diff --git a/components/engine/integration-cli/check_test.go b/components/engine/integration-cli/check_test.go index 8df93dc2f65..18d3d3780a1 100644 --- a/components/engine/integration-cli/check_test.go +++ b/components/engine/integration-cli/check_test.go @@ -84,7 +84,7 @@ type DockerSuite struct { } func (s *DockerSuite) OnTimeout(c *check.C) { - if !testEnv.IsLocalDaemon() { + if testEnv.IsRemoteDaemon() { return } path := filepath.Join(os.Getenv("DEST"), "docker.pid") diff --git a/components/engine/integration-cli/docker_api_events_test.go b/components/engine/integration-cli/docker_api_events_test.go deleted file mode 100644 index a95422f5817..00000000000 --- a/components/engine/integration-cli/docker_api_events_test.go +++ /dev/null @@ -1,74 +0,0 @@ -package main - -import ( - "encoding/json" - "io" - "net/http" - "net/url" - "strconv" - "strings" - "time" - - "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/integration-cli/request" - "github.com/docker/docker/pkg/jsonmessage" - "github.com/go-check/check" -) - -func (s *DockerSuite) TestEventsAPIEmptyOutput(c *check.C) { - type apiResp struct { - resp *http.Response - err error - } - chResp := make(chan *apiResp) - go func() { - resp, body, err := request.Get("/events") - body.Close() - chResp <- &apiResp{resp, err} - }() - - select { - case r := <-chResp: - c.Assert(r.err, checker.IsNil) - c.Assert(r.resp.StatusCode, checker.Equals, http.StatusOK) - case <-time.After(3 * time.Second): - c.Fatal("timeout waiting for events api to respond, should have responded immediately") - } -} - -func (s *DockerSuite) TestEventsAPIBackwardsCompatible(c *check.C) { - since := daemonTime(c).Unix() - ts := strconv.FormatInt(since, 10) - - out := runSleepingContainer(c, "--name=foo", "-d") - containerID := strings.TrimSpace(out) - c.Assert(waitRun(containerID), checker.IsNil) - - q := url.Values{} - q.Set("since", ts) - - _, body, err := request.Get("/events?" + q.Encode()) - c.Assert(err, checker.IsNil) - defer body.Close() - - dec := json.NewDecoder(body) - var containerCreateEvent *jsonmessage.JSONMessage - for { - var event jsonmessage.JSONMessage - if err := dec.Decode(&event); err != nil { - if err == io.EOF { - break - } - c.Fatal(err) - } - if event.Status == "create" && event.ID == containerID { - containerCreateEvent = &event - break - } - } - - c.Assert(containerCreateEvent, checker.Not(checker.IsNil)) - c.Assert(containerCreateEvent.Status, checker.Equals, "create") - c.Assert(containerCreateEvent.ID, checker.Equals, containerID) - c.Assert(containerCreateEvent.From, checker.Equals, "busybox") -} diff --git a/components/engine/integration-cli/docker_api_volumes_test.go b/components/engine/integration-cli/docker_api_volumes_test.go deleted file mode 100644 index 65a96520921..00000000000 --- a/components/engine/integration-cli/docker_api_volumes_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package main - -import ( - "fmt" - "path/filepath" - "strings" - "time" - - "github.com/docker/docker/api/types/filters" - volumetypes "github.com/docker/docker/api/types/volume" - "github.com/docker/docker/client" - "github.com/docker/docker/integration-cli/checker" - "github.com/go-check/check" - "golang.org/x/net/context" -) - -func (s *DockerSuite) TestVolumesAPIList(c *check.C) { - prefix, _ := getPrefixAndSlashFromDaemonPlatform() - cid, _ := dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "busybox") - - cli, err := client.NewEnvClient() - c.Assert(err, checker.IsNil) - defer cli.Close() - - container, err := cli.ContainerInspect(context.Background(), strings.TrimSpace(cid)) - c.Assert(err, checker.IsNil) - vname := container.Mounts[0].Name - - volumes, err := cli.VolumeList(context.Background(), filters.Args{}) - c.Assert(err, checker.IsNil) - - found := false - for _, vol := range volumes.Volumes { - if vol.Name == vname { - found = true - break - } - } - c.Assert(found, checker.Equals, true) -} - -func (s *DockerSuite) TestVolumesAPICreate(c *check.C) { - config := volumetypes.VolumesCreateBody{ - Name: "test", - } - - cli, err := client.NewEnvClient() - c.Assert(err, checker.IsNil) - defer cli.Close() - - vol, err := cli.VolumeCreate(context.Background(), config) - c.Assert(err, check.IsNil) - - c.Assert(filepath.Base(filepath.Dir(vol.Mountpoint)), checker.Equals, config.Name) -} - -func (s *DockerSuite) TestVolumesAPIRemove(c *check.C) { - prefix, _ := getPrefixAndSlashFromDaemonPlatform() - cid, _ := dockerCmd(c, "run", "-d", "-v", prefix+"/foo", "--name=test", "busybox") - - cli, err := client.NewEnvClient() - c.Assert(err, checker.IsNil) - defer cli.Close() - - container, err := cli.ContainerInspect(context.Background(), strings.TrimSpace(cid)) - c.Assert(err, checker.IsNil) - vname := container.Mounts[0].Name - - err = cli.VolumeRemove(context.Background(), vname, false) - c.Assert(err.Error(), checker.Contains, "volume is in use") - - dockerCmd(c, "rm", "-f", "test") - err = cli.VolumeRemove(context.Background(), vname, false) - c.Assert(err, checker.IsNil) -} - -func (s *DockerSuite) TestVolumesAPIInspect(c *check.C) { - config := volumetypes.VolumesCreateBody{ - Name: "test", - } - - // sampling current time minus a minute so to now have false positive in case of delays - now := time.Now().Truncate(time.Minute) - - cli, err := client.NewEnvClient() - c.Assert(err, checker.IsNil) - defer cli.Close() - - _, err = cli.VolumeCreate(context.Background(), config) - c.Assert(err, check.IsNil) - - vol, err := cli.VolumeInspect(context.Background(), config.Name) - c.Assert(err, checker.IsNil) - c.Assert(vol.Name, checker.Equals, config.Name) - - // comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive - testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt)) - c.Assert(err, check.IsNil) - testCreatedAt = testCreatedAt.Truncate(time.Minute) - if !testCreatedAt.Equal(now) { - c.Assert(fmt.Errorf("Time Volume is CreatedAt not equal to current time"), check.NotNil) - } -} diff --git a/components/engine/integration-cli/docker_cli_config_inspect_test.go b/components/engine/integration-cli/docker_cli_config_inspect_test.go deleted file mode 100644 index ba4e80f0705..00000000000 --- a/components/engine/integration-cli/docker_cli_config_inspect_test.go +++ /dev/null @@ -1,68 +0,0 @@ -// +build !windows - -package main - -import ( - "encoding/json" - - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/integration-cli/checker" - "github.com/go-check/check" -) - -func (s *DockerSwarmSuite) TestConfigInspect(c *check.C) { - d := s.AddDaemon(c, true, true) - - testName := "test_config" - id := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: testName, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) - - config := d.GetConfig(c, id) - c.Assert(config.Spec.Name, checker.Equals, testName) - - out, err := d.Cmd("config", "inspect", testName) - c.Assert(err, checker.IsNil, check.Commentf(out)) - - var configs []swarm.Config - c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil) - c.Assert(configs, checker.HasLen, 1) -} - -func (s *DockerSwarmSuite) TestConfigInspectMultiple(c *check.C) { - d := s.AddDaemon(c, true, true) - - testNames := []string{ - "test0", - "test1", - } - for _, n := range testNames { - id := d.CreateConfig(c, swarm.ConfigSpec{ - Annotations: swarm.Annotations{ - Name: n, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("configs: %s", id)) - - config := d.GetConfig(c, id) - c.Assert(config.Spec.Name, checker.Equals, n) - - } - - args := []string{ - "config", - "inspect", - } - args = append(args, testNames...) - out, err := d.Cmd(args...) - c.Assert(err, checker.IsNil, check.Commentf(out)) - - var configs []swarm.Config - c.Assert(json.Unmarshal([]byte(out), &configs), checker.IsNil) - c.Assert(configs, checker.HasLen, 2) -} diff --git a/components/engine/integration-cli/docker_cli_export_import_test.go b/components/engine/integration-cli/docker_cli_export_import_test.go index 45f29d54ed7..6405c1bb5e5 100644 --- a/components/engine/integration-cli/docker_cli_export_import_test.go +++ b/components/engine/integration-cli/docker_cli_export_import_test.go @@ -9,25 +9,8 @@ import ( "github.com/gotestyourself/gotestyourself/icmd" ) -// export an image and try to import it into a new one -func (s *DockerSuite) TestExportContainerAndImportImage(c *check.C) { - testRequires(c, DaemonIsLinux) - containerID := "testexportcontainerandimportimage" - - dockerCmd(c, "run", "--name", containerID, "busybox", "true") - - out, _ := dockerCmd(c, "export", containerID) - - result := icmd.RunCmd(icmd.Cmd{ - Command: []string{dockerBinary, "import", "-", "repo/testexp:v1"}, - Stdin: strings.NewReader(out), - }) - result.Assert(c, icmd.Success) - - cleanedImageID := strings.TrimSpace(result.Combined()) - c.Assert(cleanedImageID, checker.Not(checker.Equals), "", check.Commentf("output should have been an image id")) -} - +// TODO: Move this test to docker/cli, as it is essentially the same test +// as TestExportContainerAndImportImage except output to a file. // Used to test output flag in the export command func (s *DockerSuite) TestExportContainerWithOutputAndImportImage(c *check.C) { testRequires(c, DaemonIsLinux) diff --git a/components/engine/integration-cli/docker_cli_rm_test.go b/components/engine/integration-cli/docker_cli_rm_test.go deleted file mode 100644 index d281704a7b3..00000000000 --- a/components/engine/integration-cli/docker_cli_rm_test.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "io/ioutil" - "os" - - "github.com/docker/docker/integration-cli/checker" - "github.com/docker/docker/integration-cli/cli/build" - "github.com/go-check/check" -) - -func (s *DockerSuite) TestRmContainerWithRemovedVolume(c *check.C) { - testRequires(c, SameHostDaemon) - - prefix, slash := getPrefixAndSlashFromDaemonPlatform() - - tempDir, err := ioutil.TempDir("", "test-rm-container-with-removed-volume-") - if err != nil { - c.Fatalf("failed to create temporary directory: %s", tempDir) - } - defer os.RemoveAll(tempDir) - - dockerCmd(c, "run", "--name", "losemyvolumes", "-v", tempDir+":"+prefix+slash+"test", "busybox", "true") - - err = os.RemoveAll(tempDir) - c.Assert(err, check.IsNil) - - dockerCmd(c, "rm", "-v", "losemyvolumes") -} - -func (s *DockerSuite) TestRmContainerWithVolume(c *check.C) { - prefix, slash := getPrefixAndSlashFromDaemonPlatform() - - dockerCmd(c, "run", "--name", "foo", "-v", prefix+slash+"srv", "busybox", "true") - - dockerCmd(c, "rm", "-v", "foo") -} - -func (s *DockerSuite) TestRmContainerRunning(c *check.C) { - createRunningContainer(c, "foo") - - res, _, err := dockerCmdWithError("rm", "foo") - c.Assert(err, checker.NotNil, check.Commentf("Expected error, can't rm a running container")) - c.Assert(res, checker.Contains, "cannot remove a running container") -} - -func (s *DockerSuite) TestRmContainerForceRemoveRunning(c *check.C) { - createRunningContainer(c, "foo") - - // Stop then remove with -f - dockerCmd(c, "rm", "-f", "foo") -} - -func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) { - dockerfile1 := `FROM busybox:latest - ENTRYPOINT ["true"]` - img := "test-container-orphaning" - dockerfile2 := `FROM busybox:latest - ENTRYPOINT ["true"] - MAINTAINER Integration Tests` - - // build first dockerfile - buildImageSuccessfully(c, img, build.WithDockerfile(dockerfile1)) - img1 := getIDByName(c, img) - // run container on first image - dockerCmd(c, "run", img) - // rebuild dockerfile with a small addition at the end - buildImageSuccessfully(c, img, build.WithDockerfile(dockerfile2)) - // try to remove the image, should not error out. - out, _, err := dockerCmdWithError("rmi", img) - c.Assert(err, check.IsNil, check.Commentf("Expected to removing the image, but failed: %s", out)) - - // check if we deleted the first image - out, _ = dockerCmd(c, "images", "-q", "--no-trunc") - c.Assert(out, checker.Contains, img1, check.Commentf("Orphaned container (could not find %q in docker images): %s", img1, out)) - -} - -func (s *DockerSuite) TestRmInvalidContainer(c *check.C) { - out, _, err := dockerCmdWithError("rm", "unknown") - c.Assert(err, checker.NotNil, check.Commentf("Expected error on rm unknown container, got none")) - c.Assert(out, checker.Contains, "No such container") -} - -func createRunningContainer(c *check.C, name string) { - runSleepingContainer(c, "-dt", "--name", name) -} diff --git a/components/engine/integration-cli/docker_cli_secret_create_test.go b/components/engine/integration-cli/docker_cli_secret_create_test.go index 839c3922acb..a807e4e7e73 100644 --- a/components/engine/integration-cli/docker_cli_secret_create_test.go +++ b/components/engine/integration-cli/docker_cli_secret_create_test.go @@ -12,45 +12,6 @@ import ( "github.com/go-check/check" ) -func (s *DockerSwarmSuite) TestSecretCreate(c *check.C) { - d := s.AddDaemon(c, true, true) - - testName := "test_secret" - id := d.CreateSecret(c, swarm.SecretSpec{ - Annotations: swarm.Annotations{ - Name: testName, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id)) - - secret := d.GetSecret(c, id) - c.Assert(secret.Spec.Name, checker.Equals, testName) -} - -func (s *DockerSwarmSuite) TestSecretCreateWithLabels(c *check.C) { - d := s.AddDaemon(c, true, true) - - testName := "test_secret" - id := d.CreateSecret(c, swarm.SecretSpec{ - Annotations: swarm.Annotations{ - Name: testName, - Labels: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id)) - - secret := d.GetSecret(c, id) - c.Assert(secret.Spec.Name, checker.Equals, testName) - c.Assert(len(secret.Spec.Labels), checker.Equals, 2) - c.Assert(secret.Spec.Labels["key1"], checker.Equals, "value1") - c.Assert(secret.Spec.Labels["key2"], checker.Equals, "value2") -} - // Test case for 28884 func (s *DockerSwarmSuite) TestSecretCreateResolve(c *check.C) { d := s.AddDaemon(c, true, true) diff --git a/components/engine/integration-cli/docker_cli_secret_inspect_test.go b/components/engine/integration-cli/docker_cli_secret_inspect_test.go deleted file mode 100644 index 429e9ad1089..00000000000 --- a/components/engine/integration-cli/docker_cli_secret_inspect_test.go +++ /dev/null @@ -1,45 +0,0 @@ -// +build !windows - -package main - -import ( - "encoding/json" - - "github.com/docker/docker/api/types/swarm" - "github.com/docker/docker/integration-cli/checker" - "github.com/go-check/check" -) - -func (s *DockerSwarmSuite) TestSecretInspectMultiple(c *check.C) { - d := s.AddDaemon(c, true, true) - - testNames := []string{ - "test0", - "test1", - } - for _, n := range testNames { - id := d.CreateSecret(c, swarm.SecretSpec{ - Annotations: swarm.Annotations{ - Name: n, - }, - Data: []byte("TESTINGDATA"), - }) - c.Assert(id, checker.Not(checker.Equals), "", check.Commentf("secrets: %s", id)) - - secret := d.GetSecret(c, id) - c.Assert(secret.Spec.Name, checker.Equals, n) - - } - - args := []string{ - "secret", - "inspect", - } - args = append(args, testNames...) - out, err := d.Cmd(args...) - c.Assert(err, checker.IsNil, check.Commentf(out)) - - var secrets []swarm.Secret - c.Assert(json.Unmarshal([]byte(out), &secrets), checker.IsNil) - c.Assert(secrets, checker.HasLen, 2) -} diff --git a/components/engine/integration-cli/docker_cli_service_update_test.go b/components/engine/integration-cli/docker_cli_service_update_test.go index 8f0717074b2..7b590a0f4e5 100644 --- a/components/engine/integration-cli/docker_cli_service_update_test.go +++ b/components/engine/integration-cli/docker_cli_service_update_test.go @@ -11,41 +11,6 @@ import ( "github.com/go-check/check" ) -func (s *DockerSwarmSuite) TestServiceUpdatePort(c *check.C) { - d := s.AddDaemon(c, true, true) - - serviceName := "TestServiceUpdatePort" - serviceArgs := append([]string{"service", "create", "--detach", "--no-resolve-image", "--name", serviceName, "-p", "8080:8081", defaultSleepImage}, sleepCommandForDaemonPlatform()...) - - // Create a service with a port mapping of 8080:8081. - out, err := d.Cmd(serviceArgs...) - c.Assert(err, checker.IsNil) - waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1) - - // Update the service: changed the port mapping from 8080:8081 to 8082:8083. - _, err = d.Cmd("service", "update", "--detach", "--publish-add", "8082:8083", "--publish-rm", "8081", serviceName) - c.Assert(err, checker.IsNil) - - // Inspect the service and verify port mapping - expected := []swarm.PortConfig{ - { - Protocol: "tcp", - PublishedPort: 8082, - TargetPort: 8083, - PublishMode: "ingress", - }, - } - - out, err = d.Cmd("service", "inspect", "--format", "{{ json .Spec.EndpointSpec.Ports }}", serviceName) - c.Assert(err, checker.IsNil) - - var portConfig []swarm.PortConfig - if err := json.Unmarshal([]byte(out), &portConfig); err != nil { - c.Fatalf("invalid JSON in inspect result: %v (%s)", err, out) - } - c.Assert(portConfig, checker.DeepEquals, expected) -} - func (s *DockerSwarmSuite) TestServiceUpdateLabel(c *check.C) { d := s.AddDaemon(c, true, true) out, err := d.Cmd("service", "create", "--detach", "--no-resolve-image", "--name=test", "busybox", "top") diff --git a/components/engine/integration-cli/docker_experimental_network_test.go b/components/engine/integration-cli/docker_experimental_network_test.go index 888970a0a33..fb20331631a 100644 --- a/components/engine/integration-cli/docker_experimental_network_test.go +++ b/components/engine/integration-cli/docker_experimental_network_test.go @@ -42,6 +42,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) { master := "dm-dummy0" // simulate the master link the vlan tagged subinterface parent link will use createMasterDummy(c, master) + // cleanup the master interface that also collects the slave dev + defer deleteInterface(c, master) // create a network specifying the desired sub-interface name dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.60", "dm-persist") assertNwIsAvailable(c, "dm-persist") @@ -49,8 +51,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanPersistance(c *check.C) { s.d.Restart(c) // verify network is recreated from persistence assertNwIsAvailable(c, "dm-persist") - // cleanup the master interface that also collects the slave dev - deleteInterface(c, "dm-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) { @@ -60,6 +60,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) { master := "di-dummy0" // simulate the master link the vlan tagged subinterface parent link will use createMasterDummy(c, master) + // cleanup the master interface that also collects the slave dev + defer deleteInterface(c, master) // create a network specifying the desired sub-interface name dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.70", "di-persist") assertNwIsAvailable(c, "di-persist") @@ -67,8 +69,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanPersistance(c *check.C) { s.d.Restart(c) // verify network is recreated from persistence assertNwIsAvailable(c, "di-persist") - // cleanup the master interface that also collects the slave dev - deleteInterface(c, "di-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) { @@ -78,11 +78,11 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanSubIntCreate(c *check.C) { master := "dm-dummy0" // simulate the master link the vlan tagged subinterface parent link will use createMasterDummy(c, master) + // cleanup the master interface which also collects the slave dev + defer deleteInterface(c, master) // create a network specifying the desired sub-interface name dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.50", "dm-subinterface") assertNwIsAvailable(c, "dm-subinterface") - // cleanup the master interface which also collects the slave dev - deleteInterface(c, "dm-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) { @@ -92,11 +92,11 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanSubIntCreate(c *check.C) { master := "di-dummy0" // simulate the master link the vlan tagged subinterface parent link will use createMasterDummy(c, master) + // cleanup the master interface which also collects the slave dev + defer deleteInterface(c, master) // create a network specifying the desired sub-interface name dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.60", "di-subinterface") assertNwIsAvailable(c, "di-subinterface") - // cleanup the master interface which also collects the slave dev - deleteInterface(c, "di-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) { @@ -105,6 +105,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) { // master dummy interface 'dm' abbreviation represents 'docker macvlan' master := "dm-dummy0" createMasterDummy(c, master) + // cleanup the master interface which also collects the slave dev + defer deleteInterface(c, master) createVlanInterface(c, master, "dm-dummy0.40", "40") // create a network using an existing parent interface dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-subinterface") @@ -113,8 +115,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkMacvlanOverlapParent(c *check.C) { out, _, err := dockerCmdWithError("network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.40", "dm-parent-net-overlap") // verify that the overlap returns an error c.Assert(err, check.NotNil, check.Commentf(out)) - // cleanup the master interface which also collects the slave dev - deleteInterface(c, "dm-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) { @@ -123,6 +123,8 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) { // master dummy interface 'dm' abbreviation represents 'docker ipvlan' master := "di-dummy0" createMasterDummy(c, master) + // cleanup the master interface which also collects the slave dev + defer deleteInterface(c, master) createVlanInterface(c, master, "di-dummy0.30", "30") // create a network using an existing parent interface dockerCmd(c, "network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-subinterface") @@ -131,8 +133,6 @@ func (s *DockerNetworkSuite) TestDockerNetworkIpvlanOverlapParent(c *check.C) { out, _, err := dockerCmdWithError("network", "create", "--driver=ipvlan", "-o", "parent=di-dummy0.30", "di-parent-net-overlap") // verify that the overlap returns an error c.Assert(err, check.NotNil, check.Commentf(out)) - // cleanup the master interface which also collects the slave dev - deleteInterface(c, "di-dummy0") } func (s *DockerNetworkSuite) TestDockerNetworkMacvlanMultiSubnet(c *check.C) { @@ -471,6 +471,7 @@ func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) { testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) netName := "dm-parent-exists" createMasterDummy(c, "dm-dummy0") + defer deleteInterface(c, "dm-dummy0") //out, err := createVlanInterface(c, "dm-parent", "dm-slave", "macvlan", "bridge") // create a network using an existing parent interface dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0", netName) @@ -480,7 +481,6 @@ func (s *DockerSuite) TestDockerNetworkMacVlanExistingParent(c *check.C) { assertNwNotAvailable(c, netName) // verify the network delete did not delete the predefined link linkExists(c, "dm-dummy0") - deleteInterface(c, "dm-dummy0") } func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) { @@ -488,6 +488,8 @@ func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) { testRequires(c, DaemonIsLinux, macvlanKernelSupport, NotUserNamespace, NotArm, ExperimentalDaemon) netName := "dm-subinterface" createMasterDummy(c, "dm-dummy0") + // delete the parent interface which also collects the slave + defer deleteInterface(c, "dm-dummy0") createVlanInterface(c, "dm-dummy0", "dm-dummy0.20", "20") // create a network using an existing parent interface dockerCmd(c, "network", "create", "--driver=macvlan", "-o", "parent=dm-dummy0.20", netName) @@ -510,8 +512,6 @@ func (s *DockerSuite) TestDockerNetworkMacVlanSubinterface(c *check.C) { assertNwNotAvailable(c, netName) // verify the network delete did not delete the predefined sub-interface linkExists(c, "dm-dummy0.20") - // delete the parent interface which also collects the slave - deleteInterface(c, "dm-dummy0") } func createMasterDummy(c *check.C, master string) { diff --git a/components/engine/integration/config/config_test.go b/components/engine/integration/config/config_test.go index c152be59bf2..4e31b205ee2 100644 --- a/components/engine/integration/config/config_test.go +++ b/components/engine/integration/config/config_test.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "encoding/json" "sort" "testing" "time" @@ -327,3 +328,27 @@ func waitAndAssert(t *testing.T, timeout time.Duration, f func(*testing.T) bool) time.Sleep(100 * time.Millisecond) } } + +func TestConfigInspect(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + defer setupTest(t)() + d := swarm.NewSwarm(t, testEnv) + defer d.Stop(t) + client, err := client.NewClientWithOpts(client.WithHost((d.Sock()))) + require.NoError(t, err) + + ctx := context.Background() + + testName := t.Name() + configID := createConfig(ctx, t, client, testName, []byte("TESTINGDATA"), nil) + + insp, body, err := client.ConfigInspectWithRaw(ctx, configID) + require.NoError(t, err) + assert.Equal(t, insp.Spec.Name, testName) + + var config swarmtypes.Config + err = json.Unmarshal(body, &config) + require.NoError(t, err) + assert.Equal(t, config, insp) +} diff --git a/components/engine/integration/container/daemon_linux_test.go b/components/engine/integration/container/daemon_linux_test.go index 872c7ab4cce..1424e74009f 100644 --- a/components/engine/integration/container/daemon_linux_test.go +++ b/components/engine/integration/container/daemon_linux_test.go @@ -9,8 +9,9 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration-cli/daemon" + "github.com/docker/docker/integration/internal/container" + "github.com/gotestyourself/gotestyourself/skip" "github.com/stretchr/testify/assert" "golang.org/x/sys/unix" ) @@ -26,6 +27,7 @@ import ( // the container process, then start dockerd back up and attempt to start the // container again. func TestContainerStartOnDaemonRestart(t *testing.T) { + skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run") t.Parallel() d := daemon.New(t, "", "dockerd", daemon.Config{}) @@ -36,22 +38,14 @@ func TestContainerStartOnDaemonRestart(t *testing.T) { assert.NoError(t, err, "error creating client") ctx := context.Background() - c, err := client.ContainerCreate(ctx, - &container.Config{ - Image: "busybox", - Cmd: []string{"top"}, - }, - nil, - nil, - "", - ) - assert.NoError(t, err, "error creating test container") - defer client.ContainerRemove(ctx, c.ID, types.ContainerRemoveOptions{Force: true}) - - err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{}) + + cID := container.Create(t, ctx, client) + defer client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{Force: true}) + + err = client.ContainerStart(ctx, cID, types.ContainerStartOptions{}) assert.NoError(t, err, "error starting test container") - inspect, err := client.ContainerInspect(ctx, c.ID) + inspect, err := client.ContainerInspect(ctx, cID) assert.NoError(t, err, "error getting inspect data") ppid := getContainerdShimPid(t, inspect) @@ -67,7 +61,7 @@ func TestContainerStartOnDaemonRestart(t *testing.T) { d.Start(t, "--iptables=false") - err = client.ContainerStart(ctx, c.ID, types.ContainerStartOptions{}) + err = client.ContainerStart(ctx, cID, types.ContainerStartOptions{}) assert.NoError(t, err, "failed to start test container") } diff --git a/components/engine/integration/container/diff_test.go b/components/engine/integration/container/diff_test.go index 63ac19e9ad4..de5ff4e21aa 100644 --- a/components/engine/integration/container/diff_test.go +++ b/components/engine/integration/container/diff_test.go @@ -10,13 +10,11 @@ import ( "github.com/docker/docker/integration/internal/request" "github.com/docker/docker/pkg/archive" "github.com/gotestyourself/gotestyourself/poll" - "github.com/gotestyourself/gotestyourself/skip" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// ensure that an added file shows up in docker diff -func TestDiffFilenameShownInOutput(t *testing.T) { +func TestDiff(t *testing.T) { defer setupTest(t)() client := request.NewAPIClient(t) ctx := context.Background() @@ -27,72 +25,19 @@ func TestDiffFilenameShownInOutput(t *testing.T) { // it will take a few seconds to exit. Also there's no way in Windows to // differentiate between an Add or a Modify, and all files are under // a "Files/" prefix. - lookingFor := containertypes.ContainerChangeResponseItem{Kind: archive.ChangeAdd, Path: "/foo/bar"} - if testEnv.OSType == "windows" { - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(60*time.Second)) - lookingFor = containertypes.ContainerChangeResponseItem{Kind: archive.ChangeModify, Path: "Files/foo/bar"} + expected := []containertypes.ContainerChangeResponseItem{ + {Kind: archive.ChangeAdd, Path: "/foo"}, + {Kind: archive.ChangeAdd, Path: "/foo/bar"}, } - - items, err := client.ContainerDiff(ctx, cID) - require.NoError(t, err) - assert.Contains(t, items, lookingFor) -} - -// test to ensure GH #3840 doesn't occur any more -func TestDiffEnsureInitLayerFilesAreIgnored(t *testing.T) { - skip.If(t, testEnv.DaemonInfo.OSType != "linux") - - defer setupTest(t)() - client := request.NewAPIClient(t) - ctx := context.Background() - - // this is a list of files which shouldn't show up in `docker diff` - initLayerFiles := []string{"/etc/resolv.conf", "/etc/hostname", "/etc/hosts", "/.dockerenv"} - containerCount := 5 - - // we might not run into this problem from the first run, so start a few containers - for i := 0; i < containerCount; i++ { - cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", `echo foo > /root/bar`)) - - items, err := client.ContainerDiff(ctx, cID) - require.NoError(t, err) - for _, item := range items { - assert.NotContains(t, initLayerFiles, item.Path) + if testEnv.OSType == "windows" { + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(60*time.Second)) + expected = []containertypes.ContainerChangeResponseItem{ + {Kind: archive.ChangeModify, Path: "Files/foo"}, + {Kind: archive.ChangeModify, Path: "Files/foo/bar"}, } } -} - -func TestDiffEnsureDefaultDevs(t *testing.T) { - skip.If(t, testEnv.DaemonInfo.OSType != "linux") - - defer setupTest(t)() - client := request.NewAPIClient(t) - ctx := context.Background() - - cID := container.Run(t, ctx, client, container.WithCmd("sleep", "0")) items, err := client.ContainerDiff(ctx, cID) require.NoError(t, err) - - expected := []containertypes.ContainerChangeResponseItem{ - {Kind: archive.ChangeModify, Path: "/dev"}, - {Kind: archive.ChangeAdd, Path: "/dev/full"}, // busybox - {Kind: archive.ChangeModify, Path: "/dev/ptmx"}, // libcontainer - {Kind: archive.ChangeAdd, Path: "/dev/mqueue"}, - {Kind: archive.ChangeAdd, Path: "/dev/kmsg"}, - {Kind: archive.ChangeAdd, Path: "/dev/fd"}, - {Kind: archive.ChangeAdd, Path: "/dev/ptmx"}, - {Kind: archive.ChangeAdd, Path: "/dev/null"}, - {Kind: archive.ChangeAdd, Path: "/dev/random"}, - {Kind: archive.ChangeAdd, Path: "/dev/stdout"}, - {Kind: archive.ChangeAdd, Path: "/dev/stderr"}, - {Kind: archive.ChangeAdd, Path: "/dev/tty1"}, - {Kind: archive.ChangeAdd, Path: "/dev/stdin"}, - {Kind: archive.ChangeAdd, Path: "/dev/tty"}, - {Kind: archive.ChangeAdd, Path: "/dev/urandom"}, - } - - for _, item := range items { - assert.Contains(t, expected, item) - } + assert.Equal(t, expected, items) } diff --git a/components/engine/integration/container/export_test.go b/components/engine/integration/container/export_test.go new file mode 100644 index 00000000000..657b1fce412 --- /dev/null +++ b/components/engine/integration/container/export_test.go @@ -0,0 +1,53 @@ +package container // import "github.com/docker/docker/integration/container" + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/integration/internal/request" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/gotestyourself/gotestyourself/poll" + "github.com/gotestyourself/gotestyourself/skip" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// export an image and try to import it into a new one +func TestExportContainerAndImportImage(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType != "linux") + + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + cID := container.Run(t, ctx, client, container.WithCmd("true")) + poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) + + reference := "repo/testexp:v1" + exportResp, err := client.ContainerExport(ctx, cID) + require.NoError(t, err) + importResp, err := client.ImageImport(ctx, types.ImageImportSource{ + Source: exportResp, + SourceName: "-", + }, reference, types.ImageImportOptions{}) + require.NoError(t, err) + + // If the import is successfully, then the message output should contain + // the image ID and match with the output from `docker images`. + + dec := json.NewDecoder(importResp) + var jm jsonmessage.JSONMessage + err = dec.Decode(&jm) + require.NoError(t, err) + + images, err := client.ImageList(ctx, types.ImageListOptions{ + Filters: filters.NewArgs(filters.Arg("reference", reference)), + }) + require.NoError(t, err) + assert.Equal(t, jm.Status, images[0].ID) +} diff --git a/components/engine/integration/container/inspect_test.go b/components/engine/integration/container/inspect_test.go index 5ecd287bfe2..c7ea23b5179 100644 --- a/components/engine/integration/container/inspect_test.go +++ b/components/engine/integration/container/inspect_test.go @@ -30,7 +30,7 @@ func TestInspectCpusetInConfigPre120(t *testing.T) { c.HostConfig.Resources.CpusetCpus = "0" }, ) - poll.WaitOn(t, containerIsInState(ctx, client, name, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, name, "exited"), poll.WithDelay(100*time.Millisecond)) _, body, err := client.ContainerInspectWithRaw(ctx, name, false) require.NoError(t, err) @@ -40,9 +40,9 @@ func TestInspectCpusetInConfigPre120(t *testing.T) { require.NoError(t, err, "unable to unmarshal body for version 1.19: %s", err) config, ok := inspectJSON["Config"] - assert.Equal(t, ok, true, "Unable to find 'Config'") + assert.Equal(t, true, ok, "Unable to find 'Config'") cfg := config.(map[string]interface{}) _, ok = cfg["Cpuset"] - assert.Equal(t, ok, true, "API version 1.19 expected to include Cpuset in 'Config'") + assert.Equal(t, true, ok, "API version 1.19 expected to include Cpuset in 'Config'") } diff --git a/components/engine/integration/container/kill_test.go b/components/engine/integration/container/kill_test.go index d399213ac62..dbffc802a55 100644 --- a/components/engine/integration/container/kill_test.go +++ b/components/engine/integration/container/kill_test.go @@ -23,11 +23,11 @@ func TestKillContainerInvalidSignal(t *testing.T) { err := client.ContainerKill(ctx, id, "0") require.EqualError(t, err, "Error response from daemon: Invalid signal: 0") - poll.WaitOn(t, containerIsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) err = client.ContainerKill(ctx, id, "SIG42") require.EqualError(t, err, "Error response from daemon: Invalid signal: SIG42") - poll.WaitOn(t, containerIsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) } func TestKillContainer(t *testing.T) { @@ -64,7 +64,7 @@ func TestKillContainer(t *testing.T) { err := client.ContainerKill(ctx, id, tc.signal) require.NoError(t, err) - poll.WaitOn(t, containerIsInState(ctx, client, id, tc.status), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, tc.status), poll.WithDelay(100*time.Millisecond)) }) } } @@ -104,7 +104,7 @@ func TestKillWithStopSignalAndRestartPolicies(t *testing.T) { err := client.ContainerKill(ctx, id, "TERM") require.NoError(t, err) - poll.WaitOn(t, containerIsInState(ctx, client, id, tc.status), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, tc.status), poll.WithDelay(100*time.Millisecond)) }) } } @@ -141,11 +141,11 @@ func TestKillDifferentUserContainer(t *testing.T) { id := container.Run(t, ctx, client, func(c *container.TestContainerConfig) { c.Config.User = "daemon" }) - poll.WaitOn(t, containerIsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerKill(ctx, id, "SIGKILL") require.NoError(t, err) - poll.WaitOn(t, containerIsInState(ctx, client, id, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, id, "exited"), poll.WithDelay(100*time.Millisecond)) } func TestInspectOomKilledTrue(t *testing.T) { @@ -155,16 +155,15 @@ func TestInspectOomKilledTrue(t *testing.T) { ctx := context.Background() client := request.NewAPIClient(t) - name := "testoomkilled" - cID := container.Run(t, ctx, client, container.WithName(name), container.WithCmd("sh", "-c", "x=a; while true; do x=$x$x$x$x; done"), func(c *container.TestContainerConfig) { + cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", "x=a; while true; do x=$x$x$x$x; done"), func(c *container.TestContainerConfig) { c.HostConfig.Resources.Memory = 32 * 1024 * 1024 }) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.State.OOMKilled, true) + assert.Equal(t, true, inspect.State.OOMKilled) } func TestInspectOomKilledFalse(t *testing.T) { @@ -174,12 +173,11 @@ func TestInspectOomKilledFalse(t *testing.T) { ctx := context.Background() client := request.NewAPIClient(t) - name := "testoomkilled" - cID := container.Run(t, ctx, client, container.WithName(name), container.WithCmd("sh", "-c", "echo hello world")) + cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", "echo hello world")) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.State.OOMKilled, false) + assert.Equal(t, false, inspect.State.OOMKilled) } diff --git a/components/engine/integration/container/links_linux_test.go b/components/engine/integration/container/links_linux_test.go index e2e0b9d9eaa..c844c4916fb 100644 --- a/components/engine/integration/container/links_linux_test.go +++ b/components/engine/integration/container/links_linux_test.go @@ -1,26 +1,22 @@ package container // import "github.com/docker/docker/integration/container" import ( - "bytes" "context" "io/ioutil" "os" "testing" - "time" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/request" - "github.com/docker/docker/pkg/stdcopy" - "github.com/gotestyourself/gotestyourself/poll" "github.com/gotestyourself/gotestyourself/skip" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestLinksEtcHostsContentMatch(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) hosts, err := ioutil.ReadFile("/etc/hosts") skip.If(t, os.IsNotExist(err)) @@ -29,21 +25,13 @@ func TestLinksEtcHostsContentMatch(t *testing.T) { client := request.NewAPIClient(t) ctx := context.Background() - cID := container.Run(t, ctx, client, container.WithCmd("cat", "/etc/hosts"), container.WithNetworkMode("host")) - - poll.WaitOn(t, containerIsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) - - body, err := client.ContainerLogs(ctx, cID, types.ContainerLogsOptions{ - ShowStdout: true, - }) - require.NoError(t, err) - defer body.Close() - - var b bytes.Buffer - _, err = stdcopy.StdCopy(&b, ioutil.Discard, body) + cID := container.Run(t, ctx, client, container.WithNetworkMode("host")) + res, err := container.Exec(ctx, client, cID, []string{"cat", "/etc/hosts"}) require.NoError(t, err) + require.Empty(t, res.Stderr()) + require.Equal(t, 0, res.ExitCode) - assert.Equal(t, string(hosts), b.String()) + assert.Equal(t, string(hosts), res.Stdout()) } func TestLinksContainerNames(t *testing.T) { diff --git a/components/engine/integration/container/logs_test.go b/components/engine/integration/container/logs_test.go index 1157da14b8f..2d7306582fd 100644 --- a/components/engine/integration/container/logs_test.go +++ b/components/engine/integration/container/logs_test.go @@ -20,7 +20,6 @@ func TestLogsFollowTailEmpty(t *testing.T) { ctx := context.Background() id := container.Run(t, ctx, client, container.WithCmd("sleep", "100000")) - defer client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{Force: true}) logs, err := client.ContainerLogs(ctx, id, types.ContainerLogsOptions{ShowStdout: true, Tail: "2"}) if logs != nil { diff --git a/components/engine/integration/container/mounts_linux_test.go b/components/engine/integration/container/mounts_linux_test.go index ceec706271a..71bdccc71be 100644 --- a/components/engine/integration/container/mounts_linux_test.go +++ b/components/engine/integration/container/mounts_linux_test.go @@ -23,6 +23,7 @@ import ( ) func TestContainerShmNoLeak(t *testing.T) { + skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run") t.Parallel() d := daemon.New(t, "docker", "dockerd", daemon.Config{}) client, err := d.NewClient() @@ -94,7 +95,7 @@ func TestContainerShmNoLeak(t *testing.T) { func TestContainerNetworkMountsNoChown(t *testing.T) { // chown only applies to Linux bind mounted volumes; must be same host to verify - skip.If(t, testEnv.DaemonInfo.OSType != "linux" || !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.DaemonInfo.OSType != "linux" || testEnv.IsRemoteDaemon()) defer setupTest(t)() diff --git a/components/engine/integration/container/nat_test.go b/components/engine/integration/container/nat_test.go index df3451fbc56..e2bb0f9b072 100644 --- a/components/engine/integration/container/nat_test.go +++ b/components/engine/integration/container/nat_test.go @@ -22,7 +22,7 @@ import ( ) func TestNetworkNat(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() @@ -36,11 +36,11 @@ func TestNetworkNat(t *testing.T) { data, err := ioutil.ReadAll(conn) require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(string(data)), msg) + assert.Equal(t, msg, strings.TrimSpace(string(data))) } func TestNetworkLocalhostTCPNat(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() @@ -53,11 +53,11 @@ func TestNetworkLocalhostTCPNat(t *testing.T) { data, err := ioutil.ReadAll(conn) require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(string(data)), msg) + assert.Equal(t, msg, strings.TrimSpace(string(data))) } func TestNetworkLoopbackNat(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) msg := "it works" startServerContainer(t, msg, 8080) @@ -69,7 +69,7 @@ func TestNetworkLoopbackNat(t *testing.T) { cID := container.Run(t, ctx, client, container.WithCmd("sh", "-c", fmt.Sprintf("stty raw && nc -w 5 %s 8080", endpoint.String())), container.WithTty(true), container.WithNetworkMode("container:server")) - poll.WaitOn(t, containerIsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) body, err := client.ContainerLogs(ctx, cID, types.ContainerLogsOptions{ ShowStdout: true, @@ -81,7 +81,7 @@ func TestNetworkLoopbackNat(t *testing.T) { _, err = io.Copy(&b, body) require.NoError(t, err) - assert.Equal(t, strings.TrimSpace(b.String()), msg) + assert.Equal(t, msg, strings.TrimSpace(b.String())) } func startServerContainer(t *testing.T, msg string, port int) string { @@ -98,7 +98,7 @@ func startServerContainer(t *testing.T, msg string, port int) string { } }) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) return cID } @@ -109,7 +109,7 @@ func getExternalAddress(t *testing.T) net.IP { ifaceAddrs, err := iface.Addrs() require.NoError(t, err) - assert.NotEqual(t, len(ifaceAddrs), 0) + assert.NotEqual(t, 0, len(ifaceAddrs)) ifaceIP, _, err := net.ParseCIDR(ifaceAddrs[0].String()) require.NoError(t, err) diff --git a/components/engine/integration/container/pause_test.go b/components/engine/integration/container/pause_test.go index 4e643572213..bf9f9c3d8ff 100644 --- a/components/engine/integration/container/pause_test.go +++ b/components/engine/integration/container/pause_test.go @@ -25,20 +25,19 @@ func TestPause(t *testing.T) { client := request.NewAPIClient(t) ctx := context.Background() - name := "testeventpause" - cID := container.Run(t, ctx, client, container.WithName(name)) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + cID := container.Run(t, ctx, client) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) since := request.DaemonUnixTime(ctx, t, client, testEnv) - err := client.ContainerPause(ctx, name) + err := client.ContainerPause(ctx, cID) require.NoError(t, err) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.State.Paused, true) + assert.Equal(t, true, inspect.State.Paused) - err = client.ContainerUnpause(ctx, name) + err = client.ContainerUnpause(ctx, cID) require.NoError(t, err) until := request.DaemonUnixTime(ctx, t, client, testEnv) @@ -46,9 +45,9 @@ func TestPause(t *testing.T) { messages, errs := client.Events(ctx, types.EventsOptions{ Since: since, Until: until, - Filters: filters.NewArgs(filters.Arg("container", name)), + Filters: filters.NewArgs(filters.Arg("container", cID)), }) - assert.Equal(t, getEventActions(t, messages, errs), []string{"pause", "unpause"}) + assert.Equal(t, []string{"pause", "unpause"}, getEventActions(t, messages, errs)) } func TestPauseFailsOnWindowsServerContainers(t *testing.T) { @@ -59,7 +58,7 @@ func TestPauseFailsOnWindowsServerContainers(t *testing.T) { ctx := context.Background() cID := container.Run(t, ctx, client) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerPause(ctx, cID) testutil.ErrorContains(t, err, "cannot pause Windows Server Containers") @@ -73,7 +72,7 @@ func TestPauseStopPausedContainer(t *testing.T) { ctx := context.Background() cID := container.Run(t, ctx, client) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerPause(ctx, cID) require.NoError(t, err) @@ -81,7 +80,7 @@ func TestPauseStopPausedContainer(t *testing.T) { err = client.ContainerStop(ctx, cID, nil) require.NoError(t, err) - poll.WaitOn(t, containerIsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) } func getEventActions(t *testing.T, messages <-chan events.Message, errs <-chan error) []string { @@ -89,7 +88,7 @@ func getEventActions(t *testing.T, messages <-chan events.Message, errs <-chan e for { select { case err := <-errs: - assert.Equal(t, err == nil || err == io.EOF, true) + assert.True(t, err == nil || err == io.EOF) return actions case e := <-messages: actions = append(actions, e.Status) diff --git a/components/engine/integration/container/ps_test.go b/components/engine/integration/container/ps_test.go index dfcb0e2efe6..358276b36af 100644 --- a/components/engine/integration/container/ps_test.go +++ b/components/engine/integration/container/ps_test.go @@ -5,9 +5,8 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/network" + "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/request" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -18,23 +17,9 @@ func TestPsFilter(t *testing.T) { client := request.NewAPIClient(t) ctx := context.Background() - createContainerForFilter := func(ctx context.Context, name string) string { - body, err := client.ContainerCreate(ctx, - &container.Config{ - Cmd: []string{"top"}, - Image: "busybox", - }, - &container.HostConfig{}, - &network.NetworkingConfig{}, - name, - ) - require.NoError(t, err) - return body.ID - } - - prev := createContainerForFilter(ctx, "prev") - createContainerForFilter(ctx, "top") - next := createContainerForFilter(ctx, "next") + prev := container.Create(t, ctx, client, container.WithName("prev")) + container.Create(t, ctx, client, container.WithName("top")) + next := container.Create(t, ctx, client, container.WithName("next")) containerIDs := func(containers []types.Container) []string { entries := []string{} diff --git a/components/engine/integration/container/remove_test.go b/components/engine/integration/container/remove_test.go new file mode 100644 index 00000000000..98aacdd2056 --- /dev/null +++ b/components/engine/integration/container/remove_test.go @@ -0,0 +1,113 @@ +package container // import "github.com/docker/docker/integration/container" + +import ( + "context" + "os" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/integration/internal/request" + "github.com/docker/docker/internal/testutil" + "github.com/gotestyourself/gotestyourself/fs" + "github.com/gotestyourself/gotestyourself/poll" + "github.com/gotestyourself/gotestyourself/skip" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { + if testEnv.OSType == "windows" { + return "c:", `\` + } + return "", "/" +} + +// Test case for #5244: `docker rm` fails if bind dir doesn't exist anymore +func TestRemoveContainerWithRemovedVolume(t *testing.T) { + skip.If(t, testEnv.IsRemoteDaemon()) + + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + prefix, slash := getPrefixAndSlashFromDaemonPlatform() + + tempDir := fs.NewDir(t, "test-rm-container-with-removed-volume", fs.WithMode(0755)) + defer tempDir.Remove() + + cID := container.Run(t, ctx, client, container.WithCmd("true"), container.WithBind(tempDir.Path(), prefix+slash+"test")) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + + err := os.RemoveAll(tempDir.Path()) + require.NoError(t, err) + + err = client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{ + RemoveVolumes: true, + }) + require.NoError(t, err) + + _, _, err = client.ContainerInspectWithRaw(ctx, cID, true) + testutil.ErrorContains(t, err, "No such container") +} + +// Test case for #2099/#2125 +func TestRemoveContainerWithVolume(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + prefix, slash := getPrefixAndSlashFromDaemonPlatform() + + cID := container.Run(t, ctx, client, container.WithCmd("true"), container.WithVolume(prefix+slash+"srv")) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + + insp, _, err := client.ContainerInspectWithRaw(ctx, cID, true) + require.NoError(t, err) + assert.Equal(t, 1, len(insp.Mounts)) + volName := insp.Mounts[0].Name + + err = client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{ + RemoveVolumes: true, + }) + require.NoError(t, err) + + volumes, err := client.VolumeList(ctx, filters.NewArgs(filters.Arg("name", volName))) + require.NoError(t, err) + assert.Equal(t, 0, len(volumes.Volumes)) +} + +func TestRemoveContainerRunning(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + cID := container.Run(t, ctx, client) + + err := client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{}) + testutil.ErrorContains(t, err, "cannot remove a running container") +} + +func TestRemoveContainerForceRemoveRunning(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + cID := container.Run(t, ctx, client) + + err := client.ContainerRemove(ctx, cID, types.ContainerRemoveOptions{ + Force: true, + }) + require.NoError(t, err) +} + +func TestRemoveInvalidContainer(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + err := client.ContainerRemove(ctx, "unknown", types.ContainerRemoveOptions{}) + testutil.ErrorContains(t, err, "No such container") +} diff --git a/components/engine/integration/container/rename_test.go b/components/engine/integration/container/rename_test.go index da2bed17c7c..3567aee1f54 100644 --- a/components/engine/integration/container/rename_test.go +++ b/components/engine/integration/container/rename_test.go @@ -51,11 +51,11 @@ func TestRenameStoppedContainer(t *testing.T) { oldName := "first_name" cID := container.Run(t, ctx, client, container.WithName(oldName), container.WithCmd("sh")) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.Name, "/"+oldName) + assert.Equal(t, "/"+oldName, inspect.Name) newName := "new_name" + stringid.GenerateNonCryptoID() err = client.ContainerRename(ctx, oldName, newName) @@ -63,7 +63,7 @@ func TestRenameStoppedContainer(t *testing.T) { inspect, err = client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.Name, "/"+newName) + assert.Equal(t, "/"+newName, inspect.Name) } func TestRenameRunningContainerAndReuse(t *testing.T) { @@ -73,7 +73,7 @@ func TestRenameRunningContainerAndReuse(t *testing.T) { oldName := "first_name" cID := container.Run(t, ctx, client, container.WithName(oldName)) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) newName := "new_name" + stringid.GenerateNonCryptoID() err := client.ContainerRename(ctx, oldName, newName) @@ -81,17 +81,17 @@ func TestRenameRunningContainerAndReuse(t *testing.T) { inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.Name, "/"+newName) + assert.Equal(t, "/"+newName, inspect.Name) _, err = client.ContainerInspect(ctx, oldName) testutil.ErrorContains(t, err, "No such container: "+oldName) cID = container.Run(t, ctx, client, container.WithName(oldName)) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) inspect, err = client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.Name, "/"+oldName) + assert.Equal(t, "/"+oldName, inspect.Name) } func TestRenameInvalidName(t *testing.T) { @@ -101,14 +101,14 @@ func TestRenameInvalidName(t *testing.T) { oldName := "first_name" cID := container.Run(t, ctx, client, container.WithName(oldName)) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerRename(ctx, oldName, "new:invalid") testutil.ErrorContains(t, err, "Invalid container name") inspect, err := client.ContainerInspect(ctx, oldName) require.NoError(t, err) - assert.Equal(t, inspect.ID, cID) + assert.Equal(t, cID, inspect.ID) } // Test case for GitHub issue 22466 @@ -133,10 +133,14 @@ func TestRenameAnonymousContainer(t *testing.T) { }) err = client.ContainerRename(ctx, cID, "container1") require.NoError(t, err) + // Stop/Start the container to get registered + // FIXME(vdemeester) this is a really weird behavior as it fails otherwise + err = client.ContainerStop(ctx, "container1", nil) + require.NoError(t, err) err = client.ContainerStart(ctx, "container1", types.ContainerStartOptions{}) require.NoError(t, err) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) count := "-c" if testEnv.OSType == "windows" { @@ -148,11 +152,11 @@ func TestRenameAnonymousContainer(t *testing.T) { } c.HostConfig.NetworkMode = "network1" }, container.WithCmd("ping", count, "1", "container1")) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) - assert.Equal(t, inspect.State.ExitCode, 0) + assert.Equal(t, 0, inspect.State.ExitCode, "container %s exited with the wrong exitcode: %+v", cID, inspect) } // TODO: should be a unit test @@ -162,7 +166,7 @@ func TestRenameContainerWithSameName(t *testing.T) { client := request.NewAPIClient(t) cID := container.Run(t, ctx, client, container.WithName("old")) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerRename(ctx, "old", "old") testutil.ErrorContains(t, err, "Renaming a container with the same name") err = client.ContainerRename(ctx, cID, "old") @@ -175,22 +179,22 @@ func TestRenameContainerWithSameName(t *testing.T) { // of the linked container should be updated so that the other // container could still reference to the container that is renamed. func TestRenameContainerWithLinkedContainer(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() ctx := context.Background() client := request.NewAPIClient(t) db1ID := container.Run(t, ctx, client, container.WithName("db1")) - poll.WaitOn(t, containerIsInState(ctx, client, db1ID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, db1ID, "running"), poll.WithDelay(100*time.Millisecond)) app1ID := container.Run(t, ctx, client, container.WithName("app1"), container.WithLinks("db1:/mysql")) - poll.WaitOn(t, containerIsInState(ctx, client, app1ID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, app1ID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerRename(ctx, "app1", "app2") require.NoError(t, err) inspect, err := client.ContainerInspect(ctx, "app2/mysql") require.NoError(t, err) - assert.Equal(t, inspect.ID, db1ID) + assert.Equal(t, db1ID, inspect.ID) } diff --git a/components/engine/integration/container/resize_test.go b/components/engine/integration/container/resize_test.go index 0ada5c32e4c..18438ea8253 100644 --- a/components/engine/integration/container/resize_test.go +++ b/components/engine/integration/container/resize_test.go @@ -23,7 +23,7 @@ func TestResize(t *testing.T) { cID := container.Run(t, ctx, client) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerResize(ctx, cID, types.ResizeOptions{ Height: 40, @@ -39,12 +39,12 @@ func TestResizeWithInvalidSize(t *testing.T) { cID := container.Run(t, ctx, client) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) endpoint := "/containers/" + cID + "/resize?h=foo&w=bar" res, _, err := req.Post(endpoint) require.NoError(t, err) - assert.Equal(t, res.StatusCode, http.StatusBadRequest) + assert.Equal(t, http.StatusBadRequest, res.StatusCode) } func TestResizeWhenContainerNotStarted(t *testing.T) { @@ -54,7 +54,7 @@ func TestResizeWhenContainerNotStarted(t *testing.T) { cID := container.Run(t, ctx, client, container.WithCmd("echo")) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond)) err := client.ContainerResize(ctx, cID, types.ResizeOptions{ Height: 40, diff --git a/components/engine/integration/container/restart_test.go b/components/engine/integration/container/restart_test.go index accaf2cd80b..7a2576e219f 100644 --- a/components/engine/integration/container/restart_test.go +++ b/components/engine/integration/container/restart_test.go @@ -9,9 +9,11 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/docker/docker/integration-cli/daemon" + "github.com/gotestyourself/gotestyourself/skip" ) func TestDaemonRestartKillContainers(t *testing.T) { + skip.If(t, testEnv.IsRemoteDaemon(), "cannot start daemon on remote test run") type testCase struct { desc string config *container.Config diff --git a/components/engine/integration/container/stats_test.go b/components/engine/integration/container/stats_test.go index fdf85f44f63..9c0b9484983 100644 --- a/components/engine/integration/container/stats_test.go +++ b/components/engine/integration/container/stats_test.go @@ -28,7 +28,7 @@ func TestStats(t *testing.T) { cID := container.Run(t, ctx, client) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) resp, err := client.ContainerStats(ctx, cID, false) require.NoError(t, err) diff --git a/components/engine/integration/container/stop_test.go b/components/engine/integration/container/stop_test.go index 305596ac85e..4ecd06dd2c3 100644 --- a/components/engine/integration/container/stop_test.go +++ b/components/engine/integration/container/stop_test.go @@ -8,7 +8,6 @@ import ( "time" "github.com/docker/docker/api/types" - "github.com/docker/docker/client" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/request" "github.com/gotestyourself/gotestyourself/icmd" @@ -30,7 +29,7 @@ func TestStopContainerWithRestartPolicyAlways(t *testing.T) { } for _, name := range names { - poll.WaitOn(t, containerIsInState(ctx, client, name, "running", "restarting"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, name, "running", "restarting"), poll.WithDelay(100*time.Millisecond)) } for _, name := range names { @@ -39,7 +38,7 @@ func TestStopContainerWithRestartPolicyAlways(t *testing.T) { } for _, name := range names { - poll.WaitOn(t, containerIsStopped(ctx, client, name), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsStopped(ctx, client, name), poll.WithDelay(100*time.Millisecond)) } } @@ -52,7 +51,7 @@ func TestDeleteDevicemapper(t *testing.T) { id := container.Run(t, ctx, client, container.WithName("foo"), container.WithCmd("echo")) - poll.WaitOn(t, containerIsStopped(ctx, client, id), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsStopped(ctx, client, id), poll.WithDelay(100*time.Millisecond)) inspect, err := client.ContainerInspect(ctx, id) require.NoError(t, err) @@ -70,33 +69,3 @@ func TestDeleteDevicemapper(t *testing.T) { err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{}) require.NoError(t, err) } - -func containerIsStopped(ctx context.Context, client client.APIClient, containerID string) func(log poll.LogT) poll.Result { - return func(log poll.LogT) poll.Result { - inspect, err := client.ContainerInspect(ctx, containerID) - - switch { - case err != nil: - return poll.Error(err) - case !inspect.State.Running: - return poll.Success() - default: - return poll.Continue("waiting for container to be stopped") - } - } -} - -func containerIsInState(ctx context.Context, client client.APIClient, containerID string, state ...string) func(log poll.LogT) poll.Result { - return func(log poll.LogT) poll.Result { - inspect, err := client.ContainerInspect(ctx, containerID) - if err != nil { - return poll.Error(err) - } - for _, v := range state { - if inspect.State.Status == v { - return poll.Success() - } - } - return poll.Continue("waiting for container to be running, currently %s", inspect.State.Status) - } -} diff --git a/components/engine/integration/container/update_linux_test.go b/components/engine/integration/container/update_linux_test.go index 9028f303868..c898dc1d3b8 100644 --- a/components/engine/integration/container/update_linux_test.go +++ b/components/engine/integration/container/update_linux_test.go @@ -31,7 +31,7 @@ func TestUpdateMemory(t *testing.T) { } }) - poll.WaitOn(t, containerIsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "running"), poll.WithDelay(100*time.Millisecond)) const ( setMemory int64 = 314572800 diff --git a/components/engine/integration/container/update_test.go b/components/engine/integration/container/update_test.go index aee95804a1c..651e84cb222 100644 --- a/components/engine/integration/container/update_test.go +++ b/components/engine/integration/container/update_test.go @@ -39,7 +39,7 @@ func TestUpdateRestartPolicy(t *testing.T) { timeout = 180 * time.Second } - poll.WaitOn(t, containerIsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(timeout)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(timeout)) inspect, err := client.ContainerInspect(ctx, cID) require.NoError(t, err) diff --git a/components/engine/integration/image/commit_test.go b/components/engine/integration/image/commit_test.go index a515b706af4..39fc956db11 100644 --- a/components/engine/integration/image/commit_test.go +++ b/components/engine/integration/image/commit_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/docker/docker/api/types" - "github.com/docker/docker/api/types/container" + "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/request" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -16,10 +16,9 @@ func TestCommitInheritsEnv(t *testing.T) { client := request.NewAPIClient(t) ctx := context.Background() - createResp1, err := client.ContainerCreate(ctx, &container.Config{Image: "busybox"}, nil, nil, "") - require.NoError(t, err) + cID1 := container.Create(t, ctx, client) - commitResp1, err := client.ContainerCommit(ctx, createResp1.ID, types.ContainerCommitOptions{ + commitResp1, err := client.ContainerCommit(ctx, cID1, types.ContainerCommitOptions{ Changes: []string{"ENV PATH=/bin"}, Reference: "test-commit-image", }) @@ -31,10 +30,9 @@ func TestCommitInheritsEnv(t *testing.T) { expectedEnv1 := []string{"PATH=/bin"} assert.Equal(t, expectedEnv1, image1.Config.Env) - createResp2, err := client.ContainerCreate(ctx, &container.Config{Image: image1.ID}, nil, nil, "") - require.NoError(t, err) + cID2 := container.Create(t, ctx, client, container.WithImage(image1.ID)) - commitResp2, err := client.ContainerCommit(ctx, createResp2.ID, types.ContainerCommitOptions{ + commitResp2, err := client.ContainerCommit(ctx, cID2, types.ContainerCommitOptions{ Changes: []string{"ENV PATH=/usr/bin:$PATH"}, Reference: "test-commit-image", }) diff --git a/components/engine/integration/image/remove_test.go b/components/engine/integration/image/remove_test.go new file mode 100644 index 00000000000..825724bd039 --- /dev/null +++ b/components/engine/integration/image/remove_test.go @@ -0,0 +1,60 @@ +package image // import "github.com/docker/docker/integration/image" + +import ( + "context" + "testing" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/integration/internal/request" + "github.com/docker/docker/internal/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRemoveImageOrphaning(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + img := "test-container-orphaning" + + // Create a container from busybox, and commit a small change so we have a new image + cID1 := container.Create(t, ctx, client, container.WithCmd("")) + commitResp1, err := client.ContainerCommit(ctx, cID1, types.ContainerCommitOptions{ + Changes: []string{`ENTRYPOINT ["true"]`}, + Reference: img, + }) + require.NoError(t, err) + + // verifies that reference now points to first image + resp, _, err := client.ImageInspectWithRaw(ctx, img) + require.NoError(t, err) + assert.Equal(t, resp.ID, commitResp1.ID) + + // Create a container from created image, and commit a small change with same reference name + cID2 := container.Create(t, ctx, client, container.WithImage(img), container.WithCmd("")) + commitResp2, err := client.ContainerCommit(ctx, cID2, types.ContainerCommitOptions{ + Changes: []string{`LABEL Maintainer="Integration Tests"`}, + Reference: img, + }) + require.NoError(t, err) + + // verifies that reference now points to second image + resp, _, err = client.ImageInspectWithRaw(ctx, img) + require.NoError(t, err) + assert.Equal(t, resp.ID, commitResp2.ID) + + // try to remove the image, should not error out. + _, err = client.ImageRemove(ctx, img, types.ImageRemoveOptions{}) + require.NoError(t, err) + + // check if the first image is still there + resp, _, err = client.ImageInspectWithRaw(ctx, commitResp1.ID) + require.NoError(t, err) + assert.Equal(t, resp.ID, commitResp1.ID) + + // check if the second image has been deleted + _, _, err = client.ImageInspectWithRaw(ctx, commitResp2.ID) + testutil.ErrorContains(t, err, "No such image:") +} diff --git a/components/engine/integration/internal/container/ops.go b/components/engine/integration/internal/container/ops.go index e3d538bef16..b4ad66f93dd 100644 --- a/components/engine/integration/internal/container/ops.go +++ b/components/engine/integration/internal/container/ops.go @@ -1,6 +1,8 @@ package container import ( + "fmt" + containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/strslice" "github.com/docker/go-connections/nat" @@ -20,6 +22,13 @@ func WithLinks(links ...string) func(*TestContainerConfig) { } } +// WithImage sets the image of the container +func WithImage(image string) func(*TestContainerConfig) { + return func(c *TestContainerConfig) { + c.Config.Image = image + } +} + // WithCmd sets the comannds of the container func WithCmd(cmds ...string) func(*TestContainerConfig) { return func(c *TestContainerConfig) { @@ -57,3 +66,20 @@ func WithWorkingDir(dir string) func(*TestContainerConfig) { c.Config.WorkingDir = dir } } + +// WithVolume sets the volume of the container +func WithVolume(name string) func(*TestContainerConfig) { + return func(c *TestContainerConfig) { + if c.Config.Volumes == nil { + c.Config.Volumes = map[string]struct{}{} + } + c.Config.Volumes[name] = struct{}{} + } +} + +// WithBind sets the bind mount of the container +func WithBind(src, target string) func(*TestContainerConfig) { + return func(c *TestContainerConfig) { + c.HostConfig.Binds = append(c.HostConfig.Binds, fmt.Sprintf("%s:%s", src, target)) + } +} diff --git a/components/engine/integration/internal/container/states.go b/components/engine/integration/internal/container/states.go new file mode 100644 index 00000000000..27d9973e00a --- /dev/null +++ b/components/engine/integration/internal/container/states.go @@ -0,0 +1,41 @@ +package container + +import ( + "strings" + + "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/poll" + "golang.org/x/net/context" +) + +// IsStopped verifies the container is in stopped state. +func IsStopped(ctx context.Context, client client.APIClient, containerID string) func(log poll.LogT) poll.Result { + return func(log poll.LogT) poll.Result { + inspect, err := client.ContainerInspect(ctx, containerID) + + switch { + case err != nil: + return poll.Error(err) + case !inspect.State.Running: + return poll.Success() + default: + return poll.Continue("waiting for container to be stopped") + } + } +} + +// IsInState verifies the container is in one of the specified state, e.g., "running", "exited", etc. +func IsInState(ctx context.Context, client client.APIClient, containerID string, state ...string) func(log poll.LogT) poll.Result { + return func(log poll.LogT) poll.Result { + inspect, err := client.ContainerInspect(ctx, containerID) + if err != nil { + return poll.Error(err) + } + for _, v := range state { + if inspect.State.Status == v { + return poll.Success() + } + } + return poll.Continue("waiting for container to be one of (%s), currently %s", strings.Join(state, ", "), inspect.State.Status) + } +} diff --git a/components/engine/integration/internal/request/client.go b/components/engine/integration/internal/request/client.go index 367db14c591..34e589ec866 100644 --- a/components/engine/integration/internal/request/client.go +++ b/components/engine/integration/internal/request/client.go @@ -20,8 +20,8 @@ func NewAPIClient(t *testing.T, ops ...func(*client.Client) error) client.APICli return clt } -// daemonTime provides the current time on the daemon host -func daemonTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time { +// DaemonTime provides the current time on the daemon host +func DaemonTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) time.Time { if testEnv.IsLocalDaemon() { return time.Now() } @@ -37,6 +37,6 @@ func daemonTime(ctx context.Context, t *testing.T, client client.APIClient, test // DaemonUnixTime returns the current time on the daemon host with nanoseconds precision. // It return the time formatted how the client sends timestamps to the server. func DaemonUnixTime(ctx context.Context, t *testing.T, client client.APIClient, testEnv *environment.Execution) string { - dt := daemonTime(ctx, t, client, testEnv) + dt := DaemonTime(ctx, t, client, testEnv) return fmt.Sprintf("%d.%09d", dt.Unix(), int64(dt.Nanosecond())) } diff --git a/components/engine/integration/secret/secret_test.go b/components/engine/integration/secret/secret_test.go index 3b5e66a5bf1..8a292f005b2 100644 --- a/components/engine/integration/secret/secret_test.go +++ b/components/engine/integration/secret/secret_test.go @@ -129,7 +129,7 @@ func createSecret(ctx context.Context, t *testing.T, client client.APIClient, na return secret.ID } -func TestSecretsCreate(t *testing.T) { +func TestSecretsCreateAndDelete(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType != "linux") defer setupTest(t)() @@ -141,8 +141,7 @@ func TestSecretsCreate(t *testing.T) { ctx := context.Background() testName := "test_secret" - createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil) - require.NoError(t, err) + secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil) // create an already existin secret, daemon should return a status code of 409 _, err = client.SecretCreate(ctx, swarmtypes.SecretSpec{ @@ -152,27 +151,8 @@ func TestSecretsCreate(t *testing.T) { Data: []byte("TESTINGDATA"), }) testutil.ErrorContains(t, err, "already exists") -} - -func TestSecretsDelete(t *testing.T) { - skip.If(t, testEnv.DaemonInfo.OSType != "linux") - - defer setupTest(t)() - d := swarm.NewSwarm(t, testEnv) - defer d.Stop(t) - client, err := client.NewClientWithOpts(client.WithHost((d.Sock()))) - require.NoError(t, err) - - ctx := context.Background() - - testName := "test_secret" - secretID := createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), nil) - require.NoError(t, err) - - insp, _, err := client.SecretInspectWithRaw(ctx, secretID) - require.NoError(t, err) - assert.Equal(t, insp.ID, secretID) + // Ported from original TestSecretsDelete err = client.SecretRemove(ctx, secretID) require.NoError(t, err) @@ -181,6 +161,20 @@ func TestSecretsDelete(t *testing.T) { err = client.SecretRemove(ctx, "non-existin") testutil.ErrorContains(t, err, "No such secret: non-existin") + + // Ported from original TestSecretsCreteaWithLabels + testName = "test_secret_with_labels" + secretID = createSecret(ctx, t, client, testName, []byte("TESTINGDATA"), map[string]string{ + "key1": "value1", + "key2": "value2", + }) + + insp, _, err := client.SecretInspectWithRaw(ctx, secretID) + require.NoError(t, err) + assert.Equal(t, insp.Spec.Name, testName) + assert.Equal(t, len(insp.Spec.Labels), 2) + assert.Equal(t, insp.Spec.Labels["key1"], "value1") + assert.Equal(t, insp.Spec.Labels["key2"], "value2") } func TestSecretsUpdate(t *testing.T) { diff --git a/components/engine/integration/service/inspect_test.go b/components/engine/integration/service/inspect_test.go index fdb22cf2f75..8cd24bc31b6 100644 --- a/components/engine/integration/service/inspect_test.go +++ b/components/engine/integration/service/inspect_test.go @@ -18,7 +18,7 @@ import ( ) func TestInspect(t *testing.T) { - skip.IfCondition(t, !testEnv.IsLocalDaemon()) + skip.IfCondition(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() d := swarm.NewSwarm(t, testEnv) defer d.Stop(t) diff --git a/components/engine/integration/system/event_test.go b/components/engine/integration/system/event_test.go index 7f041956904..688d7c27def 100644 --- a/components/engine/integration/system/event_test.go +++ b/components/engine/integration/system/event_test.go @@ -2,15 +2,22 @@ package system // import "github.com/docker/docker/integration/system" import ( "context" + "encoding/json" + "io" + "net/http" + "net/url" + "strconv" "testing" - "time" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/strslice" + req "github.com/docker/docker/integration-cli/request" "github.com/docker/docker/integration/internal/container" "github.com/docker/docker/integration/internal/request" + "github.com/docker/docker/pkg/jsonmessage" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -58,3 +65,55 @@ func TestEvents(t *testing.T) { } } + +// Test case for #18888: Events messages have been switched from generic +// `JSONMessage` to `events.Message` types. The switch does not break the +// backward compatibility so old `JSONMessage` could still be used. +// This test verifies that backward compatibility maintains. +func TestEventsBackwardsCompatible(t *testing.T) { + defer setupTest(t)() + ctx := context.Background() + client := request.NewAPIClient(t) + + since := request.DaemonTime(ctx, t, client, testEnv) + ts := strconv.FormatInt(since.Unix(), 10) + + cID := container.Create(t, ctx, client) + + // In case there is no events, the API should have responded immediately (not blocking), + // The test here makes sure the response time is less than 3 sec. + expectedTime := time.Now().Add(3 * time.Second) + emptyResp, emptyBody, err := req.Get("/events") + require.NoError(t, err) + defer emptyBody.Close() + assert.Equal(t, http.StatusOK, emptyResp.StatusCode) + assert.True(t, time.Now().Before(expectedTime), "timeout waiting for events api to respond, should have responded immediately") + + // We also test to make sure the `events.Message` is compatible with `JSONMessage` + q := url.Values{} + q.Set("since", ts) + _, body, err := req.Get("/events?" + q.Encode()) + require.NoError(t, err) + defer body.Close() + + dec := json.NewDecoder(body) + var containerCreateEvent *jsonmessage.JSONMessage + for { + var event jsonmessage.JSONMessage + if err := dec.Decode(&event); err != nil { + if err == io.EOF { + break + } + t.Fatal(err) + } + if event.Status == "create" && event.ID == cID { + containerCreateEvent = &event + break + } + } + + assert.NotNil(t, containerCreateEvent) + assert.Equal(t, "create", containerCreateEvent.Status) + assert.Equal(t, cID, containerCreateEvent.ID) + assert.Equal(t, "busybox", containerCreateEvent.From) +} diff --git a/components/engine/integration/volume/main_test.go b/components/engine/integration/volume/main_test.go new file mode 100644 index 00000000000..206f7377ae0 --- /dev/null +++ b/components/engine/integration/volume/main_test.go @@ -0,0 +1,33 @@ +package volume // import "github.com/docker/docker/integration/volume" + +import ( + "fmt" + "os" + "testing" + + "github.com/docker/docker/internal/test/environment" +) + +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) + } + err = environment.EnsureFrozenImagesLinux(testEnv) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + testEnv.Print() + os.Exit(m.Run()) +} + +func setupTest(t *testing.T) func() { + environment.ProtectAll(t, testEnv) + return func() { testEnv.Clean(t) } +} diff --git a/components/engine/integration/volume/volume_test.go b/components/engine/integration/volume/volume_test.go new file mode 100644 index 00000000000..20dcfef9a69 --- /dev/null +++ b/components/engine/integration/volume/volume_test.go @@ -0,0 +1,113 @@ +package volume + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + volumetypes "github.com/docker/docker/api/types/volume" + "github.com/docker/docker/integration/internal/container" + "github.com/docker/docker/integration/internal/request" + "github.com/docker/docker/internal/testutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVolumesCreateAndList(t *testing.T) { + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + name := t.Name() + vol, err := client.VolumeCreate(ctx, volumetypes.VolumesCreateBody{ + Name: name, + }) + require.NoError(t, err) + + expected := types.Volume{ + // Ignore timestamp of CreatedAt + CreatedAt: vol.CreatedAt, + Driver: "local", + Scope: "local", + Name: name, + Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name), + } + assert.Equal(t, vol, expected) + + volumes, err := client.VolumeList(ctx, filters.Args{}) + require.NoError(t, err) + + assert.Equal(t, len(volumes.Volumes), 1) + assert.NotNil(t, volumes.Volumes[0]) + assert.Equal(t, *volumes.Volumes[0], expected) +} + +func TestVolumesRemove(t *testing.T) { + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + prefix, _ := getPrefixAndSlashFromDaemonPlatform() + + id := container.Create(t, ctx, client, container.WithVolume(prefix+"foo")) + + c, err := client.ContainerInspect(ctx, id) + require.NoError(t, err) + vname := c.Mounts[0].Name + + err = client.VolumeRemove(ctx, vname, false) + testutil.ErrorContains(t, err, "volume is in use") + + err = client.ContainerRemove(ctx, id, types.ContainerRemoveOptions{ + Force: true, + }) + require.NoError(t, err) + + err = client.VolumeRemove(ctx, vname, false) + require.NoError(t, err) +} + +func TestVolumesInspect(t *testing.T) { + defer setupTest(t)() + client := request.NewAPIClient(t) + ctx := context.Background() + + // sampling current time minus a minute so to now have false positive in case of delays + now := time.Now().Truncate(time.Minute) + + name := t.Name() + _, err := client.VolumeCreate(ctx, volumetypes.VolumesCreateBody{ + Name: name, + }) + require.NoError(t, err) + + vol, err := client.VolumeInspect(ctx, name) + require.NoError(t, err) + + expected := types.Volume{ + // Ignore timestamp of CreatedAt + CreatedAt: vol.CreatedAt, + Driver: "local", + Scope: "local", + Name: name, + Mountpoint: fmt.Sprintf("%s/volumes/%s/_data", testEnv.DaemonInfo.DockerRootDir, name), + } + assert.Equal(t, vol, expected) + + // comparing CreatedAt field time for the new volume to now. Removing a minute from both to avoid false positive + testCreatedAt, err := time.Parse(time.RFC3339, strings.TrimSpace(vol.CreatedAt)) + require.NoError(t, err) + testCreatedAt = testCreatedAt.Truncate(time.Minute) + assert.Equal(t, testCreatedAt.Equal(now), true, "Time Volume is CreatedAt not equal to current time") +} + +func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) { + if testEnv.OSType == "windows" { + return "c:", `\` + } + return "", "/" +} diff --git a/components/engine/internal/test/environment/environment.go b/components/engine/internal/test/environment/environment.go index bde4c0a1bc6..16f61463346 100644 --- a/components/engine/internal/test/environment/environment.go +++ b/components/engine/internal/test/environment/environment.go @@ -96,7 +96,7 @@ func toSlash(path string) string { } // IsLocalDaemon is true if the daemon under test is on the same -// host as the CLI. +// host as the test process. // // Deterministically working out the environment in which CI is running // to evaluate whether the daemon is local or remote is not possible through @@ -115,6 +115,12 @@ func (e *Execution) IsLocalDaemon() bool { return os.Getenv("DOCKER_REMOTE_DAEMON") == "" } +// IsRemoteDaemon is true if the daemon under test is on different host +// as the test process. +func (e *Execution) IsRemoteDaemon() bool { + return !e.IsLocalDaemon() +} + // Print the execution details to stdout // TODO: print everything func (e *Execution) Print() { diff --git a/components/engine/pkg/plugins/client_test.go b/components/engine/pkg/plugins/client_test.go index 44dd0fa81c7..10c8d8fd567 100644 --- a/components/engine/pkg/plugins/client_test.go +++ b/components/engine/pkg/plugins/client_test.go @@ -2,6 +2,7 @@ package plugins // import "github.com/docker/docker/pkg/plugins" import ( "bytes" + "context" "encoding/json" "io" "net/http" @@ -13,7 +14,9 @@ import ( "github.com/docker/docker/pkg/plugins/transport" "github.com/docker/go-connections/tlsconfig" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var ( @@ -232,3 +235,43 @@ func TestClientSendFile(t *testing.T) { } assert.Equal(t, m, output) } + +func TestClientWithRequestTimeout(t *testing.T) { + timeout := 1 * time.Millisecond + testHandler := func(w http.ResponseWriter, r *http.Request) { + time.Sleep(timeout + 1*time.Millisecond) + w.WriteHeader(http.StatusOK) + } + + srv := httptest.NewServer(http.HandlerFunc(testHandler)) + defer srv.Close() + + client := &Client{http: srv.Client(), requestFactory: &testRequestWrapper{srv}} + _, err := client.callWithRetry("/Plugin.Hello", nil, false, WithRequestTimeout(timeout)) + require.Error(t, err, "expected error") + + err = errors.Cause(err) + + switch e := err.(type) { + case *url.Error: + err = e.Err + } + require.Equal(t, context.DeadlineExceeded, err) +} + +type testRequestWrapper struct { + *httptest.Server +} + +func (w *testRequestWrapper) NewRequest(path string, data io.Reader) (*http.Request, error) { + req, err := http.NewRequest("POST", path, data) + if err != nil { + return nil, err + } + u, err := url.Parse(w.Server.URL) + if err != nil { + return nil, err + } + req.URL = u + return req, nil +} diff --git a/components/engine/volume/store/db.go b/components/engine/volume/store/db.go index fe6dbae9afa..5a280ca2dca 100644 --- a/components/engine/volume/store/db.go +++ b/components/engine/volume/store/db.go @@ -4,6 +4,7 @@ import ( "encoding/json" "github.com/boltdb/bolt" + "github.com/docker/docker/errdefs" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -28,7 +29,10 @@ func setMeta(tx *bolt.Tx, name string, meta volumeMetadata) error { if err != nil { return err } - b := tx.Bucket(volumeBucketName) + b, err := tx.CreateBucketIfNotExists(volumeBucketName) + if err != nil { + return errors.Wrap(err, "error creating volume bucket") + } return errors.Wrap(b.Put([]byte(name), metaJSON), "error setting volume metadata") } @@ -42,8 +46,11 @@ func (s *VolumeStore) getMeta(name string) (volumeMetadata, error) { func getMeta(tx *bolt.Tx, name string, meta *volumeMetadata) error { b := tx.Bucket(volumeBucketName) + if b == nil { + return errdefs.NotFound(errors.New("volume bucket does not exist")) + } val := b.Get([]byte(name)) - if string(val) == "" { + if len(val) == 0 { return nil } if err := json.Unmarshal(val, meta); err != nil { diff --git a/components/engine/volume/store/db_test.go b/components/engine/volume/store/db_test.go new file mode 100644 index 00000000000..9c45cd13baf --- /dev/null +++ b/components/engine/volume/store/db_test.go @@ -0,0 +1,51 @@ +package store + +import ( + "io/ioutil" + "os" + "path/filepath" + "testing" + "time" + + "github.com/boltdb/bolt" + "github.com/stretchr/testify/require" +) + +func TestSetGetMeta(t *testing.T) { + t.Parallel() + + dir, err := ioutil.TempDir("", "test-set-get") + require.NoError(t, err) + defer os.RemoveAll(dir) + + db, err := bolt.Open(filepath.Join(dir, "db"), 0600, &bolt.Options{Timeout: 1 * time.Second}) + require.NoError(t, err) + + store := &VolumeStore{db: db} + + _, err = store.getMeta("test") + require.Error(t, err) + + err = db.Update(func(tx *bolt.Tx) error { + _, err := tx.CreateBucket(volumeBucketName) + return err + }) + require.NoError(t, err) + + meta, err := store.getMeta("test") + require.NoError(t, err) + require.Equal(t, volumeMetadata{}, meta) + + testMeta := volumeMetadata{ + Name: "test", + Driver: "fake", + Labels: map[string]string{"a": "1", "b": "2"}, + Options: map[string]string{"foo": "bar"}, + } + err = store.setMeta("test", testMeta) + require.NoError(t, err) + + meta, err = store.getMeta("test") + require.NoError(t, err) + require.Equal(t, testMeta, meta) +} diff --git a/components/engine/volume/store/restore_test.go b/components/engine/volume/store/restore_test.go new file mode 100644 index 00000000000..83efd36b63e --- /dev/null +++ b/components/engine/volume/store/restore_test.go @@ -0,0 +1,55 @@ +package store + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/docker/docker/volume" + volumedrivers "github.com/docker/docker/volume/drivers" + volumetestutils "github.com/docker/docker/volume/testutils" + "github.com/stretchr/testify/require" +) + +func TestRestore(t *testing.T) { + t.Parallel() + + dir, err := ioutil.TempDir("", "test-restore") + require.NoError(t, err) + defer os.RemoveAll(dir) + + driverName := "test-restore" + volumedrivers.Register(volumetestutils.NewFakeDriver(driverName), driverName) + defer volumedrivers.Unregister("test-restore") + + s, err := New(dir) + require.NoError(t, err) + defer s.Shutdown() + + _, err = s.Create("test1", driverName, nil, nil) + require.NoError(t, err) + + testLabels := map[string]string{"a": "1"} + testOpts := map[string]string{"foo": "bar"} + _, err = s.Create("test2", driverName, testOpts, testLabels) + require.NoError(t, err) + + s.Shutdown() + + s, err = New(dir) + require.NoError(t, err) + + v, err := s.Get("test1") + require.NoError(t, err) + + dv := v.(volume.DetailedVolume) + var nilMap map[string]string + require.Equal(t, nilMap, dv.Options()) + require.Equal(t, nilMap, dv.Labels()) + + v, err = s.Get("test2") + require.NoError(t, err) + dv = v.(volume.DetailedVolume) + require.Equal(t, testOpts, dv.Options()) + require.Equal(t, testLabels, dv.Labels()) +} diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index 643096a781a..70dd8b2d89a 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -29,7 +29,10 @@ type volumeWrapper struct { } func (v volumeWrapper) Options() map[string]string { - options := map[string]string{} + if v.options == nil { + return nil + } + options := make(map[string]string, len(v.options)) for key, value := range v.options { options[key] = value } @@ -37,7 +40,15 @@ func (v volumeWrapper) Options() map[string]string { } func (v volumeWrapper) Labels() map[string]string { - return v.labels + if v.labels == nil { + return nil + } + + labels := make(map[string]string, len(v.labels)) + for key, value := range v.labels { + labels[key] = value + } + return labels } func (v volumeWrapper) Scope() string { diff --git a/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index 54b7d7cfcef..e53c728dd0a 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/volume/store/store_test.go @@ -12,6 +12,8 @@ import ( "github.com/docker/docker/volume" "github.com/docker/docker/volume/drivers" volumetestutils "github.com/docker/docker/volume/testutils" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCreate(t *testing.T) { @@ -291,6 +293,7 @@ func TestDefererencePluginOnCreateError(t *testing.T) { pg := volumetestutils.NewFakePluginGetter(p) volumedrivers.RegisterPluginGetter(pg) + defer volumedrivers.RegisterPluginGetter(nil) dir, err := ioutil.TempDir("", "test-plugin-deref-err") if err != nil { @@ -320,3 +323,103 @@ func TestDefererencePluginOnCreateError(t *testing.T) { t.Fatalf("expected 1 plugin reference, got: %d", refs) } } + +func TestRefDerefRemove(t *testing.T) { + t.Parallel() + + driverName := "test-ref-deref-remove" + s, cleanup := setupTest(t, driverName) + defer cleanup(t) + + v, err := s.CreateWithRef("test", driverName, "test-ref", nil, nil) + require.NoError(t, err) + + err = s.Remove(v) + require.Error(t, err) + require.Equal(t, errVolumeInUse, err.(*OpErr).Err) + + s.Dereference(v, "test-ref") + err = s.Remove(v) + require.NoError(t, err) +} + +func TestGet(t *testing.T) { + t.Parallel() + + driverName := "test-get" + s, cleanup := setupTest(t, driverName) + defer cleanup(t) + + _, err := s.Get("not-exist") + require.Error(t, err) + require.Equal(t, errNoSuchVolume, err.(*OpErr).Err) + + v1, err := s.Create("test", driverName, nil, map[string]string{"a": "1"}) + require.NoError(t, err) + + v2, err := s.Get("test") + require.NoError(t, err) + require.Equal(t, v1, v2) + + dv := v2.(volume.DetailedVolume) + require.Equal(t, "1", dv.Labels()["a"]) + + err = s.Remove(v1) + require.NoError(t, err) +} + +func TestGetWithRef(t *testing.T) { + t.Parallel() + + driverName := "test-get-with-ref" + s, cleanup := setupTest(t, driverName) + defer cleanup(t) + + _, err := s.GetWithRef("not-exist", driverName, "test-ref") + require.Error(t, err) + + v1, err := s.Create("test", driverName, nil, map[string]string{"a": "1"}) + require.NoError(t, err) + + v2, err := s.GetWithRef("test", driverName, "test-ref") + require.NoError(t, err) + require.Equal(t, v1, v2) + + err = s.Remove(v2) + require.Error(t, err) + require.Equal(t, errVolumeInUse, err.(*OpErr).Err) + + s.Dereference(v2, "test-ref") + err = s.Remove(v2) + require.NoError(t, err) +} + +func setupTest(t *testing.T, name string) (*VolumeStore, func(*testing.T)) { + t.Helper() + s, cleanup := newTestStore(t) + + volumedrivers.Register(volumetestutils.NewFakeDriver(name), name) + return s, func(t *testing.T) { + cleanup(t) + volumedrivers.Unregister(name) + } +} + +func newTestStore(t *testing.T) (*VolumeStore, func(*testing.T)) { + t.Helper() + + dir, err := ioutil.TempDir("", "store-root") + require.NoError(t, err) + + cleanup := func(t *testing.T) { + err := os.RemoveAll(dir) + assert.NoError(t, err) + } + + s, err := New(dir) + assert.NoError(t, err) + return s, func(t *testing.T) { + s.Shutdown() + cleanup(t) + } +}