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
This commit is contained in:
haircommander
2018-07-20 13:28:19 -04:00
committed by Atomic Bot
parent 50fea69fbc
commit f258e43c7c
10 changed files with 504 additions and 9 deletions

View File

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

73
cmd/podman/pod_pause.go Normal file
View File

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

73
cmd/podman/pod_unpause.go Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

116
test/e2e/pod_pause_test.go Normal file
View File

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