From 4632a029d929ebb250e148799682dedc50f7777a Mon Sep 17 00:00:00 2001 From: Brian Goff Date: Tue, 15 Nov 2016 12:10:02 -0500 Subject: [PATCH] Handle `run --rm` against older daemons on the cli For previous versions of Docker, `--rm` was handled client side, as such there was no support in the daemon for it. Now it is handled daemon side, but we still need to handle the case of a newer client talking to an older daemon. Falls back to client-side removal when the daemon does not support it. Signed-off-by: Brian Goff --- command/container/utils.go | 55 +++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/command/container/utils.go b/command/container/utils.go index 6161e07140..f4ad09b912 100644 --- a/command/container/utils.go +++ b/command/container/utils.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/versions" "github.com/docker/docker/cli/command" clientapi "github.com/docker/docker/client" ) @@ -19,11 +20,21 @@ func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, contai panic("Internal Error: waitExitOrRemoved needs a containerID as parameter") } + var removeErr error statusChan := make(chan int) exitCode := 125 - eventProcessor := func(e events.Message) bool { + // Get events via Events API + f := filters.NewArgs() + f.Add("type", "container") + f.Add("container", containerID) + options := types.EventsOptions{ + Filters: f, + } + eventCtx, cancel := context.WithCancel(ctx) + eventq, errq := dockerCli.Client().Events(eventCtx, options) + eventProcessor := func(e events.Message) bool { stopProcessing := false switch e.Status { case "die": @@ -37,6 +48,18 @@ func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, contai } if !waitRemove { stopProcessing = true + } else { + // 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 + if versions.LessThan(dockerCli.Client().ClientVersion(), "1.25") { + go func() { + removeErr = dockerCli.Client().ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{RemoveVolumes: true}) + if removeErr != nil { + logrus.Errorf("error removing container: %v", removeErr) + cancel() // cancel the event Q + } + }() + } } case "detach": exitCode = 0 @@ -44,39 +67,27 @@ func waitExitOrRemoved(ctx context.Context, dockerCli *command.DockerCli, contai case "destroy": stopProcessing = true } - - if stopProcessing { - statusChan <- exitCode - return true - } - - return false + return stopProcessing } - // Get events via Events API - f := filters.NewArgs() - f.Add("type", "container") - f.Add("container", containerID) - options := types.EventsOptions{ - Filters: f, - } - - eventCtx, cancel := context.WithCancel(ctx) - eventq, errq := dockerCli.Client().Events(eventCtx, options) - go func() { - defer cancel() + 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) - statusChan <- exitCode return } }