diff options
-rw-r--r-- | Makefile | 16 | ||||
-rw-r--r-- | cmd/podman/cliconfig/config.go | 3 | ||||
-rw-r--r-- | cmd/podman/containers_prune.go | 21 | ||||
-rw-r--r-- | cmd/podman/shared/container.go | 22 | ||||
-rw-r--r-- | cmd/podman/system_prune.go | 2 | ||||
-rw-r--r-- | completions/bash/podman | 3 | ||||
-rw-r--r-- | docs/source/markdown/podman-container-prune.1.md | 22 | ||||
-rw-r--r-- | install.md | 2 | ||||
-rw-r--r-- | libpod/runtime.go | 18 | ||||
-rw-r--r-- | libpod/volume_internal.go | 12 | ||||
-rw-r--r-- | pkg/adapter/containers.go | 31 | ||||
-rw-r--r-- | pkg/adapter/containers_remote.go | 2 | ||||
-rw-r--r-- | test/e2e/prune_test.go | 2 |
13 files changed, 131 insertions, 25 deletions
@@ -318,7 +318,7 @@ $(MANPAGES): %: %.md .gopathok docdir: mkdir -p docs/build/man -docs: docdir $(MANPAGES) ## Generate documentation +docs: .install.md2man docdir $(MANPAGES) ## Generate documentation install-podman-remote-%-docs: podman-remote docs $(MANPAGES) rm -rf docs/build/remote @@ -532,19 +532,23 @@ vendor-in-container: .PHONY: \ .gopathok \ binaries \ + changelog \ clean \ - validate.completions \ default \ docs \ gofmt \ + golangci-lint \ help \ install \ - golangci-lint \ + install.libseccomp.sudo \ lint \ pause \ - uninstall \ shell \ - changelog \ + uninstall \ validate \ - install.libseccomp.sudo \ + validate.completions \ vendor + +rpm: + @echo "Building rpms ..." + ./contrib/build_rpm.sh diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go index df7ea6c33..c15ce8829 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 3d0fef37d..78c50268c 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" @@ -36,9 +41,23 @@ 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=<timestamp>')") } 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") @@ -49,7 +68,7 @@ func pruneContainersCmd(c *cliconfig.PruneContainersValues) error { 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 0a2e96cf7..4f2002992 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" @@ -274,7 +275,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 { @@ -396,6 +398,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) } @@ -413,7 +431,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/completions/bash/podman b/completions/bash/podman index 5634a5d8c..18e2460ec 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2832,9 +2832,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 diff --git a/install.md b/install.md index 39b639176..bd3732083 100644 --- a/install.md +++ b/install.md @@ -68,7 +68,7 @@ The latest builds are available in a PPA. Take note of the [Build and Run Depend ```bash sudo apt-get update -qq -sudo apt-get install -qq -y software-properties-common uidmap +sudo apt-get install -qq -y software-properties-common uidmap slirp4netns sudo add-apt-repository -y ppa:projectatomic/ppa sudo apt-get update -qq sudo apt-get -qq -y install podman diff --git a/libpod/runtime.go b/libpod/runtime.go index 42e6782e9..3873079ce 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -625,7 +625,8 @@ func (r *Runtime) refresh(alivePath string) error { } // Next refresh the state of all containers to recreate dirs and - // namespaces, and all the pods to recreate cgroups + // namespaces, and all the pods to recreate cgroups. + // Containers, pods, and volumes must also reacquire their locks. ctrs, err := r.state.AllContainers() if err != nil { return errors.Wrapf(err, "error retrieving all containers from state") @@ -634,10 +635,14 @@ func (r *Runtime) refresh(alivePath string) error { if err != nil { return errors.Wrapf(err, "error retrieving all pods from state") } - // No locks are taken during pod and container refresh. - // Furthermore, the pod and container refresh() functions are not + vols, err := r.state.AllVolumes() + if err != nil { + return errors.Wrapf(err, "error retrieving all volumes from state") + } + // No locks are taken during pod, volume, and container refresh. + // Furthermore, the pod/volume/container refresh() functions are not // allowed to take locks themselves. - // We cannot assume that any pod or container has a valid lock until + // We cannot assume that any pod/volume/container has a valid lock until // after this function has returned. // The runtime alive lock should suffice to provide mutual exclusion // until this has run. @@ -651,6 +656,11 @@ func (r *Runtime) refresh(alivePath string) error { logrus.Errorf("Error refreshing pod %s: %v", pod.ID(), err) } } + for _, vol := range vols { + if err := vol.refresh(); err != nil { + logrus.Errorf("Error refreshing volume %s: %v", vol.Name(), err) + } + } // Create a file indicating the runtime is alive and ready file, err := os.OpenFile(alivePath, os.O_RDONLY|os.O_CREATE, 0644) diff --git a/libpod/volume_internal.go b/libpod/volume_internal.go index 42b935e7c..e89b3484d 100644 --- a/libpod/volume_internal.go +++ b/libpod/volume_internal.go @@ -5,6 +5,7 @@ import ( "path/filepath" "github.com/containers/libpod/libpod/define" + "github.com/pkg/errors" ) // Creates a new volume @@ -46,3 +47,14 @@ func (v *Volume) update() error { func (v *Volume) save() error { return v.runtime.state.SaveVolume(v) } + +// Refresh volume state after a restart. +func (v *Volume) refresh() error { + lock, err := v.runtime.lockManager.AllocateAndRetrieveLock(v.config.LockID) + if err != nil { + return errors.Wrapf(err, "error acquiring lock %d for volume %s", v.config.LockID, v.Name()) + } + v.lock = lock + + return nil +} diff --git a/pkg/adapter/containers.go b/pkg/adapter/containers.go index bc9554193..0c73977c7 100644 --- a/pkg/adapter/containers.go +++ b/pkg/adapter/containers.go @@ -461,7 +461,8 @@ func (r *LocalRuntime) Run(ctx context.Context, c *cliconfig.RunValues, exitCode if c.IsSet("rm") { if err := r.Runtime.RemoveContainer(ctx, ctr, false, true); err != nil { - if errors.Cause(err) == define.ErrNoSuchCtr { + if errors.Cause(err) == define.ErrNoSuchCtr || + errors.Cause(err) == define.ErrCtrRemoved { logrus.Warnf("Container %s does not exist: %v", ctr.ID(), err) } else { logrus.Errorf("Error removing container %s: %v", ctr.ID(), err) @@ -1009,16 +1010,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 +1047,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)) |