From 31feda42cffead481f092914082cca316d65630d Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sat, 1 Aug 2026 19:00:33 +0300 Subject: [PATCH 1/8] test: use compose rm for run cleanup Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 26 ++++++++----------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 2f05e732fd8..4d047889aff 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -72,7 +72,7 @@ services: }, Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)), Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") }, }, @@ -105,7 +105,7 @@ services: } }, Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") }, }, @@ -139,8 +139,6 @@ services: data.Labels().Set("composeYAML", composePath) data.Labels().Set("hostPort", strconv.Itoa(hostPort)) - // specify the name of container in order to remove - // TODO: when `compose rm` is implemented, replace it. cmd := helpers.Command("compose", "-f", composePath, "run", "--service-ports", "--name", data.Identifier(), "web") cmd.WithPseudoTTY() cmd.Background() @@ -148,8 +146,8 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) if composeYAML := data.Labels().Get("composeYAML"); composeYAML != "" { + helpers.Anyhow("compose", "-f", composeYAML, "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", composeYAML, "down", "-v") } if portStr := data.Labels().Get("hostPort"); portStr != "" { @@ -204,8 +202,6 @@ services: data.Labels().Set("composeYAML", composePath) data.Labels().Set("hostPort", strconv.Itoa(hostPort)) - // specify the name of container in order to remove - // TODO: when `compose rm` is implemented, replace it. cmd := helpers.Command("compose", "-f", composePath, "run", "--publish", fmt.Sprintf("%d:80", hostPort), "--name", data.Identifier(), "web") cmd.WithPseudoTTY() cmd.Background() @@ -213,8 +209,8 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) if composeYAML := data.Labels().Get("composeYAML"); composeYAML != "" { + helpers.Anyhow("compose", "-f", composeYAML, "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", composeYAML, "down", "-v") } if portStr := data.Labels().Get("hostPort"); portStr != "" { @@ -286,7 +282,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -332,7 +328,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -377,7 +373,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)) testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -435,7 +431,7 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -479,7 +475,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -525,7 +521,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } @@ -583,7 +579,7 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") } From e0a2f6905ecdd1173972c03b9b30f39ee32756cf Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sat, 1 Aug 2026 23:32:39 +0300 Subject: [PATCH 2/8] test: make compose run cleanup work before setup Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 72 ++++++++----------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 4d047889aff..73e93c2d440 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -38,6 +38,21 @@ import ( "github.com/containerd/nerdctl/v2/pkg/testutil/testregistry" ) +func composeRunCleanup(service string) test.Butler { + composeYAML := fmt.Sprintf(` +services: + %s: + image: %s +`, service, testutil.CommonImage) + + return func(data test.Data, helpers test.Helpers) { + // Tigron runs cleanup before setup too, so create the compose file here. + composePath := data.Temp().Save(composeYAML, "compose-cleanup.yaml") + helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") + helpers.Anyhow("compose", "-f", composePath, "down", "-v") + } +} + func TestComposeRun(t *testing.T) { const expectedOutput = "speed 38400 baud" @@ -71,10 +86,7 @@ services: return cmd }, Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)), - Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - }, + Cleanup: composeRunCleanup("alpine"), }, { Description: "pty run with --rm", @@ -104,10 +116,7 @@ services: Output: expect.Contains(expectedOutput), } }, - Cleanup: func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - }, + Cleanup: composeRunCleanup("alpine"), }, } @@ -116,6 +125,7 @@ services: func TestComposeRunWithServicePorts(t *testing.T) { testCase := nerdtest.Setup() + cleanup := composeRunCleanup("web") testCase.Setup = func(data test.Data, helpers test.Helpers) { hostPort, err := portlock.Acquire(0) @@ -146,10 +156,7 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - if composeYAML := data.Labels().Get("composeYAML"); composeYAML != "" { - helpers.Anyhow("compose", "-f", composeYAML, "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", composeYAML, "down", "-v") - } + cleanup(data, helpers) if portStr := data.Labels().Get("hostPort"); portStr != "" { if port, err := strconv.Atoi(portStr); err == nil { _ = portlock.Release(port) @@ -181,6 +188,7 @@ services: func TestComposeRunWithPublish(t *testing.T) { testCase := nerdtest.Setup() + cleanup := composeRunCleanup("web") testCase.Setup = func(data test.Data, helpers test.Helpers) { hostPort, err := portlock.Acquire(0) @@ -209,10 +217,7 @@ services: } testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - if composeYAML := data.Labels().Get("composeYAML"); composeYAML != "" { - helpers.Anyhow("compose", "-f", composeYAML, "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", composeYAML, "down", "-v") - } + cleanup(data, helpers) if portStr := data.Labels().Get("hostPort"); portStr != "" { if port, err := strconv.Atoi(portStr); err == nil { _ = portlock.Release(port) @@ -281,10 +286,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -327,10 +329,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -372,10 +371,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)) - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -430,10 +426,7 @@ services: } } - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -474,10 +467,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -520,10 +510,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } @@ -578,10 +565,7 @@ services: } } - testCase.Cleanup = func(data test.Data, helpers test.Helpers) { - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", data.Temp().Path("compose.yaml"), "down", "-v") - } + testCase.Cleanup = composeRunCleanup("alpine") testCase.Run(t) } From 6a3c235a6c7c6b2ffaa42aab2b584648900fca5a Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 01:10:48 +0300 Subject: [PATCH 3/8] test: use full compose config for post-cleanup Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 73e93c2d440..c593e11a6bf 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -19,6 +19,7 @@ package compose import ( "fmt" "io" + "os" "path/filepath" "strconv" "strings" @@ -46,8 +47,12 @@ services: `, service, testutil.CommonImage) return func(data test.Data, helpers test.Helpers) { - // Tigron runs cleanup before setup too, so create the compose file here. - composePath := data.Temp().Save(composeYAML, "compose-cleanup.yaml") + composePath := data.Temp().Path("compose.yaml") + // Tigron runs cleanup before setup too. Seed the project file for that + // first pass; setup replaces it with the test's full configuration. + if _, err := os.Stat(composePath); os.IsNotExist(err) { + data.Temp().Save(composeYAML, "compose.yaml") + } helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") helpers.Anyhow("compose", "-f", composePath, "down", "-v") } From e551904809bdd0053eb4d540f27d9e1359d1c50d Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:51:06 +0300 Subject: [PATCH 4/8] test: skip initial compose down The initial Tigron cleanup has no project network to remove. Avoid entering the globally serialized compose down path before setup, which starved parallel compose run tests in CI. Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index c593e11a6bf..800a5f35da8 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -50,11 +50,17 @@ services: composePath := data.Temp().Path("compose.yaml") // Tigron runs cleanup before setup too. Seed the project file for that // first pass; setup replaces it with the test's full configuration. + // There is no project network to tear down before setup, and running + // compose down adds unnecessary work behind the global compose lock. + initialCleanup := false if _, err := os.Stat(composePath); os.IsNotExist(err) { data.Temp().Save(composeYAML, "compose.yaml") + initialCleanup = true } helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") - helpers.Anyhow("compose", "-f", composePath, "down", "-v") + if !initialCleanup { + helpers.Anyhow("compose", "-f", composePath, "down", "-v") + } } } From cb0e4f86df8151368a7584cdbcfede7172b4b5b1 Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:18:11 +0300 Subject: [PATCH 5/8] test: clean Docker one-off containers Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 800a5f35da8..7948e7f28ef 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -58,6 +58,11 @@ services: initialCleanup = true } helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") + // Docker Compose can leave explicitly named one-off containers behind. + // Keep the compatibility run isolated from concurrently running tests. + if nerdtest.IsDocker() { + helpers.Anyhow("rm", "-f", "-v", data.Identifier()) + } if !initialCleanup { helpers.Anyhow("compose", "-f", composePath, "down", "-v") } From daaeabd798ef9190ebdd7f0104d3a1c4c770f363 Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 20:52:43 +0300 Subject: [PATCH 6/8] test: skip compose cleanup before setup Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 44 +++++++------------ 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 7948e7f28ef..648cf1a9e49 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -39,23 +39,13 @@ import ( "github.com/containerd/nerdctl/v2/pkg/testutil/testregistry" ) -func composeRunCleanup(service string) test.Butler { - composeYAML := fmt.Sprintf(` -services: - %s: - image: %s -`, service, testutil.CommonImage) - +func composeRunCleanup() test.Butler { return func(data test.Data, helpers test.Helpers) { composePath := data.Temp().Path("compose.yaml") - // Tigron runs cleanup before setup too. Seed the project file for that - // first pass; setup replaces it with the test's full configuration. - // There is no project network to tear down before setup, and running - // compose down adds unnecessary work behind the global compose lock. - initialCleanup := false + // Tigron runs cleanup before setup too. A fresh temp project has no + // manifest or resources yet, so avoid waiting for the global compose lock. if _, err := os.Stat(composePath); os.IsNotExist(err) { - data.Temp().Save(composeYAML, "compose.yaml") - initialCleanup = true + return } helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") // Docker Compose can leave explicitly named one-off containers behind. @@ -63,9 +53,7 @@ services: if nerdtest.IsDocker() { helpers.Anyhow("rm", "-f", "-v", data.Identifier()) } - if !initialCleanup { - helpers.Anyhow("compose", "-f", composePath, "down", "-v") - } + helpers.Anyhow("compose", "-f", composePath, "down", "-v") } } @@ -102,7 +90,7 @@ services: return cmd }, Expected: test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)), - Cleanup: composeRunCleanup("alpine"), + Cleanup: composeRunCleanup(), }, { Description: "pty run with --rm", @@ -132,7 +120,7 @@ services: Output: expect.Contains(expectedOutput), } }, - Cleanup: composeRunCleanup("alpine"), + Cleanup: composeRunCleanup(), }, } @@ -141,7 +129,7 @@ services: func TestComposeRunWithServicePorts(t *testing.T) { testCase := nerdtest.Setup() - cleanup := composeRunCleanup("web") + cleanup := composeRunCleanup() testCase.Setup = func(data test.Data, helpers test.Helpers) { hostPort, err := portlock.Acquire(0) @@ -204,7 +192,7 @@ services: func TestComposeRunWithPublish(t *testing.T) { testCase := nerdtest.Setup() - cleanup := composeRunCleanup("web") + cleanup := composeRunCleanup() testCase.Setup = func(data test.Data, helpers test.Helpers) { hostPort, err := portlock.Acquire(0) @@ -302,7 +290,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -345,7 +333,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -387,7 +375,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(expectedOutput)) - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -442,7 +430,7 @@ services: } } - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -483,7 +471,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -526,7 +514,7 @@ services: testCase.Expected = test.Expects(expect.ExitCodeSuccess, nil, expect.Contains(partialOutput)) - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } @@ -581,7 +569,7 @@ services: } } - testCase.Cleanup = composeRunCleanup("alpine") + testCase.Cleanup = composeRunCleanup() testCase.Run(t) } From 79e38c7f51d903c5c892f3e48083000035c753b7 Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 21:23:34 +0300 Subject: [PATCH 7/8] test: release compose lock before cleanup Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 648cf1a9e49..89c3942d796 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -47,6 +47,9 @@ func composeRunCleanup() test.Butler { if _, err := os.Stat(composePath); os.IsNotExist(err) { return } + // A background compose run holds the global compose lock. Stop its exact + // test container first so the process exits before compose rm acquires it. + helpers.Anyhow("stop", data.Identifier()) helpers.Anyhow("compose", "-f", composePath, "rm", "-f", "-s", "-v") // Docker Compose can leave explicitly named one-off containers behind. // Keep the compatibility run isolated from concurrently running tests. From c83e73bbbf7abc69eb1ef16fa488dcc6c57cb3bc Mon Sep 17 00:00:00 2001 From: Subota Ivan <73706465+subotac@users.noreply.github.com> Date: Sun, 2 Aug 2026 21:57:04 +0300 Subject: [PATCH 8/8] test: serialize background compose runs Signed-off-by: Subota Ivan <73706465+subotac@users.noreply.github.com> --- cmd/nerdctl/compose/compose_run_linux_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/nerdctl/compose/compose_run_linux_test.go b/cmd/nerdctl/compose/compose_run_linux_test.go index 89c3942d796..e68edb7d44c 100644 --- a/cmd/nerdctl/compose/compose_run_linux_test.go +++ b/cmd/nerdctl/compose/compose_run_linux_test.go @@ -132,6 +132,8 @@ services: func TestComposeRunWithServicePorts(t *testing.T) { testCase := nerdtest.Setup() + // A background compose run holds the global compose lock until cleanup. + testCase.NoParallel = true cleanup := composeRunCleanup() testCase.Setup = func(data test.Data, helpers test.Helpers) { @@ -195,6 +197,8 @@ services: func TestComposeRunWithPublish(t *testing.T) { testCase := nerdtest.Setup() + // A background compose run holds the global compose lock until cleanup. + testCase.NoParallel = true cleanup := composeRunCleanup() testCase.Setup = func(data test.Data, helpers test.Helpers) {