Implement Pause Resume support for Windows

Signed-off-by: Darren Stahl <darst@microsoft.com>
This commit is contained in:
Darren Stahl 2016-09-08 17:31:04 -07:00
parent a7db0a8402
commit 69985e85d3
11 changed files with 121 additions and 33 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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