mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Merge pull request #26795 from darrenstahlmsft/PauseResume
Implement Pause Resume support for Windows
This commit is contained in:
commit
dd383898cd
11 changed files with 121 additions and 33 deletions
|
@ -19,11 +19,12 @@ Options:
|
||||||
--help Print usage
|
--help Print usage
|
||||||
```
|
```
|
||||||
|
|
||||||
The `docker pause` command uses the cgroups freezer to suspend all processes in
|
The `docker pause` command suspends all processes in a container. On Linux,
|
||||||
a container. Traditionally, when suspending a process the `SIGSTOP` signal is
|
this uses the cgroups freezer. Traditionally, when suspending a process the
|
||||||
used, which is observable by the process being suspended. With the cgroups freezer
|
`SIGSTOP` signal is used, which is observable by the process being suspended.
|
||||||
the process is unaware, and unable to capture, that it is being suspended,
|
With the cgroups freezer the process is unaware, and unable to capture,
|
||||||
and subsequently resumed.
|
that it is being suspended, and subsequently resumed. On Windows, only Hyper-V
|
||||||
|
containers can be paused.
|
||||||
|
|
||||||
See the
|
See the
|
||||||
[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
||||||
|
|
|
@ -19,8 +19,8 @@ Options:
|
||||||
--help Print usage
|
--help Print usage
|
||||||
```
|
```
|
||||||
|
|
||||||
The `docker unpause` command uses the cgroups freezer to un-suspend all
|
The `docker unpause` command un-suspends all processes in a container.
|
||||||
processes in a container.
|
On Linux, it does this using the cgroups freezer.
|
||||||
|
|
||||||
See the
|
See the
|
||||||
[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt)
|
[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) {
|
func (s *DockerSuite) TestAttachPausedContainer(c *check.C) {
|
||||||
testRequires(c, DaemonIsLinux) // Containers cannot be paused on Windows
|
testRequires(c, IsPausable)
|
||||||
defer unpauseAllContainers()
|
defer unpauseAllContainers()
|
||||||
dockerCmd(c, "run", "-d", "--name=test", "busybox", "top")
|
runSleepingContainer(c, "-d", "--name=test")
|
||||||
dockerCmd(c, "pause", "test")
|
dockerCmd(c, "pause", "test")
|
||||||
|
|
||||||
result := dockerCmdWithResult("attach", "test")
|
result := dockerCmdWithResult("attach", "test")
|
||||||
|
|
|
@ -127,11 +127,10 @@ func (s *DockerSuite) TestExecExitStatus(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
|
func (s *DockerSuite) TestExecPausedContainer(c *check.C) {
|
||||||
// Windows does not support pause
|
testRequires(c, IsPausable)
|
||||||
testRequires(c, DaemonIsLinux)
|
|
||||||
defer unpauseAllContainers()
|
defer unpauseAllContainers()
|
||||||
|
|
||||||
out, _ := dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")
|
out, _ := runSleepingContainer(c, "-d", "--name", "testing")
|
||||||
ContainerID := strings.TrimSpace(out)
|
ContainerID := strings.TrimSpace(out)
|
||||||
|
|
||||||
dockerCmd(c, "pause", "testing")
|
dockerCmd(c, "pause", "testing")
|
||||||
|
|
|
@ -138,9 +138,9 @@ func (s *DockerSuite) TestInfoDisplaysRunningContainers(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestInfoDisplaysPausedContainers(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)
|
cleanedContainerID := strings.TrimSpace(out)
|
||||||
|
|
||||||
dockerCmd(c, "pause", cleanedContainerID)
|
dockerCmd(c, "pause", cleanedContainerID)
|
||||||
|
|
|
@ -8,11 +8,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *DockerSuite) TestPause(c *check.C) {
|
func (s *DockerSuite) TestPause(c *check.C) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, IsPausable)
|
||||||
defer unpauseAllContainers()
|
defer unpauseAllContainers()
|
||||||
|
|
||||||
name := "testeventpause"
|
name := "testeventpause"
|
||||||
dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
|
runSleepingContainer(c, "-d", "--name", name)
|
||||||
|
|
||||||
dockerCmd(c, "pause", name)
|
dockerCmd(c, "pause", name)
|
||||||
pausedContainers, err := getSliceOfPausedContainers()
|
pausedContainers, err := getSliceOfPausedContainers()
|
||||||
|
@ -30,7 +30,7 @@ func (s *DockerSuite) TestPause(c *check.C) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, IsPausable)
|
||||||
defer unpauseAllContainers()
|
defer unpauseAllContainers()
|
||||||
|
|
||||||
containers := []string{
|
containers := []string{
|
||||||
|
@ -38,7 +38,7 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||||
"testpausewithmorecontainers2",
|
"testpausewithmorecontainers2",
|
||||||
}
|
}
|
||||||
for _, name := range containers {
|
for _, name := range containers {
|
||||||
dockerCmd(c, "run", "-d", "--name", name, "busybox", "top")
|
runSleepingContainer(c, "-d", "--name", name)
|
||||||
}
|
}
|
||||||
dockerCmd(c, append([]string{"pause"}, containers...)...)
|
dockerCmd(c, append([]string{"pause"}, containers...)...)
|
||||||
pausedContainers, err := getSliceOfPausedContainers()
|
pausedContainers, err := getSliceOfPausedContainers()
|
||||||
|
@ -58,9 +58,9 @@ func (s *DockerSuite) TestPauseMultipleContainers(c *check.C) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerSuite) TestPauseFailsOnWindows(c *check.C) {
|
func (s *DockerSuite) TestPauseFailsOnWindowsServerContainers(c *check.C) {
|
||||||
testRequires(c, DaemonIsWindows)
|
testRequires(c, DaemonIsWindows, NotPausable)
|
||||||
dockerCmd(c, "run", "-d", "--name=test", "busybox", "sleep 3")
|
runSleepingContainer(c, "-d", "--name=test")
|
||||||
out, _, _ := dockerCmdWithError("pause", "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) {
|
func (s *DockerSuite) TestStartPausedContainer(c *check.C) {
|
||||||
// Windows does not support pausing containers
|
// Windows does not support pausing containers
|
||||||
testRequires(c, DaemonIsLinux)
|
testRequires(c, IsPausable)
|
||||||
defer unpauseAllContainers()
|
defer unpauseAllContainers()
|
||||||
|
|
||||||
dockerCmd(c, "run", "-d", "--name", "testing", "busybox", "top")
|
runSleepingContainer(c, "-d", "--name", "testing")
|
||||||
|
|
||||||
dockerCmd(c, "pause", "testing")
|
dockerCmd(c, "pause", "testing")
|
||||||
|
|
||||||
|
|
|
@ -201,6 +201,24 @@ var (
|
||||||
},
|
},
|
||||||
"Test cannot be run when remapping root",
|
"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
|
// 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
|
// Pause handles pause requests for containers
|
||||||
func (clnt *client) Pause(containerID string) error {
|
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
|
// Resume handles resume requests for containers
|
||||||
func (clnt *client) Resume(containerID string) error {
|
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
|
// Stats handles stats requests for containers
|
||||||
|
|
|
@ -10,11 +10,12 @@ CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
The `docker pause` command uses the cgroups freezer to suspend all processes in
|
The `docker pause` command suspends all processes in a container. On Linux,
|
||||||
a container. Traditionally when suspending a process the `SIGSTOP` signal is
|
this uses the cgroups freezer. Traditionally, when suspending a process the
|
||||||
used, which is observable by the process being suspended. With the cgroups freezer
|
`SIGSTOP` signal is used, which is observable by the process being suspended.
|
||||||
the process is unaware, and unable to capture, that it is being suspended,
|
With the cgroups freezer the process is unaware, and unable to capture,
|
||||||
and subsequently resumed.
|
that it is being suspended, and subsequently resumed. On Windows, only Hyper-V
|
||||||
|
containers can be paused.
|
||||||
|
|
||||||
See the [cgroups freezer documentation]
|
See the [cgroups freezer documentation]
|
||||||
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
||||||
|
|
|
@ -10,8 +10,8 @@ CONTAINER [CONTAINER...]
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
The `docker unpause` command uses the cgroups freezer to un-suspend all
|
The `docker unpause` command un-suspends all processes in a container.
|
||||||
processes in a container.
|
On Linux, it does this using the cgroups freezer.
|
||||||
|
|
||||||
See the [cgroups freezer documentation]
|
See the [cgroups freezer documentation]
|
||||||
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
(https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) for
|
||||||
|
|
Loading…
Add table
Reference in a new issue