Windows: Remove servicing mode

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2018-02-09 10:08:47 -08:00
parent 600475715e
commit d4f37c0885
7 changed files with 32 additions and 170 deletions

View file

@ -111,10 +111,7 @@ func (daemon *Daemon) ProcessEvent(id string, e libcontainerd.EventType, ei libc
} }
daemon.setStateCounter(c) daemon.setStateCounter(c)
if err := c.CheckpointTo(daemon.containersReplica); err != nil { return c.CheckpointTo(daemon.containersReplica)
return err
}
return daemon.postRunProcessing(c, ei)
} }
if execConfig := c.ExecCommands.Get(ei.ProcessID); execConfig != nil { if execConfig := c.ExecCommands.Get(ei.ProcessID); execConfig != nil {

View file

@ -1,11 +0,0 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"github.com/docker/docker/container"
"github.com/docker/docker/libcontainerd"
)
// postRunProcessing perfoms any processing needed on the container after it has stopped.
func (daemon *Daemon) postRunProcessing(_ *container.Container, _ libcontainerd.EventInfo) error {
return nil
}

View file

@ -1,53 +0,0 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"context"
"github.com/docker/docker/container"
"github.com/docker/docker/libcontainerd"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// postRunProcessing starts a servicing container if required
func (daemon *Daemon) postRunProcessing(c *container.Container, ei libcontainerd.EventInfo) error {
if ei.ExitCode == 0 && ei.UpdatePending {
spec, err := daemon.createSpec(c)
if err != nil {
return err
}
// Turn on servicing
spec.Windows.Servicing = true
copts, err := daemon.getLibcontainerdCreateOptions(c)
if err != nil {
return err
}
// 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.
ctx := context.Background()
svcID := c.ID + "_servicing"
logger := logrus.WithField("container", svcID)
if err := daemon.containerd.Create(ctx, svcID, spec, copts); err != nil {
c.SetExitCode(-1)
return errors.Wrap(err, "post-run update servicing failed")
}
_, err = daemon.containerd.Start(ctx, svcID, "", false, nil)
if err != nil {
logger.WithError(err).Warn("failed to run servicing container")
if err := daemon.containerd.Delete(ctx, svcID); err != nil {
logger.WithError(err).Warn("failed to delete servicing container")
}
} else {
if _, _, err := daemon.containerd.DeleteTask(ctx, svcID); err != nil {
logger.WithError(err).Warn("failed to delete servicing container task")
}
if err := daemon.containerd.Delete(ctx, svcID); err != nil {
logger.WithError(err).Warn("failed to delete servicing container")
}
}
}
return nil
}

View file

@ -326,9 +326,6 @@ func (daemon *Daemon) createSpecWindowsFields(c *container.Container, s *specs.S
s.Windows.CredentialSpec = cs s.Windows.CredentialSpec = cs
} }
// Assume we are not starting a container for a servicing operation
s.Windows.Servicing = false
return nil return nil
} }

View file

