diff --git a/daemon/exec.go b/daemon/exec.go index 6f9e53bfae..ed620dc25a 100644 --- a/daemon/exec.go +++ b/daemon/exec.go @@ -159,7 +159,7 @@ func (daemon *Daemon) ContainerExecCreate(name string, options *containertypes.E // ContainerExecStart starts a previously set up exec instance. The // std streams are set up. // If ctx is cancelled, the process is terminated. -func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, options backend.ExecStartConfig) (err error) { +func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, options backend.ExecStartConfig) (retErr error) { var ( cStdin io.ReadCloser cStdout, cStderr io.Writer @@ -173,13 +173,12 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio ec.Lock() if ec.ExitCode != nil { ec.Unlock() - err := fmt.Errorf("Error: Exec command %s has already run", ec.ID) - return errdefs.Conflict(err) + return errdefs.Conflict(fmt.Errorf("exec command %s has already run", ec.ID)) } if ec.Running { ec.Unlock() - return errdefs.Conflict(fmt.Errorf("Error: Exec command %s is already running", ec.ID)) + return errdefs.Conflict(fmt.Errorf("exec command %s is already running", ec.ID)) } ec.Running = true ec.Unlock() @@ -190,7 +189,7 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio }) defer func() { - if err != nil { + if retErr != nil { ec.Lock() ec.Container.ExecCommands.Delete(ec.ID) ec.Running = false @@ -210,9 +209,11 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio if ec.OpenStdin && options.Stdin != nil { r, w := io.Pipe() go func() { - defer w.Close() - defer log.G(ctx).Debug("Closing buffered stdin pipe") - pools.Copy(w, options.Stdin) + defer func() { + log.G(ctx).Debug("Closing buffered stdin pipe") + _ = w.Close() + }() + _, _ = pools.Copy(w, options.Stdin) }() cStdin = r } @@ -231,6 +232,9 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio p := &specs.Process{} if runtime.GOOS != "windows" { + // TODO(thaJeztah): also enable on Windows; + // This was added in https://github.com/moby/moby/commit/7603c22c7365d7d7150597fe396e0707d6e561da, + // which mentions that it failed on Windows "Probably needs to wait for container to be in running state" ctr, err := daemon.containerdClient.LoadContainer(ctx, ec.Container.ID) if err != nil { return err @@ -239,11 +243,16 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio if err != nil { return err } - spec := specs.Spec{Process: p} + + // Technically, this should be a [specs.Spec], but we're only + // interested in the Process field. + spec := struct{ Process *specs.Process }{p} if err := json.Unmarshal(md.Spec.GetValue(), &spec); err != nil { return err } } + + // merge/override properties of the container's process with the exec-config. p.Args = append([]string{ec.Entrypoint}, ec.Args...) p.Env = ec.Env p.Cwd = ec.WorkingDir @@ -308,15 +317,15 @@ func (daemon *Daemon) ContainerExecStart(ctx context.Context, name string, optio select { case <-ctx.Done(): - log := log.G(ctx). - WithField("container", ec.Container.ID). - WithField("exec", ec.ID) - log.Debug("Sending KILL signal to container process") + logger := log.G(ctx).WithFields(log.Fields{ + "container": ec.Container.ID, + "exeec": ec.ID, + }) + logger.Debug("Sending KILL signal to container process") sigCtx, cancelFunc := context.WithTimeout(context.Background(), 30*time.Second) defer cancelFunc() - err := ec.Process.Kill(sigCtx, signal.SignalMap["KILL"]) - if err != nil { - log.WithError(err).Error("Could not send KILL signal to container process") + if err := ec.Process.Kill(sigCtx, signal.SignalMap["KILL"]); err != nil { + logger.WithError(err).Error("Could not send KILL signal to container process") } return ctx.Err() case err := <-attachErr: