From 0602ce4dc04ec1b78724019e03066b8562877b73 Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Thu, 28 Nov 2019 11:45:26 +0900 Subject: command output fixed as per docker equivalent container prune command ask for confirmation by default. Signed-off-by: Kunal Kushwaha --- cmd/podman/containers_prune.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index 3d0fef37d..e18c829e0 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -1,6 +1,11 @@ package main import ( + "bufio" + "fmt" + "os" + "strings" + "github.com/containers/libpod/cmd/podman/cliconfig" "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod/define" @@ -39,6 +44,19 @@ func init() { } func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { + if !c.Force { + reader := bufio.NewReader(os.Stdin) + fmt.Printf(`WARNING! This will remove all stopped containers. +Are you sure you want to continue? [y/N] `) + ans, err := reader.ReadString('\n') + if err != nil { + return errors.Wrapf(err, "error reading input") + } + if strings.ToLower(ans)[0] != 'y' { + return nil + } + } + runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) if err != nil { return errors.Wrapf(err, "could not get runtime") -- cgit v1.2.3-54-g00ecf From 10c37a2c93c76fb698e41dbdf076793c9a16d02b Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Fri, 29 Nov 2019 14:28:34 +0900 Subject: filter added to container prune command filter flag helps to filter the containers based on labels, until(time), name, etc for prune command. Signed-off-by: Kunal Kushwaha --- cmd/podman/cliconfig/config.go | 3 ++- cmd/podman/containers_prune.go | 3 ++- cmd/podman/shared/container.go | 22 ++++++++++++++++++++-- cmd/podman/system_prune.go | 2 +- pkg/adapter/containers.go | 28 ++++++++++++++++++++++------ pkg/adapter/containers_remote.go | 2 +- test/e2e/prune_test.go | 2 +- 7 files changed, 49 insertions(+), 13 deletions(-) diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index a34afa827..102186854 100644 --- a/cmd/podman/cliconfig/config.go +++ b/cmd/podman/cliconfig/config.go @@ -183,7 +183,8 @@ type PruneImagesValues struct { type PruneContainersValues struct { PodmanCommand - Force bool + Force bool + Filter []string } type PodPruneValues struct { diff --git a/cmd/podman/containers_prune.go b/cmd/podman/containers_prune.go index e18c829e0..78c50268c 100644 --- a/cmd/podman/containers_prune.go +++ b/cmd/podman/containers_prune.go @@ -41,6 +41,7 @@ func init() { pruneContainersCommand.SetUsageTemplate(UsageTemplate()) flags := pruneContainersCommand.Flags() flags.BoolVarP(&pruneContainersCommand.Force, "force", "f", false, "Force removal of a running container. The default is false") + flags.StringArrayVar(&pruneContainersCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'until=')") } func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { @@ -67,7 +68,7 @@ Are you sure you want to continue? [y/N] `) if c.GlobalIsSet("max-workers") { maxWorkers = c.GlobalFlags.MaxWorks } - ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force) + ok, failures, err := runtime.Prune(getContext(), maxWorkers, c.Force, c.Filter) if err != nil { if errors.Cause(err) == define.ErrNoSuchCtr { if len(c.InputArgs) > 1 { diff --git a/cmd/podman/shared/container.go b/cmd/podman/shared/container.go index f49943477..364fede76 100644 --- a/cmd/podman/shared/container.go +++ b/cmd/podman/shared/container.go @@ -17,6 +17,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/timetype" "github.com/containers/libpod/pkg/util" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/docker/go-units" @@ -272,7 +273,8 @@ func worker(wg *sync.WaitGroup, jobs <-chan workerInput, results chan<- PsContai } } -func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { +// GenerateContainerFilterFuncs return ContainerFilter functions based of filter. +func GenerateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) (func(container *libpod.Container) bool, error) { switch filter { case "id": return func(c *libpod.Container) bool { @@ -394,6 +396,22 @@ func generateContainerFilterFuncs(filter, filterValue string, r *libpod.Runtime) } return hcStatus == filterValue }, nil + case "until": + ts, err := timetype.GetTimestamp(filterValue, time.Now()) + if err != nil { + return nil, err + } + seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0) + if err != nil { + return nil, err + } + until := time.Unix(seconds, nanoseconds) + return func(c *libpod.Container) bool { + if !until.IsZero() && c.CreatedTime().After((until)) { + return true + } + return false + }, nil } return nil, errors.Errorf("%s is an invalid filter", filter) } @@ -411,7 +429,7 @@ func GetPsContainerOutput(r *libpod.Runtime, opts PsOptions, filters []string, m if len(filterSplit) < 2 { return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) } - generatedFunc, err := generateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) + generatedFunc, err := GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r) if err != nil { return nil, errors.Wrapf(err, "invalid filter") } diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go index ae5d7ed2d..74fdcde99 100644 --- a/cmd/podman/system_prune.go +++ b/cmd/podman/system_prune.go @@ -92,7 +92,7 @@ Are you sure you want to continue? [y/N] `, volumeString) rmWorkers := shared.Parallelize("rm") fmt.Println("Deleted Containers") - ok, failures, err = runtime.Prune(ctx, rmWorkers, false) + ok, failures, err = runtime.Prune(ctx, rmWorkers, false, []string{}) if err != nil { if lasterr != nil { logrus.Errorf("%q", err) diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index bc9554193..2b838452c 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -1009,16 +1009,30 @@ func (r *LocalRuntime) ExecContainer(ctx context.Context, cli *cliconfig.ExecVal } // Prune removes stopped containers -func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([]string, map[string]error, error) { +func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, filters []string) ([]string, map[string]error, error) { var ( - ok = []string{} - failures = map[string]error{} - err error + ok = []string{} + failures = map[string]error{} + err error + filterFunc []libpod.ContainerFilter ) logrus.Debugf("Setting maximum rm workers to %d", maxWorkers) - filter := func(c *libpod.Container) bool { + for _, filter := range filters { + filterSplit := strings.SplitN(filter, "=", 2) + if len(filterSplit) < 2 { + return ok, failures, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", filter) + } + + f, err := shared.GenerateContainerFilterFuncs(filterSplit[0], filterSplit[1], r.Runtime) + if err != nil { + return ok, failures, err + } + filterFunc = append(filterFunc, f) + } + + containerStateFilter := func(c *libpod.Container) bool { state, err := c.State() if err != nil { logrus.Error(err) @@ -1032,7 +1046,9 @@ func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([ } return false } - delContainers, err := r.Runtime.GetContainers(filter) + filterFunc = append(filterFunc, containerStateFilter) + + delContainers, err := r.Runtime.GetContainers(filterFunc...) if err != nil { return ok, failures, err } diff --git a/pkg/adapter/containers_remote.go b/pkg/adapter/containers_remote.go index e34b8ffd9..36db4af68 100644 --- a/pkg/adapter/containers_remote.go +++ b/pkg/adapter/containers_remote.go @@ -922,7 +922,7 @@ func (r *LocalRuntime) Top(cli *cliconfig.TopValues) ([]string, error) { } // Prune removes stopped containers -func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool) ([]string, map[string]error, error) { +func (r *LocalRuntime) Prune(ctx context.Context, maxWorkers int, force bool, filter []string) ([]string, map[string]error, error) { var ( ok = []string{} diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go index 16522785b..c9b01ad4a 100644 --- a/test/e2e/prune_test.go +++ b/test/e2e/prune_test.go @@ -52,7 +52,7 @@ var _ = Describe("Podman prune", func() { stop.WaitWithDefaultTimeout() Expect(stop.ExitCode()).To(Equal(0)) - prune := podmanTest.Podman([]string{"container", "prune"}) + prune := podmanTest.Podman([]string{"container", "prune", "-f"}) prune.WaitWithDefaultTimeout() Expect(prune.ExitCode()).To(Equal(0)) -- cgit v1.2.3-54-g00ecf From bca01ed46121aebd7b770cd98130f1b14834fc91 Mon Sep 17 00:00:00 2001 From: Kunal Kushwaha Date: Fri, 29 Nov 2019 15:18:12 +0900 Subject: man page updated with examples of filter option examples with filter added Signed-off-by: Kunal Kushwaha --- completions/bash/podman | 3 +++ docs/source/markdown/podman-container-prune.1.md | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/completions/bash/podman b/completions/bash/podman index 6d145030f..f1f64e347 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2816,9 +2816,12 @@ _podman_images_prune() { _podman_container_prune() { local options_with_args=" + --filter " local boolean_options=" + -f + --force -h --help " diff --git a/docs/source/markdown/podman-container-prune.1.md b/docs/source/markdown/podman-container-prune.1.md index d8a4b7f4e..856843a80 100644 --- a/docs/source/markdown/podman-container-prune.1.md +++ b/docs/source/markdown/podman-container-prune.1.md @@ -20,6 +20,8 @@ Print usage statement Remove all stopped containers from local storage ``` $ sudo podman container prune +WARNING! This will remove all stopped containers. +Are you sure you want to continue? [y/N] y 878392adf2e6c5c9bb1fc19b69d37d2e98c8abf9d539c0bce4b15b46bbcce471 37664467fbe3618bf9479c34393ac29c02696675addf1750f9e346581636cde7 ed0c6468b8e1cb641b4621d1fe30cb477e1fefc5c0bceb66feaf2f7cb50e5962 @@ -28,6 +30,26 @@ fff1c5b6c3631746055ec40598ce8ecaa4b82aef122f9e3a85b03b55c0d06c23 602d343cd47e7cb3dfc808282a9900a3e4555747787ec6723bb68cedab8384d5 ``` +Remove all stopped containers from local storage without confirmation. +``` +$ sudo podman container prune -f +878392adf2e6c5c9bb1fc19b69d37d2e98c8abf9d539c0bce4b15b46bbcce471 +37664467fbe3618bf9479c34393ac29c02696675addf1750f9e346581636cde7 +ed0c6468b8e1cb641b4621d1fe30cb477e1fefc5c0bceb66feaf2f7cb50e5962 +6ac6c8f0067b7a4682e6b8e18902665b57d1a0e07e885d9abcd382232a543ccd +fff1c5b6c3631746055ec40598ce8ecaa4b82aef122f9e3a85b03b55c0d06c23 +602d343cd47e7cb3dfc808282a9900a3e4555747787ec6723bb68cedab8384d5 + +``` + +Remove all stopped containers from local storage created within last 10 minutes +``` +$ sudo podman container prune --filter until="10m" +WARNING! This will remove all stopped containers. +Are you sure you want to continue? [y/N] y +3d366295e33d8cc612c4d873199bacadd55088d90d17dcafaa9a2d317ad50b4e +``` + ## SEE ALSO podman(1), podman-ps -- cgit v1.2.3-54-g00ecf