Browse code

Implement docker start with standalone client lib.

Signed-off-by: David Calavera <david.calavera@gmail.com>

David Calavera authored on 2015/12/06 12:53:52
Showing 3 changed files
... ...
@@ -29,6 +29,7 @@ type apiClient interface {
29 29
 	ContainerRename(containerID, newContainerName string) error
30 30
 	ContainerRestart(containerID string, timeout int) error
31 31
 	ContainerStatPath(containerID, path string) (types.ContainerPathStat, error)
32
+	ContainerStart(containerID string) error
32 33
 	ContainerStop(containerID string, timeout int) error
33 34
 	ContainerTop(containerID string, arguments []string) (types.ContainerProcessList, error)
34 35
 	ContainerUnpause(containerID string) error
35 36
new file mode 100644
... ...
@@ -0,0 +1,8 @@
0
+package lib
1
+
2
+// ContainerStart sends a request to the docker daemon to start a container.
3
+func (cli *Client) ContainerStart(containerID string) error {
4
+	resp, err := cli.post("/containers/"+containerID+"/start", nil, nil, nil)
5
+	ensureReaderClosed(resp)
6
+	return err
7
+}
... ...
@@ -1,11 +1,10 @@
1 1
 package client
2 2
 
3 3
 import (
4
-	"encoding/json"
5 4
 	"fmt"
6 5
 	"io"
7
-	"net/url"
8 6
 	"os"
7
+	"strings"
9 8
 
10 9
 	"github.com/Sirupsen/logrus"
11 10
 	"github.com/docker/docker/api/types"
... ...
@@ -54,119 +53,98 @@ func (cli *DockerCli) CmdStart(args ...string) error {
54 54
 
55 55
 	cmd.ParseFlags(args, true)
56 56
 
57
-	var (
58
-		cErr chan error
59
-		tty  bool
60
-	)
61
-
62 57
 	if *attach || *openStdin {
58
+		// We're going to attach to a container.
59
+		// 1. Ensure we only have one container.
63 60
 		if cmd.NArg() > 1 {
64 61
 			return fmt.Errorf("You cannot start and attach multiple containers at once.")
65 62
 		}
66 63
 
67
-		serverResp, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, nil)
64
+		// 2. Attach to the container.
65
+		containerID := cmd.Arg(0)
66
+		c, err := cli.client.ContainerInspect(containerID)
68 67
 		if err != nil {
69 68
 			return err
70 69
 		}
71 70
 
72
-		defer serverResp.body.Close()
73
-
74
-		var c types.ContainerJSON
75
-		if err := json.NewDecoder(serverResp.body).Decode(&c); err != nil {
76
-			return err
71
+		if !c.Config.Tty {
72
+			sigc := cli.forwardAllSignals(containerID)
73
+			defer signal.StopCatch(sigc)
77 74
 		}
78 75
 
79
-		tty = c.Config.Tty
80
-
81
-		if !tty {
82
-			sigc := cli.forwardAllSignals(cmd.Arg(0))
83
-			defer signal.StopCatch(sigc)
76
+		options := types.ContainerAttachOptions{
77
+			ContainerID: containerID,
78
+			Stream:      true,
79
+			Stdin:       *openStdin && c.Config.OpenStdin,
80
+			Stdout:      true,
81
+			Stderr:      true,
84 82
 		}
85 83
 
86 84
 		var in io.ReadCloser
87
-
88
-		v := url.Values{}
89
-		v.Set("stream", "1")
90
-
91
-		if *openStdin && c.Config.OpenStdin {
92
-			v.Set("stdin", "1")
85
+		if options.Stdin {
93 86
 			in = cli.in
94 87
 		}
95 88
 
96
-		v.Set("stdout", "1")
97
-		v.Set("stderr", "1")
89
+		resp, err := cli.client.ContainerAttach(options)
90
+		if err != nil {
91
+			return err
92
+		}
93
+		defer resp.Close()
98 94
 
99
-		hijacked := make(chan io.Closer)
100
-		// Block the return until the chan gets closed
101
-		defer func() {
102
-			logrus.Debugf("CmdStart() returned, defer waiting for hijack to finish.")
103
-			if _, ok := <-hijacked; ok {
104
-				fmt.Fprintln(cli.err, "Hijack did not finish (chan still open)")
105
-			}
106
-			cli.in.Close()
107
-		}()
108
-		cErr = promise.Go(func() error {
109
-			return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil)
95
+		cErr := promise.Go(func() error {
96
+			return cli.holdHijackedConnection(c.Config.Tty, in, cli.out, cli.err, resp)
110 97
 		})
111 98
 
112
-		// Acknowledge the hijack before starting
113 99
 		select {
114
-		case closer := <-hijacked:
115
-			// Make sure that the hijack gets closed when returning (results
116
-			// in closing the hijack chan and freeing server's goroutines)
117
-			if closer != nil {
118
-				defer closer.Close()
119
-			}
120 100
 		case err := <-cErr:
121 101
 			if err != nil {
122 102
 				return err
123 103
 			}
124 104
 		}
125
-	}
126 105
 
127
-	var encounteredError error
128
-	var errNames []string
129
-	for _, name := range cmd.Args() {
130
-		_, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, nil))
131
-		if err != nil {
132
-			if !*attach && !*openStdin {
133
-				// attach and openStdin is false means it could be starting multiple containers
134
-				// when a container start failed, show the error message and start next
135
-				fmt.Fprintf(cli.err, "%s\n", err)
136
-				errNames = append(errNames, name)
137
-			} else {
138
-				encounteredError = err
139
-			}
140
-		} else {
141
-			if !*attach && !*openStdin {
142
-				fmt.Fprintf(cli.out, "%s\n", name)
143
-			}
106
+		// 3. Start the container.
107
+		if err := cli.client.ContainerStart(containerID); err != nil {
108
+			return err
144 109
 		}
145
-	}
146 110
 
147
-	if len(errNames) > 0 {
148
-		encounteredError = fmt.Errorf("Error: failed to start containers: %v", errNames)
149
-	}
150
-	if encounteredError != nil {
151
-		return encounteredError
152
-	}
153
-
154
-	if *openStdin || *attach {
155
-		if tty && cli.isTerminalOut {
156
-			if err := cli.monitorTtySize(cmd.Arg(0), false); err != nil {
111
+		// 4. Wait for attachement to break.
112
+		if c.Config.Tty && cli.isTerminalOut {
113
+			if err := cli.monitorTtySize(containerID, false); err != nil {
157 114
 				fmt.Fprintf(cli.err, "Error monitoring TTY size: %s\n", err)
158 115
 			}
159 116
 		}
160 117
 		if attchErr := <-cErr; attchErr != nil {
161 118
 			return attchErr
162 119
 		}
163
-		_, status, err := getExitCode(cli, cmd.Arg(0))
120
+		_, status, err := getExitCode(cli, containerID)
164 121
 		if err != nil {
165 122
 			return err
166 123
 		}
167 124
 		if status != 0 {
168 125
 			return Cli.StatusError{StatusCode: status}
169 126
 		}
127
+	} else {
128
+		// We're not going to attach to anything.
129
+		// Start as many containers as we want.
130
+		return cli.startContainersWithoutAttachments(cmd.Args())
131
+	}
132
+
133
+	return nil
134
+}
135
+
136
+func (cli *DockerCli) startContainersWithoutAttachments(containerIDs []string) error {
137
+	var failedContainers []string
138
+	for _, containerID := range containerIDs {
139
+		if err := cli.client.ContainerStart(containerID); err != nil {
140
+			fmt.Fprintf(cli.err, "%s\n", err)
141
+			failedContainers = append(failedContainers, containerID)
142
+		} else {
143
+			fmt.Fprintf(cli.out, "%s\n", containerID)
144
+		}
145
+	}
146
+
147
+	if len(failedContainers) > 0 {
148
+		return fmt.Errorf("Error: failed to start containers: %v", strings.Join(failedContainers, ", "))
170 149
 	}
171 150
 	return nil
172 151
 }