From 26014d49a5a275459e23b2e74ebb6019efe5a846 Mon Sep 17 00:00:00 2001 From: Milas Bowman Date: Tue, 28 Jun 2022 12:28:37 -0400 Subject: [PATCH] e2e: split out `pause` tests and add more cases Pause/unpause was being partially tested under the start/stop test. This removes it from that test and adds dedicated pause + unpause tests. Note that the tests assert on current behavior, though it's been noted where that is undesirable due to divergence from the Docker CLI. Will change the behavior + update tests in a subsequent PR. Signed-off-by: Milas Bowman --- pkg/e2e/fixtures/pause/compose.yaml | 9 ++ pkg/e2e/pause_test.go | 155 ++++++++++++++++++++++++++++ pkg/e2e/start_stop_test.go | 14 --- 3 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 pkg/e2e/fixtures/pause/compose.yaml create mode 100644 pkg/e2e/pause_test.go diff --git a/pkg/e2e/fixtures/pause/compose.yaml b/pkg/e2e/fixtures/pause/compose.yaml new file mode 100644 index 000000000..f41461fdc --- /dev/null +++ b/pkg/e2e/fixtures/pause/compose.yaml @@ -0,0 +1,9 @@ +services: + a: + image: nginx:alpine + ports: [80] + b: + image: nginx:alpine + ports: [80] + depends_on: + - a diff --git a/pkg/e2e/pause_test.go b/pkg/e2e/pause_test.go new file mode 100644 index 000000000..a91c7a202 --- /dev/null +++ b/pkg/e2e/pause_test.go @@ -0,0 +1,155 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package e2e + +import ( + "encoding/json" + "fmt" + "net" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/require" + "gotest.tools/v3/icmd" +) + +func TestPause(t *testing.T) { + cli := NewParallelCLI(t, WithEnv( + "COMPOSE_PROJECT_NAME=e2e-pause", + "COMPOSE_FILE=./fixtures/pause/compose.yaml")) + + cleanup := func() { + cli.RunDockerComposeCmd(t, "down", "-v", "--remove-orphans", "-t", "0") + } + cleanup() + t.Cleanup(cleanup) + + // launch both services and verify that they are accessible + cli.RunDockerComposeCmd(t, "up", "-d") + urls := map[string]string{ + "a": urlForService(t, cli, "a", 80), + "b": urlForService(t, cli, "b", 80), + } + for _, url := range urls { + HTTPGetWithRetry(t, url, http.StatusOK, 50*time.Millisecond, 5*time.Second) + } + + // pause a and verify that it can no longer be hit but b still can + cli.RunDockerComposeCmd(t, "pause", "a") + httpClient := http.Client{Timeout: 250 * time.Millisecond} + resp, err := httpClient.Get(urls["a"]) + if resp != nil { + _ = resp.Body.Close() + } + require.Error(t, err, "a should no longer respond") + require.True(t, err.(net.Error).Timeout(), "Error should have indicated a timeout") + HTTPGetWithRetry(t, urls["b"], http.StatusOK, 50*time.Millisecond, 5*time.Second) + + // unpause a and verify that both containers work again + cli.RunDockerComposeCmd(t, "unpause", "a") + for _, url := range urls { + HTTPGetWithRetry(t, url, http.StatusOK, 50*time.Millisecond, 5*time.Second) + } +} + +func TestPauseServiceNotRunning(t *testing.T) { + cli := NewParallelCLI(t, WithEnv( + "COMPOSE_PROJECT_NAME=e2e-pause-svc-not-running", + "COMPOSE_FILE=./fixtures/pause/compose.yaml")) + + cleanup := func() { + cli.RunDockerComposeCmd(t, "down", "-v", "--remove-orphans", "-t", "0") + } + cleanup() + t.Cleanup(cleanup) + + // pause a and verify that it can no longer be hit but b still can + res := cli.RunDockerComposeCmdNoCheck(t, "pause", "a") + + // TODO: `docker pause` errors in this case, should Compose be consistent? + res.Assert(t, icmd.Expected{ExitCode: 0}) +} + +func TestPauseServiceAlreadyPaused(t *testing.T) { + cli := NewParallelCLI(t, WithEnv( + "COMPOSE_PROJECT_NAME=e2e-pause-svc-already-paused", + "COMPOSE_FILE=./fixtures/pause/compose.yaml")) + + cleanup := func() { + cli.RunDockerComposeCmd(t, "down", "-v", "--remove-orphans", "-t", "0") + } + cleanup() + t.Cleanup(cleanup) + + // launch a and wait for it to come up + cli.RunDockerComposeCmd(t, "up", "-d", "a") + HTTPGetWithRetry(t, urlForService(t, cli, "a", 80), http.StatusOK, 50*time.Millisecond, 5*time.Second) + + // pause a twice - first time should pass, second time fail + cli.RunDockerComposeCmd(t, "pause", "a") + res := cli.RunDockerComposeCmdNoCheck(t, "pause", "a") + res.Assert(t, icmd.Expected{ExitCode: 1, Err: "already paused"}) +} + +func TestPauseServiceDoesNotExist(t *testing.T) { + cli := NewParallelCLI(t, WithEnv( + "COMPOSE_PROJECT_NAME=e2e-pause-svc-not-exist", + "COMPOSE_FILE=./fixtures/pause/compose.yaml")) + + cleanup := func() { + cli.RunDockerComposeCmd(t, "down", "-v", "--remove-orphans", "-t", "0") + } + cleanup() + t.Cleanup(cleanup) + + // pause a and verify that it can no longer be hit but b still can + res := cli.RunDockerComposeCmdNoCheck(t, "pause", "does_not_exist") + // TODO: `compose down does_not_exist` and similar error, this should too + res.Assert(t, icmd.Expected{ExitCode: 0}) +} + +func urlForService(t testing.TB, cli *CLI, service string, targetPort int) string { + t.Helper() + return fmt.Sprintf( + "http://localhost:%d", + publishedPortForService(t, cli, service, targetPort), + ) +} + +func publishedPortForService(t testing.TB, cli *CLI, service string, targetPort int) int { + t.Helper() + res := cli.RunDockerComposeCmd(t, "ps", "--format=json", service) + var psOut []struct { + Publishers []struct { + TargetPort int + PublishedPort int + } + } + require.NoError(t, json.Unmarshal([]byte(res.Stdout()), &psOut), + "Failed to parse `%s` output", res.Cmd.String()) + require.Len(t, psOut, 1, "Expected exactly 1 service") + svc := psOut[0] + for _, pp := range svc.Publishers { + if pp.TargetPort == targetPort { + return pp.PublishedPort + } + } + require.Failf(t, "No published port for target port", + "Target port: %d\nService: %s", targetPort, res.Combined()) + return -1 +} diff --git a/pkg/e2e/start_stop_test.go b/pkg/e2e/start_stop_test.go index 5191cc721..bcc13bd85 100644 --- a/pkg/e2e/start_stop_test.go +++ b/pkg/e2e/start_stop_test.go @@ -78,20 +78,6 @@ func TestStartStop(t *testing.T) { testify.Regexp(t, getProjectRegx("running"), res.Stdout()) }) - t.Run("pause project", func(t *testing.T) { - c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "pause") - - res := c.RunDockerComposeCmd(t, "ls", "--all") - testify.Regexp(t, getProjectRegx("paused"), res.Stdout()) - }) - - t.Run("unpause project", func(t *testing.T) { - c.RunDockerComposeCmd(t, "-f", "./fixtures/start-stop/compose.yaml", "--project-name", projectName, "unpause") - - res := c.RunDockerComposeCmd(t, "ls") - testify.Regexp(t, getProjectRegx("running"), res.Stdout()) - }) - t.Run("down", func(t *testing.T) { _ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down") })