diff options
-rw-r--r-- | cmd/podman/cliconfig/config.go | 5 | ||||
-rw-r--r-- | cmd/podman/pod.go | 1 | ||||
-rw-r--r-- | cmd/podman/pods_prune.go | 83 | ||||
-rw-r--r-- | cmd/podman/shared/pod.go | 58 | ||||
-rw-r--r-- | cmd/podman/system_prune.go | 11 | ||||
-rw-r--r-- | cmd/podman/varlink/io.podman.varlink | 3 | ||||
-rw-r--r-- | completions/bash/podman | 16 | ||||
-rw-r--r-- | docs/podman-container-prune.1.md | 2 | ||||
-rw-r--r-- | docs/podman-pod-prune.1.md | 29 | ||||
-rw-r--r-- | docs/podman-pod.1.md | 31 | ||||
-rw-r--r-- | docs/podman-system-prune.1.md | 4 | ||||
-rw-r--r-- | pkg/adapter/pods.go | 2 | ||||
-rw-r--r-- | pkg/adapter/pods_remote.go | 31 | ||||
-rw-r--r-- | pkg/adapter/runtime.go | 36 | ||||
-rw-r--r-- | pkg/varlinkapi/pods.go | 22 | ||||
-rw-r--r-- | test/e2e/prune_test.go | 35 |
16 files changed, 319 insertions, 50 deletions
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index 982c77c17..16c98a13e 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -159,6 +159,11 @@ type PruneContainersValues struct { Force bool } +type PrunePodsValues struct { + PodmanCommand + Force bool +} + type ImportValues struct { PodmanCommand Change []string diff --git a/cmd/podman/pod.go b/cmd/podman/pod.go index 2d9bca21d..ed331965e 100644 --- a/cmd/podman/pod.go +++ b/cmd/podman/pod.go @@ -24,6 +24,7 @@ var podSubCommands = []*cobra.Command{ _podInspectCommand, _podKillCommand, _podPauseCommand, + _prunePodsCommand, _podPsCommand, _podRestartCommand, _podRmCommand, diff --git a/cmd/podman/pods_prune.go b/cmd/podman/pods_prune.go new file mode 100644 index 000000000..89401a98a --- /dev/null +++ b/cmd/podman/pods_prune.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + + "github.com/containers/libpod/cmd/podman/cliconfig" + "github.com/containers/libpod/cmd/podman/shared" + "github.com/containers/libpod/pkg/adapter" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +var ( + prunePodsCommand cliconfig.PrunePodsValues + prunePodsDescription = ` + podman pod prune + + Removes all exited pods +` + _prunePodsCommand = &cobra.Command{ + Use: "prune", + Args: noSubArgs, + Short: "Remove all stopped pods", + Long: prunePodsDescription, + RunE: func(cmd *cobra.Command, args []string) error { + prunePodsCommand.InputArgs = args + prunePodsCommand.GlobalFlags = MainGlobalOpts + return prunePodsCmd(&prunePodsCommand) + }, + } +) + +func init() { + prunePodsCommand.Command = _prunePodsCommand + prunePodsCommand.SetHelpTemplate(HelpTemplate()) + prunePodsCommand.SetUsageTemplate(UsageTemplate()) + flags := prunePodsCommand.Flags() + flags.BoolVarP(&prunePodsCommand.Force, "force", "f", false, "Force removal of a running pods. The default is false") +} + +func prunePods(runtime *adapter.LocalRuntime, ctx context.Context, maxWorkers int, force bool) error { + var deleteFuncs []shared.ParallelWorkerInput + + states := []string{shared.PodStateStopped, shared.PodStateExited} + delPods, err := runtime.GetPodsByStatus(states) + if err != nil { + return err + } + if len(delPods) < 1 { + return nil + } + for _, pod := range delPods { + p := pod + f := func() error { + return runtime.RemovePod(ctx, p, force, force) + } + + deleteFuncs = append(deleteFuncs, shared.ParallelWorkerInput{ + ContainerID: p.ID(), + ParallelFunc: f, + }) + } + // Run the parallel funcs + deleteErrors, errCount := shared.ParallelExecuteWorkerPool(maxWorkers, deleteFuncs) + return printParallelOutput(deleteErrors, errCount) +} + +func prunePodsCmd(c *cliconfig.PrunePodsValues) error { + runtime, err := adapter.GetRuntime(&c.PodmanCommand) + if err != nil { + return errors.Wrapf(err, "could not get runtime") + } + defer runtime.Shutdown(false) + + maxWorkers := shared.Parallelize("rm") + if c.GlobalIsSet("max-workers") { + maxWorkers = c.GlobalFlags.MaxWorks + } + logrus.Debugf("Setting maximum workers to %d", maxWorkers) + + return prunePods(runtime, getContext(), maxWorkers, c.Bool("force")) +} diff --git a/cmd/podman/shared/pod.go b/cmd/podman/shared/pod.go index 4d936d61c..3f4cb0312 100644 --- a/cmd/podman/shared/pod.go +++ b/cmd/podman/shared/pod.go @@ -10,12 +10,12 @@ import ( ) const ( - stopped = "Stopped" - running = "Running" - paused = "Paused" - exited = "Exited" - errored = "Error" - created = "Created" + PodStateStopped = "Stopped" + PodStateRunning = "Running" + PodStatePaused = "Paused" + PodStateExited = "Exited" + PodStateErrored = "Error" + PodStateCreated = "Created" ) // GetPodStatus determines the status of the pod based on the @@ -24,7 +24,7 @@ const ( func GetPodStatus(pod *libpod.Pod) (string, error) { ctrStatuses, err := pod.Status() if err != nil { - return errored, err + return PodStateErrored, err } return CreatePodStatusResults(ctrStatuses) } @@ -32,44 +32,44 @@ func GetPodStatus(pod *libpod.Pod) (string, error) { func CreatePodStatusResults(ctrStatuses map[string]libpod.ContainerStatus) (string, error) { ctrNum := len(ctrStatuses) if ctrNum == 0 { - return created, nil + return PodStateCreated, nil } statuses := map[string]int{ - stopped: 0, - running: 0, - paused: 0, - created: 0, - errored: 0, + PodStateStopped: 0, + PodStateRunning: 0, + PodStatePaused: 0, + PodStateCreated: 0, + PodStateErrored: 0, } for _, ctrStatus := range ctrStatuses { switch ctrStatus { case libpod.ContainerStateExited: fallthrough case libpod.ContainerStateStopped: - statuses[stopped]++ + statuses[PodStateStopped]++ case libpod.ContainerStateRunning: - statuses[running]++ + statuses[PodStateRunning]++ case libpod.ContainerStatePaused: - statuses[paused]++ + statuses[PodStatePaused]++ case libpod.ContainerStateCreated, libpod.ContainerStateConfigured: - statuses[created]++ + statuses[PodStateCreated]++ default: - statuses[errored]++ + statuses[PodStateErrored]++ } } - if statuses[running] > 0 { - return running, nil - } else if statuses[paused] == ctrNum { - return paused, nil - } else if statuses[stopped] == ctrNum { - return exited, nil - } else if statuses[stopped] > 0 { - return stopped, nil - } else if statuses[errored] > 0 { - return errored, nil + if statuses[PodStateRunning] > 0 { + return PodStateRunning, nil + } else if statuses[PodStatePaused] == ctrNum { + return PodStatePaused, nil + } else if statuses[PodStateStopped] == ctrNum { + return PodStateExited, nil + } else if statuses[PodStateStopped] > 0 { + return PodStateStopped, nil + } else if statuses[PodStateErrored] > 0 { + return PodStateErrored, nil } - return created, nil + return PodStateCreated, nil } // GetNamespaceOptions transforms a slice of kernel namespaces diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index 436d54823..14cb96941 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -59,6 +59,7 @@ func pruneSystemCmd(c *cliconfig.SystemPruneValues) error { fmt.Printf(` WARNING! This will remove: - all stopped containers%s + - all stopped pods - all dangling images - all build cache Are you sure you want to continue? [y/N] `, volumeString) @@ -77,9 +78,17 @@ Are you sure you want to continue? [y/N] `, volumeString) } defer runtime.Shutdown(false) + rmWorkers := shared.Parallelize("rm") ctx := getContext() fmt.Println("Deleted Containers") - lasterr := pruneContainers(runtime, ctx, shared.Parallelize("rm"), false, false) + lasterr := pruneContainers(runtime, ctx, rmWorkers, false, false) + fmt.Println("Deleted Pods") + if err := prunePods(runtime, ctx, rmWorkers, true); err != nil { + if lasterr != nil { + logrus.Errorf("%q", lasterr) + } + lasterr = err + } if c.Bool("volumes") { fmt.Println("Deleted Volumes") err := volumePrune(runtime, getContext()) diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink index b5295273a..497f130bc 100644 --- a/cmd/podman/varlink/io.podman.varlink +++ b/cmd/podman/varlink/io.podman.varlink @@ -1053,6 +1053,9 @@ method TopPod(pod: string, latest: bool, descriptors: []string) -> (stats: []str # ~~~ method GetPodStats(name: string) -> (pod: string, containers: []ContainerStats) +# GetPodsByStatus searches for pods whose status is included in statuses +method GetPodsByStatus(statuses: []string) -> (pods: []string) + # ImageExists talks a full or partial image ID or name and returns an int as to whether # the image exists in local storage. An int result of 0 means the image does exist in # local storage; whereas 1 indicates the image does not exists in local storage. diff --git a/completions/bash/podman b/completions/bash/podman index 3616c6ca1..dce23df2b 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2740,6 +2740,22 @@ _podman_pod_ps() { __podman_pod_ps } +_podman_pod_prune() { + local options_with_args=" + " + + local boolean_options=" + -f + -h + --help + " + case "$cur" in + -*) + COMPREPLY=($(compgen -W "$boolean_options $options_with_args" -- "$cur")) + ;; + esac +} + _podman_pod_restart() { local options_with_args=" " diff --git a/docs/podman-container-prune.1.md b/docs/podman-container-prune.1.md index 194dd3dae..6fd741a3d 100644 --- a/docs/podman-container-prune.1.md +++ b/docs/podman-container-prune.1.md @@ -1,4 +1,4 @@ -% PODMAN(1) Podman Man Pages +% podman-container-prune (1) % Brent Baude % December 2018 # NAME diff --git a/docs/podman-pod-prune.1.md b/docs/podman-pod-prune.1.md new file mode 100644 index 000000000..121198de7 --- /dev/null +++ b/docs/podman-pod-prune.1.md @@ -0,0 +1,29 @@ +% % podman-pod-prune (1) +% Peter Hunt +% April 2019 +# NAME +podman-pod-prune - Remove all stopped pods + +# SYNOPSIS +**podman pod prune** [*-h*|*--help*] + +# DESCRIPTION +**podman pod prune** removes all stopped pods from local storage. + +## Examples ## + +Remove all stopped pods from local storage +``` +$ sudo podman pod prune +22b8813332948064b6566370088c5e0230eeaf15a58b1c5646859fd9fc364fe7 +2afb26869fe5beab979c234afb75c7506063cd4655b1a73557c9d583ff1aebe9 +49161ad2a722cf18722f0e17199a9e840703a17d1158cdeda502b6d54080f674 +5ca429f37fb83a9f54eea89e3a9102b7780a6e6ae5f132db0672da551d862c4a +6bb06573787efb8b0675bc88ebf8361f1a56d3ac7922d1a6436d8f59ffd955f1 +``` + +## SEE ALSO +podman-pod(1), podman-pod-ps(1), podman-pod-rm(1) + +# HISTORY +April 2019, Originally compiled by Peter Hunt (pehunt at redhat dot com) diff --git a/docs/podman-pod.1.md b/docs/podman-pod.1.md index 1846d0411..d11614358 100644 --- a/docs/podman-pod.1.md +++ b/docs/podman-pod.1.md @@ -11,21 +11,22 @@ podman pod is a set of subcommands that manage pods, or groups of containers. ## SUBCOMMANDS -| Command | Man Page | Description | -| ------- | ------------------------------------------------- | ------------------------------------------------------------------------------ | -| create | [podman-pod-create(1)](podman-pod-create.1.md) | Create a new pod. | -| exists | [podman-pod-exists(1)](podman-pod-exists.1.md) | Check if a pod exists in local storage. | -| inspect | [podman-pod-inspect(1)](podman-pod-inspect.1.md) | Displays information describing a pod. | -| kill | [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. | -| pause | [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. | -| ps | [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. | -| restart | [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or more pods. | -| rm | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more pods. | -| start | [podman-pod-start(1)](podman-pod-start.1.md) | Start one or more pods. | -| stats | [podman-pod-stats(1)](podman-pod-stats.1.md) | Display live stream resource usage stats for containers in one or more pods. | -| stop | [podman-pod-stop(1)](podman-pod-stop.1.md) | Stop one or more pods. | -| top | [podman-pod-top(1)](podman-pod-top.1.md) | Display the running processes of containers in a pod. | -| unpause | [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. | +| Command | Man Page | Description | +| ------- | -------------------------------------------------------- | ------------------------------------------------------------------------------ | +| create | [podman-pod-create(1)](podman-pod-create.1.md) | Create a new pod. | +| exists | [podman-pod-exists(1)](podman-pod-exists.1.md) | Check if a pod exists in local storage. | +| inspect | [podman-pod-inspect(1)](podman-pod-inspect.1.md) | Displays information describing a pod. | +| kill | [podman-pod-kill(1)](podman-pod-kill.1.md) | Kill the main process of each container in pod. | +| pause | [podman-pod-pause(1)](podman-pod-pause.1.md) | Pause one or more pods. | +| prune | [podman-container-prune(1)](podman-container-prune.1.md) | Remove all stopped containers from local storage. | +| ps | [podman-pod-ps(1)](podman-pod-ps.1.md) | Prints out information about pods. | +| restart | [podman-pod-restart(1)](podman-pod-restart.1.md) | Restart one or more pods. | +| rm | [podman-pod-rm(1)](podman-pod-rm.1.md) | Remove one or more pods. | +| start | [podman-pod-start(1)](podman-pod-start.1.md) | Start one or more pods. | +| stats | [podman-pod-stats(1)](podman-pod-stats.1.md) | Display live stream resource usage stats for containers in one or more pods. | +| stop | [podman-pod-stop(1)](podman-pod-stop.1.md) | Stop one or more pods. | +| top | [podman-pod-top(1)](podman-pod-top.1.md) | Display the running processes of containers in a pod. | +| unpause | [podman-pod-unpause(1)](podman-pod-unpause.1.md) | Unpause one or more pods. | ## SEE ALSO podman(1) diff --git a/docs/podman-system-prune.1.md b/docs/podman-system-prune.1.md index 1cdafb774..6a284a110 100644 --- a/docs/podman-system-prune.1.md +++ b/docs/podman-system-prune.1.md @@ -11,7 +11,7 @@ podman\-system\-prune - Remove all unused container, image and volume data [**-volumes**|**--v**] ## DESCRIPTION -**podman system prune** removes all unused containers, (both dangling and unreferenced) from local storage and optionally, volumes. +**podman system prune** removes all unused containers (both dangling and unreferenced), pods and optionally, volumes from local storage. With the `all` option, you can delete all unused images. Unused images are dangling images as well as any image that does not have any containers based on it. @@ -31,7 +31,7 @@ Do not prompt for confirmation Prune volumes not used by at least one container ## SEE ALSO -podman(1), podman-image-prune(1), podman-container-prune(1), podman-volume-prune(1) +podman(1), podman-image-prune(1), podman-container-prune(1), podman-pod-prune(1), podman-volume-prune(1) # HISTORY February 2019, Originally compiled by Dan Walsh (dwalsh at redhat dot com) diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 669971789..901c1857b 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -38,7 +38,7 @@ func (r *LocalRuntime) RemovePods(ctx context.Context, cli *cliconfig.PodRmValue } for _, p := range pods { - if err := r.RemovePod(ctx, p, cli.Force, cli.Force); err != nil { + if err := r.Runtime.RemovePod(ctx, p, cli.Force, cli.Force); err != nil { errs = append(errs, err) } else { podids = append(podids, p.ID()) diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 4a32607a2..00a5d9a32 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -214,6 +214,23 @@ func (r *LocalRuntime) GetAllPods() ([]*Pod, error) { return pods, nil } +// GetPodsByStatus returns a slice of pods filtered by a libpod status +func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*Pod, error) { + podIDs, err := iopodman.GetPodsByStatus().Call(r.Conn, statuses) + if err != nil { + return nil, err + } + pods := make([]*Pod, 0, len(podIDs)) + for _, p := range podIDs { + pod, err := r.LookupPod(p) + if err != nil { + return nil, err + } + pods = append(pods, pod) + } + return pods, nil +} + // ID returns the id of a remote pod func (p *Pod) ID() string { return p.config.ID @@ -508,3 +525,17 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*libpod.ContainerSta } return newContainerStats, nil } + +// RemovePod removes a pod +// If removeCtrs is specified, containers will be removed +// Otherwise, a pod that is not empty will return an error and not be removed +// If force is specified with removeCtrs, all containers will be stopped before +// being removed +// Otherwise, the pod will not be removed if any containers are running +func (r *LocalRuntime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { + _, err := iopodman.RemovePod().Call(r.Conn, p.ID(), force) + if err != nil { + return err + } + return nil +} diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go index b5ec9f7a9..6ed9cee77 100644 --- a/pkg/adapter/runtime.go +++ b/pkg/adapter/runtime.go @@ -369,3 +369,39 @@ func (r *LocalRuntime) Diff(c *cliconfig.DiffValues, to string) ([]archive.Chang func (r *LocalRuntime) GenerateKube(c *cliconfig.GenerateKubeValues) (*v1.Pod, *v1.Service, error) { return shared.GenerateKube(c.InputArgs[0], c.Service, r.Runtime) } + +// GetPodsByStatus returns a slice of pods filtered by a libpod status +func (r *LocalRuntime) GetPodsByStatus(statuses []string) ([]*Pod, error) { + var adapterPods []*Pod + + filterFunc := func(p *libpod.Pod) bool { + state, _ := shared.GetPodStatus(p) + for _, status := range statuses { + if state == status { + return true + } + } + return false + } + pods, err := r.Runtime.Pods(filterFunc) + if err != nil { + return nil, err + } + for _, p := range pods { + adapterPod := Pod{ + p, + } + adapterPods = append(adapterPods, &adapterPod) + } + return adapterPods, nil +} + +// RemovePod removes a pod +// If removeCtrs is specified, containers will be removed +// Otherwise, a pod that is not empty will return an error and not be removed +// If force is specified with removeCtrs, all containers will be stopped before +// being removed +// Otherwise, the pod will not be removed if any containers are running +func (r *LocalRuntime) RemovePod(ctx context.Context, p *Pod, removeCtrs, force bool) error { + return r.Runtime.RemovePod(ctx, p.Pod, removeCtrs, force) +} diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index ac8e24747..f34375bf5 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -101,6 +101,28 @@ func (i *LibpodAPI) GetPod(call iopodman.VarlinkCall, name string) error { return call.ReplyGetPod(listPod) } +// GetPodsByStatus returns a slice of pods filtered by a libpod status +func (i *LibpodAPI) GetPodsByStatus(call iopodman.VarlinkCall, statuses []string) error { + filterFuncs := func(p *libpod.Pod) bool { + state, _ := shared.GetPodStatus(p) + for _, status := range statuses { + if state == status { + return true + } + } + return false + } + filteredPods, err := i.Runtime.Pods(filterFuncs) + if err != nil { + return call.ReplyErrorOccurred(err.Error()) + } + podIDs := make([]string, 0, len(filteredPods)) + for _, p := range filteredPods { + podIDs = append(podIDs, p.ID()) + } + return call.ReplyGetPodsByStatus(podIDs) +} + // InspectPod ... func (i *LibpodAPI) InspectPod(call iopodman.VarlinkCall, name string) error { pod, err := i.Runtime.LookupPod(name) diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 682f7ff2b..544d54b50 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -14,7 +14,7 @@ LABEL RUN podman --version RUN apk update RUN apk add bash` -var _ = Describe("Podman rm", func() { +var _ = Describe("Podman prune", func() { var ( tempdir string err error @@ -101,4 +101,37 @@ var _ = Describe("Podman rm", func() { Expect(len(images.OutputToStringArray())).To(Equal(0)) }) + It("podman system prune pods", func() { + SkipIfRemote() + + session := podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "create"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "start", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + session = podmanTest.Podman([]string{"pod", "stop", "-l"}) + session.WaitWithDefaultTimeout() + Expect(session.ExitCode()).To(Equal(0)) + + pods := podmanTest.Podman([]string{"pod", "ps"}) + pods.WaitWithDefaultTimeout() + Expect(pods.ExitCode()).To(Equal(0)) + Expect(len(pods.OutputToStringArray())).To(Equal(3)) + + prune := podmanTest.Podman([]string{"system", "prune", "-f"}) + prune.WaitWithDefaultTimeout() + Expect(prune.ExitCode()).To(Equal(0)) + + pods = podmanTest.Podman([]string{"pod", "ps"}) + pods.WaitWithDefaultTimeout() + Expect(pods.ExitCode()).To(Equal(0)) + Expect(len(pods.OutputToStringArray())).To(Equal(2)) + }) }) |