From 63c5254201e9e6818baf3a79c88ec2a0dc004f3b Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 30 Sep 2025 18:16:57 +0200 Subject: [PATCH] remove support for AutoRemove (`--rm`) on API < 1.30 Support for daemon-side auto-remove was added in API v1.25; on older versions of the daemon, the client was responsible for removing the container after it exited (see [moby@6dd8e10]) On API versions < 1.30, it used the events API for this purpose, and would wait for a "die", "detach" or "detroy" events to know the container exited, and could be removed or (when attached, but without a TTY) to get the container's exit-status. (see [cli@38591f2]). API version 1.24 (docker 1.12) is 9 Years old (July 29, 2016), and API 1.30 (docker 17.06) is 8 Years old (Jun 20, 2017), and long EOL. While technically, a CLI could negotiate API 1.30 or older, this would only be in cases where either API version negotiation failed, or the version was explicitly overridden through `DOCKER_API_VERSION` for testing. Either of those cases would be rare, and not worth the technical complexity to support. This patch removes support for AutoRemove on API < 1.30. [moby@6dd8e10]: https://github.com/moby/moby/commit/6dd8e10d6ed7a7371c5c1824ad58c4403a7b3bfd [cli@38591f2]: https://github.com/docker/cli/commit/38591f20d07795aaef45d400df89ca12f29c603b Signed-off-by: Sebastiaan van Stijn --- cli/command/container/utils.go | 86 ---------------------------------- 1 file changed, 86 deletions(-) diff --git a/cli/command/container/utils.go b/cli/command/container/utils.go index efe41b63c690..363f710f52a3 100644 --- a/cli/command/container/utils.go +++ b/cli/command/container/utils.go @@ -3,12 +3,8 @@ package container import ( "context" "errors" - "strconv" "github.com/moby/moby/api/types/container" - "github.com/moby/moby/api/types/events" - "github.com/moby/moby/api/types/filters" - "github.com/moby/moby/api/types/versions" "github.com/moby/moby/client" "github.com/sirupsen/logrus" ) @@ -19,13 +15,6 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe panic("Internal Error: waitExitOrRemoved needs a containerID as parameter") } - // Older versions used the Events API, and even older versions did not - // support server-side removal. This legacyWaitExitOrRemoved method - // preserves that old behavior and any issues it may have. - if versions.LessThan(apiClient.ClientVersion(), "1.30") { - return legacyWaitExitOrRemoved(ctx, apiClient, containerID, waitRemove) - } - condition := container.WaitConditionNextExit if waitRemove { condition = container.WaitConditionRemoved @@ -58,81 +47,6 @@ func waitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containe return statusC } -func legacyWaitExitOrRemoved(ctx context.Context, apiClient client.APIClient, containerID string, waitRemove bool) <-chan int { - var removeErr error - statusChan := make(chan int) - exitCode := 125 - - // Get events via Events API - f := filters.NewArgs() - f.Add("type", "container") - f.Add("container", containerID) - - eventCtx, cancel := context.WithCancel(ctx) - eventq, errq := apiClient.Events(eventCtx, client.EventsListOptions{ - Filters: f, - }) - - eventProcessor := func(e events.Message) bool { - stopProcessing := false - switch e.Action { //nolint:exhaustive // TODO(thaJeztah): make exhaustive - case events.ActionDie: - if v, ok := e.Actor.Attributes["exitCode"]; ok { - code, cerr := strconv.Atoi(v) - if cerr != nil { - logrus.Errorf("failed to convert exitcode '%q' to int: %v", v, cerr) - } else { - exitCode = code - } - } - if !waitRemove { - stopProcessing = true - } else if versions.LessThan(apiClient.ClientVersion(), "1.25") { - // If we are talking to an older daemon, `AutoRemove` is not supported. - // We need to fall back to the old behavior, which is client-side removal - go func() { - removeErr = apiClient.ContainerRemove(ctx, containerID, client.ContainerRemoveOptions{RemoveVolumes: true}) - if removeErr != nil { - logrus.Errorf("error removing container: %v", removeErr) - cancel() // cancel the event Q - } - }() - } - case events.ActionDetach: - exitCode = 0 - stopProcessing = true - case events.ActionDestroy: - stopProcessing = true - } - return stopProcessing - } - - go func() { - defer func() { - statusChan <- exitCode // must always send an exit code or the caller will block - cancel() - }() - - for { - select { - case <-eventCtx.Done(): - if removeErr != nil { - return - } - case evt := <-eventq: - if eventProcessor(evt) { - return - } - case err := <-errq: - logrus.Errorf("error getting events from daemon: %v", err) - return - } - } - }() - - return statusChan -} - func parallelOperation(ctx context.Context, containers []string, op func(ctx context.Context, containerID string) error) chan error { if len(containers) == 0 { return nil