diff --git a/libcontainerd/remote/client.go b/libcontainerd/remote/client.go index 23c008d931..7e9234ca61 100644 --- a/libcontainerd/remote/client.go +++ b/libcontainerd/remote/client.go @@ -28,6 +28,7 @@ import ( "github.com/docker/docker/libcontainerd/queue" libcontainerdtypes "github.com/docker/docker/libcontainerd/types" "github.com/docker/docker/pkg/ioutils" + "github.com/hashicorp/go-multierror" v1 "github.com/opencontainers/image-spec/specs-go/v1" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -502,27 +503,43 @@ func (c *container) createIO(fifos *cio.FIFOSet, stdinCloseSync chan containerd. if io.Stdin != nil { var ( - err error + closeErr error stdinOnce sync.Once ) pipe := io.Stdin io.Stdin = ioutils.NewWriteCloserWrapper(pipe, func() error { stdinOnce.Do(func() { - err = pipe.Close() - // Do the rest in a new routine to avoid a deadlock if the - // Exec/Start call failed. - go func() { - p, ok := <-stdinCloseSync + closeErr = pipe.Close() + + select { + case p, ok := <-stdinCloseSync: if !ok { return } - err = p.CloseIO(context.Background(), containerd.WithStdinCloser) - if err != nil && strings.Contains(err.Error(), "transport is closing") { - err = nil + if err := closeStdin(context.Background(), p); err != nil { + if closeErr != nil { + closeErr = multierror.Append(closeErr, err) + } else { + // Avoid wrapping a single error in a multierror. + closeErr = err + } } - }() + default: + // The process wasn't ready. Close its stdin asynchronously. + go func() { + p, ok := <-stdinCloseSync + if !ok { + return + } + if err := closeStdin(context.Background(), p); err != nil { + c.client.logger.WithError(err). + WithField("container", c.c8dCtr.ID()). + Error("failed to close container stdin") + } + }() + } }) - return err + return closeErr }) } @@ -534,6 +551,14 @@ func (c *container) createIO(fifos *cio.FIFOSet, stdinCloseSync chan containerd. return rio, err } +func closeStdin(ctx context.Context, p containerd.Process) error { + err := p.CloseIO(ctx, containerd.WithStdinCloser) + if err != nil && strings.Contains(err.Error(), "transport is closing") { + err = nil + } + return err +} + func (c *client) processEvent(ctx context.Context, et libcontainerdtypes.EventType, ei libcontainerdtypes.EventInfo) { c.eventQ.Append(ei.ContainerID, func() { err := c.backend.ProcessEvent(ei.ContainerID, et, ei)