Implement Pause Resume support for Windows
Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
parent
a7db0a8402
commit
69985e85d3
11 changed files with 121 additions and 33 deletions
|
@ -19,11 +19,12 @@ Options:
|
|||
--help Print usage
|
||||
```
|
||||
|
||||
The `docker pause` command uses the cgroups freezer to suspend all processes in
|
||||
a container. Traditionally, when suspending a process the `SIGSTOP` signal is
|
||||
used, which is observable by the process being suspended. With the cgroups freezer
|
||||
the process is unaware, and unable to capture, that it is being suspended,
|
||||
and subsequently resumed.
|
||||
The `docker pause` command suspends all processes in a container. On Linux,
|
||||
this uses the cgroups freezer. Traditionally, when suspending a process the
|
||||
`SIGSTOP` signal is used, which is observable by the process being suspended.
|
||||
With the cgroups freezer the process is unaware, and unable to capture,
|
||||
that it is being suspended, and subsequently resumed. On Windows, only Hyper-V
|
||||
containers can be paused.
|
||||
|
||||
See the
|
||||
[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
||||
|
|
|
@ -19,8 +19,8 @@ Options:
|
|||
--help Print usage
|
||||
```
|
||||
|
||||
The `docker unpause` command uses the cgroups freezer to un-suspend all
|
||||
processes in a container.
|
||||
The `docker unpause` command un-suspends all processes in a container.
|
||||
On Linux, it does this using the cgroups freezer.
|
||||
|
||||
See the
|
||||
[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
||||
|
|
|
@ -154,9 +154,9 @@ func (s *DockerSuite) TestAttachDisconnect(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux) // Containers cannot be paused on Windows
|
||||
testRequires(c, IsPausable)
|
||||
defer unpauseAllContainers()
|
||||
dockerCmd(c, "run", "-d", "--name=test", "busybox", "top")
|
||||
runSleepingContainer(c, "-d", "--name=test")
|
||||
dockerCmd(c, "pause", "test")
|
||||
|
||||
result := dockerCmdWithResult("attach", "test")
|
||||
|
|
|
@ -127,11 +127,10 @@ func (s *DockerSuite) TestExecExitStatus(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
|
||||
// Windows does not support pause
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, IsPausable)
|
||||
defer unpauseAllContainers()
|
||||
|
||||
out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")
|
||||
out, _ := runSleepingContainer(c, "-d", "--name", "testing")
|
||||
ContainerID := strings.TrimSpace(out)
|
||||
|
||||
dockerCmd(c, "pause", "testing")
|
||||
|
|
|
@ -138,9 +138,9 @@ func (s *DockerSuite) TestInfoDisplaysRunningContainers(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestInfoDisplaysPausedContainers(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, IsPausable)
|
||||
|
||||
out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
|
||||
out, _ := runSleepingContainer(c, "-d")
|
||||
cleanedContainerID := strings.TrimSpace(out)
|
||||
|
||||
dockerCmd(c, "pause", cleanedContainerID)
|
||||
|
|
|
@ -8,11 +8,11 @@ import (
|
|||
)
|
||||
|
||||
func (s *DockerSuite) TestPause(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, IsPausable)
|
||||
defer unpauseAllContainers()
|
||||
|
||||
name := "testeventpause"
|
||||
dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
|
||||
runSleepingContainer(c, "-d", "--name", name)
|
||||
|
||||
dockerCmd(c, "pause", name)
|
||||
pausedContainers, err := getSliceOfPausedContainers()
|
||||
|
@ -30,7 +30,7 @@ func (s *DockerSuite) TestPause(c *check.C) {
|
|||
}
|
||||
|
||||
func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, IsPausable)
|
||||
defer unpauseAllContainers()
|
||||
|
||||
containers := []string{
|
||||
|
@ -38,7 +38,7 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
|||
"testpausewithmorecontainers2",
|
||||
}
|
||||
for _, name := range containers {
|
||||
dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
|
||||
runSleepingContainer(c, "-d", "--name", name)
|
||||
}
|
||||
dockerCmd(c, append([]string{"pause"}, containers...)...)
|
||||
pausedContainers, err := getSliceOfPausedContainers()
|
||||
|
@ -58,9 +58,9 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *DockerSuite) TestPauseFailsOnWindows(c *check.C) {
|
||||
testRequires(c, DaemonIsWindows)
|
||||
dockerCmd(c, "run", "-d", "--name=test", "busybox", "sleep 3")
|
||||
func (s *DockerSuite) TestPauseFailsOnWindowsServerContainers(c *check.C) {
|
||||
testRequires(c, DaemonIsWindows, NotPausable)
|
||||
runSleepingContainer(c, "-d", "--name=test")
|
||||
out, _, _ := dockerCmdWithError("pause", "test")
|
||||
c.Assert(out, checker.Contains, "Windows: Containers cannot be paused")
|
||||
c.Assert(out, checker.Contains, "cannot pause Windows Server Containers")
|
||||
}
|
||||
|
|
|
@ -95,10 +95,10 @@ func (s *DockerSuite) TestStartRecordError(c *check.C) {
|
|||
|
||||
func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
|
||||
// Windows does not support pausing containers
|
||||
testRequires(c, DaemonIsLinux)
|
||||
testRequires(c, IsPausable)
|
||||
defer unpauseAllContainers()
|
||||
|
||||
dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")
|
||||
runSleepingContainer(c, "-d", "--name", "testing")
|
||||
|
||||
dockerCmd(c, "pause", "testing")
|
||||
|
||||
|
|
|
@ -201,6 +201,24 @@ var (
|
|||
},
|
||||
"Test cannot be run when remapping root",
|
||||
}
|
||||
IsPausable = testRequirement{
|
||||
func() bool {
|
||||
if daemonPlatform == "windows" {
|
||||
return isolation == "hyperv"
|
||||
}
|
||||
return true
|
||||
},
|
||||
"Test requires containers are pausable.",
|
||||
}
|
||||
NotPausable = testRequirement{
|
||||
func() bool {
|
||||
if daemonPlatform == "windows" {
|
||||
return isolation == "process"
|
||||
}
|
||||
return false
|
||||
},
|
||||
"Test requires containers are not pausable.",
|
||||
}
|
||||
)
|
||||
|
||||
// testRequires checks if the environment satisfies the requirements
|
||||
|
|
|
@ -447,12 +447,81 @@ func (clnt *client) Resize(containerID, processFriendlyName string, width, heigh
|
|||
|
||||
// Pause handles pause requests for containers
|
||||
func (clnt *client) Pause(containerID string) error {
|
||||
return errors.New("Windows: Containers cannot be paused")
|
||||
unlockContainer := true
|
||||
// Get the libcontainerd container object
|
||||
clnt.lock(containerID)
|
||||
defer func() {
|
||||
if unlockContainer {
|
||||
clnt.unlock(containerID)
|
||||
}
|
||||
}()
|
||||
container, err := clnt.getContainer(containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, option := range container.options {
|
||||
if h, ok := option.(*HyperVIsolationOption); ok {
|
||||
if !h.IsHyperV {
|
||||
return errors.New("cannot pause Windows Server Containers")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = container.hcsContainer.Pause()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unlock container before calling back into the daemon
|
||||
unlockContainer = false
|
||||
clnt.unlock(containerID)
|
||||
|
||||
return clnt.backend.StateChanged(containerID, StateInfo{
|
||||
CommonStateInfo: CommonStateInfo{
|
||||
State: StatePause,
|
||||
}})
|
||||
}
|
||||
|
||||
// Resume handles resume requests for containers
|
||||
func (clnt *client) Resume(containerID string) error {
|
||||
return errors.New("Windows: Containers cannot be paused")
|
||||
unlockContainer := true
|
||||
// Get the libcontainerd container object
|
||||
clnt.lock(containerID)
|
||||
defer func() {
|
||||
if unlockContainer {
|
||||
clnt.unlock(containerID)
|
||||
}
|
||||
}()
|
||||
container, err := clnt.getContainer(containerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// This should never happen, since Windows Server Containers cannot be paused
|
||||
for _, option := range container.options {
|
||||
if h, ok := option.(*HyperVIsolationOption); ok {
|
||||
if !h.IsHyperV {
|
||||
return errors.New("cannot resume Windows Server Containers")
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
err = container.hcsContainer.Resume()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unlock container before calling back into the daemon
|
||||
unlockContainer = false
|
||||
clnt.unlock(containerID)
|
||||
|
||||
return clnt.backend.StateChanged(containerID, StateInfo{
|
||||
CommonStateInfo: CommonStateInfo{
|
||||
State: StateResume,
|
||||
}})
|
||||
}
|
||||
|
||||
// Stats handles stats requests for containers
|
||||
|
|
|
@ -10,11 +10,12 @@ CONTAINER [CONTAINER...]
|
|||
|
||||
# DESCRIPTION
|
||||
|
||||
The `docker pause` command uses the cgroups freezer to suspend all processes in
|
||||
a container. Traditionally when suspending a process the `SIGSTOP` signal is
|
||||
used, which is observable by the process being suspended. With the cgroups freezer
|
||||
the process is unaware, and unable to capture, that it is being suspended,
|
||||
and subsequently resumed.
|
||||
The `docker pause` command suspends all processes in a container. On Linux,
|
||||
this uses the cgroups freezer. Traditionally, when suspending a process the
|
||||
`SIGSTOP` signal is used, which is observable by the process being suspended.
|
||||
With the cgroups freezer the process is unaware, and unable to capture,
|
||||
that it is being suspended, and subsequently resumed. On Windows, only Hyper-V
|
||||
containers can be paused.
|
||||
|
||||
See the [cgroups freezer documentation]
|
||||
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
||||
|
|
|
@ -10,8 +10,8 @@ CONTAINER [CONTAINER...]
|
|||
|
||||
# DESCRIPTION
|
||||
|
||||
The `docker unpause` command uses the cgroups freezer to un-suspend all
|
||||
processes in a container.
|
||||
The `docker unpause` command un-suspends all processes in a container.
|
||||
On Linux, it does this using the cgroups freezer.
|
||||
|
||||
See the [cgroups freezer documentation]
|
||||
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
||||
|
|
Loading…
Reference in a new issue