github.com/rish1988/moby@v25.0.2+incompatible/daemon/images/image_list.go (about) 1 package images // import "github.com/docker/docker/daemon/images" 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "sort" 8 "time" 9 10 "github.com/distribution/reference" 11 "github.com/docker/docker/api/types/backend" 12 imagetypes "github.com/docker/docker/api/types/image" 13 timetypes "github.com/docker/docker/api/types/time" 14 "github.com/docker/docker/container" 15 "github.com/docker/docker/image" 16 "github.com/docker/docker/layer" 17 ) 18 19 var acceptedImageFilterTags = map[string]bool{ 20 "dangling": true, 21 "label": true, 22 "before": true, 23 "since": true, 24 "reference": true, 25 "until": true, 26 } 27 28 // byCreated is a temporary type used to sort a list of images by creation 29 // time. 30 type byCreated []*imagetypes.Summary 31 32 func (r byCreated) Len() int { return len(r) } 33 func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 34 func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } 35 36 // Images returns a filtered list of images. 37 func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions) ([]*imagetypes.Summary, error) { 38 if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil { 39 return nil, err 40 } 41 42 danglingOnly, err := opts.Filters.GetBoolOrDefault("dangling", false) 43 if err != nil { 44 return nil, err 45 } 46 47 var beforeFilter, sinceFilter time.Time 48 err = opts.Filters.WalkValues("before", func(value string) error { 49 img, err := i.GetImage(ctx, value, backend.GetImageOpts{}) 50 if err != nil { 51 return err 52 } 53 // Resolve multiple values to the oldest image, 54 // equivalent to ANDing all the values together. 55 if img.Created != nil && (beforeFilter.IsZero() || beforeFilter.After(*img.Created)) { 56 beforeFilter = *img.Created 57 } 58 return nil 59 }) 60 if err != nil { 61 return nil, err 62 } 63 64 err = opts.Filters.WalkValues("until", func(value string) error { 65 ts, err := timetypes.GetTimestamp(value, time.Now()) 66 if err != nil { 67 return err 68 } 69 seconds, nanoseconds, err := timetypes.ParseTimestamps(ts, 0) 70 if err != nil { 71 return err 72 } 73 timestamp := time.Unix(seconds, nanoseconds) 74 if beforeFilter.IsZero() || beforeFilter.After(timestamp) { 75 beforeFilter = timestamp 76 } 77 return nil 78 }) 79 if err != nil { 80 return nil, err 81 } 82 83 err = opts.Filters.WalkValues("since", func(value string) error { 84 img, err := i.GetImage(ctx, value, backend.GetImageOpts{}) 85 if err != nil { 86 return err 87 } 88 // Resolve multiple values to the newest image, 89 // equivalent to ANDing all the values together. 90 if img.Created != nil && sinceFilter.Before(*img.Created) { 91 sinceFilter = *img.Created 92 } 93 return nil 94 }) 95 if err != nil { 96 return nil, err 97 } 98 99 var selectedImages map[image.ID]*image.Image 100 if danglingOnly { 101 selectedImages = i.imageStore.Heads() 102 } else { 103 selectedImages = i.imageStore.Map() 104 } 105 106 var ( 107 summaries = make([]*imagetypes.Summary, 0, len(selectedImages)) 108 summaryMap map[*image.Image]*imagetypes.Summary 109 allContainers []*container.Container 110 ) 111 for id, img := range selectedImages { 112 select { 113 case <-ctx.Done(): 114 return nil, ctx.Err() 115 default: 116 } 117 118 if !beforeFilter.IsZero() && (img.Created == nil || !img.Created.Before(beforeFilter)) { 119 continue 120 } 121 if !sinceFilter.IsZero() && (img.Created == nil || !img.Created.After(sinceFilter)) { 122 continue 123 } 124 125 if opts.Filters.Contains("label") { 126 // Very old image that do not have image.Config (or even labels) 127 if img.Config == nil { 128 continue 129 } 130 // We are now sure image.Config is not nil 131 if !opts.Filters.MatchKVList("label", img.Config.Labels) { 132 continue 133 } 134 } 135 136 // Skip any images with an unsupported operating system to avoid a potential 137 // panic when indexing through the layerstore. Don't error as we want to list 138 // the other images. This should never happen, but here as a safety precaution. 139 if err := image.CheckOS(img.OperatingSystem()); err != nil { 140 continue 141 } 142 143 var size int64 144 if layerID := img.RootFS.ChainID(); layerID != "" { 145 l, err := i.layerStore.Get(layerID) 146 if err != nil { 147 // The layer may have been deleted between the call to `Map()` or 148 // `Heads()` and the call to `Get()`, so we just ignore this error 149 if errors.Is(err, layer.ErrLayerDoesNotExist) { 150 continue 151 } 152 return nil, err 153 } 154 155 size = l.Size() 156 layer.ReleaseAndLog(i.layerStore, l) 157 } 158 159 summary := newImageSummary(img, size) 160 161 for _, ref := range i.referenceStore.References(id.Digest()) { 162 if opts.Filters.Contains("reference") { 163 var found bool 164 var matchErr error 165 for _, pattern := range opts.Filters.Get("reference") { 166 found, matchErr = reference.FamiliarMatch(pattern, ref) 167 if matchErr != nil { 168 return nil, matchErr 169 } 170 if found { 171 break 172 } 173 } 174 if !found { 175 continue 176 } 177 } 178 if _, ok := ref.(reference.Canonical); ok { 179 summary.RepoDigests = append(summary.RepoDigests, reference.FamiliarString(ref)) 180 } 181 if _, ok := ref.(reference.NamedTagged); ok { 182 summary.RepoTags = append(summary.RepoTags, reference.FamiliarString(ref)) 183 } 184 } 185 if summary.RepoDigests == nil && summary.RepoTags == nil { 186 if opts.All || len(i.imageStore.Children(id)) == 0 { 187 if opts.Filters.Contains("dangling") && !danglingOnly { 188 // dangling=false case, so dangling image is not needed 189 continue 190 } 191 if opts.Filters.Contains("reference") { // skip images with no references if filtering by reference 192 continue 193 } 194 } else { 195 continue 196 } 197 } else if danglingOnly && len(summary.RepoTags) > 0 { 198 continue 199 } 200 201 if opts.ContainerCount { 202 // Lazily init allContainers. 203 if allContainers == nil { 204 allContainers = i.containers.List() 205 } 206 207 // Get container count 208 var containers int64 209 for _, c := range allContainers { 210 if c.ImageID == id { 211 containers++ 212 } 213 } 214 // NOTE: By default, Containers is -1, or "not set" 215 summary.Containers = containers 216 } 217 218 if opts.ContainerCount || opts.SharedSize { 219 // Lazily init summaryMap. 220 if summaryMap == nil { 221 summaryMap = make(map[*image.Image]*imagetypes.Summary, len(selectedImages)) 222 } 223 summaryMap[img] = summary 224 } 225 summaries = append(summaries, summary) 226 } 227 228 if opts.SharedSize { 229 allLayers := i.layerStore.Map() 230 layerRefs := make(map[layer.ChainID]int, len(allLayers)) 231 232 allImages := selectedImages 233 if danglingOnly { 234 // If danglingOnly is true, then selectedImages include only dangling images, 235 // but we need to consider all existing images to correctly perform reference counting. 236 // If danglingOnly is false, selectedImages (and, hence, allImages) is already equal to i.imageStore.Map() 237 // and we can avoid performing an otherwise redundant method call. 238 allImages = i.imageStore.Map() 239 } 240 // Count layer references across all known images 241 for _, img := range allImages { 242 rootFS := *img.RootFS 243 rootFS.DiffIDs = nil 244 for _, id := range img.RootFS.DiffIDs { 245 rootFS.Append(id) 246 layerRefs[rootFS.ChainID()]++ 247 } 248 } 249 250 // Get Shared sizes 251 for img, summary := range summaryMap { 252 rootFS := *img.RootFS 253 rootFS.DiffIDs = nil 254 255 // Indicate that we collected shared size information (default is -1, or "not set") 256 summary.SharedSize = 0 257 for _, id := range img.RootFS.DiffIDs { 258 rootFS.Append(id) 259 chid := rootFS.ChainID() 260 261 if layerRefs[chid] > 1 { 262 if _, ok := allLayers[chid]; !ok { 263 return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) 264 } 265 summary.SharedSize += allLayers[chid].DiffSize() 266 } 267 } 268 } 269 } 270 271 sort.Sort(sort.Reverse(byCreated(summaries))) 272 273 return summaries, nil 274 } 275 276 func newImageSummary(image *image.Image, size int64) *imagetypes.Summary { 277 var created int64 278 if image.Created != nil { 279 created = image.Created.Unix() 280 } 281 summary := &imagetypes.Summary{ 282 ParentID: image.Parent.String(), 283 ID: image.ID().String(), 284 Created: created, 285 Size: size, 286 // -1 indicates that the value has not been set (avoids ambiguity 287 // between 0 (default) and "not set". We cannot use a pointer (nil) 288 // for this, as the JSON representation uses "omitempty", which would 289 // consider both "0" and "nil" to be "empty". 290 SharedSize: -1, 291 Containers: -1, 292 } 293 if image.Config != nil { 294 summary.Labels = image.Config.Labels 295 } 296 return summary 297 }