@ -4237,35 +4237,6 @@ func (s *DockerSuite) TestRunCredentialSpecWellFormed(c *check.C) {
dockerCmd(c, "run", `--security-opt=credentialspec=file://valid.json`, "busybox", "true") dockerCmd(c, "run", `--security-opt=credentialspec=file://valid.json`, "busybox", "true")
} }
// Windows specific test to ensure that a servicing app container is started
// if necessary once a container exits. It does this by forcing a no-op
// servicing event and verifying the event from Hyper-V-Compute
func (s *DockerSuite) TestRunServicingContainer(c *check.C) {
testRequires(c, DaemonIsWindows, SameHostDaemon)
// This functionality does not exist in post-RS3 builds.
// Note we get the version number from the full build string, as Windows
// reports Windows 8 version 6.2 build 9200 from non-manifested binaries.
// Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx
v, err := kernel.GetKernelVersion()
c.Assert(err, checker.IsNil)
build, _ := strconv.Atoi(strings.Split(strings.SplitN(v.String(), " ", 3)[2][1:], ".")[0])
if build > 16299 {
c.Skip("Disabled on post-RS3 builds")
}
out := cli.DockerCmd(c, "run", "-d", testEnv.PlatformDefaults.BaseImage, "cmd", "/c", "mkdir c:\\programdata\\Microsoft\\Windows\\ContainerUpdates\\000_000_d99f45d0-ffc8-4af7-bd9c-ea6a62e035c9_200 && sc control cexecsvc 255").Combined()
containerID := strings.TrimSpace(out)
cli.WaitExited(c, containerID, 60*time.Second)
result := icmd.RunCommand("powershell", "echo", `(Get-WinEvent -ProviderName "Microsoft-Windows-Hyper-V-Compute" -FilterXPath 'Event[System[EventID=2010]]' -MaxEvents 1).Message`)
result.Assert(c, icmd.Success)
out2 := result.Combined()
c.Assert(out2, checker.Contains, `"Servicing":true`, check.Commentf("Servicing container does not appear to have been started: %s", out2))
c.Assert(out2, checker.Contains, `Windows Container (Servicing)`, check.Commentf("Didn't find 'Windows Container (Servicing): %s", out2))
c.Assert(out2, checker.Contains, containerID+"_servicing", check.Commentf("Didn't find '%s_servicing': %s", containerID+"_servicing", out2))
}
func (s *DockerSuite) TestRunDuplicateMount(c *check.C) { func (s *DockerSuite) TestRunDuplicateMount(c *check.C) {
testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace) testRequires(c, SameHostDaemon, DaemonIsLinux, NotUserNamespace)

View file

@ -82,7 +82,7 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) {
// | | Isolation=Process | Isolation=Hyper-V | // | | Isolation=Process | Isolation=Hyper-V |
// +-----------------+--------------------------------------------+---------------------------------------------------+ // +-----------------+--------------------------------------------+---------------------------------------------------+
// | VolumePath | \\?\\Volume{GUIDa} | | // | VolumePath | \\?\\Volume{GUIDa} | |
// | LayerFolderPath | %root%\windowsfilter\containerID | %root%\windowsfilter\containerID (servicing only) | // | LayerFolderPath | %root%\windowsfilter\containerID | |
// | Layers[] | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID | // | Layers[] | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID |
// | HvRuntime | | ImagePath=%root%\BaseLayerID\UtilityVM | // | HvRuntime | | ImagePath=%root%\BaseLayerID\UtilityVM |
// +-----------------+--------------------------------------------+---------------------------------------------------+ // +-----------------+--------------------------------------------+---------------------------------------------------+
@ -104,7 +104,6 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) {
// "MappedDirectories": [], // "MappedDirectories": [],
// "HvPartition": false, // "HvPartition": false,
// "EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"], // "EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
// "Servicing": false
//} //}
// //
// Isolation=Hyper-V example: // Isolation=Hyper-V example:
@ -126,7 +125,6 @@ func (c *client) Version(ctx context.Context) (containerd.Version, error) {
// "HvRuntime": { // "HvRuntime": {
// "ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM" // "ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
// }, // },
// "Servicing": false
//} //}
func (c *client) Create(_ context.Context, id string, spec *specs.Spec, runtimeOptions interface{}) error { func (c *client) Create(_ context.Context, id string, spec *specs.Spec, runtimeOptions interface{}) error {
if ctr := c.getContainer(id); ctr != nil { if ctr := c.getContainer(id); ctr != nil {
@ -155,7 +153,6 @@ func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions inter
IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot, IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
HostName: spec.Hostname, HostName: spec.Hostname,
HvPartition: false, HvPartition: false,
Servicing: spec.Windows.Servicing,
} }
if spec.Windows.Resources != nil { if spec.Windows.Resources != nil {
@ -324,9 +321,6 @@ func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions inter
waitCh: make(chan struct{}), waitCh: make(chan struct{}),
} }
// Start the container. If this is a servicing container, this call
// will block until the container is done with the servicing
// execution.
logger.Debug("starting container") logger.Debug("starting container")
if err = hcsContainer.Start(); err != nil { if err = hcsContainer.Start(); err != nil {
c.logger.WithError(err).Error("failed to start container") c.logger.WithError(err).Error("failed to start container")
@ -525,9 +519,7 @@ func (c *client) createLinux(id string, spec *specs.Spec, runtimeOptions interfa
waitCh: make(chan struct{}), waitCh: make(chan struct{}),
} }
// Start the container. If this is a servicing container, this call // Start the container.
// will block until the container is done with the servicing
// execution.
logger.Debug("starting container") logger.Debug("starting container")
if err = hcsContainer.Start(); err != nil { if err = hcsContainer.Start(); err != nil {
c.logger.WithError(err).Error("failed to start container") c.logger.WithError(err).Error("failed to start container")
@ -588,14 +580,14 @@ func (c *client) Start(_ context.Context, id, _ string, withStdin bool, attachSt
) )
if ctr.ociSpec.Process != nil { if ctr.ociSpec.Process != nil {
emulateConsole = ctr.ociSpec.Process.Terminal emulateConsole = ctr.ociSpec.Process.Terminal
createStdErrPipe = !ctr.ociSpec.Process.Terminal && !ctr.ociSpec.Windows.Servicing createStdErrPipe = !ctr.ociSpec.Process.Terminal
} }
createProcessParms := &hcsshim.ProcessConfig{ createProcessParms := &hcsshim.ProcessConfig{
EmulateConsole: emulateConsole, EmulateConsole: emulateConsole,
WorkingDirectory: ctr.ociSpec.Process.Cwd, WorkingDirectory: ctr.ociSpec.Process.Cwd,
CreateStdInPipe: !ctr.ociSpec.Windows.Servicing, CreateStdInPipe: true,
CreateStdOutPipe: !ctr.ociSpec.Windows.Servicing, CreateStdOutPipe: true,
CreateStdErrPipe: createStdErrPipe, CreateStdErrPipe: createStdErrPipe,
} }
@ -655,21 +647,6 @@ func (c *client) Start(_ context.Context, id, _ string, withStdin bool, attachSt
} }
logger.WithField("pid", p.pid).Debug("init process started") logger.WithField("pid", p.pid).Debug("init process started")
// If this is a servicing container, wait on the process synchronously here and
// if it succeeds, wait for it cleanly shutdown and merge into the parent container.
if ctr.ociSpec.Windows.Servicing {
// reapProcess takes the lock
ctr.Unlock()
defer ctr.Lock()
exitCode := c.reapProcess(ctr, p)
if exitCode != 0 {
return -1, errors.Errorf("libcontainerd: servicing container %s returned non-zero exit code %d", ctr.id, exitCode)
}
return p.pid, nil
}
dio, err := newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal) dio, err := newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal)
if err != nil { if err != nil {
logger.WithError(err).Error("failed to get stdio pipes") logger.WithError(err).Error("failed to get stdio pipes")
@ -1275,7 +1252,6 @@ func (c *client) reapProcess(ctr *container, p *process) int {
eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err) eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err)
} }
var pendingUpdates bool
if p.id == InitProcessName { if p.id == InitProcessName {
// Update container status // Update container status
ctr.Lock() ctr.Lock()
@ -1285,16 +1261,6 @@ func (c *client) reapProcess(ctr *container, p *process) int {
close(ctr.waitCh) close(ctr.waitCh)
ctr.Unlock() ctr.Unlock()
// Handle any servicing
if exitCode == 0 && ctr.isWindows && !ctr.ociSpec.Windows.Servicing {
pendingUpdates, err = ctr.hcsContainer.HasPendingUpdates()
logger.Infof("Pending updates: %v", pendingUpdates)
if err != nil {
logger.WithError(err).
Warnf("failed to check for pending updates (container may have been killed)")
}
}
if err := c.shutdownContainer(ctr); err != nil { if err := c.shutdownContainer(ctr); err != nil {
exitCode = -1 exitCode = -1
logger.WithError(err).Warn("failed to shutdown container") logger.WithError(err).Warn("failed to shutdown container")
@ -1320,37 +1286,34 @@ func (c *client) reapProcess(ctr *container, p *process) int {
} }
} }
if !(ctr.isWindows && ctr.ociSpec.Windows.Servicing) { c.eventQ.append(ctr.id, func() {
c.eventQ.append(ctr.id, func() { ei := EventInfo{
ei := EventInfo{ ContainerID: ctr.id,
ContainerID: ctr.id, ProcessID: p.id,
ProcessID: p.id, Pid: uint32(p.pid),
Pid: uint32(p.pid), ExitCode: uint32(exitCode),
ExitCode: uint32(exitCode), ExitedAt: exitedAt,
ExitedAt: exitedAt, Error: eventErr,
UpdatePending: pendingUpdates, }
Error: eventErr, c.logger.WithFields(logrus.Fields{
} "container": ctr.id,
c.logger.WithFields(logrus.Fields{ "event": EventExit,
"event-info": ei,
}).Info("sending event")
err := c.backend.ProcessEvent(ctr.id, EventExit, ei)
if err != nil {
c.logger.WithError(err).WithFields(logrus.Fields{
"container": ctr.id, "container": ctr.id,
"event": EventExit, "event": EventExit,
"event-info": ei, "event-info": ei,
}).Info("sending event") }).Error("failed to process event")
err := c.backend.ProcessEvent(ctr.id, EventExit, ei) }
if err != nil { if p.id != InitProcessName {
c.logger.WithError(err).WithFields(logrus.Fields{ ctr.Lock()
"container": ctr.id, delete(ctr.execs, p.id)
"event": EventExit, ctr.Unlock()
"event-info": ei, }
}).Error("failed to process event") })
}
if p.id != InitProcessName {
ctr.Lock()
delete(ctr.execs, p.id)
ctr.Unlock()
}
})
}
return exitCode return exitCode
} }

View file

@ -71,9 +71,7 @@ type EventInfo struct {
ExitCode uint32 ExitCode uint32
ExitedAt time.Time ExitedAt time.Time
OOMKilled bool OOMKilled bool
// Windows Only field Error error
UpdatePending bool
Error error
} }
// Backend defines callbacks that the client of the library needs to implement. // Backend defines callbacks that the client of the library needs to implement.