diff --git a/components/engine/integration-cli/check_test.go b/components/engine/integration-cli/check_test.go index dd802fbc113..a9f94fb5762 100644 --- a/components/engine/integration-cli/check_test.go +++ b/components/engine/integration-cli/check_test.go @@ -85,7 +85,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_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_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 index d281704a7b3..5942b8286d9 100644 --- a/components/engine/integration-cli/docker_cli_rm_test.go +++ b/components/engine/integration-cli/docker_cli_rm_test.go @@ -1,56 +1,11 @@ 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"]` @@ -75,13 +30,3 @@ func (s *DockerSuite) TestRmContainerOrphaning(c *check.C) { 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/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..df602369983 100644 --- a/components/engine/integration/container/diff_test.go +++ b/components/engine/integration/container/diff_test.go @@ -29,7 +29,7 @@ func TestDiffFilenameShownInOutput(t *testing.T) { // 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)) + poll.WaitOn(t, container.IsInState(ctx, client, cID, "exited"), poll.WithDelay(100*time.Millisecond), poll.WithTimeout(60*time.Second)) lookingFor = containertypes.ContainerChangeResponseItem{Kind: archive.ChangeModify, Path: "Files/foo/bar"} } 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..2eace554e4c 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) diff --git a/components/engine/integration/container/kill_test.go b/components/engine/integration/container/kill_test.go index d399213ac62..09e37ac0d19 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) { @@ -160,7 +160,7 @@ func TestInspectOomKilledTrue(t *testing.T) { 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) @@ -177,7 +177,7 @@ func TestInspectOomKilledFalse(t *testing.T) { name := "testoomkilled" cID := container.Run(t, ctx, client, container.WithName(name), 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) diff --git a/components/engine/integration/container/links_linux_test.go b/components/engine/integration/container/links_linux_test.go index e2e0b9d9eaa..d230898ed0e 100644 --- a/components/engine/integration/container/links_linux_test.go +++ b/components/engine/integration/container/links_linux_test.go @@ -20,7 +20,7 @@ import ( ) 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)) @@ -31,7 +31,7 @@ func TestLinksEtcHostsContentMatch(t *testing.T) { 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)) + poll.WaitOn(t, container.IsStopped(ctx, client, cID), poll.WithDelay(100*time.Millisecond)) body, err := client.ContainerLogs(ctx, cID, types.ContainerLogsOptions{ ShowStdout: true, 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..4400d18fecd 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)() @@ -40,7 +40,7 @@ func TestNetworkNat(t *testing.T) { } func TestNetworkLocalhostTCPNat(t *testing.T) { - skip.If(t, !testEnv.IsLocalDaemon()) + skip.If(t, testEnv.IsRemoteDaemon()) defer setupTest(t)() @@ -57,7 +57,7 @@ func TestNetworkLocalhostTCPNat(t *testing.T) { } 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, @@ -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 } diff --git a/components/engine/integration/container/pause_test.go b/components/engine/integration/container/pause_test.go index 4e643572213..7c2aa96c754 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) - err = client.ContainerUnpause(ctx, name) + err = client.ContainerUnpause(ctx, cID) require.NoError(t, err) until := request.DaemonUnixTime(ctx, t, client, testEnv) @@ -46,7 +45,7 @@ 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"}) } @@ -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 { 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..22fa26af67c --- /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, len(insp.Mounts), 1) + 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, len(volumes.Volumes), 0) +} + +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..0a5f26067e8 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,7 +39,7 @@ 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) @@ -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/internal/container/ops.go b/components/engine/integration/internal/container/ops.go index e3d538bef16..9360527d370 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" @@ -57,3 +59,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/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/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..38ce5782e1d --- /dev/null +++ b/components/engine/integration/volume/volume_test.go @@ -0,0 +1,115 @@ +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, + Options: map[string]string{}, + 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, + Options: map[string]string{}, + 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() {