diff --git a/pkg/e2e/container_name_test.go b/pkg/e2e/container_name_test.go index 99de738c2..453d34e79 100644 --- a/pkg/e2e/container_name_test.go +++ b/pkg/e2e/container_name_test.go @@ -20,24 +20,37 @@ package e2e import ( "testing" - - "gotest.tools/v3/icmd" ) func TestUpContainerNameConflict(t *testing.T) { - c := NewParallelCLI(t) - const projectName = "e2e-container_name_conflict" + s := NewScenario(t, "two services claiming the same container_name must be rejected together, but each must run alone") + name := s.Project() + "-fixed" + s.Compose(` +services: + test: + image: alpine + container_name: ${FIXED_NAME} + command: /bin/true - t.Cleanup(func() { - c.RunDockerComposeCmd(t, "--project-name", projectName, "down") - }) - - res := c.RunDockerComposeCmdNoCheck(t, "-f", "fixtures/container_name/compose.yaml", "--project-name", projectName, "up") - res.Assert(t, icmd.Expected{ExitCode: 1, Err: `container name "test" is already in use`}) - - c.RunDockerComposeCmd(t, "--project-name", projectName, "down") - c.RunDockerComposeCmd(t, "-f", "fixtures/container_name/compose.yaml", "--project-name", projectName, "up", "test") - - c.RunDockerComposeCmd(t, "--project-name", projectName, "down") - c.RunDockerComposeCmd(t, "-f", "fixtures/container_name/compose.yaml", "--project-name", projectName, "up", "another_test") + another_test: + image: alpine + container_name: ${FIXED_NAME} + command: /bin/true +`). + Env("FIXED_NAME="+name). + Step("up with both services is rejected over the name conflict", + ComposeCmd("up").MayFail(), + ExitCode(1), + OutputContains(`container name "`+name+`" is already in use`)). + Step("the failed up leaves nothing behind", + ComposeCmd("down")). + Step("the first service runs alone under the shared name", + ComposeCmd("up", "test"), + ServiceState("test", "exited")). + Step("down frees the name", + ComposeCmd("down"), + ServiceNotCreated("test")). + Step("the other service runs alone under the shared name", + ComposeCmd("up", "another_test"), + ServiceState("another_test", "exited")) } diff --git a/pkg/e2e/expose_test.go b/pkg/e2e/expose_test.go index 438f7b549..abac545e8 100644 --- a/pkg/e2e/expose_test.go +++ b/pkg/e2e/expose_test.go @@ -19,30 +19,20 @@ package e2e import ( - "os" - "path/filepath" "testing" - - "gotest.tools/v3/assert" ) -// see https://github.com/docker/compose/issues/13378 func TestExposeRange(t *testing.T) { - c := NewParallelCLI(t) - - f := filepath.Join(t.TempDir(), "compose.yaml") - err := os.WriteFile(f, []byte(` -name: test-expose-range + // Regression test for https://github.com/docker/compose/issues/13378 + // a port range in expose used to fail container creation. + NewScenario(t, "a port range in expose must be accepted"). + Compose(` services: test: image: alpine expose: - "9091-9092" -`), 0o644) - assert.NilError(t, err) - - t.Cleanup(func() { - c.cleanupWithDown(t, "test-expose-range") - }) - c.RunDockerComposeCmd(t, "-f", f, "up") +`). + Step("up creates and runs the service", + ComposeCmd("up")) } diff --git a/pkg/e2e/fixtures/container_name/compose.yaml b/pkg/e2e/fixtures/container_name/compose.yaml deleted file mode 100644 index c967b885d..000000000 --- a/pkg/e2e/fixtures/container_name/compose.yaml +++ /dev/null @@ -1,10 +0,0 @@ -services: - test: - image: alpine - container_name: test - command: /bin/true - - another_test: - image: alpine - container_name: test - command: /bin/true diff --git a/pkg/e2e/fixtures/ipc-test/compose.yaml b/pkg/e2e/fixtures/ipc-test/compose.yaml deleted file mode 100644 index 0659dd96a..000000000 --- a/pkg/e2e/fixtures/ipc-test/compose.yaml +++ /dev/null @@ -1,13 +0,0 @@ -services: - service: - image: alpine - command: top - ipc: "service:shareable" - container: - image: alpine - command: top - ipc: "container:ipc_mode_container" - shareable: - image: alpine - command: top - ipc: shareable diff --git a/pkg/e2e/ipc_test.go b/pkg/e2e/ipc_test.go index 7a46192a6..2267435a6 100644 --- a/pkg/e2e/ipc_test.go +++ b/pkg/e2e/ipc_test.go @@ -17,48 +17,47 @@ package e2e import ( - "fmt" "strings" "testing" - - "gotest.tools/v3/icmd" ) func TestIPC(t *testing.T) { - c := NewParallelCLI(t) + s := NewScenario(t, "every ipc mode must materialize in the created containers: shareable, service: and container:") + external := s.Project() + "-external" - const projectName = "ipc_e2e" - var cid string - t.Run("create ipc mode container", func(t *testing.T) { - res := c.RunDockerCmd(t, "run", "-d", "--rm", "--ipc=shareable", "--name", "ipc_mode_container", "alpine", - "top") - cid = strings.Trim(res.Stdout(), "\n") - }) + // the container: mode references a container compose doesn't manage; + // create it up front so its id is known when the steps assert against it + cid := strings.TrimSpace(s.CLI().RunDockerCmd(t, "run", "-d", "--rm", "--ipc=shareable", "--name", external, "alpine", "top").Stdout()) + s.Defer(DockerCmd("rm", "-f", external).MayFail()) - t.Run("up", func(t *testing.T) { - c.RunDockerComposeCmd(t, "-f", "./fixtures/ipc-test/compose.yaml", "--project-name", projectName, "up", "-d") - }) - - t.Run("check running project", func(t *testing.T) { - res := c.RunDockerComposeCmd(t, "-p", projectName, "ps") - res.Assert(t, icmd.Expected{Out: `shareable`}) - }) - - t.Run("check ipcmode in container inspect", func(t *testing.T) { - res := c.RunDockerCmd(t, "inspect", projectName+"-shareable-1") - res.Assert(t, icmd.Expected{Out: `"IpcMode": "shareable",`}) - - res = c.RunDockerCmd(t, "inspect", projectName+"-service-1") - res.Assert(t, icmd.Expected{Out: `"IpcMode": "container:`}) - - res = c.RunDockerCmd(t, "inspect", projectName+"-container-1") - res.Assert(t, icmd.Expected{Out: fmt.Sprintf(`"IpcMode": "container:%s",`, cid)}) - }) - - t.Run("down", func(t *testing.T) { - _ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down") - }) - t.Run("remove ipc mode container", func(t *testing.T) { - _ = c.RunDockerCmd(t, "rm", "-f", "ipc_mode_container") - }) + s.Compose(` +services: + service: + image: alpine + command: top + ipc: "service:shareable" + container: + image: alpine + command: top + ipc: "container:${EXTERNAL_NAME}" + shareable: + image: alpine + command: top + ipc: shareable +`). + Env("EXTERNAL_NAME="+external). + Step("up starts the three services", + ComposeCmd("up", "-d"), + ServiceState("service", "running"), + ServiceState("container", "running"), + ServiceState("shareable", "running")). + Step("the shareable service owns a shareable ipc namespace", + DockerCmd("inspect", "-f", "{{.HostConfig.IpcMode}}", s.Project()+"-shareable-1"), + OutputContains("shareable")). + Step("the service: mode resolves to the target service's container", + DockerCmd("inspect", "-f", "{{.HostConfig.IpcMode}}", s.Project()+"-service-1"), + OutputContains("container:")). + Step("the container: mode resolves to the external container", + DockerCmd("inspect", "-f", "{{.HostConfig.IpcMode}}", s.Project()+"-container-1"), + OutputContains("container:"+cid)) } diff --git a/pkg/e2e/wait_test.go b/pkg/e2e/wait_test.go index 171027fd9..08527bd82 100644 --- a/pkg/e2e/wait_test.go +++ b/pkg/e2e/wait_test.go @@ -17,7 +17,6 @@ package e2e import ( - "strings" "testing" "time" @@ -25,32 +24,40 @@ import ( "gotest.tools/v3/icmd" ) +const waitCompose = ` +services: + faster: + image: alpine + command: sleep 2 + slower: + image: alpine + command: sleep 5 + infinity: + image: alpine + command: sleep infinity +` + func TestWaitOnFaster(t *testing.T) { - const projectName = "e2e-wait-faster" - c := NewParallelCLI(t) - - cleanup := func() { - c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans") - } - t.Cleanup(cleanup) - cleanup() - - c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d") - c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "faster") + NewScenario(t, "wait must return once the selected service exits, whatever the others do"). + Compose(waitCompose). + Step("up starts all services", + ComposeCmd("up", "-d")). + Step("wait returns when the fastest service exits", + ComposeCmd("wait", "faster"), + ServiceState("faster", "exited"), + ServiceState("infinity", "running")) } func TestWaitOnSlower(t *testing.T) { - const projectName = "e2e-wait-slower" - c := NewParallelCLI(t) - - cleanup := func() { - c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans") - } - t.Cleanup(cleanup) - cleanup() - - c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d") - c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "slower") + NewScenario(t, "wait must block until the selected service exits, even if others exited first"). + Compose(waitCompose). + Step("up starts all services", + ComposeCmd("up", "-d")). + Step("wait returns when the slower service exits", + ComposeCmd("wait", "slower"), + ServiceState("faster", "exited"), + ServiceState("slower", "exited"), + ServiceState("infinity", "running")) } func TestWaitOnInfinity(t *testing.T) { @@ -89,18 +96,13 @@ func TestWaitOnInfinity(t *testing.T) { } func TestWaitAndDrop(t *testing.T) { - const projectName = "e2e-wait-and-drop" - c := NewParallelCLI(t) - - cleanup := func() { - c.RunDockerComposeCmd(t, "--project-name", projectName, "down", "--timeout=0", "--remove-orphans") - } - t.Cleanup(cleanup) - cleanup() - - c.RunDockerComposeCmd(t, "-f", "./fixtures/wait/compose.yaml", "--project-name", projectName, "up", "-d") - c.RunDockerComposeCmd(t, "--project-name", projectName, "wait", "--down-project", "faster") - - res := c.RunDockerCmd(t, "ps", "--all") - assert.Assert(t, !strings.Contains(res.Combined(), projectName), res.Combined()) + NewScenario(t, "wait --down-project must take the whole project down once the selected service exits"). + Compose(waitCompose). + Step("up starts all services", + ComposeCmd("up", "-d")). + Step("wait --down-project removes every container when the service exits", + ComposeCmd("wait", "--down-project", "faster"), + ServiceNotCreated("faster"), + ServiceNotCreated("slower"), + ServiceNotCreated("infinity")) }