Pārlūkot izejas kodu

Merge pull request #22055 from Microsoft/sjw/servicing

Adding servicing update to postRunProcessing for Windows containers.
John Howard 9 gadi atpakaļ
vecāks
revīzija
66ebc34235

+ 14 - 2
daemon/monitor_windows.go

@@ -16,9 +16,21 @@ func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatu
 
 // postRunProcessing perfoms any processing needed on the container after it has stopped.
 func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
-	//TODO Windows - handle update processing here...
 	if e.UpdatePending {
-		return fmt.Errorf("Windows: Update handling not implemented.")
+		spec, err := daemon.createSpec(container)
+		if err != nil {
+			return err
+		}
+
+		servicingOption := &libcontainerd.ServicingOption{
+			IsServicing: true,
+		}
+
+		// Create a new servicing container, which will start, complete the update, and merge back the
+		// results if it succeeded, all as part of the below function call.
+		if err := daemon.containerd.Create((container.ID + "_servicing"), *spec, servicingOption); err != nil {
+			return fmt.Errorf("Post-run update servicing failed: %s", err)
+		}
 	}
 	return nil
 }

+ 1 - 1
hack/vendor.sh

@@ -7,7 +7,7 @@ source 'hack/.vendor-helpers.sh'
 
 # the following lines are in sorted order, FYI
 clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe
-clone git github.com/Microsoft/hcsshim v0.2.1
+clone git github.com/Microsoft/hcsshim v0.2.2
 clone git github.com/Microsoft/go-winio v0.3.0
 clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps
 clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a

+ 8 - 0
libcontainerd/client_windows.go

@@ -96,6 +96,7 @@ type containerInit struct {
 	HvPartition             bool        // True if it a Hyper-V Container
 	EndpointList            []string    // List of networking endpoints to be attached to container
 	HvRuntime               *hvRuntime  // Hyper-V container settings
+	Servicing               bool        // True if this container is for servicing
 }
 
 // defaultOwner is a tag passed to HCS to allow it to differentiate between
@@ -157,6 +158,13 @@ func (clnt *client) Create(containerID string, spec Spec, options ...CreateOptio
 		}
 	}
 
