Skip to content

Commit 78d3c05

Browse files
committed
fix: make e2e tests pass reliably locally with Docker Desktop
- Fix stale image/container reuse across test runs - Add registry readiness check and async removal polling - Skip multi-arch test when docker driver supports it - Use t.Cleanup for reliable teardown, fix project name mismatches - Re-enable 4 previously skipped tests that now pass Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
1 parent 977a431 commit 78d3c05

12 files changed

Lines changed: 80 additions & 33 deletions

pkg/e2e/build_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,25 @@ func TestBuildPlatformsStandardErrors(t *testing.T) {
410410
})
411411

412412
t.Run("builder does not support multi-arch", func(t *testing.T) {
413+
// Docker Desktop with containerd image store uses the docker driver
414+
// but supports multi-platform builds, so this error won't occur.
415+
inspect := c.RunDockerCmd(t, "buildx", "inspect", "--bootstrap")
416+
output := inspect.Stdout()
417+
isDockerDriver := false
418+
platforms := ""
419+
for _, line := range strings.Split(output, "\n") {
420+
trimmed := strings.TrimSpace(line)
421+
if strings.HasPrefix(trimmed, "Driver:") {
422+
isDockerDriver = strings.TrimSpace(strings.TrimPrefix(trimmed, "Driver:")) == "docker"
423+
}
424+
if strings.HasPrefix(trimmed, "Platforms:") {
425+
platforms = trimmed
426+
}
427+
}
428+
if isDockerDriver && strings.Contains(platforms, "linux/amd64") && strings.Contains(platforms, "linux/arm64") {
429+
t.Skip("docker driver supports multi-platform (containerd image store enabled)")
430+
}
431+
413432
res := c.RunDockerComposeCmdNoCheck(t, "--project-directory", "fixtures/build-test/platforms", "build")
414433
res.Assert(t, icmd.Expected{
415434
ExitCode: 1,

pkg/e2e/compose_run_build_once_test.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,23 +36,27 @@ func TestRunBuildOnce(t *testing.T) {
3636

3737
t.Run("dependency with pull_policy build is built only once", func(t *testing.T) {
3838
projectName := randomProjectName("build-once")
39-
_ = c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once.yaml", "down", "--rmi", "local", "--remove-orphans", "-v")
40-
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once.yaml", "--verbose", "run", "--build", "--rm", "curl")
39+
composeFile := "./fixtures/run-test/build-once.yaml"
40+
t.Cleanup(func() {
41+
c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "down", "--rmi", "local", "--remove-orphans", "-v")
42+
})
43+
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "--verbose", "run", "--build", "--rm", "curl")
4144

4245
output := res.Stdout()
4346

4447
nginxBuilds := countServiceBuilds(output, projectName, "nginx")
4548

4649
assert.Equal(t, nginxBuilds, 1, "nginx should build once, built %d times\nOutput:\n%s", nginxBuilds, output)
4750
assert.Assert(t, strings.Contains(res.Stdout(), "curl service"))
48-
49-
c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once.yaml", "down", "--remove-orphans")
5051
})
5152

5253
t.Run("nested dependencies build only once each", func(t *testing.T) {
5354
projectName := randomProjectName("build-nested")
54-
_ = c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-nested.yaml", "down", "--rmi", "local", "--remove-orphans", "-v")
55-
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-nested.yaml", "--verbose", "run", "--build", "--rm", "app")
55+
composeFile := "./fixtures/run-test/build-once-nested.yaml"
56+
t.Cleanup(func() {
57+
c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "down", "--rmi", "local", "--remove-orphans", "-v")
58+
})
59+
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "--verbose", "run", "--build", "--rm", "app")
5660

5761
output := res.Stdout()
5862

@@ -64,23 +68,22 @@ func TestRunBuildOnce(t *testing.T) {
6468
assert.Equal(t, apiBuilds, 1, "api should build once, built %d times\nOutput:\n%s", apiBuilds, output)
6569
assert.Equal(t, appBuilds, 1, "app should build once, built %d times\nOutput:\n%s", appBuilds, output)
6670
assert.Assert(t, strings.Contains(output, "App running"))
67-
68-
c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-nested.yaml", "down", "--rmi", "local", "--remove-orphans", "-v")
6971
})
7072

7173
t.Run("service with no dependencies builds once", func(t *testing.T) {
7274
projectName := randomProjectName("build-simple")
73-
_ = c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "down", "--rmi", "local", "--remove-orphans")
74-
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "run", "--build", "--rm", "simple")
75+
composeFile := "./fixtures/run-test/build-once-no-deps.yaml"
76+
t.Cleanup(func() {
77+
c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "down", "--rmi", "local", "--remove-orphans", "-v")
78+
})
79+
res := c.RunDockerComposeCmd(t, "-p", projectName, "-f", composeFile, "run", "--build", "--rm", "simple")
7580

7681
output := res.Stdout()
7782

7883
simpleBuilds := countServiceBuilds(output, projectName, "simple")
7984

8085
assert.Equal(t, simpleBuilds, 1, "simple should build once, built %d times\nOutput:\n%s", simpleBuilds, output)
8186
assert.Assert(t, strings.Contains(res.Stdout(), "Simple service"))
82-
83-
c.RunDockerComposeCmd(t, "-p", projectName, "-f", "./fixtures/run-test/build-once-no-deps.yaml", "down", "--remove-orphans")
8487
})
8588
}
8689

