github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/libpod/image/filters.go (about)

     1  package image
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/containers/podman/v2/pkg/inspect"
    12  	"github.com/pkg/errors"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // ResultFilter is a mock function for image filtering
    17  type ResultFilter func(*Image) bool
    18  
    19  // Filter is a function to determine whether an image is included in
    20  // command output. Images to be outputted are tested using the function. A true
    21  // return will include the image, a false return will exclude it.
    22  type Filter func(*Image, *inspect.ImageData) bool
    23  
    24  // CreatedBeforeFilter allows you to filter on images created before
    25  // the given time.Time
    26  func CreatedBeforeFilter(createTime time.Time) ResultFilter {
    27  	return func(i *Image) bool {
    28  		return i.Created().Before(createTime)
    29  	}
    30  }
    31  
    32  // IntermediateFilter returns filter for intermediate images (i.e., images
    33  // with children and no tags).
    34  func (ir *Runtime) IntermediateFilter(ctx context.Context, images []*Image) (ResultFilter, error) {
    35  	tree, err := ir.layerTree()
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	return func(i *Image) bool {
    40  		if len(i.Names()) > 0 {
    41  			return true
    42  		}
    43  		children, err := tree.children(ctx, i, false)
    44  		if err != nil {
    45  			logrus.Error(err.Error())
    46  			return false
    47  		}
    48  		return len(children) == 0
    49  	}, nil
    50  }
    51  
    52  // CreatedAfterFilter allows you to filter on images created after
    53  // the given time.Time
    54  func CreatedAfterFilter(createTime time.Time) ResultFilter {
    55  	return func(i *Image) bool {
    56  		return i.Created().After(createTime)
    57  	}
    58  }
    59  
    60  // DanglingFilter allows you to filter images for dangling images
    61  func DanglingFilter(danglingImages bool) ResultFilter {
    62  	return func(i *Image) bool {
    63  		if danglingImages {
    64  			return i.Dangling()
    65  		}
    66  		return !i.Dangling()
    67  	}
    68  }
    69  
    70  // ReadOnlyFilter allows you to filter images based on read/only and read/write
    71  func ReadOnlyFilter(readOnly bool) ResultFilter {
    72  	return func(i *Image) bool {
    73  		if readOnly {
    74  			return i.IsReadOnly()
    75  		}
    76  		return !i.IsReadOnly()
    77  	}
    78  }
    79  
    80  // LabelFilter allows you to filter by images labels key and/or value
    81  func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
    82  	// We need to handle both label=key and label=key=value
    83  	return func(i *Image) bool {
    84  		var value string
    85  		splitFilter := strings.SplitN(labelfilter, "=", 2)
    86  		key := splitFilter[0]
    87  		if len(splitFilter) > 1 {
    88  			value = splitFilter[1]
    89  		}
    90  		labels, err := i.Labels(ctx)
    91  		if err != nil {
    92  			return false
    93  		}
    94  		if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
    95  			return true
    96  		}
    97  		return labels[key] == value
    98  	}
    99  }
   100  
   101  // ReferenceFilter allows you to filter by image name
   102  // Replacing all '/' with '|' so that filepath.Match() can work
   103  // '|' character is not valid in image name, so this is safe
   104  func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
   105  	filter := fmt.Sprintf("*%s*", referenceFilter)
   106  	filter = strings.Replace(filter, "/", "|", -1)
   107  	return func(i *Image) bool {
   108  		if len(referenceFilter) < 1 {
   109  			return true
   110  		}
   111  		for _, name := range i.Names() {
   112  			newName := strings.Replace(name, "/", "|", -1)
   113  			match, err := filepath.Match(filter, newName)
   114  			if err != nil {
   115  				logrus.Errorf("failed to match %s and %s, %q", name, referenceFilter, err)
   116  			}
   117  			if match {
   118  				return true
   119  			}
   120  		}
   121  		return false
   122  	}
   123  }
   124  
   125  // IDFilter allows you to filter by image Id
   126  func IDFilter(idFilter string) ResultFilter {
   127  	return func(i *Image) bool {
   128  		return i.ID() == idFilter
   129  	}
   130  }
   131  
   132  // OutputImageFilter allows you to filter by an a specific image name
   133  func OutputImageFilter(userImage *Image) ResultFilter {
   134  	return func(i *Image) bool {
   135  		return userImage.ID() == i.ID()
   136  	}
   137  }
   138  
   139  // FilterImages filters images using a set of predefined filter funcs
   140  func FilterImages(images []*Image, filters []ResultFilter) []*Image {
   141  	var filteredImages []*Image
   142  	for _, image := range images {
   143  		include := true
   144  		for _, filter := range filters {
   145  			include = include && filter(image)
   146  		}
   147  		if include {
   148  			filteredImages = append(filteredImages, image)
   149  		}
   150  	}
   151  	return filteredImages
   152  }
   153  
   154  // createFilterFuncs returns an array of filter functions based on the user inputs
   155  // and is later used to filter images for output
   156  func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilter, error) {
   157  	var filterFuncs []ResultFilter
   158  	ctx := context.Background()
   159  	for _, filter := range filters {
   160  		splitFilter := strings.SplitN(filter, "=", 2)
   161  		if len(splitFilter) < 2 {
   162  			return nil, errors.Errorf("invalid filter syntax %s", filter)
   163  		}
   164  		switch splitFilter[0] {
   165  		case "before":
   166  			before, err := ir.NewFromLocal(splitFilter[1])
   167  			if err != nil {
   168  				return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
   169  			}
   170  			filterFuncs = append(filterFuncs, CreatedBeforeFilter(before.Created()))
   171  		case "since", "after":
   172  			after, err := ir.NewFromLocal(splitFilter[1])
   173  			if err != nil {
   174  				return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
   175  			}
   176  			filterFuncs = append(filterFuncs, CreatedAfterFilter(after.Created()))
   177  		case "readonly":
   178  			readonly, err := strconv.ParseBool(splitFilter[1])
   179  			if err != nil {
   180  				return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
   181  			}
   182  			filterFuncs = append(filterFuncs, ReadOnlyFilter(readonly))
   183  		case "dangling":
   184  			danglingImages, err := strconv.ParseBool(splitFilter[1])
   185  			if err != nil {
   186  				return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
   187  			}
   188  			filterFuncs = append(filterFuncs, DanglingFilter(danglingImages))
   189  		case "label":
   190  			labelFilter := strings.Join(splitFilter[1:], "=")
   191  			filterFuncs = append(filterFuncs, LabelFilter(ctx, labelFilter))
   192  		case "reference":
   193  			filterFuncs = append(filterFuncs, ReferenceFilter(ctx, splitFilter[1]))
   194  		case "id":
   195  			filterFuncs = append(filterFuncs, IDFilter(splitFilter[1]))
   196  		default:
   197  			return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
   198  		}
   199  	}
   200  	if img != nil {
   201  		filterFuncs = append(filterFuncs, OutputImageFilter(img))
   202  	}
   203  	return filterFuncs, nil
   204  }