Windows: Remove servicing mode
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
parent
600475715e
commit
d4f37c0885
7 changed files with 32 additions and 170 deletions
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue