Преглед на файлове

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 <cpuguy83@gmail.com>
(cherry picked from commit f4bb8c51de3a52bdbc3c3e7be426e6bb90feb421)
Signed-off-by: Victor Vieux <victorvieux@gmail.com>
Brian Goff преди 8 години
родител
ревизия
b4c0b07d89
променени са 1 файла, в които са добавени 33 реда и са изтрити 22 реда
  1. 33 22
      cli/command/container/utils.go

+ 33 - 22
cli/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(dockerCli *command.DockerCli, ctx context.Context, 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(dockerCli *command.DockerCli, ctx context.Context, 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(dockerCli *command.DockerCli, ctx context.Context, 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
 			}
 		}