fix: ignore one-off container events in up monitor

`up --abort-on-container-exit` tore down the project when a one-off
container created by `docker compose run` exited. The container list
built in monitor.Start already excludes one-off containers via
oneOffFilter(false), but the event subscription did not, and the only
guard on incoming events is the service label, which a one-off
container inherits from the service it was run from.

Regression since v2.39.0 (last unaffected release is v2.38.2), and a
recurrence of docker/compose-cli#1955, fixed there by
docker/compose-cli#1987.

Signed-off-by: Branislav Osif <brano@osif.digital>
This commit is contained in:
Branislav Osif 2026-08-11 09:20:48 +02:00 committed by Nicolas De loof
parent be30fb4dd1
commit fc860cbd1f
2 changed files with 30 additions and 1 deletions

View file

@ -77,7 +77,7 @@ func (c *monitor) Start(ctx context.Context) error {
restarting := utils.Set[string]{}
res := c.apiClient.Events(ctx, client.EventsListOptions{
Filters: projectFilter(c.project).Add("type", "container"),
Filters: projectFilter(c.project).Add("type", "container").Add("label", oneOffFilter(false)),
})
for {
if len(containers) == 0 {

View file

@ -21,8 +21,11 @@ package e2e
import (
"strings"
"testing"
"time"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
"gotest.tools/v3/poll"
)
func TestCascadeStop(t *testing.T) {
@ -39,6 +42,32 @@ func TestCascadeStop(t *testing.T) {
assert.Equal(t, res.ExitCode, 0)
}
func TestCascadeIgnoresOneOffContainer(t *testing.T) {
const projectName = "compose-e2e-cascade-oneoff"
c := NewCLI(t, WithEnv("COMPOSE_PROJECT_NAME="+projectName))
t.Cleanup(func() {
c.RunDockerComposeCmd(t, "down")
})
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/cascade/compose.yaml",
"up", "--abort-on-container-exit", "--menu=false", "running")
res := icmd.StartCmd(cmd)
t.Cleanup(func() {
_ = res.Cmd.Process.Kill()
})
poll.WaitOn(t, expectOutput(res, "Attaching to running-1"),
poll.WithDelay(100*time.Millisecond), poll.WithTimeout(30*time.Second))
c.RunDockerComposeCmd(t, "-f", "./fixtures/cascade/compose.yaml",
"run", "--rm", "--no-deps", "running", "/bin/true")
time.Sleep(3 * time.Second)
assert.Assert(t, !strings.Contains(res.Combined(), "Aborting on container exit"), res.Combined())
RequireServiceState(t, c, "running", "running")
}
func TestCascadeFail(t *testing.T) {
c := NewCLI(t)
const projectName = "compose-e2e-cascade-fail"