pkg/e2e/compose_run_test.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ import (
2020
"os"
2121
"strings"
2222
"testing"
23+
"time"
2324

2425
"gotest.tools/v3/assert"
2526
"gotest.tools/v3/icmd"
27+
"gotest.tools/v3/poll"
2628
)
2729

2830
func TestLocalComposeRun(t *testing.T) {
@@ -207,8 +209,14 @@ func TestLocalComposeRun(t *testing.T) {
207209

208210
res = c.RunDockerCmd(t, "stop", containerID)
209211
res.Assert(t, icmd.Success)
210-
res = c.RunDockerCmd(t, "ps", "--all", "--filter", "name=run-test-nginx", "--format", "'{{.Names}}'")
211-
assert.Assert(t, !strings.Contains(res.Stdout(), "run-test-nginx"), res.Stdout())
212+
// --rm auto-removal is async, wait for the container to be removed
213+
poll.WaitOn(t, func(l poll.LogT) poll.Result {
214+
res = c.RunDockerCmd(t, "ps", "--all", "--filter", "name=run-test-nginx", "--format", "'{{.Names}}'")
215+
if strings.Contains(res.Stdout(), "run-test-nginx") {
216+
return poll.Continue("container still present: %s", res.Stdout())
217+
}
218+
return poll.Success()
219+
}, poll.WithTimeout(10*time.Second), poll.WithDelay(500*time.Millisecond))
212220
})
213221

