github.com/containerd/nerdctl@v1.7.7/pkg/imgutil/filtering.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package imgutil 18 19 import ( 20 "context" 21 "fmt" 22 "regexp" 23 "strings" 24 "time" 25 26 "github.com/containerd/containerd" 27 "github.com/containerd/containerd/images" 28 "github.com/containerd/log" 29 "github.com/containerd/nerdctl/pkg/referenceutil" 30 dockerreference "github.com/distribution/reference" 31 ) 32 33 // Filter types supported to filter images. 34 var ( 35 FilterBeforeType = "before" 36 FilterSinceType = "since" 37 FilterLabelType = "label" 38 FilterReferenceType = "reference" 39 FilterDanglingType = "dangling" 40 ) 41 42 // Filters contains all types of filters to filter images. 43 type Filters struct { 44 Before []string 45 Since []string 46 Labels map[string]string 47 Reference []string 48 Dangling *bool 49 } 50 51 // ParseFilters parse filter strings. 52 func ParseFilters(filters []string) (*Filters, error) { 53 f := &Filters{Labels: make(map[string]string)} 54 for _, filter := range filters { 55 tempFilterToken := strings.Split(filter, "=") 56 switch len(tempFilterToken) { 57 case 1: 58 return nil, fmt.Errorf("invalid filter %q", filter) 59 case 2: 60 if tempFilterToken[0] == FilterDanglingType { 61 var isDangling bool 62 if tempFilterToken[1] == "true" { 63 isDangling = true 64 } else if tempFilterToken[1] == "false" { 65 isDangling = false 66 } else { 67 return nil, fmt.Errorf("invalid filter %q", filter) 68 } 69 f.Dangling = &isDangling 70 } else if tempFilterToken[0] == FilterBeforeType { 71 canonicalRef, err := referenceutil.ParseAny(tempFilterToken[1]) 72 if err != nil { 73 return nil, err 74 } 75 76 f.Before = append(f.Before, fmt.Sprintf("name==%s", canonicalRef.String())) 77 f.Before = append(f.Before, fmt.Sprintf("name==%s", tempFilterToken[1])) 78 } else if tempFilterToken[0] == FilterSinceType { 79 canonicalRef, err := referenceutil.ParseAny(tempFilterToken[1]) 80 if err != nil { 81 return nil, err 82 } 83 f.Since = append(f.Since, fmt.Sprintf("name==%s", canonicalRef.String())) 84 f.Since = append(f.Since, fmt.Sprintf("name==%s", tempFilterToken[1])) 85 } else if tempFilterToken[0] == FilterLabelType { 86 // To support filtering labels by keys. 87 f.Labels[tempFilterToken[1]] = "" 88 } else if tempFilterToken[0] == FilterReferenceType { 89 f.Reference = append(f.Reference, tempFilterToken[1]) 90 } else { 91 return nil, fmt.Errorf("invalid filter %q", filter) 92 } 93 case 3: 94 if tempFilterToken[0] == FilterLabelType { 95 f.Labels[tempFilterToken[1]] = tempFilterToken[2] 96 } else { 97 return nil, fmt.Errorf("invalid filter %q", filter) 98 } 99 default: 100 return nil, fmt.Errorf("invalid filter %q", filter) 101 } 102 } 103 return f, nil 104 } 105 106 // FilterImages returns images in `labelImages` that are created 107 // before MAX(beforeImages.CreatedAt) and after MIN(sinceImages.CreatedAt). 108 func FilterImages(labelImages []images.Image, beforeImages []images.Image, sinceImages []images.Image) []images.Image { 109 var filteredImages []images.Image 110 maxTime := time.Now() 111 minTime := time.Date(1970, time.Month(1), 1, 0, 0, 0, 0, time.UTC) 112 if len(beforeImages) > 0 { 113 maxTime = beforeImages[0].CreatedAt 114 for _, value := range beforeImages { 115 if value.CreatedAt.After(maxTime) { 116 maxTime = value.CreatedAt 117 } 118 } 119 } 120 if len(sinceImages) > 0 { 121 minTime = sinceImages[0].CreatedAt 122 for _, value := range sinceImages { 123 if value.CreatedAt.Before(minTime) { 124 minTime = value.CreatedAt 125 } 126 } 127 } 128 for _, image := range labelImages { 129 if image.CreatedAt.After(minTime) && image.CreatedAt.Before(maxTime) { 130 filteredImages = append(filteredImages, image) 131 } 132 } 133 return filteredImages 134 } 135 136 // FilterByReference filters images using references given in `filters`. 137 func FilterByReference(imageList []images.Image, filters []string) ([]images.Image, error) { 138 var filteredImageList []images.Image 139 log.L.Debug(filters) 140 for _, image := range imageList { 141 log.L.Debug(image.Name) 142 var matches int 143 for _, f := range filters { 144 var ref dockerreference.Reference 145 var err error 146 ref, err = dockerreference.ParseAnyReference(image.Name) 147 if err != nil { 148 return nil, fmt.Errorf("unable to parse image name: %s while filtering by reference because of %s", image.Name, err.Error()) 149 } 150 151 familiarMatch, err := dockerreference.FamiliarMatch(f, ref) 152 if err != nil { 153 return nil, err 154 } 155 regexpMatch, err := regexp.MatchString(f, image.Name) 156 if err != nil { 157 return nil, err 158 } 159 if familiarMatch || regexpMatch { 160 matches++ 161 } 162 } 163 if matches == len(filters) { 164 filteredImageList = append(filteredImageList, image) 165 } 166 } 167 return filteredImageList, nil 168 } 169 170 // FilterDangling filters dangling images (or keeps if `dangling` == false). 171 func FilterDangling(imageList []images.Image, dangling bool) []images.Image { 172 var filtered []images.Image 173 for _, image := range imageList { 174 _, tag := ParseRepoTag(image.Name) 175 176 if dangling && tag == "" { 177 filtered = append(filtered, image) 178 } 179 if !dangling && tag != "" { 180 filtered = append(filtered, image) 181 } 182 } 183 return filtered 184 } 185 186 // FilterByLabel filters images based on labels given in `filters`. 187 func FilterByLabel(ctx context.Context, client *containerd.Client, imageList []images.Image, filters map[string]string) ([]images.Image, error) { 188 for lk, lv := range filters { 189 var imageLabels []images.Image 190 for _, img := range imageList { 191 ci := containerd.NewImage(client, img) 192 cfg, _, err := ReadImageConfig(ctx, ci) 193 if err != nil { 194 return nil, err 195 } 196 if val, ok := cfg.Config.Labels[lk]; ok { 197 if val == lv || lv == "" { 198 imageLabels = append(imageLabels, img) 199 } 200 } 201 } 202 imageList = imageLabels 203 } 204 return imageList, nil 205 }