diff --git a/api/client/container/run.go b/api/client/container/run.go index 5d82ff79ab..ee62b60867 100644 --- a/api/client/container/run.go +++ b/api/client/container/run.go @@ -8,7 +8,6 @@ import ( "runtime" "strings" "syscall" - "time" "golang.org/x/net/context" @@ -144,7 +143,6 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, hostConfig.ConsoleSize[0], hostConfig.ConsoleSize[1] = dockerCli.GetTtySize() } - startTime := time.Now() ctx, cancelFun := context.WithCancel(context.Background()) createResponse, err := createContainer(ctx, dockerCli, config, hostConfig, networkingConfig, hostConfig.ContainerIDFile, opts.name) @@ -221,6 +219,11 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, }) } + statusChan, err := waitExitOrRemoved(dockerCli, context.Background(), createResponse.ID, hostConfig.AutoRemove) + if err != nil { + return fmt.Errorf("Error waiting container's exit code: %v", err) + } + //start the container if err := client.ContainerStart(ctx, createResponse.ID, types.ContainerStartOptions{}); err != nil { // If we have holdHijackedConnection, we should notify @@ -233,9 +236,8 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, reportError(stderr, cmdPath, err.Error(), false) if hostConfig.AutoRemove { - if _, errWait := waitExitOrRemoved(dockerCli, context.Background(), createResponse.ID, hostConfig.AutoRemove, startTime); errWait != nil { - logrus.Debugf("Error waiting container's removal: %v", errWait) - } + // wait container to be removed + <-statusChan } return runStartContainerErr(err) } @@ -260,14 +262,7 @@ func runRun(dockerCli *client.DockerCli, flags *pflag.FlagSet, opts *runOptions, return nil } - var status int - - // Attached mode - status, err = waitExitOrRemoved(dockerCli, ctx, createResponse.ID, hostConfig.AutoRemove, startTime) - if err != nil { - return fmt.Errorf("Error waiting container to exit: %v", err) - } - + status := <-statusChan if status != 0 { return cli.StatusError{StatusCode: status} } diff --git a/api/client/container/utils.go b/api/client/container/utils.go index 3693ff5e86..5a589ff680 100644 --- a/api/client/container/utils.go +++ b/api/client/container/utils.go @@ -3,7 +3,6 @@ package container import ( "fmt" "strconv" - "time" "golang.org/x/net/context" @@ -16,83 +15,66 @@ import ( "github.com/docker/engine-api/types/filters" ) -func waitExitOrRemoved(dockerCli *client.DockerCli, ctx context.Context, containerID string, waitRemove bool, since time.Time) (int, error) { +func waitExitOrRemoved(dockerCli *client.DockerCli, ctx context.Context, containerID string, waitRemove bool) (chan int, error) { if len(containerID) == 0 { // containerID can never be empty panic("Internal Error: waitExitOrRemoved needs a containerID as parameter") } - var exitCode int - exitChan := make(chan struct{}) - detachChan := make(chan struct{}) - destroyChan := make(chan struct{}) + statusChan := make(chan int) + exitCode := 125 - // Start watch events - eh := system.InitEventHandler() - eh.Handle("die", func(e events.Message) { - if len(e.Actor.Attributes) > 0 { - for k, v := range e.Actor.Attributes { - if k == "exitCode" { - var err error - if exitCode, err = strconv.Atoi(v); err != nil { - logrus.Errorf("Can't convert %q to int: %v", v, err) - } - close(exitChan) - break + eventProcessor := func(e events.Message, err error) error { + if err != nil { + statusChan <- exitCode + return fmt.Errorf("failed to decode event: %v", err) + } + + stopProcessing := false + switch e.Status { + case "die": + if v, ok := e.Actor.Attributes["exitCode"]; ok { + code, cerr := strconv.Atoi(v) + if cerr != nil { + logrus.Errorf("failed to convert exitcode '%q' to int: %v", v, cerr) + } else { + exitCode = code } } + if !waitRemove { + stopProcessing = true + } + case "detach": + exitCode = 0 + stopProcessing = true + case "destroy": + stopProcessing = true } - }) - eh.Handle("detach", func(e events.Message) { - exitCode = 0 - close(detachChan) - }) - eh.Handle("destroy", func(e events.Message) { - close(destroyChan) - }) + if stopProcessing { + statusChan <- exitCode + // stop the loop processing + return fmt.Errorf("done") + } - eventChan := make(chan events.Message) - go eh.Watch(eventChan) - defer close(eventChan) + return nil + } // Get events via Events API f := filters.NewArgs() f.Add("type", "container") f.Add("container", containerID) options := types.EventsOptions{ - Since: fmt.Sprintf("%d", since.Unix()), Filters: f, } resBody, err := dockerCli.Client().Events(ctx, options) if err != nil { - return -1, fmt.Errorf("can't get events from daemon: %v", err) + return nil, fmt.Errorf("can't get events from daemon: %v", err) } - defer resBody.Close() - go system.DecodeEvents(resBody, func(event events.Message, err error) error { - if err != nil { - return nil - } - eventChan <- event - return nil - }) + go system.DecodeEvents(resBody, eventProcessor) - if waitRemove { - select { - case <-destroyChan: - return exitCode, nil - case <-detachChan: - return 0, nil - } - } else { - select { - case <-exitChan: - return exitCode, nil - case <-detachChan: - return 0, nil - } - } + return statusChan, nil } // getExitCode performs an inspect on the container. It returns