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 }