Adding servicing update to postRunProcessing for Windows containers.

This change enables the workflow of finishing installing Windows OS updates in the container after it has completed running, via a special servicing container.

Signed-off-by: Stefan J. Wernli <swernli@microsoft.com>
This commit is contained in:
Stefan J. Wernli 2016-04-13 13:34:07 -07:00
parent e974eadd94
commit da92dad59f
9 changed files with 128 additions and 9 deletions

View file

@ -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
}

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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?

View file

@ -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)