+	for _, option := range options {
+		if s, ok := option.(*ServicingOption); ok {
+			cu.Servicing = s.IsServicing
+			break
+		}
+	}
+
 	if cu.HvPartition {
 		cu.SandboxPath = filepath.Dir(spec.Windows.LayerFolder)
 	} else {

+ 27 - 5
libcontainerd/container_windows.go

@@ -35,13 +35,32 @@ func (ctr *container) newProcess(friendlyName string) *process {
 func (ctr *container) start() error {
 	var err error
 
-	// Start the container
+	// Start the container.  If this is a servicing container, this call will block
+	// until the container is done with the servicing execution.
 	logrus.Debugln("Starting container ", ctr.containerID)
 	if err = hcsshim.StartComputeSystem(ctr.containerID); err != nil {
 		logrus.Errorf("Failed to start compute system: %s", err)
 		return err
 	}
 
+	for _, option := range ctr.options {
+		if s, ok := option.(*ServicingOption); ok && s.IsServicing {
+			// Since the servicing operation is complete when StartCommputeSystem returns without error,
+			// we can shutdown (which triggers merge) and exit early.
+			const shutdownTimeout = 5 * 60 * 1000  // 4 minutes
+			const terminateTimeout = 1 * 60 * 1000 // 1 minute
+			if err := hcsshim.ShutdownComputeSystem(ctr.containerID, shutdownTimeout, ""); err != nil {
+				logrus.Errorf("Failed during cleanup of servicing container: %s", err)
+				// Terminate the container, ignoring errors.
+				if err2 := hcsshim.TerminateComputeSystem(ctr.containerID, terminateTimeout, ""); err2 != nil {
+					logrus.Errorf("Failed to terminate container %s after shutdown failure: %q", ctr.containerID, err2)
+				}
+				return err
+			}
+			return nil
+		}
+	}
+
 	createProcessParms := hcsshim.CreateProcessParams{
 		EmulateConsole:   ctr.ociSpec.Process.Terminal,
 		WorkingDirectory: ctr.ociSpec.Process.Cwd,
@@ -149,10 +168,13 @@ func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstPr
 	// If this is the init process, always call into vmcompute.dll to
 	// shutdown the container after we have completed.
 	if isFirstProcessToStart {
-
-		// TODO Windows - add call into hcsshim to check if an update
-		// is pending once that is available.
-		//si.UpdatePending = CHECK IF UPDATE NEEDED
+		propertyCheckFlag := 1 // Include update pending check.
+		csProperties, err := hcsshim.GetComputeSystemProperties(ctr.containerID, uint32(propertyCheckFlag))
+		if err != nil {
+			logrus.Warnf("GetComputeSystemProperties failed (container may have been killed): %s", err)
+		} else {
+			si.UpdatePending = csProperties.AreUpdatesPending
+		}
 
 		logrus.Debugf("Shutting down container %s", ctr.containerID)
 		// Explicit timeout here rather than hcsshim.TimeoutInfinte to avoid a

+ 8 - 1
libcontainerd/types_windows.go

@@ -22,7 +22,8 @@ type StateInfo struct {
 	CommonStateInfo
 
 	// Platform specific StateInfo
-	UpdatePending bool
+
+	UpdatePending bool // Indicates that there are some update operations pending that should be completed by a servicing container.
 }
 
 // Stats contains a stats properties from containerd.
@@ -30,3 +31,9 @@ type Stats struct{}
 
 // Resources defines updatable container resource values.
 type Resources struct{}
+
+// ServicingOption is an empty CreateOption with a no-op application that siginifies
+// the container needs to be use for a Windows servicing operation.
+type ServicingOption struct {
+	IsServicing bool
+}

+ 5 - 0
libcontainerd/utils_windows.go

@@ -14,3 +14,8 @@ func setupEnvironmentVariables(a []string) map[string]string {
 	}
 	return r
 }
+
+// Apply for a servicing option is a no-op.
+func (s *ServicingOption) Apply(interface{}) error {
+	return nil
+}

+ 43 - 0
vendor/src/github.com/Microsoft/hcsshim/getcomputesystemproperties.go

@@ -0,0 +1,43 @@
+package hcsshim
+
+import (
+	"encoding/json"
+
+	"github.com/Sirupsen/logrus"
+)
+
+// ComputeSystemProperties is a struct describing the returned properties.
+type ComputeSystemProperties struct {
+	ID                string
+	Name              string
+	Stopped           bool
+	AreUpdatesPending bool
+}
+
+// GetComputeSystemProperties gets the properties for the compute system with the given ID.
+func GetComputeSystemProperties(id string, flags uint32) (ComputeSystemProperties, error) {
+	title := "hcsshim::GetComputeSystemProperties "
+
+	csProps := ComputeSystemProperties{
+		Stopped:           false,
+		AreUpdatesPending: false,
+	}
+
+	logrus.Debugf("Calling proc")
+	var buffer *uint16
+	err := getComputeSystemProperties(id, flags, &buffer)
+	if err != nil {
+		err = makeError(err, title, "")
+		logrus.Error(err)
+		return csProps, err
+	}
+	propData := convertAndFreeCoTaskMemString(buffer)
+	logrus.Debugf(title+" - succeeded output=%s", propData)
+
+	if err = json.Unmarshal([]byte(propData), &csProps); err != nil {
+		logrus.Error(err)
+		return csProps, err
+	}
+
+	return csProps, nil
+}

+ 1 - 0
vendor/src/github.com/Microsoft/hcsshim/hcsshim.go

@@ -48,6 +48,7 @@ import (
 //sys terminateComputeSystem(id string) (hr error) = vmcompute.TerminateComputeSystem?
 //sys terminateProcessInComputeSystem(id string, pid uint32) (hr error) = vmcompute.TerminateProcessInComputeSystem?
 //sys waitForProcessInComputeSystem(id string, pid uint32, timeout uint32, exitCode *uint32) (hr error) = vmcompute.WaitForProcessInComputeSystem?
+//sys getComputeSystemProperties(id string, flags uint32, properties **uint16) (hr error) = vmcompute.GetComputeSystemProperties?
 
 //sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall?
 

+ 21 - 0
vendor/src/github.com/Microsoft/hcsshim/zhcsshim.go

@@ -48,6 +48,7 @@ var (
 	procTerminateComputeSystem                     = modvmcompute.NewProc("TerminateComputeSystem")
 	procTerminateProcessInComputeSystem            = modvmcompute.NewProc("TerminateProcessInComputeSystem")
 	procWaitForProcessInComputeSystem              = modvmcompute.NewProc("WaitForProcessInComputeSystem")
+	procGetComputeSystemProperties                 = modvmcompute.NewProc("GetComputeSystemProperties")
 	procHNSCall                                    = modvmcompute.NewProc("HNSCall")
 )
 
@@ -713,6 +714,26 @@ func _waitForProcessInComputeSystem(id *uint16, pid uint32, timeout uint32, exit
 	return
 }
 
+func getComputeSystemProperties(id string, flags uint32, properties **uint16) (hr error) {
+	var _p0 *uint16
+	_p0, hr = syscall.UTF16PtrFromString(id)
+	if hr != nil {
+		return
+	}
+	return _getComputeSystemProperties(_p0, flags, properties)
+}
+
+func _getComputeSystemProperties(id *uint16, flags uint32, properties **uint16) (hr error) {
+	if hr = procGetComputeSystemProperties.Find(); hr != nil {
+		return
+	}
+	r0, _, _ := syscall.Syscall(procGetComputeSystemProperties.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(flags), uintptr(unsafe.Pointer(properties)))
+	if int32(r0) < 0 {
+		hr = syscall.Errno(win32FromHresult(r0))
+	}
+	return
+}
+
 func _hnsCall(method string, path string, object string, response **uint16) (hr error) {
 	var _p0 *uint16
 	_p0, hr = syscall.UTF16PtrFromString(method)