Signed-off-by: David Calavera <david.calavera@gmail.com>
| ... | ... |
@@ -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 |
} |