Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg/compose/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ func (u *upSession) stopApplication() {
err := u.stop(context.WithoutCancel(u.globalCtx), u.project.Name, api.StopOptions{
Services: u.options.Create.Services,
Project: u.project,
Timeout: u.options.Create.Timeout,
}, u.printer.HandleEvent)
u.appendErr(err)
return nil
Expand Down Expand Up @@ -575,6 +576,7 @@ func (u *upSession) stopLateStarter(service string) {
err := u.stop(context.WithoutCancel(u.globalCtx), u.project.Name, api.StopOptions{
Services: []string{service},
Project: u.project,
Timeout: u.options.Create.Timeout,
}, u.printer.HandleEvent)
u.appendErr(err)
return nil
Expand Down
37 changes: 37 additions & 0 deletions pkg/compose/up_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"gotest.tools/v3/assert"

"github.com/docker/compose/v5/pkg/api"
"github.com/docker/compose/v5/pkg/utils"
)

func TestShouldFollowStartEvent(t *testing.T) {
Expand Down Expand Up @@ -189,6 +190,42 @@ func TestAppendErrKeepsCancellationBeforeShutdown(t *testing.T) {
assert.Equal(t, len(u.errs), 1)
}

// TestUpSessionStopsWithUpTimeout locks that the stops an attached up issues
// itself carry -t (#14394).
func TestUpSessionStopsWithUpTimeout(t *testing.T) {
timeout := 2 * time.Second
stops := map[string]func(*upSession){
"stopApplication": (*upSession).stopApplication,
"stopLateStarter": func(u *upSession) { u.stopLateStarter("svc0") },
}
for name, stop := range stops {
t.Run(name, func(t *testing.T) {
svc, apiClient := newTestService(t)
project, containers := nIndependentServiceContainers(1)
apiClient.EXPECT().ContainerList(gomock.Any(), gomock.Any()).
Return(client.ContainerListResult{Items: containers}, nil)
var got client.ContainerStopOptions
apiClient.EXPECT().ContainerStop(gomock.Any(), "c0", gomock.Any()).
DoAndReturn(func(_ context.Context, _ string, opts client.ContainerStopOptions) (client.ContainerStopResult, error) {
got = opts
return client.ContainerStopResult{}, nil
})

u := &upSession{
composeService: svc,
project: project,
globalCtx: t.Context(),
printer: newLogPrinter(&testLogConsumer{}),
options: api.UpOptions{Create: api.CreateOptions{Timeout: &timeout}},
}
stop(u)
assert.NilError(t, u.eg.Wait())
assert.Equal(t, len(u.errs), 0, "%v", u.errs)
assert.DeepEqual(t, got, client.ContainerStopOptions{Timeout: utils.DurationSecondToInt(&timeout)})
})
}
}

// TestStreamContainerLogs_ConcurrencyIsBounded guards a regression flagged
// in review: streamContainerLogs (attaches logs for containers that start
// after up's initial attach, e.g. via depends_on/restart/watch rebuilds)
Expand Down
10 changes: 10 additions & 0 deletions pkg/e2e/cascade_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ func TestCascadeFail(t *testing.T) {
ServiceState("running", "exited"))
}

// TestCascadeStopHonorsTimeout is a regression test for #14394: the stop
// triggered by the first exit ignored -t and waited for stop_grace_period.
func TestCascadeStopHonorsTimeout(t *testing.T) {
NewScenario(t, "up --abort-on-container-exit must stop the project within the -t timeout").
Step("up aborts on the first exit and kills a service ignoring SIGTERM once -t elapses",
ComposeCmd("up", "-t", "1", "--abort-on-container-exit", "--exit-code-from", "tests").MayFail().Within(60*time.Second),
ExitCode(3),
ServiceState("app", "exited"))
}

func TestCascadeIgnoresOneOffContainer(t *testing.T) {
const projectName = "compose-e2e-cascade-oneoff"
c := NewCLI(t, WithEnv("COMPOSE_PROJECT_NAME="+projectName))
Expand Down
10 changes: 10 additions & 0 deletions pkg/e2e/testdata/TestCascadeStopHonorsTimeout/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
app:
image: alpine
# PID 1 without a SIGTERM handler: only the kill at the end of the timeout stops it
command: sleep infinity
stop_grace_period: 2m

tests:
image: alpine
command: sh -c "sleep 1; exit 3"
Loading