diff --git a/api/client/container/start.go b/api/client/container/start.go index 1520b04621..9b291ad2c4 100644 --- a/api/client/container/start.go +++ b/api/client/container/start.go @@ -90,8 +90,8 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { resp, errAttach := dockerCli.Client().ContainerAttach(ctx, c.ID, options) if errAttach != nil && errAttach != httputil.ErrPersistEOF { // ContainerAttach return an ErrPersistEOF (connection closed) - // means server met an error and put it in Hijacked connection - // keep the error and read detailed error message from hijacked connection + // means server met an error and already put it in Hijacked connection, + // we would keep the error and read the detailed error message from hijacked connection return errAttach } defer resp.Close() @@ -103,14 +103,22 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { return errHijack }) - // 3. Start the container. + // 3. We should open a channel for receiving status code of the container + // no matter it's detached, removed on daemon side(--rm) or exit normally. + statusChan, statusErr := waitExitOrRemoved(dockerCli, context.Background(), c.ID, c.HostConfig.AutoRemove) + + // 4. Start the container. if err := dockerCli.Client().ContainerStart(ctx, c.ID, types.ContainerStartOptions{}); err != nil { cancelFun() <-cErr + if c.HostConfig.AutoRemove && statusErr == nil { + // wait container to be removed + <-statusChan + } return err } - // 4. Wait for attachment to break. + // 5. Wait for attachment to break. if c.Config.Tty && dockerCli.IsTerminalOut() { if err := dockerCli.MonitorTtySize(ctx, c.ID, false); err != nil { fmt.Fprintf(dockerCli.Err(), "Error monitoring TTY size: %s\n", err) @@ -119,11 +127,12 @@ func runStart(dockerCli *client.DockerCli, opts *startOptions) error { if attchErr := <-cErr; attchErr != nil { return attchErr } - _, status, err := getExitCode(dockerCli, ctx, c.ID) - if err != nil { - return err + + if statusErr != nil { + return fmt.Errorf("can't get container's exit code: %v", statusErr) } - if status != 0 { + + if status := <-statusChan; status != 0 { return cli.StatusError{StatusCode: status} } } else { diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go index 1c6a3cf2ae..f96528f7d6 100644 --- a/integration-cli/docker_cli_start_test.go +++ b/integration-cli/docker_cli_start_test.go @@ -185,3 +185,15 @@ func (s *DockerSuite) TestStartAttachWithRename(c *check.C) { _, stderr, _, _ := runCommandWithStdoutStderr(exec.Command(dockerBinary, "start", "-a", "before")) c.Assert(stderr, checker.Not(checker.Contains), "No such container") } + +func (s *DockerSuite) TestStartReturnCorrectExitCode(c *check.C) { + dockerCmd(c, "create", "--restart=on-failure:2", "--name", "withRestart", "busybox", "sh", "-c", "exit 11") + dockerCmd(c, "create", "--rm", "--name", "withRm", "busybox", "sh", "-c", "exit 12") + + _, exitCode, err := dockerCmdWithError("start", "-a", "withRestart") + c.Assert(err, checker.NotNil) + c.Assert(exitCode, checker.Equals, 11) + _, exitCode, err = dockerCmdWithError("start", "-a", "withRm") + c.Assert(err, checker.NotNil) + c.Assert(exitCode, checker.Equals, 12) +}