Merge pull request #16289 from cpuguy83/11957_fix_stdin_block_after_container_exit

Ensure stdin does not block after container stop
This commit is contained in:
Jess Frazelle 2015-09-23 15:29:48 -07:00
commit c0c941627a
2 changed files with 42 additions and 25 deletions

View File

@ -16,7 +16,6 @@ import (
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
"github.com/docker/docker/api" "github.com/docker/docker/api"
"github.com/docker/docker/autogen/dockerversion" "github.com/docker/docker/autogen/dockerversion"
"github.com/docker/docker/pkg/promise"
"github.com/docker/docker/pkg/stdcopy" "github.com/docker/docker/pkg/stdcopy"
"github.com/docker/docker/pkg/term" "github.com/docker/docker/pkg/term"
) )
@ -186,8 +185,6 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
started <- rwc started <- rwc
} }
var receiveStdout chan error
var oldState *term.State var oldState *term.State
if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" { if in != nil && setRawTerminal && cli.isTerminalIn && os.Getenv("NORAW") == "" {
@ -198,19 +195,15 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
defer term.RestoreTerminal(cli.inFd, oldState) defer term.RestoreTerminal(cli.inFd, oldState)
} }
receiveStdout := make(chan error, 1)
if stdout != nil || stderr != nil { if stdout != nil || stderr != nil {
receiveStdout = promise.Go(func() (err error) { go func() {
defer func() { defer func() {
if in != nil { if in != nil {
if setRawTerminal && cli.isTerminalIn { if setRawTerminal && cli.isTerminalIn {
term.RestoreTerminal(cli.inFd, oldState) term.RestoreTerminal(cli.inFd, oldState)
} }
// For some reason this Close call blocks on darwin.. in.Close()
// As the client exists right after, simply discard the close
// until we find a better solution.
if runtime.GOOS != "darwin" {
in.Close()
}
} }
}() }()
@ -221,11 +214,12 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
_, err = stdcopy.StdCopy(stdout, stderr, br) _, err = stdcopy.StdCopy(stdout, stderr, br)
} }
logrus.Debugf("[hijack] End of stdout") logrus.Debugf("[hijack] End of stdout")
return err receiveStdout <- err
}) }()
} }
sendStdin := promise.Go(func() error { stdinDone := make(chan struct{})
go func() {
if in != nil { if in != nil {
io.Copy(rwc, in) io.Copy(rwc, in)
logrus.Debugf("[hijack] End of stdin") logrus.Debugf("[hijack] End of stdin")
@ -238,22 +232,24 @@ func (cli *DockerCli) hijack(method, path string, setRawTerminal bool, in io.Rea
logrus.Debugf("Couldn't send EOF: %s", err) logrus.Debugf("Couldn't send EOF: %s", err)
} }
} }
// Discard errors due to pipe interruption close(stdinDone)
return nil }()
})
if stdout != nil || stderr != nil { select {
if err := <-receiveStdout; err != nil { case err := <-receiveStdout:
if err != nil {
logrus.Debugf("Error receiveStdout: %s", err) logrus.Debugf("Error receiveStdout: %s", err)
return err }
if cli.isTerminalIn {
return nil
}
case <-stdinDone:
if stdout != nil || stderr != nil {
if err := <-receiveStdout; err != nil {
logrus.Debugf("Error receiveStdout: %s", err)
}
} }
} }
if !cli.isTerminalIn {
if err := <-sendStdin; err != nil {
logrus.Debugf("Error sendStdin: %s", err)
return err
}
}
return nil return nil
} }

View File

@ -3384,3 +3384,24 @@ func (s *DockerSuite) TestTwoContainersInNetHost(c *check.C) {
dockerCmd(c, "stop", "first") dockerCmd(c, "stop", "first")
dockerCmd(c, "stop", "second") dockerCmd(c, "stop", "second")
} }
// #11957 - stdin with no tty does not exit if stdin is not closed even though container exited
func (s *DockerSuite) TestRunStdinBlockedAfterContainerExit(c *check.C) {
cmd := exec.Command(dockerBinary, "run", "-i", "--name=test", "busybox", "true")
in, err := cmd.StdinPipe()
c.Assert(err, check.IsNil)
defer in.Close()
c.Assert(cmd.Start(), check.IsNil)
waitChan := make(chan error)
go func() {
waitChan <- cmd.Wait()
}()
select {
case err := <-waitChan:
c.Assert(err, check.IsNil)
case <-time.After(3 * time.Second):
c.Fatal("timeout waiting for command to exit")
}
}