Browse code

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 authored on 2016/11/16 02:10:02
Showing 1 changed files
... ...
@@ -9,6 +9,7 @@ import (
9 9
 	"github.com/docker/docker/api/types"
10 10
 	"github.com/docker/docker/api/types/events"
11 11
 	"github.com/docker/docker/api/types/filters"
12
+	"github.com/docker/docker/api/types/versions"
12 13
 	"github.com/docker/docker/cli/command"
13 14
 	clientapi "github.com/docker/docker/client"
14 15
 )
... ...
@@ -19,11 +20,21 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
19 19
 		panic("Internal Error: waitExitOrRemoved needs a containerID as parameter")
20 20
 	}
21 21
 
22
+	var removeErr error
22 23
 	statusChan := make(chan int)
23 24
 	exitCode := 125
24 25
 
25
-	eventProcessor := func(e events.Message) bool {
26
+	// Get events via Events API
27
+	f := filters.NewArgs()
28
+	f.Add("type", "container")
29
+	f.Add("container", containerID)
30
+	options := types.EventsOptions{
31
+		Filters: f,
32
+	}
33
+	eventCtx, cancel := context.WithCancel(ctx)
34
+	eventq, errq := dockerCli.Client().Events(eventCtx, options)
26 35
 
36
+	eventProcessor := func(e events.Message) bool {
27 37
 		stopProcessing := false
28 38
 		switch e.Status {
29 39
 		case "die":
... ...
@@ -37,6 +48,18 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
37 37
 			}
38 38
 			if !waitRemove {
39 39
 				stopProcessing = true
40
+			} else {
41
+				// If we are talking to an older daemon, `AutoRemove` is not supported.
42
+				// We need to fall back to the old behavior, which is client-side removal
43
+				if versions.LessThan(dockerCli.Client().ClientVersion(), "1.25") {
44
+					go func() {
45
+						removeErr = dockerCli.Client().ContainerRemove(ctx, containerID, types.ContainerRemoveOptions{RemoveVolumes: true})
46
+						if removeErr != nil {
47
+							logrus.Errorf("error removing container: %v", removeErr)
48
+							cancel() // cancel the event Q
49
+						}
50
+					}()
51
+				}
40 52
 			}
41 53
 		case "detach":
42 54
 			exitCode = 0
... ...
@@ -44,39 +67,27 @@ func waitExitOrRemoved(dockerCli *command.DockerCli, ctx context.Context, contai
44 44
 		case "destroy":
45 45
 			stopProcessing = true
46 46
 		}
47
-
48
-		if stopProcessing {
49
-			statusChan <- exitCode
50
-			return true
51
-		}
52
-
53
-		return false
47
+		return stopProcessing
54 48
 	}
55 49
 
56
-	// Get events via Events API
57
-	f := filters.NewArgs()
58
-	f.Add("type", "container")
59
-	f.Add("container", containerID)
60
-	options := types.EventsOptions{
61
-		Filters: f,
62
-	}
63
-
64
-	eventCtx, cancel := context.WithCancel(ctx)
65
-	eventq, errq := dockerCli.Client().Events(eventCtx, options)
66
-
67 50
 	go func() {
68
-		defer cancel()
51
+		defer func() {
52
+			statusChan <- exitCode // must always send an exit code or the caller will block
53
+			cancel()
54
+		}()
69 55
 
70 56
 		for {
71 57
 			select {
58
+			case <-eventCtx.Done():
59
+				if removeErr != nil {
60
+					return
61
+				}
72 62
 			case evt := <-eventq:
73 63
 				if eventProcessor(evt) {
74 64
 					return
75 65
 				}
76
-
77 66
 			case err := <-errq:
78 67
 				logrus.Errorf("error getting events from daemon: %v", err)
79
-				statusChan <- exitCode
80 68
 				return
81 69
 			}
82 70
 		}