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]:6dd8e10d6e[cli@38591f2]:38591f20d0Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
@ -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
|
||||
|
||||
Reference in New Issue
Block a user