214222
t.Run("compose run --env", func(t *testing.T) {

pkg/e2e/compose_test.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,16 +140,13 @@ func TestDownComposefileInParentFolder(t *testing.T) {
140140
}
141141

142142
func TestAttachRestart(t *testing.T) {
143-
t.Skip("Skipping test until we can fix it")
144-
145-
if _, ok := os.LookupEnv("CI"); ok {
146-
t.Skip("Skipping test on CI... flaky")
147-
}
148143
c := NewParallelCLI(t)
149144

150145
cmd := c.NewDockerComposeCmd(t, "--ansi=never", "--project-directory", "./fixtures/attach-restart", "up")
151146
res := icmd.StartCmd(cmd)
152-
defer c.RunDockerComposeCmd(t, "-p", "attach-restart", "down")
147+
t.Cleanup(func() {
148+
c.RunDockerComposeCmd(t, "-p", "attach-restart", "down")
149+
})
153150

154151
c.WaitForCondition(t, func() (bool, string) {
155152
debug := res.Combined()

pkg/e2e/env_file_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ func TestUnusedMissingEnvFile(t *testing.T) {
4646

4747
func TestRunEnvFile(t *testing.T) {
4848
c := NewParallelCLI(t)
49-
defer c.cleanupWithDown(t, "run_dotenv")
49+
const projectName = "run-dotenv"
50+
defer c.cleanupWithDown(t, projectName)
5051

51-
res := c.RunDockerComposeCmd(t, "--project-directory", "./fixtures/env_file", "run", "serviceC", "env")
52+
res := c.RunDockerComposeCmd(t, "-p", projectName, "--project-directory", "./fixtures/env_file", "run", "--rm", "serviceC", "env")
5253
res.Assert(t, icmd.Expected{Out: "FOO=BAR"})
5354
}

pkg/e2e/model_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import (
2121
)
2222

2323
func TestComposeModel(t *testing.T) {
24-
t.Skip("waiting for docker-model release")
24+
if _, err := findPluginExecutable(DockerModelExecutableName); err != nil {
25+
t.Skip("docker-model plugin not available")
26+
}
2527
c := NewParallelCLI(t)
2628
defer c.cleanupWithDown(t, "model-test")
2729

28-
c.RunDockerComposeCmd(t, "-f", "./fixtures/model/compose.yaml", "run", "test", "sh", "-c", "curl ${FOO_URL}")
30+
c.RunDockerComposeCmd(t, "-p", "model-test", "-f", "./fixtures/model/compose.yaml", "run", "--rm", "test", "sh", "-c", "curl ${FOO_URL}")
2931
}

pkg/e2e/networks_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ func TestNetworkModes(t *testing.T) {
147147
}
148148

149149
func TestNetworkConfigChanged(t *testing.T) {
150-
t.Skip("unstable")
151150
// fixture is shared with TestNetworks and is not safe to run concurrently
152151
c := NewCLI(t)
153152
const projectName = "network_config_change"

pkg/e2e/pause_test.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"fmt"
2323
"net"
2424
"net/http"
25-
"os"
2625
"testing"
2726
"time"
2827

@@ -31,9 +30,6 @@ import (
3130
)
3231

3332
func TestPause(t *testing.T) {
34-
if _, ok := os.LookupEnv("CI"); ok {
35-
t.Skip("Skipping test on CI... flaky")
36-
}
3733
cli := NewParallelCLI(t, WithEnv(
3834
"COMPOSE_PROJECT_NAME=e2e-pause",
3935
"COMPOSE_FILE=./fixtures/pause/compose.yaml"))

pkg/e2e/ps_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func TestPs(t *testing.T) {
3232
c := NewParallelCLI(t)
3333
const projectName = "e2e-ps"
3434

35+
// ensure clean state from any previous failed run
36+
c.RunDockerComposeCmdNoCheck(t, "--project-name", projectName, "down", "--remove-orphans")
37+
3538
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/ps-test/compose.yaml", "--project-name", projectName, "up", "-d")
3639
assert.NilError(t, res.Error)
3740
t.Cleanup(func() {

pkg/e2e/publish_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ package e2e
1818

1919
import (
2020
"fmt"
21+
"net/http"
2122
"strings"
2223
"testing"
24+
"time"
2325

2426
"gotest.tools/v3/assert"
2527
"gotest.tools/v3/icmd"
28+
"gotest.tools/v3/poll"
2629
)
2730

2831
func TestPublishChecks(t *testing.T) {
@@ -136,6 +139,20 @@ func TestPublish(t *testing.T) {
136139
c.RunDockerCmd(t, "rm", "--force", registryName)
137140
})
138141

142+
// Wait for registry to be ready
143+
registryURL := "http://" + registry + "/v2/"
144+
poll.WaitOn(t, func(l poll.LogT) poll.Result {
145+
resp, err := http.Get(registryURL) //nolint:gosec,noctx
146+
if err != nil {
147+
return poll.Continue("registry not ready: %v", err)
148+
}
149+
_ = resp.Body.Close()
150+
if resp.StatusCode < 500 {
151+
return poll.Success()
152+
}
153+
return poll.Continue("registry not ready, status %d", resp.StatusCode)
154+
}, poll.WithTimeout(10*time.Second), poll.WithDelay(100*time.Millisecond))
155+
139156
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/publish/oci/compose.yaml", "-f", "./fixtures/publish/oci/compose-override.yaml",
140157
"-p", projectName, "publish", "--with-env", "--yes", "--insecure-registry", registry+"/test:test")
141158
res.Assert(t, icmd.Expected{ExitCode: 0})

0 commit comments

Comments
 (0)