diff --git a/api/client/commands.go b/api/client/commands.go index 7d1f6e7002..0db602f0cd 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -786,8 +786,8 @@ func (cli *DockerCli) CmdStart(args ...string) error { } func (cli *DockerCli) CmdUnpause(args ...string) error { - cmd := cli.Subcmd("unpause", "CONTAINER", "Unpause all processes within a container", true) - cmd.Require(flag.Exact, 1) + cmd := cli.Subcmd("unpause", "CONTAINER [CONTAINER...]", "Unpause all processes within a container", true) + cmd.Require(flag.Min, 1) utils.ParseFlags(cmd, args, false) var encounteredError error @@ -803,8 +803,8 @@ func (cli *DockerCli) CmdUnpause(args ...string) error { } func (cli *DockerCli) CmdPause(args ...string) error { - cmd := cli.Subcmd("pause", "CONTAINER", "Pause all processes within a container", true) - cmd.Require(flag.Exact, 1) + cmd := cli.Subcmd("pause", "CONTAINER [CONTAINER...]", "Pause all processes within a container", true) + cmd.Require(flag.Min, 1) utils.ParseFlags(cmd, args, false) var encounteredError error diff --git a/docs/man/docker-pause.1.md b/docs/man/docker-pause.1.md index 7b4b091a06..372b0a7eeb 100644 --- a/docs/man/docker-pause.1.md +++ b/docs/man/docker-pause.1.md @@ -6,7 +6,7 @@ docker-pause - Pause all processes within a container # SYNOPSIS **docker pause** -CONTAINER +CONTAINER [CONTAINER...] # DESCRIPTION diff --git a/docs/man/docker-unpause.1.md b/docs/man/docker-unpause.1.md index dfce16324e..96de68b6fd 100644 --- a/docs/man/docker-unpause.1.md +++ b/docs/man/docker-unpause.1.md @@ -6,7 +6,7 @@ docker-unpause - Unpause all processes within a container # SYNOPSIS **docker unpause** -CONTAINER +CONTAINER [CONTAINER...] # DESCRIPTION diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index 1d784d72e1..3b7c24abb4 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -1359,7 +1359,7 @@ nano-second part of the timestamp will be padded with zero when necessary. ## pause - Usage: docker pause CONTAINER + Usage: docker pause CONTAINER [CONTAINER...] Pause all processes within a container @@ -1396,22 +1396,6 @@ just a specific mapping: $ sudo docker port test 7890 0.0.0.0:4321 -## pause - - Usage: docker pause CONTAINER - - Pause all processes within a container - -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. - -See the -[cgroups freezer documentation](https://www.kernel.org/doc/Documentation/cgroups/freezer-subsystem.txt) -for further details. - ## rename Usage: docker rename OLD_NAME NEW_NAME @@ -2081,7 +2065,7 @@ them to [*Share Images via Repositories*]( ## unpause - Usage: docker unpause CONTAINER + Usage: docker unpause CONTAINER [CONTAINER...] Unpause all processes within a container diff --git a/integration-cli/docker_cli_events_test.go b/integration-cli/docker_cli_events_test.go index 322d622b55..ff8261e626 100644 --- a/integration-cli/docker_cli_events_test.go +++ b/integration-cli/docker_cli_events_test.go @@ -30,41 +30,6 @@ func TestEventsUntag(t *testing.T) { logDone("events - untags are logged") } -func TestEventsPause(t *testing.T) { - name := "testeventpause" - out, _, _ := dockerCmd(t, "images", "-q") - image := strings.Split(out, "\n")[0] - dockerCmd(t, "run", "-d", "--name", name, image, "sleep", "2") - dockerCmd(t, "pause", name) - dockerCmd(t, "unpause", name) - - defer deleteAllContainers() - - eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", time.Now().Unix())) - out, _, _ = runCommandWithOutput(eventsCmd) - events := strings.Split(out, "\n") - if len(events) <= 1 { - t.Fatalf("Missing expected event") - } - - pauseEvent := strings.Fields(events[len(events)-3]) - unpauseEvent := strings.Fields(events[len(events)-2]) - - if pauseEvent[len(pauseEvent)-1] != "pause" { - t.Fatalf("event should be pause, not %#v", pauseEvent) - } - if unpauseEvent[len(unpauseEvent)-1] != "unpause" { - t.Fatalf("event should be unpause, not %#v", unpauseEvent) - } - - waitCmd := exec.Command(dockerBinary, "wait", name) - if waitOut, _, err := runCommandWithOutput(waitCmd); err != nil { - t.Fatalf("error thrown while waiting for container: %s, %v", waitOut, err) - } - - logDone("events - pause/unpause is logged") -} - func TestEventsContainerFailStartDie(t *testing.T) { defer deleteAllContainers() diff --git a/integration-cli/docker_cli_pause_test.go b/integration-cli/docker_cli_pause_test.go new file mode 100644 index 0000000000..1d57c5729d --- /dev/null +++ b/integration-cli/docker_cli_pause_test.go @@ -0,0 +1,113 @@ +package main + +import ( + "fmt" + "os/exec" + "strings" + "testing" + "time" +) + +func TestPause(t *testing.T) { + defer deleteAllContainers() + defer unpauseAllContainers() + + name := "testeventpause" + out, _, _ := dockerCmd(t, "images", "-q") + image := strings.Split(out, "\n")[0] + dockerCmd(t, "run", "-d", "--name", name, image, "sleep", "2") + + dockerCmd(t, "pause", name) + pausedContainers, err := getSliceOfPausedContainers() + if err != nil { + t.Fatalf("error thrown while checking if containers were paused: %v", err) + } + if len(pausedContainers) != 1 { + t.Fatalf("there should be one paused container and not", len(pausedContainers)) + } + + dockerCmd(t, "unpause", name) + + eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", time.Now().Unix())) + out, _, _ = runCommandWithOutput(eventsCmd) + events := strings.Split(out, "\n") + if len(events) <= 1 { + t.Fatalf("Missing expected event") + } + + pauseEvent := strings.Fields(events[len(events)-3]) + unpauseEvent := strings.Fields(events[len(events)-2]) + + if pauseEvent[len(pauseEvent)-1] != "pause" { + t.Fatalf("event should be pause, not %#v", pauseEvent) + } + if unpauseEvent[len(unpauseEvent)-1] != "unpause" { + t.Fatalf("event should be unpause, not %#v", unpauseEvent) + } + + waitCmd := exec.Command(dockerBinary, "wait", name) + if waitOut, _, err := runCommandWithOutput(waitCmd); err != nil { + t.Fatalf("error thrown while waiting for container: %s, %v", waitOut, err) + } + + logDone("pause - pause/unpause is logged") +} + +func TestPauseMultipleContainers(t *testing.T) { + defer deleteAllContainers() + defer unpauseAllContainers() + + containers := []string{ + "testpausewithmorecontainers1", + "testpausewithmorecontainers2", + } + out, _, _ := dockerCmd(t, "images", "-q") + image := strings.Split(out, "\n")[0] + for _, name := range containers { + dockerCmd(t, "run", "-d", "--name", name, image, "sleep", "2") + } + dockerCmd(t, append([]string{"pause"}, containers...)...) + pausedContainers, err := getSliceOfPausedContainers() + if err != nil { + t.Fatalf("error thrown while checking if containers were paused: %v", err) + } + if len(pausedContainers) != len(containers) { + t.Fatalf("there should be %d paused container and not %d", len(containers), len(pausedContainers)) + } + + dockerCmd(t, append([]string{"unpause"}, containers...)...) + + eventsCmd := exec.Command(dockerBinary, "events", "--since=0", fmt.Sprintf("--until=%d", time.Now().Unix())) + out, _, _ = runCommandWithOutput(eventsCmd) + events := strings.Split(out, "\n") + if len(events) <= len(containers)*3-2 { + t.Fatalf("Missing expected event") + } + + pauseEvents := make([][]string, len(containers)) + unpauseEvents := make([][]string, len(containers)) + for i := range containers { + pauseEvents[i] = strings.Fields(events[len(events)-len(containers)*2-1+i]) + unpauseEvents[i] = strings.Fields(events[len(events)-len(containers)-1+i]) + } + + for _, pauseEvent := range pauseEvents { + if pauseEvent[len(pauseEvent)-1] != "pause" { + t.Fatalf("event should be pause, not %#v", pauseEvent) + } + } + for _, unpauseEvent := range unpauseEvents { + if unpauseEvent[len(unpauseEvent)-1] != "unpause" { + t.Fatalf("event should be unpause, not %#v", unpauseEvent) + } + } + + for _, name := range containers { + waitCmd := exec.Command(dockerBinary, "wait", name) + if waitOut, _, err := runCommandWithOutput(waitCmd); err != nil { + t.Fatalf("error thrown while waiting for container: %s, %v", waitOut, err) + } + } + + logDone("pause - multi pause/unpause is logged") +} diff --git a/integration-cli/docker_utils.go b/integration-cli/docker_utils.go index 35a97feca7..525e64dc5e 100644 --- a/integration-cli/docker_utils.go +++ b/integration-cli/docker_utils.go @@ -380,6 +380,16 @@ func getPausedContainers() (string, error) { return out, err } +func getSliceOfPausedContainers() ([]string, error) { + out, err := getPausedContainers() + if err == nil { + slice := strings.Split(strings.TrimSpace(out), "\n") + return slice, err + } else { + return []string{out}, err + } +} + func unpauseContainer(container string) error { unpauseCmd := exec.Command(dockerBinary, "unpause", container) exitCode, err := runCommand(unpauseCmd)