mirror of
https://github.com/docker/compose.git
synced 2026-10-12 02:13:08 +00:00
test(e2e): configs, commit, export and logs scenarios
ImageExists and FileExists join the vocabulary, giving commit and export a real observable: the legacy tests only asserted the commands exited 0, the scenarios verify the committed image and the exported archive actually exist. Both also drop their fixed image tags and cwd-relative tar outputs for project-scoped tags and temp files, so parallel runs cannot collide and nothing is left behind in the repo. TestLocalComposeLogs waits for the echo service to exit before reading logs (Eventually), closing the race the legacy test tolerated. The follow and large-logs tests stay legacy: both drive a long-running command in the background. ps_test.go stays legacy as well — it verifies ps table/JSON formatting, which is inherently output-shaped. Fixtures configs/, commit/ and export/ are removed. Signed-off-by: Nicolas De Loof <[email protected]>
This commit is contained in:
committed by
Nicolas De loof
parent
3923931b8c
commit
cd1c0ca6ca
@@ -24,6 +24,7 @@ package e2e
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -376,6 +377,39 @@ func OneOffsRemoved(service string) Check {
|
||||
}
|
||||
}
|
||||
|
||||
// ImageExists expects an image with the given reference to be present in the
|
||||
// local store.
|
||||
func ImageExists(ref string) Check {
|
||||
return Check{
|
||||
name: fmt.Sprintf("image %q exists", ref),
|
||||
fn: func(ctx *CheckContext) error {
|
||||
res := icmd.RunCmd(ctx.scenario.cli.NewDockerCmd(ctx.scenario.t, "image", "inspect", "--format", "{{.Id}}", ref))
|
||||
if res.ExitCode != 0 {
|
||||
return fmt.Errorf("not found: %s", strings.TrimSpace(res.Combined()))
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// FileExists expects a non-empty file at the given host path, e.g. the output
|
||||
// of an export command.
|
||||
func FileExists(path string) Check {
|
||||
return Check{
|
||||
name: fmt.Sprintf("file %q exists", path),
|
||||
fn: func(ctx *CheckContext) error {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if info.Size() == 0 {
|
||||
return fmt.Errorf("file is empty")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// LabelSet expects every container of the service to carry a non-empty label.
|
||||
func LabelSet(service, key string) Check {
|
||||
return Check{
|
||||
|
||||
+27
-66
@@ -21,73 +21,34 @@ import (
|
||||
)
|
||||
|
||||
func TestCommit(t *testing.T) {
|
||||
const projectName = "e2e-commit-service"
|
||||
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/commit/compose.yaml", "--project-name", projectName, "up", "-d", "service")
|
||||
|
||||
c.RunDockerComposeCmd(
|
||||
t,
|
||||
"--project-name",
|
||||
projectName,
|
||||
"commit",
|
||||
"-a",
|
||||
"John Hannibal Smith <[email protected]>",
|
||||
"-c",
|
||||
"ENV DEBUG=true",
|
||||
"-m",
|
||||
"sample commit",
|
||||
"service",
|
||||
"service:latest",
|
||||
)
|
||||
s := NewScenario(t, "commit must produce an image from the service's container")
|
||||
image := s.Project() + ":latest"
|
||||
s.Defer(DockerCmd("image", "rm", "-f", image).MayFail()).
|
||||
Step("up starts the service",
|
||||
ComposeCmd("up", "-d", "service"),
|
||||
ServiceState("service", "running")).
|
||||
Step("commit turns the container into a tagged image",
|
||||
ComposeCmd("commit",
|
||||
"-a", "John Hannibal Smith <[email protected]>",
|
||||
"-c", "ENV DEBUG=true",
|
||||
"-m", "sample commit",
|
||||
"service", image),
|
||||
ImageExists(image))
|
||||
}
|
||||
|
||||
func TestCommitWithReplicas(t *testing.T) {
|
||||
const projectName = "e2e-commit-service-with-replicas"
|
||||
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/commit/compose.yaml", "--project-name", projectName, "up", "-d", "service-with-replicas")
|
||||
|
||||
c.RunDockerComposeCmd(
|
||||
t,
|
||||
"--project-name",
|
||||
projectName,
|
||||
"commit",
|
||||
"-a",
|
||||
"John Hannibal Smith <[email protected]>",
|
||||
"-c",
|
||||
"ENV DEBUG=true",
|
||||
"-m",
|
||||
"sample commit",
|
||||
"--index=1",
|
||||
"service-with-replicas",
|
||||
"service-with-replicas:1",
|
||||
)
|
||||
c.RunDockerComposeCmd(
|
||||
t,
|
||||
"--project-name",
|
||||
projectName,
|
||||
"commit",
|
||||
"-a",
|
||||
"John Hannibal Smith <[email protected]>",
|
||||
"-c",
|
||||
"ENV DEBUG=true",
|
||||
"-m",
|
||||
"sample commit",
|
||||
"--index=2",
|
||||
"service-with-replicas",
|
||||
"service-with-replicas:2",
|
||||
)
|
||||
s := NewScenario(t, "commit --index must pick the requested replica of a scaled service")
|
||||
image1, image2 := s.Project()+":1", s.Project()+":2"
|
||||
s.Defer(
|
||||
DockerCmd("image", "rm", "-f", image1).MayFail(),
|
||||
DockerCmd("image", "rm", "-f", image2).MayFail()).
|
||||
Step("up starts the replicas",
|
||||
ComposeCmd("up", "-d", "service-with-replicas"),
|
||||
ServiceScale("service-with-replicas", 3)).
|
||||
Step("commit --index=1 images the first replica",
|
||||
ComposeCmd("commit", "-m", "sample commit", "--index=1", "service-with-replicas", image1),
|
||||
ImageExists(image1)).
|
||||
Step("commit --index=2 images the second replica",
|
||||
ComposeCmd("commit", "-m", "sample commit", "--index=2", "service-with-replicas", image2),
|
||||
ImageExists(image2))
|
||||
}
|
||||
|
||||
+14
-33
@@ -18,40 +18,21 @@ package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gotest.tools/v3/icmd"
|
||||
)
|
||||
|
||||
func TestConfigFromEnv(t *testing.T) {
|
||||
c := NewParallelCLI(t)
|
||||
defer c.cleanupWithDown(t, "configs")
|
||||
|
||||
t.Run("config from file", func(t *testing.T) {
|
||||
res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/configs/compose.yaml", "run", "from_file"))
|
||||
res.Assert(t, icmd.Expected{Out: "This is my config file"})
|
||||
})
|
||||
|
||||
t.Run("config from env", func(t *testing.T) {
|
||||
res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/configs/compose.yaml", "run", "from_env"),
|
||||
func(cmd *icmd.Cmd) {
|
||||
cmd.Env = append(cmd.Env, "CONFIG=config")
|
||||
})
|
||||
res.Assert(t, icmd.Expected{Out: "config"})
|
||||
})
|
||||
|
||||
t.Run("config inlined", func(t *testing.T) {
|
||||
res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/configs/compose.yaml", "run", "inlined"),
|
||||
func(cmd *icmd.Cmd) {
|
||||
cmd.Env = append(cmd.Env, "CONFIG=config")
|
||||
})
|
||||
res.Assert(t, icmd.Expected{Out: "This is my config"})
|
||||
})
|
||||
|
||||
t.Run("custom target", func(t *testing.T) {
|
||||
res := icmd.RunCmd(c.NewDockerComposeCmd(t, "-f", "./fixtures/configs/compose.yaml", "run", "target"),
|
||||
func(cmd *icmd.Cmd) {
|
||||
cmd.Env = append(cmd.Env, "CONFIG=config")
|
||||
})
|
||||
res.Assert(t, icmd.Expected{Out: "This is my config"})
|
||||
})
|
||||
NewScenario(t, "each config source — file, environment, inline content — must mount with the right content").
|
||||
Env("CONFIG=config").
|
||||
Step("a file-sourced config mounts its file's content",
|
||||
ComposeCmd("run", "from_file"),
|
||||
OutputContains("This is my config file")).
|
||||
Step("an environment-sourced config mounts the variable's value",
|
||||
ComposeCmd("run", "from_env"),
|
||||
OutputContains("config")).
|
||||
Step("an inline config mounts its interpolated content",
|
||||
ComposeCmd("run", "inlined"),
|
||||
OutputContains("This is my config")).
|
||||
Step("a custom target mounts the config at the requested path",
|
||||
ComposeCmd("run", "target"),
|
||||
OutputContains("This is my config"))
|
||||
}
|
||||
|
||||
+21
-23
@@ -17,34 +17,32 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExport(t *testing.T) {
|
||||
const projectName = "e2e-export-service"
|
||||
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/export/compose.yaml", "--project-name", projectName, "up", "-d", "service")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "export", "-o", "service.tar", "service")
|
||||
out := filepath.Join(t.TempDir(), "service.tar")
|
||||
NewScenario(t, "export must write the service container's filesystem as a tar archive").
|
||||
Step("up starts the service",
|
||||
ComposeCmd("up", "-d", "service"),
|
||||
ServiceState("service", "running")).
|
||||
Step("export writes the container filesystem to the output file",
|
||||
ComposeCmd("export", "-o", out, "service"),
|
||||
FileExists(out))
|
||||
}
|
||||
|
||||
func TestExportWithReplicas(t *testing.T) {
|
||||
const projectName = "e2e-export-service-with-replicas"
|
||||
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/export/compose.yaml", "--project-name", projectName, "up", "-d", "service-with-replicas")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "export", "-o", "r1.tar", "--index=1", "service-with-replicas")
|
||||
c.RunDockerComposeCmd(t, "--project-name", projectName, "export", "-o", "r2.tar", "--index=2", "service-with-replicas")
|
||||
dir := t.TempDir()
|
||||
r1, r2 := filepath.Join(dir, "r1.tar"), filepath.Join(dir, "r2.tar")
|
||||
NewScenario(t, "export --index must pick the requested replica of a scaled service").
|
||||
Step("up starts the replicas",
|
||||
ComposeCmd("up", "-d", "service-with-replicas"),
|
||||
ServiceScale("service-with-replicas", 3)).
|
||||
Step("export --index=1 writes the first replica's filesystem",
|
||||
ComposeCmd("export", "-o", r1, "--index=1", "service-with-replicas"),
|
||||
FileExists(r1)).
|
||||
Step("export --index=2 writes the second replica's filesystem",
|
||||
ComposeCmd("export", "-o", r2, "--index=2", "service-with-replicas"),
|
||||
FileExists(r2))
|
||||
}
|
||||
|
||||
+20
-40
@@ -31,46 +31,26 @@ import (
|
||||
)
|
||||
|
||||
func TestLocalComposeLogs(t *testing.T) {
|
||||
c := NewParallelCLI(t)
|
||||
|
||||
const projectName = "compose-e2e-logs"
|
||||
|
||||
t.Run("up", func(t *testing.T) {
|
||||
c.RunDockerComposeCmd(t, "-f", "./fixtures/logs-test/compose.yaml", "--project-name", projectName, "up", "-d")
|
||||
})
|
||||
|
||||
t.Run("logs", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs")
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost`})
|
||||
res.Assert(t, icmd.Expected{Out: `hello`})
|
||||
})
|
||||
|
||||
t.Run("logs ping", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs", "ping")
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost`})
|
||||
assert.Assert(t, !strings.Contains(res.Stdout(), "hello"))
|
||||
})
|
||||
|
||||
t.Run("logs hello", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs", "hello", "ping")
|
||||
res.Assert(t, icmd.Expected{Out: `PING localhost`})
|
||||
res.Assert(t, icmd.Expected{Out: `hello`})
|
||||
})
|
||||
|
||||
t.Run("logs hello index", func(t *testing.T) {
|
||||
res := c.RunDockerComposeCmd(t, "--project-name", projectName, "logs", "--index", "2", "hello")
|
||||
|
||||
// docker-compose logs hello
|
||||
// logs-test-hello-2 | hello
|
||||
// logs-test-hello-1 | hello
|
||||
t.Log(res.Stdout())
|
||||
assert.Assert(t, !strings.Contains(res.Stdout(), "hello-1"))
|
||||
assert.Assert(t, strings.Contains(res.Stdout(), "hello-2"))
|
||||
})
|
||||
|
||||
t.Run("down", func(t *testing.T) {
|
||||
_ = c.RunDockerComposeCmd(t, "--project-name", projectName, "down")
|
||||
})
|
||||
NewScenario(t, "logs must aggregate the requested services' output, and --index must select one replica").
|
||||
Step("up starts the services",
|
||||
ComposeCmd("up", "-d"),
|
||||
Eventually(ServiceState("hello", "exited"), 10*time.Second)).
|
||||
Step("logs aggregates every service's output",
|
||||
ComposeCmd("logs"),
|
||||
OutputContains("PING localhost"),
|
||||
OutputContains("hello")).
|
||||
Step("logs on one service filters the others out",
|
||||
ComposeCmd("logs", "ping"),
|
||||
OutputContains("PING localhost"),
|
||||
OutputNotContains("hello")).
|
||||
Step("logs accepts several services",
|
||||
ComposeCmd("logs", "hello", "ping"),
|
||||
OutputContains("PING localhost"),
|
||||
OutputContains("hello")).
|
||||
Step("logs --index selects a single replica",
|
||||
ComposeCmd("logs", "--index", "2", "hello"),
|
||||
OutputContains("hello-2"),
|
||||
OutputNotContains("hello-1"))
|
||||
}
|
||||
|
||||
func TestLocalComposeLogsFollow(t *testing.T) {
|
||||
|
||||
+1
-1
@@ -6,4 +6,4 @@ services:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
replicas: 3
|
||||
replicas: 3
|
||||
Vendored
+1
-1
@@ -6,4 +6,4 @@ services:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
replicas: 3
|
||||
replicas: 3
|
||||
+1
@@ -1 +1,2 @@
|
||||
This is my config file
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
services:
|
||||
service:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
service-with-replicas:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
replicas: 3
|
||||
@@ -0,0 +1,9 @@
|
||||
services:
|
||||
service:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
service-with-replicas:
|
||||
image: alpine
|
||||
command: sleep infinity
|
||||
deploy:
|
||||
replicas: 3
|
||||
@@ -0,0 +1,10 @@
|
||||
services:
|
||||
ping:
|
||||
image: alpine
|
||||
init: true
|
||||
command: ping localhost -c 1
|
||||
hello:
|
||||
image: alpine
|
||||
command: echo hello
|
||||
deploy:
|
||||
replicas: 2
|
||||
Reference in New Issue
Block a user