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