Skip to content

Commit

Permalink
filter added to image pruge command.
Browse files Browse the repository at this point in the history
filter option accepts two filters.
- label
- until
label supports "label=value" or "label=key=value" format
until supports all golang compatible time/duration formats.

Signed-off-by: Kunal Kushwaha <kunal.kushwaha@gmail.com>
  • Loading branch information
kunalkushwaha committed Nov 15, 2019
1 parent c200583 commit ebfb3ed
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 19 deletions.
4 changes: 2 additions & 2 deletions API.md
Expand Up @@ -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)

Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions cmd/podman/cliconfig/config.go
Expand Up @@ -175,8 +175,9 @@ type HistoryValues struct {
}
type PruneImagesValues struct {
PodmanCommand
All bool
Force bool
All bool
Force bool
Filter []string
}

type PruneContainersValues struct {
Expand Down
3 changes: 2 additions & 1 deletion cmd/podman/images_prune.go
Expand Up @@ -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 {
Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion cmd/podman/system_prune.go
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion cmd/podman/varlink/io.podman.varlink
Expand Up @@ -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)
Expand Down
5 changes: 5 additions & 0 deletions libpod/image/image.go
Expand Up @@ -74,6 +74,11 @@ type InfoImage struct {
Layers []LayerInfo
}

// Filter 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 Filter func(*Image) bool

// 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")

Expand Down
77 changes: 74 additions & 3 deletions libpod/image/prune.go
Expand Up @@ -2,23 +2,93 @@ package image

import (
"context"
"fmt"
"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) (Filter, 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, filters []string) ([]*Image, error) {
var (
filterFuncs []Filter
pruneImages []*Image
)

for _, f := range filters {
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)
}

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
Expand All @@ -30,6 +100,7 @@ func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
}
if len(containers) < 1 {
pruneImages = append(pruneImages, i)
fmt.Println(i.ID(), " Marked to delete")
}
}
}
Expand All @@ -38,9 +109,9 @@ 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) {
func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
var prunedCids []string
pruneImages, err := ir.GetPruneImages(all)
pruneImages, err := ir.GetPruneImages(all, filter)
if err != nil {
return nil, errors.Wrap(err, "unable to get images to prune")
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/adapter/runtime.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pkg/adapter/runtime_remote.go
Expand Up @@ -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
Expand Down
8 changes: 4 additions & 4 deletions pkg/varlinkapi/images.go
Expand Up @@ -21,15 +21,15 @@ 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"
"github.com/containers/libpod/pkg/channelwriter"
"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"
Expand Down Expand Up @@ -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())
}
Expand Down

0 comments on commit ebfb3ed

Please sign in to comment.