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 }