From 7dc1af146d24c346ac74c9dcb1df98f9ef51339d Mon Sep 17 00:00:00 2001 From: Mabin Date: Mon, 16 Mar 2015 17:08:22 +0800 Subject: [PATCH] Fix hanging up problem when start and attach multiple containers Signed-off-by: Mabin --- api/client/commands.go | 50 +++++++++++------------- integration-cli/docker_cli_start_test.go | 46 ++++++++++++++++++++++ 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/api/client/commands.go b/api/client/commands.go index e10bee3b18..9adf09abf0 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -723,18 +723,6 @@ func (cli *DockerCli) CmdStart(args ...string) error { cmd.Require(flag.Min, 1) utils.ParseFlags(cmd, args, true) - hijacked := make(chan io.Closer) - // Block the return until the chan gets closed - defer func() { - log.Debugf("CmdStart() returned, defer waiting for hijack to finish.") - if _, ok := <-hijacked; ok { - log.Errorf("Hijack did not finish (chan still open)") - } - if *openStdin || *attach { - cli.in.Close() - } - }() - if *attach || *openStdin { if cmd.NArg() > 1 { return fmt.Errorf("You cannot start and attach multiple containers at once.") @@ -770,26 +758,34 @@ func (cli *DockerCli) CmdStart(args ...string) error { v.Set("stdout", "1") v.Set("stderr", "1") + hijacked := make(chan io.Closer) + // Block the return until the chan gets closed + defer func() { + log.Debugf("CmdStart() returned, defer waiting for hijack to finish.") + if _, ok := <-hijacked; ok { + log.Errorf("Hijack did not finish (chan still open)") + } + cli.in.Close() + }() cErr = promise.Go(func() error { return cli.hijack("POST", "/containers/"+cmd.Arg(0)+"/attach?"+v.Encode(), tty, in, cli.out, cli.err, hijacked, nil) }) - } else { - close(hijacked) + + // Acknowledge the hijack before starting + select { + case closer := <-hijacked: + // Make sure that the hijack gets closed when returning (results + // in closing the hijack chan and freeing server's goroutines) + if closer != nil { + defer closer.Close() + } + case err := <-cErr: + if err != nil { + return err + } + } } - // Acknowledge the hijack before starting - select { - case closer := <-hijacked: - // Make sure that the hijack gets closed when returning (results - // in closing the hijack chan and freeing server's goroutines) - if closer != nil { - defer closer.Close() - } - case err := <-cErr: - if err != nil { - return err - } - } var encounteredError error for _, name := range cmd.Args() { _, _, err := readBody(cli.call("POST", "/containers/"+name+"/start", nil, false)) diff --git a/integration-cli/docker_cli_start_test.go b/integration-cli/docker_cli_start_test.go index 01f0ef95a1..3ec04c9169 100644 --- a/integration-cli/docker_cli_start_test.go +++ b/integration-cli/docker_cli_start_test.go @@ -240,3 +240,49 @@ func TestStartMultipleContainers(t *testing.T) { logDone("start - start multiple containers continue on one failed") } + +func TestStartAttachMultipleContainers(t *testing.T) { + + var cmd *exec.Cmd + + defer deleteAllContainers() + // run multiple containers to test + for _, container := range []string{"test1", "test2", "test3"} { + cmd = exec.Command(dockerBinary, "run", "-d", "--name", container, "busybox", "top") + if out, _, err := runCommandWithOutput(cmd); err != nil { + t.Fatal(out, err) + } + } + + // stop all the containers + for _, container := range []string{"test1", "test2", "test3"} { + cmd = exec.Command(dockerBinary, "stop", container) + if out, _, err := runCommandWithOutput(cmd); err != nil { + t.Fatal(out, err) + } + } + + // test start and attach multiple containers at once, expected error + for _, option := range []string{"-a", "-i", "-ai"} { + cmd = exec.Command(dockerBinary, "start", option, "test1", "test2", "test3") + out, _, err := runCommandWithOutput(cmd) + if !strings.Contains(out, "You cannot start and attach multiple containers at once.") || err == nil { + t.Fatal("Expected error but got none") + } + } + + // confirm the state of all the containers be stopped + for container, expected := range map[string]string{"test1": "false", "test2": "false", "test3": "false"} { + cmd = exec.Command(dockerBinary, "inspect", "-f", "{{.State.Running}}", container) + out, _, err := runCommandWithOutput(cmd) + if err != nil { + t.Fatal(out, err) + } + out = strings.Trim(out, "\r\n") + if out != expected { + t.Fatal("Container running state wrong") + } + } + + logDone("start - error on start and attach multiple containers at once") +}