Prechádzať zdrojové kódy

Updating call sequence for servicing Windows containers

This change adjusts the calling pattern for servcing containers to use waiting on the process instead of expecting start to block.  This is safer, as it avoids timeouts in the start code path for the potentially expensive update operation.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>
Stefan J. Wernli 9 rokov pred
rodič
commit
f2ad7be2c4
1 zmenil súbory, kde vykonal 36 pridanie a 17 odobranie
  1. 36 17
      libcontainerd/container_windows.go

+ 36 - 17
libcontainerd/container_windows.go

@@ -37,6 +37,13 @@ func (ctr *container) newProcess(friendlyName string) *process {
 
 
 func (ctr *container) start() error {
 func (ctr *container) start() error {
 	var err 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
 	// Start the container.  If this is a servicing container, this call will block
 	// until the container is done with the servicing execution.
 	// until the container is done with the servicing execution.
@@ -51,14 +58,6 @@ func (ctr *container) start() error {
 		return err
 		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
 	// Note we always tell HCS to
 	// create stdout as it's required regardless of '-i' or '-t' options, so that
 	// 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
 	// 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,
 		EmulateConsole:   ctr.ociSpec.Process.Terminal,
 		WorkingDirectory: ctr.ociSpec.Process.Cwd,
 		WorkingDirectory: ctr.ociSpec.Process.Cwd,
 		ConsoleSize:      ctr.ociSpec.Process.InitialConsoleSize,
 		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
 	// Configure the environment for the process
@@ -95,6 +94,19 @@ func (ctr *container) start() error {
 	pid := hcsProcess.Pid()
 	pid := hcsProcess.Pid()
 	ctr.process.hcsProcess = hcsProcess
 	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 stdout, stderr io.ReadCloser
 	var stdin io.WriteCloser
 	var stdin io.WriteCloser
 	stdin, stdout, stderr, err = hcsProcess.Stdio()
 	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.
 	// Block indefinitely for the process to exit.
 	err := process.hcsProcess.Wait()
 	err := process.hcsProcess.Wait()
 	if err != nil {
 	if err != nil {
@@ -176,6 +184,17 @@ func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) err
 		logrus.Error(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
 	// Assume the container has exited
 	si := StateInfo{
 	si := StateInfo{
 		CommonStateInfo: CommonStateInfo{
 		CommonStateInfo: CommonStateInfo{