Files
podman/libpod/image/filters.go
Jakub Guzik 5eab1b0742 Unification of label filter across list/prune endpoints
Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
2021-03-24 00:40:30 +01:00

197 lines
5.7 KiB
Go

package image
import (
"context"
"fmt"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/containers/podman/v3/pkg/inspect"
"github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// ResultFilter is a mock function for image filtering
type ResultFilter func(*Image) bool
// Filter is a function to determine whether an 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, *inspect.ImageData) bool
// CreatedBeforeFilter allows you to filter on images created before
// the given time.Time
func CreatedBeforeFilter(createTime time.Time) ResultFilter {
return func(i *Image) bool {
return i.Created().Before(createTime)
}
}
// IntermediateFilter returns filter for intermediate images (i.e., images
// with children and no tags).
func (ir *Runtime) IntermediateFilter(ctx context.Context, images []*Image) (ResultFilter, error) {
tree, err := ir.layerTree()
if err != nil {
return nil, err
}
return func(i *Image) bool {
if len(i.Names()) > 0 {
return true
}
children, err := tree.children(ctx, i, false)
if err != nil {
logrus.Error(err.Error())
return false
}
return len(children) == 0
}, nil
}
// CreatedAfterFilter allows you to filter on images created after
// the given time.Time
func CreatedAfterFilter(createTime time.Time) ResultFilter {
return func(i *Image) bool {
return i.Created().After(createTime)
}
}
// DanglingFilter allows you to filter images for dangling images
func DanglingFilter(danglingImages bool) ResultFilter {
return func(i *Image) bool {
if danglingImages {
return i.Dangling()
}
return !i.Dangling()
}
}
// ReadOnlyFilter allows you to filter images based on read/only and read/write
func ReadOnlyFilter(readOnly bool) ResultFilter {
return func(i *Image) bool {
if readOnly {
return i.IsReadOnly()
}
return !i.IsReadOnly()
}
}
// LabelFilter allows you to filter by images labels key and/or value
func LabelFilter(ctx context.Context, filter string) ResultFilter {
// We need to handle both label=key and label=key=value
return func(i *Image) bool {
labels, err := i.Labels(ctx)
if err != nil {
return false
}
return util.MatchLabelFilters([]string{filter}, labels)
}
}
// ReferenceFilter allows you to filter by image name
// Replacing all '/' with '|' so that filepath.Match() can work
// '|' character is not valid in image name, so this is safe
func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
filter := fmt.Sprintf("*%s*", referenceFilter)
filter = strings.Replace(filter, "/", "|", -1)
return func(i *Image) bool {
if len(referenceFilter) < 1 {
return true
}
for _, name := range i.Names() {
newName := strings.Replace(name, "/", "|", -1)
match, err := filepath.Match(filter, newName)
if err != nil {
logrus.Errorf("failed to match %s and %s, %q", name, referenceFilter, err)
}
if match {
return true
}
}
return false
}
}
// IDFilter allows you to filter by image Id
func IDFilter(idFilter string) ResultFilter {
return func(i *Image) bool {
return i.ID() == idFilter
}
}
// OutputImageFilter allows you to filter by an a specific image name
func OutputImageFilter(userImage *Image) ResultFilter {
return func(i *Image) bool {
return userImage.ID() == i.ID()
}
}
// FilterImages filters images using a set of predefined filter funcs
func FilterImages(images []*Image, filters []ResultFilter) []*Image {
var filteredImages []*Image
for _, image := range images {
include := true
for _, filter := range filters {
include = include && filter(image)
}
if include {
filteredImages = append(filteredImages, image)
}
}
return filteredImages
}
// createFilterFuncs returns an array of filter functions based on the user inputs
// and is later used to filter images for output
func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilter, error) {
var filterFuncs []ResultFilter
ctx := context.Background()
for _, filter := range filters {
splitFilter := strings.SplitN(filter, "=", 2)
if len(splitFilter) < 2 {
return nil, errors.Errorf("invalid filter syntax %s", filter)
}
switch splitFilter[0] {
case "before":
before, err := ir.NewFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, CreatedBeforeFilter(before.Created()))
case "since", "after":
after, err := ir.NewFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, CreatedAfterFilter(after.Created()))
case "readonly":
readonly, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, ReadOnlyFilter(readonly))
case "dangling":
danglingImages, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, DanglingFilter(danglingImages))
case "label":
labelFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, LabelFilter(ctx, labelFilter))
case "reference":
filterFuncs = append(filterFuncs, ReferenceFilter(ctx, splitFilter[1]))
case "id":
filterFuncs = append(filterFuncs, IDFilter(splitFilter[1]))
default:
return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
}
}
if img != nil {
filterFuncs = append(filterFuncs, OutputImageFilter(img))
}
return filterFuncs, nil
}