Browse code

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 authored on 2016/06/09 10:57:25
Showing 1 changed files
... ...
@@ -37,6 +37,13 @@ func (ctr *container) newProcess(friendlyName string) *process {
37 37
 
38 38
 func (ctr *container) start() error {
39 39
 	var err error
40
+	isServicing := false
41
+
42
+	for _, option := range ctr.options {
43
+		if s, ok := option.(*ServicingOption); ok && s.IsServicing {
44
+			isServicing = true
45
+		}
46
+	}
40 47
 
41 48
 	// Start the container.  If this is a servicing container, this call will block
42 49
 	// until the container is done with the servicing execution.
... ...
@@ -51,14 +58,6 @@ func (ctr *container) start() error {
51 51
 		return err
52 52
 	}
53 53
 
54
-	for _, option := range ctr.options {
55
-		if s, ok := option.(*ServicingOption); ok && s.IsServicing {
56
-			// Since the servicing operation is complete when Start returns without error,
57
-			// we can shutdown (which triggers merge) and exit early.
58
-			return ctr.shutdown()
59
-		}
60
-	}
61
-
62 54
 	// Note we always tell HCS to
63 55
 	// create stdout as it's required regardless of '-i' or '-t' options, so that
64 56
 	// docker can always grab the output through logs. We also tell HCS to always
... ...
@@ -68,9 +67,9 @@ func (ctr *container) start() error {
68 68
 		EmulateConsole:   ctr.ociSpec.Process.Terminal,
69 69
 		WorkingDirectory: ctr.ociSpec.Process.Cwd,
70 70
 		ConsoleSize:      ctr.ociSpec.Process.InitialConsoleSize,
71
-		CreateStdInPipe:  true,
72
-		CreateStdOutPipe: true,
73
-		CreateStdErrPipe: !ctr.ociSpec.Process.Terminal,
71
+		CreateStdInPipe:  !isServicing,
72
+		CreateStdOutPipe: !isServicing,
73
+		CreateStdErrPipe: !ctr.ociSpec.Process.Terminal && !isServicing,
74 74
 	}
75 75
 
76 76
 	// Configure the environment for the process
... ...
@@ -95,6 +94,19 @@ func (ctr *container) start() error {
95 95
 	pid := hcsProcess.Pid()
96 96
 	ctr.process.hcsProcess = hcsProcess
97 97
 
98
+	// If this is a servicing container, wait on the process synchronously here and
99
+	// immediately call shutdown/terminate when it returns.
100
+	if isServicing {
101
+		exitCode := ctr.waitProcessExitCode(&ctr.process)
102
+
103
+		if exitCode != 0 {
104
+			logrus.Warnf("Servicing container %s returned non-zero exit code %d", ctr.containerID, exitCode)
105
+			return ctr.terminate()
106
+		}
107
+
108
+		return ctr.shutdown()
109
+	}
110
+
98 111
 	var stdout, stderr io.ReadCloser
99 112
 	var stdin io.WriteCloser
100 113
 	stdin, stdout, stderr, err = hcsProcess.Stdio()
... ...
@@ -145,12 +157,8 @@ func (ctr *container) start() error {
145 145
 
146 146
 }
147 147
 
148
-// waitExit runs as a goroutine waiting for the process to exit. It's
149
-// equivalent to (in the linux containerd world) where events come in for
150
-// state change notifications from containerd.
151
-func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error {
152
-	logrus.Debugln("waitExit on pid", process.systemPid)
153
-
148
+// waitProcessExitCode will wait for the given process to exit and return its error code.
149
+func (ctr *container) waitProcessExitCode(process *process) int {
154 150
 	// Block indefinitely for the process to exit.
155 151
 	err := process.hcsProcess.Wait()
156 152
 	if err != nil {
... ...
@@ -176,6 +184,17 @@ func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) err
176 176
 		logrus.Error(err)
177 177
 	}
178 178
 
179
+	return exitCode
180
+}
181
+
182
+// waitExit runs as a goroutine waiting for the process to exit. It's
183
+// equivalent to (in the linux containerd world) where events come in for
184
+// state change notifications from containerd.
185
+func (ctr *container) waitExit(process *process, isFirstProcessToStart bool) error {
186
+	logrus.Debugln("waitExit on pid", process.systemPid)
187
+
188
+	exitCode := ctr.waitProcessExitCode(process)
189
+
179 190
 	// Assume the container has exited
180 191
 	si := StateInfo{
181 192
 		CommonStateInfo: CommonStateInfo{