summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xAPI.md4
-rw-r--r--cmd/podman/cliconfig/config.go5
-rw-r--r--cmd/podman/images_prune.go3
-rw-r--r--cmd/podman/system_prune.go3
-rw-r--r--cmd/podman/varlink/io.podman.varlink2
-rw-r--r--libpod/image/image.go5
-rw-r--r--libpod/image/prune.go79
-rw-r--r--pkg/adapter/runtime.go6
-rw-r--r--pkg/adapter/runtime_remote.go4
-rw-r--r--pkg/varlinkapi/images.go8
-rw-r--r--test/e2e/prune_test.go4
11 files changed, 101 insertions, 22 deletions
diff --git a/API.md b/API.md
index d96ea6cd0..c288e6b28 100755
--- a/API.md
+++ b/API.md
@@ -95,7 +95,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave)
-[func ImagesPrune(all: bool) []string](#ImagesPrune)
+[func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune)
[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
@@ -766,7 +766,7 @@ ImageSave allows you to save an image from the local image storage to a tarball
### <a name="ImagesPrune"></a>func ImagesPrune
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
-method ImagesPrune(all: [bool](https://godoc.org/builtin#bool)) [[]string](#[]string)</div>
+method ImagesPrune(all: [bool](https://godoc.org/builtin#bool), filter: [[]string](#[]string)) [[]string](#[]string)</div>
ImagesPrune removes all unused images from the local store. Upon successful pruning,
the IDs of the removed images are returned.
### <a name="ImportImage"></a>func ImportImage
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index ff38064f7..158306ccc 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -175,8 +175,9 @@ type HistoryValues struct {
}
type PruneImagesValues struct {
PodmanCommand
- All bool
- Force bool
+ All bool
+ Force bool
+ Filter []string
}
type PruneContainersValues struct {
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go
index 9c0a18c41..2b498f83d 100644
--- a/cmd/podman/images_prune.go
+++ b/cmd/podman/images_prune.go
@@ -38,6 +38,7 @@ func init() {
flags := pruneImagesCommand.Flags()
flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones")
flags.BoolVarP(&pruneImagesCommand.Force, "force", "f", false, "Do not prompt for confirmation")
+ flags.StringArrayVar(&pruneImagesCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
}
func pruneImagesCmd(c *cliconfig.PruneImagesValues) error {
@@ -62,7 +63,7 @@ Are you sure you want to continue? [y/N] `)
// Call prune; if any cids are returned, print them and then
// return err in case an error also came up
- pruneCids, err := runtime.PruneImages(getContext(), c.All)
+ pruneCids, err := runtime.PruneImages(getContext(), c.All, c.Filter)
if len(pruneCids) > 0 {
for _, cid := range pruneCids {
fmt.Println(cid)
diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go
index b499d8dd2..c4d76b2dd 100644
--- a/cmd/podman/system_prune.go
+++ b/cmd/podman/system_prune.go
@@ -117,7 +117,8 @@ Are you sure you want to continue? [y/N] `, volumeString)
// Call prune; if any cids are returned, print them and then
// return err in case an error also came up
- pruneCids, err := runtime.PruneImages(ctx, c.All)
+ // TODO: support for filters in system prune
+ pruneCids, err := runtime.PruneImages(ctx, c.All, []string{})
if len(pruneCids) > 0 {
fmt.Println("Deleted Images")
for _, cid := range pruneCids {
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index f9339fccb..4f810dd53 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1217,7 +1217,7 @@ method UnmountContainer(name: string, force: bool) -> ()
# ImagesPrune removes all unused images from the local store. Upon successful pruning,
# the IDs of the removed images are returned.
-method ImagesPrune(all: bool) -> (pruned: []string)
+method ImagesPrune(all: bool, filter: []string) -> (pruned: []string)
# This function is not implemented yet.
# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
diff --git a/libpod/image/image.go b/libpod/image/image.go
index c912ac2ca..5871c8a57 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -74,6 +74,11 @@ type InfoImage struct {
Layers []LayerInfo
}
+// ImageFilter is a function to determine whether a image is included
+// in command output. Images to be outputted are tested using the function.
+// A true return will include the image, a false return will exclude it.
+type ImageFilter func(*Image) bool //nolint
+
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag")
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index 006cbdf22..f5be8ed50 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -2,23 +2,78 @@ package image
import (
"context"
+ "strings"
+ "time"
"github.com/containers/libpod/libpod/events"
+ "github.com/containers/libpod/pkg/timetype"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
+func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
+ switch filter {
+ case "label":
+ var filterArray = strings.SplitN(filterValue, "=", 2)
+ var filterKey = filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ return func(i *Image) bool {
+ labels, err := i.Labels(context.Background())
+ if err != nil {
+ return false
+ }
+ for labelKey, labelValue := range labels {
+ if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
+ return true
+ }
+ }
+ return false
+ }, 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(i *Image) bool {
+ if !until.IsZero() && i.Created().After((until)) {
+ return true
+ }
+ return false
+ }, nil
+
+ }
+ return nil, nil
+}
+
// GetPruneImages returns a slice of images that have no names/unused
-func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
+func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image, error) {
var (
pruneImages []*Image
)
+
allImages, err := ir.GetRWImages()
if err != nil {
return nil, err
}
for _, i := range allImages {
+ // filter the images based on this.
+ for _, filterFunc := range filterFuncs {
+ if !filterFunc(i) {
+ continue
+ }
+ }
+
if len(i.Names()) == 0 {
pruneImages = append(pruneImages, i)
continue
@@ -38,9 +93,25 @@ func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
// PruneImages prunes dangling and optionally all unused images from the local
// image store
-func (ir *Runtime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- var prunedCids []string
- pruneImages, err := ir.GetPruneImages(all)
+func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ var (
+ prunedCids []string
+ filterFuncs []ImageFilter
+ )
+ for _, f := range filter {
+ filterSplit := strings.SplitN(f, "=", 2)
+ if len(filterSplit) < 2 {
+ return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
+ }
+
+ generatedFunc, err := generatePruneFilterFuncs(filterSplit[0], filterSplit[1])
+ if err != nil {
+ return nil, errors.Wrapf(err, "invalid filter")
+ }
+ filterFuncs = append(filterFuncs, generatedFunc)
+ }
+
+ pruneImages, err := ir.GetPruneImages(all, filterFuncs)
if err != nil {
return nil, errors.Wrap(err, "unable to get images to prune")
}
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 81a43853c..069283bde 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -27,7 +27,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
- "k8s.io/api/core/v1"
+ v1 "k8s.io/api/core/v1"
)
// LocalRuntime describes a typical libpod runtime
@@ -147,8 +147,8 @@ func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, for
}
// PruneImages is wrapper into PruneImages within the image pkg
-func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- return r.ImageRuntime().PruneImages(ctx, all)
+func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ return r.ImageRuntime().PruneImages(ctx, all, filter)
}
// Export is a wrapper to container export to a tarfile
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 12bf550f2..32eb934bf 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -415,8 +415,8 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
}
// PruneImages is the wrapper call for a remote-client to prune images
-func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
- return iopodman.ImagesPrune().Call(r.Conn, all)
+func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
+ return iopodman.ImagesPrune().Call(r.Conn, all, filter)
}
// Export is a wrapper to container export to a tarfile
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 8d44e6373..c27088805 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -21,7 +21,7 @@ import (
"github.com/containers/image/v5/transports/alltransports"
"github.com/containers/image/v5/types"
"github.com/containers/libpod/cmd/podman/shared"
- "github.com/containers/libpod/cmd/podman/varlink"
+ iopodman "github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/define"
"github.com/containers/libpod/libpod/image"
@@ -29,7 +29,7 @@ import (
"github.com/containers/libpod/pkg/util"
"github.com/containers/libpod/utils"
"github.com/containers/storage/pkg/archive"
- "github.com/opencontainers/image-spec/specs-go/v1"
+ v1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -740,8 +740,8 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.
}
// ImagesPrune ....
-func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error {
- prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all)
+func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error {
+ prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all, []string{})
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go
index df0525a79..eba12dca4 100644
--- a/test/e2e/prune_test.go
+++ b/test/e2e/prune_test.go
@@ -64,7 +64,7 @@ var _ = Describe("Podman prune", func() {
hasNone, _ := none.GrepString("<none>")
Expect(hasNone).To(BeTrue())
- prune := podmanTest.Podman([]string{"image", "prune"})
+ prune := podmanTest.Podman([]string{"image", "prune", "-f"})
prune.WaitWithDefaultTimeout()
Expect(prune.ExitCode()).To(Equal(0))
@@ -78,7 +78,7 @@ var _ = Describe("Podman prune", func() {
It("podman image prune unused images", func() {
podmanTest.RestoreAllArtifacts()
- prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-a"})
+ prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-af"})
prune.WaitWithDefaultTimeout()
Expect(prune.ExitCode()).To(Equal(0))