diff --git a/libcontainerd/container_windows.go b/libcontainerd/container_windows.go index 152a7a2814..7e36e857ec 100644 --- a/libcontainerd/container_windows.go +++ b/libcontainerd/container_windows.go @@ -37,6 +37,13 @@ func (ctr *container) newProcess(friendlyName string) *process { func (ctr *container) start() error { var err error + isServicing := false + + for _, option := range ctr.options { + if s, ok := option.(*ServicingOption); ok && s.IsServicing { + isServicing = true + } + } // Start the container. If this is a servicing container, this call will block // until the container is done with the servicing execution. @@ -51,14 +58,6 @@ func (ctr *container) start() error { return err } - for _, option := range ctr.options { - if s, ok := option.(*ServicingOption); ok && s.IsServicing { - // Since the servicing operation is complete when Start returns without error, - // we can shutdown (which triggers merge) and exit early. - return ctr.shutdown() - } - } - // Note we always tell HCS to // create stdout as it's required regardless of '-i' or '-t' options, so that // docker can always grab the output through logs. We also tell HCS to always @@ -68,9 +67,9 @@ func (ctr *container) start() error { EmulateConsole: ctr.ociSpec.Process.Terminal, WorkingDirectory: ctr.ociSpec.Process.Cwd, ConsoleSize: ctr.ociSpec.Process.InitialConsoleSize, - CreateStdInPipe: true, - CreateStdOutPipe: true, - CreateStdErrPipe: !ctr.ociSpec.Process.Terminal, + CreateStdInPipe: !isServicing, + CreateStdOutPipe: !isServicing, + CreateStdErrPipe: !ctr.ociSpec.Process.Terminal && !isServicing, } // Configure the environment for the process @@ -95,6 +94,19 @@ func (ctr *container) start() error { pid := hcsProcess.Pid() ctr.process.hcsProcess = hcsProcess + // If this is a servicing container, wait on the process synchronously here and + // immediately call shutdown/terminate when it returns. + if isServicing { + exitCode := ctr.waitProcessExitCode(&ctr.process) + + if exitCode != 0 { + logrus.Warnf("Servicing container %s returned non-zero exit code %d", ctr.containerID, exitCode) + return ctr.terminate() + } + + return ctr.shutdown() + } + var stdout, stderr io.ReadCloser var stdin io.WriteCloser stdin, stdout, stderr, err = hcsProcess.Stdio() @@ -145,12 +157,8 @@ func (ctr *container) start() error { } -// waitExit runs as a goroutine waiting for the process to exit. It's -// equivalent to (in the linux containerd world) where events come in for -// state change notifications from containerd. -func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error { - logrus.Debugln("waitExit on pid", process.systemPid) - +// waitProcessExitCode will wait for the given process to exit and return its error code. +func (ctr *container) waitProcessExitCode(process *process) int { // Block indefinitely for the process to exit. err := process.hcsProcess.Wait() if err != nil { @@ -176,6 +184,17 @@ func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) err logrus.Error(err) } + return exitCode +} + +// waitExit runs as a goroutine waiting for the process to exit. It's +// equivalent to (in the linux containerd world) where events come in for +// state change notifications from containerd. +func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error { + logrus.Debugln("waitExit on pid", process.systemPid) + + exitCode := ctr.waitProcessExitCode(process) + // Assume the container has exited si := StateInfo{ CommonStateInfo: CommonStateInfo{