summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/pod.go2
-rw-r--r--cmd/podman/pod_kill.go114
-rw-r--r--cmd/podman/pod_restart.go98
-rw-r--r--commands.md2
-rw-r--r--completions/bash/podman46
-rw-r--r--docs/podman-pod-kill.1.md43
-rw-r--r--docs/podman-pod-restart.1.md50
-rw-r--r--docs/podman-pod.1.md1
-rw-r--r--libpod/container_api.go34
-rw-r--r--libpod/container_internal.go37
-rw-r--r--libpod/pod.go78
-rw-r--r--pkg/spec/createconfig.go9
-rw-r--r--test/e2e/pod_kill_test.go162
-rw-r--r--test/e2e/pod_restart_test.go175
-rw-r--r--test/e2e/run_test.go13
-rw-r--r--vendor.conf2
-rw-r--r--vendor/github.com/containers/storage/drivers/overlay/overlay.go15
-rw-r--r--vendor/github.com/containers/storage/pkg/mount/flags.go6
-rw-r--r--vendor/github.com/containers/storage/pkg/mount/mount.go4
19 files changed, 833 insertions, 58 deletions
diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go
index e19fea01b..277b16932 100644
--- a/cmd/podman/pod.go
+++ b/cmd/podman/pod.go
@@ -18,7 +18,9 @@ var (
UseShortOptionHandling: true,
Subcommands: []cli.Command{
podCreateCommand,
+ podKillCommand,
podPsCommand,
+ podRestartCommand,
podRmCommand,
podStartCommand,
podStopCommand,
diff --git a/cmd/podman/pod_kill.go b/cmd/podman/pod_kill.go
new file mode 100644
index 000000000..e233c76b1
--- /dev/null
+++ b/cmd/podman/pod_kill.go
@@ -0,0 +1,114 @@
+package main
+
+import (
+ "fmt"
+ "syscall"
+
+ "github.com/docker/docker/pkg/signal"
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ podKillFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "Kill all containers in all pods",
+ },
+ cli.StringFlag{
+ Name: "signal, s",
+ Usage: "Signal to send to the containers in the pod",
+ Value: "KILL",
+ },
+ LatestFlag,
+ }
+ podKillDescription = "The main process of each container inside the specified pod will be sent SIGKILL, or any signal specified with option --signal."
+ podKillCommand = cli.Command{
+ Name: "kill",
+ Usage: "Send the specified signal or SIGKILL to containers in pod",
+ Description: podKillDescription,
+ Flags: podKillFlags,
+ Action: podKillCmd,
+ ArgsUsage: "[POD_NAME_OR_ID]",
+ UseShortOptionHandling: true,
+ }
+)
+
+// podKillCmd kills one or more pods with a signal
+func podKillCmd(c *cli.Context) error {
+ if err := checkMutuallyExclusiveFlags(c); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ args := c.Args()
+ var killSignal uint = uint(syscall.SIGTERM)
+ var lastError error
+ var pods []*libpod.Pod
+
+ if c.String("signal") != "" {
+ // Check if the signalString provided by the user is valid
+ // Invalid signals will return err
+ sysSignal, err := signal.ParseSignal(c.String("signal"))
+ if err != nil {
+ return err
+ }
+ killSignal = uint(sysSignal)
+ }
+
+ if c.Bool("all") {
+ pods, err = runtime.Pods()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get pods")
+ }
+ }
+ if c.Bool("latest") {
+ pod, err := runtime.GetLatestPod()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get latest pod")
+ }
+ pods = append(pods, pod)
+ }
+ for _, i := range args {
+ pod, err := runtime.LookupPod(i)
+ if err != nil {
+ logrus.Errorf("%q", lastError)
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to find pods %s", i)
+ continue
+ }
+ pods = append(pods, pod)
+ }
+
+ for _, pod := range pods {
+ ctr_errs, err := pod.Kill(killSignal)
+ if ctr_errs != nil {
+ for ctr, err := range ctr_errs {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to kill container %q in pod %q", ctr, pod.ID())
+ }
+ continue
+ }
+ if err != nil {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to kill pod %q", pod.ID())
+ continue
+ }
+ fmt.Println(pod.ID())
+ }
+ return lastError
+}
diff --git a/cmd/podman/pod_restart.go b/cmd/podman/pod_restart.go
new file mode 100644
index 000000000..cd0081c8c
--- /dev/null
+++ b/cmd/podman/pod_restart.go
@@ -0,0 +1,98 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/pkg/errors"
+ "github.com/projectatomic/libpod/cmd/podman/libpodruntime"
+ "github.com/projectatomic/libpod/libpod"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ podRestartFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "all, a",
+ Usage: "restart all pods",
+ },
+ LatestFlag,
+ }
+ podRestartDescription = `Restarts one or more pods. The pod ID or name can be used.`
+
+ podRestartCommand = cli.Command{
+ Name: "restart",
+ Usage: "Restart one or more pods",
+ Description: podRestartDescription,
+ Flags: podRestartFlags,
+ Action: podRestartCmd,
+ ArgsUsage: "POD-NAME|POD-ID [POD-NAME|POD-ID ...]",
+ UseShortOptionHandling: true,
+ }
+)
+
+func podRestartCmd(c *cli.Context) error {
+ if err := checkMutuallyExclusiveFlags(c); err != nil {
+ return err
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ args := c.Args()
+ var pods []*libpod.Pod
+ var lastError error
+
+ if c.Bool("all") {
+ pods, err = runtime.Pods()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get running pods")
+ }
+ }
+
+ if c.Bool("latest") {
+ pod, err := runtime.GetLatestPod()
+ if err != nil {
+ return errors.Wrapf(err, "unable to get latest pod")
+ }
+ pods = append(pods, pod)
+ }
+
+ for _, i := range args {
+ pod, err := runtime.LookupPod(i)
+ if err != nil {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to find pod %s", i)
+ continue
+ }
+ pods = append(pods, pod)
+ }
+
+ ctx := getContext()
+ for _, pod := range pods {
+ ctr_errs, err := pod.Restart(ctx)
+ if ctr_errs != nil {
+ for ctr, err := range ctr_errs {
+ if lastError != nil {
+ logrus.Errorf("%q", lastError)
+ }
+ lastError = errors.Wrapf(err, "unable to restart 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 restart pod %q", pod.ID())
+ continue
+ }
+ fmt.Println(pod.ID())
+ }
+ return lastError
+}
diff --git a/commands.md b/commands.md
index f15ff1c3d..8af63b60d 100644
--- a/commands.md
+++ b/commands.md
@@ -31,7 +31,9 @@
| [podman-pause(1)](/docs/podman-pause.1.md) | Pause one or more running containers |[![...](/docs/play.png)](https://asciinema.org/a/141292)|
| [podman-pod(1)](/docs/podman-pod.1.md) | Simple management tool for groups of containers, called pods ||
| [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-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 ||
diff --git a/completions/bash/podman b/completions/bash/podman
index 5d20bab3e..0019343e2 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -2086,6 +2086,29 @@ _podman_pod_create() {
_complete_ "$options_with_args" "$boolean_options"
}
+_podman_pod_kill() {
+ local options_with_args="
+ "
+
+ local boolean_options="
+ --all
+ -a
+ --signal
+ -s
+ --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_ps() {
local options_with_args="
-f
@@ -2121,6 +2144,27 @@ _podman_pod_ps() {
__podman_pod_ps
}
+_podman_pod_restart() {
+ 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_rm() {
local options_with_args="
"
@@ -2194,7 +2238,9 @@ _podman_pod() {
"
subcommands="
create
+ kill
ps
+ restart
rm
start
stop
diff --git a/docs/podman-pod-kill.1.md b/docs/podman-pod-kill.1.md
new file mode 100644
index 000000000..2f4d37f92
--- /dev/null
+++ b/docs/podman-pod-kill.1.md
@@ -0,0 +1,43 @@
+% podman-pod-kill "1"
+
+## NAME
+podman\-pod\-kill - Kills all containers in one or more pods with a signal
+
+## SYNOPSIS
+**podman pod kill** [*options*] *pod* ...
+
+## DESCRIPTION
+The main process of each container inside the pods specified will be sent SIGKILL, or any signal specified with option --signal.
+
+## OPTIONS
+**--all, -a**
+
+Sends signal to all containers associated with a pod.
+
+**--latest, -l**
+
+Instead of providing the pod name or ID, use the last created pod. If you use methods other than Podman
+to run pods such as CRI-O, the last started pod could be from either of those methods.
+
+**--signal, s**
+
+Signal to send to the containers in the pod. For more information on Linux signals, refer to *man signal(7)*.
+
+
+## EXAMPLE
+
+podman pod kill mywebserver
+
+podman pod kill 860a4b23
+
+podman pod kill --signal TERM 860a4b23
+
+podman pod kill --latest
+
+podman pod kill --all
+
+## SEE ALSO
+podman-pod(1), podman-pod-stop(1)
+
+## HISTORY
+July 2018, Originally compiled by Peter Hunt <pehunt@redhat.com>
diff --git a/docs/podman-pod-restart.1.md b/docs/podman-pod-restart.1.md
new file mode 100644
index 000000000..0990cf840
--- /dev/null
+++ b/docs/podman-pod-restart.1.md
@@ -0,0 +1,50 @@
+% podman-pod-restart "1"
+
+## NAME
+podman\-pod\-restart - Restart one or more pods
+
+## SYNOPSIS
+**podman pod restart** [*options*] *pod* ...
+
+## DESCRIPTION
+Restart containers in one or more pods. Containers will be stopped if running and then restarted.
+Stopped containers will only be started. You may use pod IDs or names as input.
+The pod ID will be printed upon successful restart.
+When restarting multiple pods, an error from restarting one pod will not effect restarting other pods.
+
+## OPTIONS
+
+**--all, -a**
+
+Restarts all pods
+
+**--latest, -l**
+
+Instead of providing the pod name or ID, restart the last created pod.
+
+## EXAMPLE
+
+podman pod restart mywebserverpod
+cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907
+
+podman pod restart 490eb 3557fb
+490eb241aaf704d4dd2629904410fe4aa31965d9310a735f8755267f4ded1de5
+3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
+
+3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
+
+podman pod restart --latest
+3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
+
+podman pod restart --all
+19456b4cd557eaf9629825113a552681a6013f8c8cad258e36ab825ef536e818
+3557fbea6ad61569de0506fe037479bd9896603c31d3069a6677f23833916fab
+490eb241aaf704d4dd2629904410fe4aa31965d9310a735f8755267f4ded1de5
+70c358daecf71ef9be8f62404f926080ca0133277ef7ce4f6aa2d5af6bb2d3e9
+cc8f0bea67b1a1a11aec1ecd38102a1be4b145577f21fc843c7c83b77fc28907
+
+## SEE ALSO
+podman-pod(1), podman-pod-start(1), podman-restart(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 9aee75e81..74b6d75b5 100644
--- a/docs/podman-pod.1.md
+++ b/docs/podman-pod.1.md
@@ -14,6 +14,7 @@ podman pod is a set of subcommands that manage pods, or groups of containers.
| Subcommand | Description |
| ------------------------------------------------- | ------------------------------------------------------------------------------ |
| [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-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. |
diff --git a/libpod/container_api.go b/libpod/container_api.go
index bb9727ec1..b5104048e 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -717,39 +717,7 @@ func (c *Container) RestartWithTimeout(ctx context.Context, timeout uint) (err e
depString := strings.Join(notRunning, ",")
return errors.Wrapf(ErrCtrStateInvalid, "some dependencies of container %s are not started: %s", c.ID(), depString)
}
- if c.state.State == ContainerStateUnknown || c.state.State == ContainerStatePaused {
- return errors.Wrapf(ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
- }
-
- if c.state.State == ContainerStateRunning {
- if err := c.stop(timeout); err != nil {
- return err
- }
- }
- if err := c.prepare(); err != nil {
- return err
- }
- defer func() {
- if err != nil {
- if err2 := c.cleanup(); err2 != nil {
- logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
- }
- }
- }()
-
- if c.state.State == ContainerStateStopped {
- // Reinitialize the container if we need to
- if err := c.reinit(ctx); err != nil {
- return err
- }
- } else if c.state.State == ContainerStateConfigured {
- // Initialize the container if it has never been initialized
- if err := c.init(ctx); err != nil {
- return err
- }
- }
-
- return c.start()
+ return c.restartWithTimeout(ctx, timeout)
}
// Refresh refreshes a container's state in the database, restarting the
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 55fd7369d..8a96af0ab 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -718,6 +718,43 @@ func (c *Container) unpause() error {
return c.save()
}
+// Internal, non-locking function to restart a container
+func (c *Container) restartWithTimeout(ctx context.Context, timeout uint) (err error) {
+ if c.state.State == ContainerStateUnknown || c.state.State == ContainerStatePaused {
+ return errors.Wrapf(ErrCtrStateInvalid, "unable to restart a container in a paused or unknown state")
+ }
+
+ if c.state.State == ContainerStateRunning {
+ if err := c.stop(timeout); err != nil {
+ return err
+ }
+ }
+ if err := c.prepare(); err != nil {
+ return err
+ }
+ defer func() {
+ if err != nil {
+ if err2 := c.cleanup(); err2 != nil {
+ logrus.Errorf("error cleaning up container %s: %v", c.ID(), err2)
+ }
+ }
+ }()
+
+ if c.state.State == ContainerStateStopped {
+ // Reinitialize the container if we need to
+ if err := c.reinit(ctx); err != nil {
+ return err
+ }
+ } else if c.state.State == ContainerStateConfigured {
+ // Initialize the container if it has never been initialized
+ if err := c.init(ctx); err != nil {
+ return err
+ }
+ }
+
+ return c.start()
+}
+
// mountStorage sets up the container's root filesystem
// It mounts the image and any other requested mounts
// TODO: Add ability to override mount label so we can use this for Mount() too
diff --git a/libpod/pod.go b/libpod/pod.go
index a5b87f8b5..6e568f2b7 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -212,15 +212,15 @@ func (p *Pod) Start(ctx context.Context) (map[string]error, error) {
// Traverse the graph beginning at nodes with no dependencies
for _, node := range graph.noDepNodes {
- startNode(ctx, node, false, ctrErrors, ctrsVisited)
+ startNode(ctx, node, false, ctrErrors, ctrsVisited, false)
}
return ctrErrors, nil
}
// Visit a node on a container graph and start the container, or set an error if
-// a dependency failed to start
-func startNode(ctx context.Context, node *containerNode, setError bool, ctrErrors map[string]error, ctrsVisited map[string]bool) {
+// a dependency failed to start. if restart is true, startNode will restart the node instead of starting it.
+func startNode(ctx context.Context, node *containerNode, setError bool, ctrErrors map[string]error, ctrsVisited map[string]bool, restart bool) {
// First, check if we have already visited the node
if ctrsVisited[node.id] {
return
@@ -235,7 +235,7 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
// Hit anyone who depends on us, and set errors on them too
for _, successor := range node.dependedOn {
- startNode(ctx, successor, true, ctrErrors, ctrsVisited)
+ startNode(ctx, successor, true, ctrErrors, ctrsVisited, restart)
}
return
@@ -287,10 +287,18 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
}
// Start the container (only if it is not running)
- if !ctrErrored && node.container.state.State != ContainerStateRunning {
- if err := node.container.initAndStart(ctx); err != nil {
- ctrErrored = true
- ctrErrors[node.id] = err
+ if !ctrErrored {
+ if !restart && node.container.state.State != ContainerStateRunning {
+ if err := node.container.initAndStart(ctx); err != nil {
+ ctrErrored = true
+ ctrErrors[node.id] = err
+ }
+ }
+ if restart && node.container.state.State != ContainerStatePaused && node.container.state.State != ContainerStateUnknown {
+ if err := node.container.restartWithTimeout(ctx, node.container.config.StopTimeout); err != nil {
+ ctrErrored = true
+ ctrErrors[node.id] = err
+ }
}
}
@@ -298,7 +306,7 @@ func startNode(ctx context.Context, node *containerNode, setError bool, ctrError
// Recurse to anyone who depends on us and start them
for _, successor := range node.dependedOn {
- startNode(ctx, successor, ctrErrored, ctrErrors, ctrsVisited)
+ startNode(ctx, successor, ctrErrored, ctrErrors, ctrsVisited, restart)
}
return
@@ -375,6 +383,58 @@ func (p *Pod) Stop(cleanup bool) (map[string]error, error) {
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.
+// All containers are started independently, in order dictated by their
+// dependencies. An error restarting one container
+// will not prevent other containers being restarted.
+// 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 restarted
+// If map is not nil, an error was encountered when restarting 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 restarted without error
+func (p *Pod) Restart(ctx context.Context) (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
+ }
+
+ // Build a dependency graph of containers in the pod
+ graph, err := buildContainerGraph(allCtrs)
+ if err != nil {
+ return nil, errors.Wrapf(err, "error generating dependency graph for pod %s", p.ID())
+ }
+
+ ctrErrors := make(map[string]error)
+ ctrsVisited := make(map[string]bool)
+
+ // If there are no containers without dependencies, we can't start
+ // Error out
+ if len(graph.noDepNodes) == 0 {
+ return nil, errors.Wrapf(ErrNoSuchCtr, "no containers in pod %s have no dependencies, cannot start pod", p.ID())
+ }
+
+ // Traverse the graph beginning at nodes with no dependencies
+ for _, node := range graph.noDepNodes {
+ startNode(ctx, node, false, ctrErrors, ctrsVisited, true)
+ }
+
+ if len(ctrErrors) > 0 {
+ return ctrErrors, errors.Wrapf(ErrCtrExists, "error stopping some containers")
+ }
+
+ return nil, nil
+}
+
// Kill sends a signal to all running containers within a pod
// Signals will only be sent to running containers. Containers that are not
// running will be ignored. All signals are sent independently, and sending will
diff --git a/pkg/spec/createconfig.go b/pkg/spec/createconfig.go
index 1dba8cdb4..0bd6c6d1c 100644
--- a/pkg/spec/createconfig.go
+++ b/pkg/spec/createconfig.go
@@ -147,8 +147,13 @@ func (c *CreateConfig) CreateBlockIO() (*spec.LinuxBlockIO, error) {
//GetVolumeMounts takes user provided input for bind mounts and creates Mount structs
func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, error) {
var m []spec.Mount
- var options []string
for _, i := range c.Volumes {
+ var (
+ options []string
+ foundrw, foundro, foundz, foundZ bool
+ rootProp string
+ )
+
// We need to handle SELinux options better here, specifically :Z
spliti := strings.Split(i, ":")
if len(spliti) > 2 {
@@ -158,8 +163,6 @@ func (c *CreateConfig) GetVolumeMounts(specMounts []spec.Mount) ([]spec.Mount, e
continue
}
options = append(options, "rbind")
- var foundrw, foundro, foundz, foundZ bool
- var rootProp string
for _, opt := range options {
switch opt {
case "rw":
diff --git a/test/e2e/pod_kill_test.go b/test/e2e/pod_kill_test.go
new file mode 100644
index 000000000..9abc83262
--- /dev/null
+++ b/test/e2e/pod_kill_test.go
@@ -0,0 +1,162 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman pod kill", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.CleanupPod()
+
+ })
+
+ It("podman pod kill bogus", func() {
+ session := podmanTest.Podman([]string{"pod", "kill", "foobar"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ })
+
+ It("podman pod kill a 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))
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "kill", podid})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("podman pod kill a pod by id with TERM", 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", "kill", "-s", "9", podid})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("podman pod kill a pod by name", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"})
+ 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", "kill", "test1"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+
+ It("podman pod kill a pod by id with a bogus signal", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"})
+ 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", "kill", "-s", "bogus", "test1"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(125))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ })
+
+ It("podman pod kill latest pod", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"pod", "create", "--name", "test2"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid2 := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid2)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("", podid2)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "kill", "-l"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(1))
+ })
+
+ It("podman pod kill all", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("", podid)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"pod", "create", "--name", "test2"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ podid2 := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", podid2)
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"pod", "kill", "-a"})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+ Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
+ })
+})
diff --git a/test/e2e/pod_restart_test.go b/test/e2e/pod_restart_test.go
new file mode 100644
index 000000000..8de3a05b1
--- /dev/null
+++ b/test/e2e/pod_restart_test.go
@@ -0,0 +1,175 @@
+package integration
+
+import (
+ "os"
+
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman pod restart", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest PodmanTest
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.CleanupPod()
+ })
+
+ It("podman pod restart bogus pod", func() {
+ session := podmanTest.Podman([]string{"pod", "restart", "123"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+
+ It("podman pod restart single empty pod", func() {
+ session := podmanTest.Podman([]string{"pod", "create"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"pod", "restart", cid})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+
+ It("podman pod restart single pod by name", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("test1", "foobar99")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"})
+ startTime.WaitWithDefaultTimeout()
+
+ session = podmanTest.Podman([]string{"pod", "restart", "foobar99"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToString()).To(Not(Equal(startTime.OutputToString())))
+ })
+
+ It("podman pod restart multiple pods", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"})
+ session.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test1", "foobar99")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"})
+ session2.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test2", "foobar100")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("test3", "foobar100")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.RunTopContainerInPod("test4", "foobar100")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2", "test3", "test4"})
+ startTime.WaitWithDefaultTimeout()
+
+ session = podmanTest.Podman([]string{"pod", "restart", "foobar99", "foobar100"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2", "test3", "test4"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0])))
+ Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
+ Expect(restartTime.OutputToStringArray()[2]).To(Not(Equal(startTime.OutputToStringArray()[2])))
+ Expect(restartTime.OutputToStringArray()[3]).To(Not(Equal(startTime.OutputToStringArray()[3])))
+ })
+
+ It("podman pod restart all pods", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"})
+ session.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test1", "foobar99")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"})
+ session2.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test2", "foobar100")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ startTime.WaitWithDefaultTimeout()
+
+ session = podmanTest.Podman([]string{"pod", "restart", "-a"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToStringArray()[0]).To(Not(Equal(startTime.OutputToStringArray()[0])))
+ Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
+ })
+
+ It("podman pod restart latest pod", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"})
+ session.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test1", "foobar99")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session2 := podmanTest.Podman([]string{"pod", "create", "--name", "foobar100"})
+ session2.WaitWithDefaultTimeout()
+
+ session = podmanTest.RunTopContainerInPod("test2", "foobar100")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ startTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ startTime.WaitWithDefaultTimeout()
+
+ session = podmanTest.Podman([]string{"pod", "restart", "-l"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ restartTime := podmanTest.Podman([]string{"inspect", "--format='{{.State.StartedAt}}'", "test1", "test2"})
+ restartTime.WaitWithDefaultTimeout()
+ Expect(restartTime.OutputToStringArray()[0]).To(Equal(startTime.OutputToStringArray()[0]))
+ Expect(restartTime.OutputToStringArray()[1]).To(Not(Equal(startTime.OutputToStringArray()[1])))
+ })
+
+ It("podman pod restart multiple pods with bogus", func() {
+ session := podmanTest.Podman([]string{"pod", "create", "--name", "foobar99"})
+ session.WaitWithDefaultTimeout()
+ cid1 := session.OutputToString()
+
+ session = podmanTest.RunTopContainerInPod("", "foobar99")
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"pod", "restart", cid1, "doesnotexist"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(125))
+ })
+})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 32206c4f5..92aa2f191 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -547,4 +547,17 @@ USER mail`
Expect(session.OutputToString()).To(ContainSubstring("data"))
})
+
+ It("podman run --volumes flag with multiple volumes", func() {
+ vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
+ err := os.MkdirAll(vol1, 0755)
+ Expect(err).To(BeNil())
+ vol2 := filepath.Join(podmanTest.TempDir, "vol-test2")
+ err = os.MkdirAll(vol2, 0755)
+ Expect(err).To(BeNil())
+
+ session := podmanTest.Podman([]string{"run", "--volume", vol1 + ":/myvol1:ro", "--volume", vol2 + ":/myvol2", ALPINE, "touch", "/myvol2/foo.txt"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/vendor.conf b/vendor.conf
index 7e133bcea..4c2ff88e3 100644
--- a/vendor.conf
+++ b/vendor.conf
@@ -11,7 +11,7 @@ github.com/containerd/continuity master
github.com/containernetworking/cni v0.7.0-alpha1
github.com/containernetworking/plugins 1fb94a4222eafc6f948eacdca9c9f2158b427e53
github.com/containers/image c6e0eee0f8eb38e78ae2e44a9aeea0576f451617
-github.com/containers/storage 8b1a0f8d6863cf05709af333b8997a437652ec4c
+github.com/containers/storage afdedba2d2ad573350aee35033d4e0c58fdbd57b
github.com/containers/psgo 4ccd87a37eaec61a669da89ffacb0f79f3550943
github.com/coreos/go-systemd v14
github.com/cri-o/ocicni master
diff --git a/vendor/github.com/containers/storage/drivers/overlay/overlay.go b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
index d2f7c373a..c59544aab 100644
--- a/vendor/github.com/containers/storage/drivers/overlay/overlay.go
+++ b/vendor/github.com/containers/storage/drivers/overlay/overlay.go
@@ -706,8 +706,11 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
workDir := path.Join(dir, "work")
opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(absLowers, ":"), diffDir, workDir)
+ if d.options.mountOptions != "" {
+ opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts)
+ }
mountData := label.FormatMountLabel(opts, mountLabel)
- mount := unix.Mount
+ mountFunc := unix.Mount
mountTarget := mergedDir
pageSize := unix.Getpagesize()
@@ -719,28 +722,26 @@ func (d *Driver) Get(id, mountLabel string) (_ string, retErr error) {
if len(mountData) > pageSize || d.options.mountProgram != "" {
//FIXME: We need to figure out to get this to work with additional stores
opts = fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", strings.Join(relLowers, ":"), path.Join(id, "diff"), path.Join(id, "work"))
- if d.options.mountOptions != "" {
- opts = fmt.Sprintf("%s,%s", d.options.mountOptions, opts)
- }
mountData = label.FormatMountLabel(opts, mountLabel)
if len(mountData) > pageSize {
return "", fmt.Errorf("cannot mount layer, mount label too large %d", len(mountData))
}
if d.options.mountProgram != "" {
- mount = func(source string, target string, mType string, flags uintptr, label string) error {
+ mountFunc = func(source string, target string, mType string, flags uintptr, label string) error {
mountProgram := exec.Command(d.options.mountProgram, "-o", label, target)
mountProgram.Dir = d.home
return mountProgram.Run()
}
} else {
- mount = func(source string, target string, mType string, flags uintptr, label string) error {
+ mountFunc = func(source string, target string, mType string, flags uintptr, label string) error {
return mountFrom(d.home, source, target, mType, flags, label)
}
}
mountTarget = path.Join(id, "merged")
}
- if err := mount("overlay", mountTarget, "overlay", 0, mountData); err != nil {
+ flags, data := mount.ParseOptions(mountData)
+ if err := mountFunc("overlay", mountTarget, "overlay", uintptr(flags), data); err != nil {
return "", fmt.Errorf("error creating overlay mount to %s: %v", mountTarget, err)
}
diff --git a/vendor/github.com/containers/storage/pkg/mount/flags.go b/vendor/github.com/containers/storage/pkg/mount/flags.go
index 607dbed43..07a0f4847 100644
--- a/vendor/github.com/containers/storage/pkg/mount/flags.go
+++ b/vendor/github.com/containers/storage/pkg/mount/flags.go
@@ -111,9 +111,9 @@ func MergeTmpfsOptions(options []string) ([]string, error) {
return newOptions, nil
}
-// Parse fstab type mount options into mount() flags
+// ParseOptions parses fstab type mount options into mount() flags
// and device specific data
-func parseOptions(options string) (int, string) {
+func ParseOptions(options string) (int, string) {
var (
flag int
data []string
@@ -138,7 +138,7 @@ func parseOptions(options string) (int, string) {
// ParseTmpfsOptions parse fstab type mount options into flags and data
func ParseTmpfsOptions(options string) (int, string, error) {
- flags, data := parseOptions(options)
+ flags, data := ParseOptions(options)
for _, o := range strings.Split(data, ",") {
opt := strings.SplitN(o, "=", 2)
if !validFlags[opt[0]] {
diff --git a/vendor/github.com/containers/storage/pkg/mount/mount.go b/vendor/github.com/containers/storage/pkg/mount/mount.go
index d3caa16bd..7197448da 100644
--- a/vendor/github.com/containers/storage/pkg/mount/mount.go
+++ b/vendor/github.com/containers/storage/pkg/mount/mount.go
@@ -39,7 +39,7 @@ func Mounted(mountpoint string) (bool, error) {
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func Mount(device, target, mType, options string) error {
- flag, _ := parseOptions(options)
+ flag, _ := ParseOptions(options)
if flag&REMOUNT != REMOUNT {
if mounted, err := Mounted(target); err != nil || mounted {
return err
@@ -53,7 +53,7 @@ func Mount(device, target, mType, options string) error {
// specified like the mount or fstab unix commands: "opt1=val1,opt2=val2". See
// flags.go for supported option flags.
func ForceMount(device, target, mType, options string) error {
- flag, data := parseOptions(options)
+ flag, data := ParseOptions(options)
return mount(device, target, mType, uintptr(flag), data)
}