github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/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/libpod/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  // CreatedAfterFilter allows you to filter on images created after
    33  // the given time.Time
    34  func CreatedAfterFilter(createTime time.Time) ResultFilter {
    35  	return func(i *Image) bool {
    36  		return i.Created().After(createTime)
    37  	}
    38  }
    39  
    40  // DanglingFilter allows you to filter images for dangling images
    41  func DanglingFilter(danglingImages bool) ResultFilter {
    42  	return func(i *Image) bool {
    43  		if danglingImages {
    44  			return i.Dangling()
    45  		}
    46  		return !i.Dangling()
    47  	}
    48  }
    49  
    50  // ReadOnlyFilter allows you to filter images based on read/only and read/write
    51  func ReadOnlyFilter(readOnly bool) ResultFilter {
    52  	return func(i *Image) bool {
    53  		if readOnly {
    54  			return i.IsReadOnly()
    55  		}
    56  		return !i.IsReadOnly()
    57  	}
    58  }
    59  
    60  // LabelFilter allows you to filter by images labels key and/or value
    61  func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
    62  	// We need to handle both label=key and label=key=value
    63  	return func(i *Image) bool {
    64  		var value string
    65  		splitFilter := strings.Split(labelfilter, "=")
    66  		key := splitFilter[0]
    67  		if len(splitFilter) > 1 {
    68  			value = splitFilter[1]
    69  		}
    70  		labels, err := i.Labels(ctx)
    71  		if err != nil {
    72  			return false
    73  		}
    74  		if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
    75  			return true
    76  		}
    77  		return labels[key] == value
    78  	}
    79  }
    80  
    81  // ReferenceFilter allows you to filter by image name
    82  // Replacing all '/' with '|' so that filepath.Match() can work
    83  // '|' character is not valid in image name, so this is safe
    84  func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
    85  	filter := fmt.Sprintf("*%s*", referenceFilter)
    86  	filter = strings.Replace(filter, "/", "|", -1)
    87  	return func(i *Image) bool {
    88  		if len(referenceFilter) < 1 {
    89  			return true
    90  		}
    91  		for _, name := range i.Names() {
    92  			newName := strings.Replace(name, "/", "|", -1)
    93  			match, err := filepath.Match(filter, newName)
    94  			if err != nil {
    95  				logrus.Errorf("failed to match %s and %s, %q", name, referenceFilter, err)
    96  			}
    97  			if match {
    98  				return true
    99  			}
   100  		}
   101  		return false
   102  	}
   103  }
   104  
   105  // IdFilter allows you to filter by image Id
   106  func IdFilter(idFilter string) ResultFilter {
   107  	return func(i *Image) bool {
   108  		return i.ID() == idFilter
   109  	}
   110  }
   111  
   112  // OutputImageFilter allows you to filter by an a specific image name
   113  func OutputImageFilter(userImage *Image) ResultFilter {
   114  	return func(i *Image) bool {
   115  		return userImage.ID() == i.ID()
   116  	}
   117  }
   118  
   119  // FilterImages filters images using a set of predefined filter funcs
   120  func FilterImages(images []*Image, filters []ResultFilter) []*Image {
   121  	var filteredImages []*Image
   122  	for _, image := range images {
   123  		include := true
   124  		for _, filter := range filters {
   125  			include = include && filter(image)
   126  		}
   127  		if include {
   128  			filteredImages = append(filteredImages, image)
   129  		}
   130  	}
   131  	return filteredImages
   132  }
   133  
   134  // createFilterFuncs returns an array of filter functions based on the user inputs
   135  // and is later used to filter images for output
   136  func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilter, error) {
   137  	var filterFuncs []ResultFilter
   138  	ctx := context.Background()
   139  	for _, filter := range filters {
   140  		splitFilter := strings.Split(filter, "=")
   141  		if len(splitFilter) < 2 {
   142  			return nil, errors.Errorf("invalid filter syntax %s", filter)
   143  		}
   144  		switch splitFilter[0] {
   145  		case "before":
   146  			before, err := ir.NewFromLocal(splitFilter[1])
   147  			if err != nil {
   148  				return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
   149  			}
   150  			filterFuncs = append(filterFuncs, CreatedBeforeFilter(before.Created()))
   151  		case "since", "after":
   152  			after, err := ir.NewFromLocal(splitFilter[1])
   153  			if err != nil {
   154  				return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
   155  			}
   156  			filterFuncs = append(filterFuncs, CreatedAfterFilter(after.Created()))
   157  		case "readonly":
   158  			readonly, err := strconv.ParseBool(splitFilter[1])
   159  			if err != nil {
   160  				return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
   161  			}
   162  			filterFuncs = append(filterFuncs, ReadOnlyFilter(readonly))
   163  		case "dangling":
   164  			danglingImages, err := strconv.ParseBool(splitFilter[1])
   165  			if err != nil {
   166  				return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
   167  			}
   168  			filterFuncs = append(filterFuncs, DanglingFilter(danglingImages))
   169  		case "label":
   170  			labelFilter := strings.Join(splitFilter[1:], "=")
   171  			filterFuncs = append(filterFuncs, LabelFilter(ctx, labelFilter))
   172  		case "reference":
   173  			referenceFilter := strings.Join(splitFilter[1:], "=")
   174  			filterFuncs = append(filterFuncs, ReferenceFilter(ctx, referenceFilter))
   175  		case "id":
   176  			filterFuncs = append(filterFuncs, IdFilter(splitFilter[1]))
   177  		default:
   178  			return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
   179  		}
   180  	}
   181  	if img != nil {
   182  		filterFuncs = append(filterFuncs, OutputImageFilter(img))
   183  	}
   184  	return filterFuncs, nil
   185  }