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:
commit
c0c941627a
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue