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>
| ... | ... |
@@ -16,9 +16,21 @@ func platformConstructExitStatus(e libcontainerd.StateInfo) *container.ExitStatu |
| 16 | 16 |
|
| 17 | 17 |
// postRunProcessing perfoms any processing needed on the container after it has stopped. |
| 18 | 18 |
func (daemon *Daemon) postRunProcessing(container *container.Container, e libcontainerd.StateInfo) error {
|
| 19 |
- //TODO Windows - handle update processing here... |
|
| 20 | 19 |
if e.UpdatePending {
|
| 21 |
- return fmt.Errorf("Windows: Update handling not implemented.")
|
|
| 20 |
+ spec, err := daemon.createSpec(container) |
|
| 21 |
+ if err != nil {
|
|
| 22 |
+ return err |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ servicingOption := &libcontainerd.ServicingOption{
|
|
| 26 |
+ IsServicing: true, |
|
| 27 |
+ } |
|
| 28 |
+ |
|
| 29 |
+ // Create a new servicing container, which will start, complete the update, and merge back the |
|
| 30 |
+ // results if it succeeded, all as part of the below function call. |
|
| 31 |
+ if err := daemon.containerd.Create((container.ID + "_servicing"), *spec, servicingOption); err != nil {
|
|
| 32 |
+ return fmt.Errorf("Post-run update servicing failed: %s", err)
|
|
| 33 |
+ } |
|
| 22 | 34 |
} |
| 23 | 35 |
return nil |
| 24 | 36 |
} |
| ... | ... |
@@ -7,7 +7,7 @@ source 'hack/.vendor-helpers.sh' |
| 7 | 7 |
|
| 8 | 8 |
# the following lines are in sorted order, FYI |
| 9 | 9 |
clone git github.com/Azure/go-ansiterm 70b2c90b260171e829f1ebd7c17f600c11858dbe |
| 10 |
-clone git github.com/Microsoft/hcsshim v0.2.1 |
|
| 10 |
+clone git github.com/Microsoft/hcsshim v0.2.2 |
|
| 11 | 11 |
clone git github.com/Microsoft/go-winio v0.3.0 |
| 12 | 12 |
clone git github.com/Sirupsen/logrus v0.9.0 # logrus is a common dependency among multiple deps |
| 13 | 13 |
clone git github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a |
| ... | ... |
@@ -96,6 +96,7 @@ type containerInit struct {
|
| 96 | 96 |
HvPartition bool // True if it a Hyper-V Container |
| 97 | 97 |
EndpointList []string // List of networking endpoints to be attached to container |
| 98 | 98 |
HvRuntime *hvRuntime // Hyper-V container settings |
| 99 |
+ Servicing bool // True if this container is for servicing |
|
| 99 | 100 |
} |
| 100 | 101 |
|
| 101 | 102 |
// 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 |
| 157 | 157 |
} |
| 158 | 158 |
} |
| 159 | 159 |
|
| 160 |
+ for _, option := range options {
|
|
| 161 |
+ if s, ok := option.(*ServicingOption); ok {
|
|
| 162 |
+ cu.Servicing = s.IsServicing |
|
| 163 |
+ break |
|
| 164 |
+ } |
|
| 165 |
+ } |
|
| 166 |
+ |
|
| 160 | 167 |
if cu.HvPartition {
|
| 161 | 168 |
cu.SandboxPath = filepath.Dir(spec.Windows.LayerFolder) |
| 162 | 169 |
} else {
|
| ... | ... |
@@ -35,13 +35,32 @@ func (ctr *container) newProcess(friendlyName string) *process {
|
| 35 | 35 |
func (ctr *container) start() error {
|
| 36 | 36 |
var err error |
| 37 | 37 |
|
| 38 |
- // Start the container |
|
| 38 |
+ // Start the container. If this is a servicing container, this call will block |
|
| 39 |
+ // until the container is done with the servicing execution. |
|
| 39 | 40 |
logrus.Debugln("Starting container ", ctr.containerID)
|
| 40 | 41 |
if err = hcsshim.StartComputeSystem(ctr.containerID); err != nil {
|
| 41 | 42 |
logrus.Errorf("Failed to start compute system: %s", err)
|
| 42 | 43 |
return err |
| 43 | 44 |
} |
| 44 | 45 |
|
| 46 |
+ for _, option := range ctr.options {
|
|
| 47 |
+ if s, ok := option.(*ServicingOption); ok && s.IsServicing {
|
|
| 48 |
+ // Since the servicing operation is complete when StartCommputeSystem returns without error, |
|
| 49 |
+ // we can shutdown (which triggers merge) and exit early. |
|
| 50 |
+ const shutdownTimeout = 5 * 60 * 1000 // 4 minutes |
|
| 51 |
+ const terminateTimeout = 1 * 60 * 1000 // 1 minute |
|
| 52 |
+ if err := hcsshim.ShutdownComputeSystem(ctr.containerID, shutdownTimeout, ""); err != nil {
|
|
| 53 |
+ logrus.Errorf("Failed during cleanup of servicing container: %s", err)
|
|
| 54 |
+ // Terminate the container, ignoring errors. |
|
| 55 |
+ if err2 := hcsshim.TerminateComputeSystem(ctr.containerID, terminateTimeout, ""); err2 != nil {
|
|
| 56 |
+ logrus.Errorf("Failed to terminate container %s after shutdown failure: %q", ctr.containerID, err2)
|
|
| 57 |
+ } |
|
| 58 |
+ return err |
|
| 59 |
+ } |
|
| 60 |
+ return nil |
|
| 61 |
+ } |
|
| 62 |
+ } |
|
| 63 |
+ |
|
| 45 | 64 |
createProcessParms := hcsshim.CreateProcessParams{
|
| 46 | 65 |
EmulateConsole: ctr.ociSpec.Process.Terminal, |
| 47 | 66 |
WorkingDirectory: ctr.ociSpec.Process.Cwd, |
| ... | ... |
@@ -149,10 +168,13 @@ func (ctr *container) waitExit(pid uint32, processFriendlyName string, isFirstPr |
| 149 | 149 |
// If this is the init process, always call into vmcompute.dll to |
| 150 | 150 |
// shutdown the container after we have completed. |
| 151 | 151 |
if isFirstProcessToStart {
|
| 152 |
- |
|
| 153 |
- // TODO Windows - add call into hcsshim to check if an update |
|
| 154 |
- // is pending once that is available. |
|
| 155 |
- //si.UpdatePending = CHECK IF UPDATE NEEDED |
|
| 152 |
+ propertyCheckFlag := 1 // Include update pending check. |
|
| 153 |
+ csProperties, err := hcsshim.GetComputeSystemProperties(ctr.containerID, uint32(propertyCheckFlag)) |
|
| 154 |
+ if err != nil {
|
|
| 155 |
+ logrus.Warnf("GetComputeSystemProperties failed (container may have been killed): %s", err)
|
|
| 156 |
+ } else {
|
|
| 157 |
+ si.UpdatePending = csProperties.AreUpdatesPending |
|
| 158 |
+ } |
|
| 156 | 159 |
|
| 157 | 160 |
logrus.Debugf("Shutting down container %s", ctr.containerID)
|
| 158 | 161 |
// Explicit timeout here rather than hcsshim.TimeoutInfinte to avoid a |
| ... | ... |
@@ -22,7 +22,8 @@ type StateInfo struct {
|
| 22 | 22 |
CommonStateInfo |
| 23 | 23 |
|
| 24 | 24 |
// Platform specific StateInfo |
| 25 |
- UpdatePending bool |
|
| 25 |
+ |
|
| 26 |
+ UpdatePending bool // Indicates that there are some update operations pending that should be completed by a servicing container. |
|
| 26 | 27 |
} |
| 27 | 28 |
|
| 28 | 29 |
// Stats contains a stats properties from containerd. |
| ... | ... |
@@ -30,3 +31,9 @@ type Stats struct{}
|
| 30 | 30 |
|
| 31 | 31 |
// Resources defines updatable container resource values. |
| 32 | 32 |
type Resources struct{}
|
| 33 |
+ |
|
| 34 |
+// ServicingOption is an empty CreateOption with a no-op application that siginifies |
|
| 35 |
+// the container needs to be use for a Windows servicing operation. |
|
| 36 |
+type ServicingOption struct {
|
|
| 37 |
+ IsServicing bool |
|
| 38 |
+} |
| 17 | 22 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,43 @@ |
| 0 |
+package hcsshim |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ |
|
| 5 |
+ "github.com/Sirupsen/logrus" |
|
| 6 |
+) |
|
| 7 |
+ |
|
| 8 |
+// ComputeSystemProperties is a struct describing the returned properties. |
|
| 9 |
+type ComputeSystemProperties struct {
|
|
| 10 |
+ ID string |
|
| 11 |
+ Name string |
|
| 12 |
+ Stopped bool |
|
| 13 |
+ AreUpdatesPending bool |
|
| 14 |
+} |
|
| 15 |
+ |
|
| 16 |
+// GetComputeSystemProperties gets the properties for the compute system with the given ID. |
|
| 17 |
+func GetComputeSystemProperties(id string, flags uint32) (ComputeSystemProperties, error) {
|
|
| 18 |
+ title := "hcsshim::GetComputeSystemProperties " |
|
| 19 |
+ |
|
| 20 |
+ csProps := ComputeSystemProperties{
|
|
| 21 |
+ Stopped: false, |
|
| 22 |
+ AreUpdatesPending: false, |
|
| 23 |
+ } |
|
| 24 |
+ |
|
| 25 |
+ logrus.Debugf("Calling proc")
|
|
| 26 |
+ var buffer *uint16 |
|
| 27 |
+ err := getComputeSystemProperties(id, flags, &buffer) |
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ err = makeError(err, title, "") |
|
| 30 |
+ logrus.Error(err) |
|
| 31 |
+ return csProps, err |
|
| 32 |
+ } |
|
| 33 |
+ propData := convertAndFreeCoTaskMemString(buffer) |
|
| 34 |
+ logrus.Debugf(title+" - succeeded output=%s", propData) |
|
| 35 |
+ |
|
| 36 |
+ if err = json.Unmarshal([]byte(propData), &csProps); err != nil {
|
|
| 37 |
+ logrus.Error(err) |
|
| 38 |
+ return csProps, err |
|
| 39 |
+ } |
|
| 40 |
+ |
|
| 41 |
+ return csProps, nil |
|
| 42 |
+} |
| ... | ... |
@@ -48,6 +48,7 @@ import ( |
| 48 | 48 |
//sys terminateComputeSystem(id string) (hr error) = vmcompute.TerminateComputeSystem? |
| 49 | 49 |
//sys terminateProcessInComputeSystem(id string, pid uint32) (hr error) = vmcompute.TerminateProcessInComputeSystem? |
| 50 | 50 |
//sys waitForProcessInComputeSystem(id string, pid uint32, timeout uint32, exitCode *uint32) (hr error) = vmcompute.WaitForProcessInComputeSystem? |
| 51 |
+//sys getComputeSystemProperties(id string, flags uint32, properties **uint16) (hr error) = vmcompute.GetComputeSystemProperties? |
|
| 51 | 52 |
|
| 52 | 53 |
//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall? |
| 53 | 54 |
|
| ... | ... |
@@ -48,6 +48,7 @@ var ( |
| 48 | 48 |
procTerminateComputeSystem = modvmcompute.NewProc("TerminateComputeSystem")
|
| 49 | 49 |
procTerminateProcessInComputeSystem = modvmcompute.NewProc("TerminateProcessInComputeSystem")
|
| 50 | 50 |
procWaitForProcessInComputeSystem = modvmcompute.NewProc("WaitForProcessInComputeSystem")
|
| 51 |
+ procGetComputeSystemProperties = modvmcompute.NewProc("GetComputeSystemProperties")
|
|
| 51 | 52 |
procHNSCall = modvmcompute.NewProc("HNSCall")
|
| 52 | 53 |
) |
| 53 | 54 |
|
| ... | ... |
@@ -713,6 +714,26 @@ func _waitForProcessInComputeSystem(id *uint16, pid uint32, timeout uint32, exit |
| 713 | 713 |
return |
| 714 | 714 |
} |
| 715 | 715 |
|
| 716 |
+func getComputeSystemProperties(id string, flags uint32, properties **uint16) (hr error) {
|
|
| 717 |
+ var _p0 *uint16 |
|
| 718 |
+ _p0, hr = syscall.UTF16PtrFromString(id) |
|
| 719 |
+ if hr != nil {
|
|
| 720 |
+ return |
|
| 721 |
+ } |
|
| 722 |
+ return _getComputeSystemProperties(_p0, flags, properties) |
|
| 723 |
+} |
|
| 724 |
+ |
|
| 725 |
+func _getComputeSystemProperties(id *uint16, flags uint32, properties **uint16) (hr error) {
|
|
| 726 |
+ if hr = procGetComputeSystemProperties.Find(); hr != nil {
|
|
| 727 |
+ return |
|
| 728 |
+ } |
|
| 729 |
+ r0, _, _ := syscall.Syscall(procGetComputeSystemProperties.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(flags), uintptr(unsafe.Pointer(properties))) |
|
| 730 |
+ if int32(r0) < 0 {
|
|
| 731 |
+ hr = syscall.Errno(win32FromHresult(r0)) |
|
| 732 |
+ } |
|
| 733 |
+ return |
|
| 734 |
+} |
|
| 735 |
+ |
|
| 716 | 736 |
func _hnsCall(method string, path string, object string, response **uint16) (hr error) {
|
| 717 | 737 |
var _p0 *uint16 |
| 718 | 738 |
_p0, hr = syscall.UTF16PtrFromString(method) |