diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go index 8bd67f4745..2c065caacc 100644 --- a/integration-cli/docker_cli_run_test.go +++ b/integration-cli/docker_cli_run_test.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "fmt" "io/ioutil" "os" @@ -12,6 +13,7 @@ import ( "strings" "sync" "testing" + "time" "github.com/docker/docker/pkg/mount" "github.com/docker/docker/pkg/networkfs/resolvconf" @@ -1492,3 +1494,58 @@ func TestRunWorkdirExistsAndIsFile(t *testing.T) { } logDone("run - error on existing file for workdir") } + +func TestRunExitOnStdinClose(t *testing.T) { + name := "testrunexitonstdinclose" + defer deleteAllContainers() + runCmd := exec.Command(dockerBinary, "run", "--name", name, "-i", "busybox", "/bin/cat") + + stdin, err := runCmd.StdinPipe() + if err != nil { + t.Fatal(err) + } + stdout, err := runCmd.StdoutPipe() + if err != nil { + t.Fatal(err) + } + + if err := runCmd.Start(); err != nil { + t.Fatal(err) + } + if _, err := stdin.Write([]byte("hello\n")); err != nil { + t.Fatal(err) + } + + r := bufio.NewReader(stdout) + line, err := r.ReadString('\n') + if err != nil { + t.Fatal(err) + } + line = strings.TrimSpace(line) + if line != "hello" { + t.Fatalf("Output should be 'hello', got '%q'", line) + } + if err := stdin.Close(); err != nil { + t.Fatal(err) + } + finish := make(chan struct{}) + go func() { + if err := runCmd.Wait(); err != nil { + t.Fatal(err) + } + close(finish) + }() + select { + case <-finish: + case <-time.After(1 * time.Second): + t.Fatal("docker run failed to exit on stdin close") + } + state, err := inspectField(name, "State.Running") + if err != nil { + t.Fatal(err) + } + if state != "false" { + t.Fatal("Container must be stopped after stdin closing") + } + logDone("run - exit on stdin closing") +} diff --git a/integration/commands_test.go b/integration/commands_test.go index 0212fdd6d4..1dce2d5204 100644 --- a/integration/commands_test.go +++ b/integration/commands_test.go @@ -113,57 +113,6 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error return nil } -func TestRunExit(t *testing.T) { - stdin, stdinPipe := io.Pipe() - stdout, stdoutPipe := io.Pipe() - - cli := client.NewDockerCli(stdin, stdoutPipe, ioutil.Discard, testDaemonProto, testDaemonAddr, nil) - defer cleanup(globalEngine, t) - - c1 := make(chan struct{}) - go func() { - cli.CmdRun("-i", unitTestImageID, "/bin/cat") - close(c1) - }() - - setTimeout(t, "Read/Write assertion timed out", 2*time.Second, func() { - if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 150); err != nil { - t.Fatal(err) - } - }) - - container := globalDaemon.List()[0] - - // Closing /bin/cat stdin, expect it to exit - if err := stdin.Close(); err != nil { - t.Fatal(err) - } - - // as the process exited, CmdRun must finish and unblock. Wait for it - setTimeout(t, "Waiting for CmdRun timed out", 10*time.Second, func() { - <-c1 - - go func() { - cli.CmdWait(container.ID) - }() - - if _, err := bufio.NewReader(stdout).ReadString('\n'); err != nil { - t.Fatal(err) - } - }) - - // Make sure that the client has been disconnected - setTimeout(t, "The client should have been disconnected once the remote process exited.", 2*time.Second, func() { - // Expecting pipe i/o error, just check that read does not block - stdin.Read([]byte{}) - }) - - // Cleanup pipes - if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil { - t.Fatal(err) - } -} - // Expected behaviour: the process dies when the client disconnects func TestRunDisconnect(t *testing.T) {