aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile16
-rw-r--r--cmd/podman/cliconfig/config.go3
-rw-r--r--cmd/podman/containers_prune.go21
-rw-r--r--cmd/podman/shared/container.go22
-rw-r--r--cmd/podman/system_prune.go2
-rw-r--r--completions/bash/podman3
-rw-r--r--docs/source/markdown/podman-container-prune.1.md22
-rw-r--r--install.md2
-rw-r--r--libpod/runtime.go18
-rw-r--r--libpod/volume_internal.go12
-rw-r--r--pkg/adapter/containers.go31
-rw-r--r--pkg/adapter/containers_remote.go2
-rw-r--r--test/e2e/prune_test.go2
13 files changed, 131 insertions, 25 deletions
diff --git a/Makefile b/Makefile
index dd948fc8e..d7c3cf7bd 100644
--- a/Makefile
+++ b/Makefile
@@ -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))