diff --git a/components/engine/commands.go b/components/engine/commands.go index 33a0997b9c..ca1f7890c3 100644 --- a/components/engine/commands.go +++ b/components/engine/commands.go @@ -799,7 +799,8 @@ func (srv *Server) CmdAttach(stdin io.ReadCloser, stdout io.Writer, args ...stri if container == nil { return fmt.Errorf("No such container: %s", name) } - return <-container.Attach(stdin, stdout, stdout) + + return <-container.Attach(stdin, nil, stdout, stdout) } // Ports type - Used to parse multiple -p flags @@ -901,11 +902,17 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) } } var ( - cStdin io.Reader + cStdin io.ReadCloser cStdout, cStderr io.Writer ) if config.AttachStdin { - cStdin = stdin + r, w := io.Pipe() + go func() { + defer w.Close() + defer Debugf("Closing buffered stdin pipe") + io.Copy(w, stdin) + }() + cStdin = r } if config.AttachStdout { cStdout = stdout @@ -913,7 +920,8 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) if config.AttachStderr { cStderr = stdout // FIXME: rcli can't differentiate stdout from stderr } - attachErr := container.Attach(cStdin, cStdout, cStderr) + + attachErr := container.Attach(cStdin, stdin, cStdout, cStderr) Debugf("Starting\n") if err := container.Start(); err != nil { return err diff --git a/components/engine/commands_test.go b/components/engine/commands_test.go index 0dffe3dc17..a68aea7a46 100644 --- a/components/engine/commands_test.go +++ b/components/engine/commands_test.go @@ -93,9 +93,7 @@ func TestRunExit(t *testing.T) { stdout, stdoutPipe := io.Pipe() c1 := make(chan struct{}) go func() { - if err := srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat"); err != nil { - t.Error(err) - } + srv.CmdRun(stdin, stdoutPipe, "-i", GetTestImage(runtime).Id, "/bin/cat") close(c1) }() @@ -123,11 +121,8 @@ func TestRunExit(t *testing.T) { // 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() { - if _, err := stdin.Read([]byte{}); err != nil { - if err != io.EOF { - t.Fatal(err) - } - } + // Expecting pipe i/o error, just check that read does not block + stdin.Read([]byte{}) }) // Cleanup pipes @@ -293,6 +288,7 @@ func TestAttachDisconnect(t *testing.T) { setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() { <-c1 }) + // We closed stdin, expect /bin/cat to still be running // Wait a little bit to make sure container.monitor() did his thing err = container.WaitTimeout(500 * time.Millisecond) diff --git a/components/engine/container.go b/components/engine/container.go index adb1602162..60c761ba8f 100644 --- a/components/engine/container.go +++ b/components/engine/container.go @@ -257,9 +257,9 @@ func (container *Container) start() error { return container.cmd.Start() } -func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io.Writer) chan error { - var cStdout io.ReadCloser - var cStderr io.ReadCloser +func (container *Container) Attach(stdin io.ReadCloser, stdinCloser io.Closer, stdout io.Writer, stderr io.Writer) chan error { + var cStdout, cStderr io.ReadCloser + var nJobs int errors := make(chan error, 3) if stdin != nil && container.Config.OpenStdin { @@ -277,7 +277,8 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io. if err != nil { Debugf("[error] attach stdout: %s\n", err) } - errors <- err + // Discard error, expecting pipe error + errors <- nil }() } } @@ -290,6 +291,15 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io. go func() { Debugf("[start] attach stdout\n") defer Debugf("[end] attach stdout\n") + // If we are in StdinOnce mode, then close stdin + if container.Config.StdinOnce { + if stdin != nil { + defer stdin.Close() + } + if stdinCloser != nil { + defer stdinCloser.Close() + } + } _, err := io.Copy(stdout, cStdout) if err != nil { Debugf("[error] attach stdout: %s\n", err) @@ -307,6 +317,15 @@ func (container *Container) Attach(stdin io.Reader, stdout io.Writer, stderr io. go func() { Debugf("[start] attach stderr\n") defer Debugf("[end] attach stderr\n") + // If we are in StdinOnce mode, then close stdin + if container.Config.StdinOnce { + if stdin != nil { + defer stdin.Close() + } + if stdinCloser != nil { + defer stdinCloser.Close() + } + } _, err := io.Copy(stderr, cStderr) if err != nil { Debugf("[error] attach stderr: %s\n", err)