aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhaircommander <pehunt@redhat.com>2018-07-20 13:28:19 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2018-07-27 14:20:08 +0000
commitf258e43c7c4685102fbbf52303348f434ca8cb3e (patch)
tree6292f4d7ec86f70d0c85a9e9c6c6effaf4234a0e
parent50fea69fbc017fd0da160b083780d933aa5462b5 (diff)
downloadpodman-f258e43c7c4685102fbbf52303348f434ca8cb3e.tar.gz
podman-f258e43c7c4685102fbbf52303348f434ca8cb3e.tar.bz2
podman-f258e43c7c4685102fbbf52303348f434ca8cb3e.zip
Add pod pause/unpause
Added Pause() and Unpause() to libpod/pod.go Added man pages, tests and completions Signed-off-by: haircommander <pehunt@redhat.com> Closes: #1126 Approved by: rhatdan
-rw-r--r--cmd/podman/pod.go2
-rw-r--r--cmd/podman/pod_pause.go73
-rw-r--r--cmd/podman/pod_unpause.go73
-rw-r--r--commands.md2
-rw-r--r--completions/bash/podman62
-rw-r--r--docs/podman-pod-pause.1.md32
-rw-r--r--docs/podman-pod-unpause.1.md32
-rw-r--r--docs/podman-pod.1.md2
-rw-r--r--libpod/pod.go119
-rw-r--r--test/e2e/pod_pause_test.go116
10 files changed, 504 insertions, 9 deletions
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index 65bc9efda..ed79f0711 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -12,11 +12,13 @@ Pods are a group of one or more containers sharing the same network, pid and ipc
podSubCommands = []cli.Command{
podCreateCommand,
podKillCommand,
+ podPauseCommand,
podPsCommand,
podRestartCommand,
podRmCommand,
podStartCommand,
podStopCommand,
+ podUnpauseCommand,
}
podCommand = cli.Command{
Name: "pod",
diff --git a/cmd/podman/pod_pause.go b/cmd/podman/pod_pause.go
new file mode 100644
index 000000000..df51a037b
--- /dev/null
+++ b/cmd/podman/pod_pause.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ podPauseFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "pause all running pods",
+ },
+ LatestPodFlag,
+ }
+ podPauseDescription = `
+ Pauses one or more pods. The pod name or ID can be used.
+`
+
+ podPauseCommand = cli.Command{
+ Name: "pause",
+ Usage: "Pause one or more pods",
+ Description: podPauseDescription,
+ Flags: podPauseFlags,
+ Action: podPauseCmd,
+ ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ UseShortOptionHandling: true,
+ }
+)
+
+func podPauseCmd(c *cli.Context) error {
+ if err := checkMutuallyExclusiveFlags(c); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ // getPodsFromContext returns an error when a requested pod
+ // isn't found. The only fatal error scenerio is when there are no pods
+ // in which case the following loop will be skipped.
+ pods, lastError := getPodsFromContext(c, runtime)
+
+ for _, pod := range pods {
+ ctr_errs, err := pod.Pause()
+ if ctr_errs != nil {
+ for ctr, err := range ctr_errs {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to pause container %q on pod %q", ctr, pod.ID())
+ }
+ continue
+ }
+ if err != nil {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to pause pod %q", pod.ID())
+ continue
+ }
+ fmt.Println(pod.ID())
+ }
+
+ return lastError
+}
diff --git a/cmd/podman/pod_unpause.go b/cmd/podman/pod_unpause.go
new file mode 100644
index 000000000..5dd5c79e4
--- /dev/null
+++ b/cmd/podman/pod_unpause.go
@@ -0,0 +1,73 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ podUnpauseFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "unpause all paused pods",
+ },
+ LatestPodFlag,
+ }
+ podUnpauseDescription = `
+ Unpauses one or more pods. The pod name or ID can be used.
+`
+
+ podUnpauseCommand = cli.Command{
+ Name: "unpause",
+ Usage: "Unpause one or more pods",
+ Description: podUnpauseDescription,
+ Flags: podUnpauseFlags,
+ Action: podUnpauseCmd,
+ ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ UseShortOptionHandling: true,
+ }
+)
+
+func podUnpauseCmd(c *cli.Context) error {
+ if err := checkMutuallyExclusiveFlags(c); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "error creating libpod runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ // getPodsFromContext returns an error when a requested pod
+ // isn't found. The only fatal error scenerio is when there are no pods
+ // in which case the following loop will be skipped.
+ pods, lastError := getPodsFromContext(c, runtime)
+
+ for _, pod := range pods {
+ ctr_errs, err := pod.Unpause()
+ if ctr_errs != nil {
+ for ctr, err := range ctr_errs {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to unpause container %q on pod %q", ctr, pod.ID())
+ }
+ continue
+ }
+ if err != nil {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to unpause pod %q", pod.ID())
+ continue
+ }
+ fmt.Println(pod.ID())
+ }
+
+ return lastError
+}
diff --git a/commands.md b/commands.md
index 8af63b60d..3ac195476 100644
--- a/commands.md
+++ b/commands.md
@@ -33,10 +33,12 @@
| [podman-pod-create(1)](/docs/podman-pod-create.1.md) | Create a new pod ||
| [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. ||
| [podman-pod-ps(1)](/docs/podman-pod-ps.1.md) | List the pods on the system ||
+| [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. ||
| [podman-pod-restart](/docs/podman-pod-restart.1.md) | Restart one or more pods ||
| [podman-pod-rm(1)](/docs/podman-pod-rm.1.md) | Remove one or more pods ||
| [podman-pod-start(1)](/docs/podman-pod-start.1.md) | Start one or more pods ||
| [podman-pod-stop(1)](/docs/podman-pod-stop.1.md) | Stop one or more pods ||
+| [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. ||
| [podman-port(1)](/docs/podman-port.1.md) | List port mappings for running containers |[![...](/docs/play.png)]()|
| [podman-ps(1)](/docs/podman-ps.1.md) | Prints out information about containers |[![...](/docs/play.png)](https://asciinema.org/a/bbT41kac6CwZ5giESmZLIaTLR)|
| [podman-pull(1)](/docs/podman-pull.1.md) | Pull an image from a registry |[![...](/docs/play.png)](https://asciinema.org/a/lr4zfoynHJOUNu1KaXa1dwG2X)|
diff --git a/completions/bash/podman b/completions/bash/podman
index 0019343e2..2c43a2d8f 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2193,10 +2193,10 @@ _podman_pod_start() {
"
local boolean_options="
- all
- a
- latest
- l
+ --all
+ -a
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
@@ -2214,11 +2214,53 @@ _podman_pod_stop() {
"
local boolean_options="
- all
- a
- cleanup
- latest
- l
+ --all
+ -a
+ --cleanup
+ --latest
+ -l
+ "
+ _complete_ "$options_with_args" "$boolean_options"
+ case "$cur" in
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
+ esac
+}
+
+_podman_pod_pause() {
+ local options_with_args="
+ "
+
+ local boolean_options="
+ --all
+ -a
+ --latest
+ -l
+ "
+ _complete_ "$options_with_args" "$boolean_options"
+ case "$cur" in
+ -*)
+ COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur"))
+ ;;
+ *)
+ __podman_complete_pod_names
+ ;;
+ esac
+}
+
+_podman_pod_unpause() {
+ local options_with_args="
+ "
+
+ local boolean_options="
+ --all
+ -a
+ --latest
+ -l
"
_complete_ "$options_with_args" "$boolean_options"
case "$cur" in
@@ -2244,6 +2286,8 @@ _podman_pod() {
rm
start
stop
+ pause
+ unpause
"
local aliases="
list
diff --git a/docs/podman-pod-pause.1.md b/docs/podman-pod-pause.1.md
new file mode 100644
index 000000000..56d63e2e2
--- /dev/null
+++ b/docs/podman-pod-pause.1.md
@@ -0,0 +1,32 @@
+% podman-pod-pause "1"
+
+## NAME
+podman\-pod\-pause - Pause one or more pods
+
+## SYNOPSIS
+**podman pod pause** [*options*] *pod* ...
+
+## DESCRIPTION
+Pauses all the running processes in the containers of one or more pods. You may use pod IDs or names as input.
+
+## OPTIONS
+
+**--all, a**
+
+Pause all pods.
+
+**--latest, -l**
+
+Instead of providing the pod name or ID, pause the last created pod.
+
+## EXAMPLE
+
+podman pod pause mywebserverpod
+
+podman pod pause 860a4b23
+
+## SEE ALSO
+podman-pod(1), podman-pod-unpause(1), podman-pause(1)
+
+## HISTORY
+July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/podman-pod-unpause.1.md b/docs/podman-pod-unpause.1.md
new file mode 100644
index 000000000..2c065976c
--- /dev/null
+++ b/docs/podman-pod-unpause.1.md
@@ -0,0 +1,32 @@
+% podman-pod-unpause "1"
+
+## NAME
+podman\-pod\-unpause - Unpause one or more pods
+
+## SYNOPSIS
+**podman pod unpause** [*options*] *pod* ...
+
+## DESCRIPTION
+Unpauses all the paused processes in the containers of one or more pods. You may use pod IDs or names as input.
+
+## OPTIONS
+
+**--all, a**
+
+Unpause all pods.
+
+**--latest, -l**
+
+Instead of providing the pod name or ID, unpause the last created pod.
+
+## EXAMPLE
+
+podman pod unpause mywebserverpod
+
+podman pod unpause 860a4b23
+
+## SEE ALSO
+podman-pod(1), podman-pod-pause(1), podman-unpause(1)
+
+## HISTORY
+July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/podman-pod.1.md b/docs/podman-pod.1.md
index 74b6d75b5..56b93819d 100644
--- a/docs/podman-pod.1.md
+++ b/docs/podman-pod.1.md
@@ -15,10 +15,12 @@ podman pod is a set of subcommands that manage pods, or groups of containers.
| ------------------------------------------------- | ------------------------------------------------------------------------------ |
| [podman-pod-create(1)](podman-pod-create.1.md) | Create a new pod. |
| [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. |
+| [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. |
| [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. |
| [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more pods. |
| [podman-pod-start(1)](podman-pod-start.1.md) | Start one or more pods. |
| [podman-pod-stop(1)](podman-pod-stop.1.md) | Stop one or more pods. |
+| [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. |
## HISTORY
July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/libpod/pod.go b/libpod/pod.go
index 6e568f2b7..58becfa7a 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -383,6 +383,124 @@ func (p *Pod) Stop(cleanup bool) (map[string]error, error) {
return nil, nil
}
+// Pause pauses all containers within a pod that are running.
+// Only running containers will be paused. Paused, stopped, or created
+// containers will be ignored.
+// All containers are paused independently. An error pausing one container
+// will not prevent other containers being paused.
+// An error and a map[string]error are returned
+// If the error is not nil and the map is nil, an error was encountered before
+// any containers were paused
+// If map is not nil, an error was encountered when pausing one or more
+// containers. The container ID is mapped to the error encountered. The error is
+// set to ErrCtrExists
+// If both error and the map are nil, all containers were paused without error
+func (p *Pod) Pause() (map[string]error, error) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if !p.valid {
+ return nil, ErrPodRemoved
+ }
+
+ allCtrs, err := p.runtime.state.PodContainers(p)
+ if err != nil {
+ return nil, err
+ }
+
+ ctrErrors := make(map[string]error)
+
+ // Pause to all containers
+ for _, ctr := range allCtrs {
+ ctr.lock.Lock()
+
+ if err := ctr.syncContainer(); err != nil {
+ ctr.lock.Unlock()
+ ctrErrors[ctr.ID()] = err
+ continue
+ }
+
+ // Ignore containers that are not running
+ if ctr.state.State != ContainerStateRunning {
+ ctr.lock.Unlock()
+ continue
+ }
+
+ if err := ctr.pause(); err != nil {
+ ctr.lock.Unlock()
+ ctrErrors[ctr.ID()] = err
+ continue
+ }
+
+ ctr.lock.Unlock()
+ }
+
+ if len(ctrErrors) > 0 {
+ return ctrErrors, errors.Wrapf(ErrCtrExists, "error pausing some containers")
+ }
+
+ return nil, nil
+}
+
+// Unpause unpauses all containers within a pod that are running.
+// Only paused containers will be unpaused. Running, stopped, or created
+// containers will be ignored.
+// All containers are unpaused independently. An error unpausing one container
+// will not prevent other containers being unpaused.
+// An error and a map[string]error are returned
+// If the error is not nil and the map is nil, an error was encountered before
+// any containers were unpaused
+// If map is not nil, an error was encountered when unpausing one or more
+// containers. The container ID is mapped to the error encountered. The error is
+// set to ErrCtrExists
+// If both error and the map are nil, all containers were unpaused without error
+func (p *Pod) Unpause() (map[string]error, error) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ if !p.valid {
+ return nil, ErrPodRemoved
+ }
+
+ allCtrs, err := p.runtime.state.PodContainers(p)
+ if err != nil {
+ return nil, err
+ }
+
+ ctrErrors := make(map[string]error)
+
+ // Pause to all containers
+ for _, ctr := range allCtrs {
+ ctr.lock.Lock()
+
+ if err := ctr.syncContainer(); err != nil {
+ ctr.lock.Unlock()
+ ctrErrors[ctr.ID()] = err
+ continue
+ }
+
+ // Ignore containers that are not paused
+ if ctr.state.State != ContainerStatePaused {
+ ctr.lock.Unlock()
+ continue
+ }
+
+ if err := ctr.unpause(); err != nil {
+ ctr.lock.Unlock()
+ ctrErrors[ctr.ID()] = err
+ continue
+ }
+
+ ctr.lock.Unlock()
+ }
+
+ if len(ctrErrors) > 0 {
+ return ctrErrors, errors.Wrapf(ErrCtrExists, "error unpausing some containers")
+ }
+
+ return nil, nil
+}
+
// Restart restarts all containers within a pod that are not paused or in an error state.
// It combines the effects of Stop() and Start() on a container
// Each container will use its own stop timeout.
@@ -403,6 +521,7 @@ func (p *Pod) Restart(ctx context.Context) (map[string]error, error) {
if !p.valid {
return nil, ErrPodRemoved
}
+
allCtrs, err := p.runtime.state.PodContainers(p)
if err != nil {
return nil, err
diff --git a/test/e2e/pod_pause_test.go b/test/e2e/pod_pause_test.go
new file mode 100644
index 000000000..aa9638ed2
--- /dev/null
+++ b/test/e2e/pod_pause_test.go
@@ -0,0 +1,116 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman pod pause", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ pausedState := "Paused"
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.CleanupPod()
+
+ })
+
+ It("podman pod pause bogus pod", func() {
+ session := podmanTest.Podman([]string{"pod", "pause", "foobar"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman unpause bogus pod", func() {
+ session := podmanTest.Podman([]string{"pod", "unpause", "foobar"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman pod pause a created pod by id", func() {
+ session := podmanTest.Podman([]string{"pod", "create"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid := session.OutputToString()
+
+ result := podmanTest.Podman([]string{"pod", "pause", podid})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ })
+
+ It("podman pod pause a running pod by id", func() {
+ session := podmanTest.Podman([]string{"pod", "create"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "pause", podid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(ContainSubstring(pausedState))
+
+ result = podmanTest.Podman([]string{"pod", "unpause", podid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ })
+
+ It("podman unpause a running pod by id", func() {
+ session := podmanTest.Podman([]string{"pod", "create"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "unpause", podid})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ })
+
+ It("podman pod pause a running pod by name", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("", "test1")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "pause", "test1"})
+ result.WaitWithDefaultTimeout()
+
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ Expect(podmanTest.GetContainerStatus()).To(Equal(pausedState))
+
+ result = podmanTest.Podman([]string{"pod", "unpause", "test1"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ })
+})