mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue