github.com/jfrazelle/docker@v1.1.2-0.20210712172922-bf78e25fe508/daemon/images/images.go (about) 1 package images // import "github.com/docker/docker/daemon/images" 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "sort" 7 "time" 8 9 "github.com/pkg/errors" 10 11 "github.com/docker/distribution/reference" 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/api/types/filters" 14 "github.com/docker/docker/container" 15 "github.com/docker/docker/image" 16 "github.com/docker/docker/layer" 17 "github.com/docker/docker/pkg/system" 18 ) 19 20 var acceptedImageFilterTags = map[string]bool{ 21 "dangling": true, 22 "label": true, 23 "before": true, 24 "since": true, 25 "reference": true, 26 } 27 28 // byCreated is a temporary type used to sort a list of images by creation 29 // time. 30 type byCreated []*types.ImageSummary 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 // Map returns a map of all images in the ImageStore 37 func (i *ImageService) Map() map[image.ID]*image.Image { 38 return i.imageStore.Map() 39 } 40 41 // Images returns a filtered list of images. filterArgs is a JSON-encoded set 42 // of filter arguments which will be interpreted by api/types/filters. 43 // filter is a shell glob string applied to repository names. The argument 44 // named all controls whether all images in the graph are filtered, or just 45 // the heads. 46 func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { 47 if err := imageFilters.Validate(acceptedImageFilterTags); err != nil { 48 return nil, err 49 } 50 51 var danglingOnly bool 52 if imageFilters.Contains("dangling") { 53 if imageFilters.ExactMatch("dangling", "true") { 54 danglingOnly = true 55 } else if !imageFilters.ExactMatch("dangling", "false") { 56 return nil, invalidFilter{"dangling", imageFilters.Get("dangling")} 57 } 58 } 59 60 var ( 61 beforeFilter, sinceFilter *image.Image 62 err error 63 ) 64 err = imageFilters.WalkValues("before", func(value string) error { 65 beforeFilter, err = i.GetImage(value, nil) 66 return err 67 }) 68 if err != nil { 69 return nil, err 70 } 71 72 err = imageFilters.WalkValues("since", func(value string) error { 73 sinceFilter, err = i.GetImage(value, nil) 74 return err 75 }) 76 if err != nil { 77 return nil, err 78 } 79 80 var selectedImages map[image.ID]*image.Image 81 if danglingOnly { 82 selectedImages = i.imageStore.Heads() 83 } else { 84 selectedImages = i.imageStore.Map() 85 } 86 87 var ( 88 summaries = make([]*types.ImageSummary, 0, len(selectedImages)) 89 summaryMap map[*image.Image]*types.ImageSummary 90 allContainers []*container.Container 91 ) 92 for id, img := range selectedImages { 93 if beforeFilter != nil { 94 if img.Created.Equal(beforeFilter.Created) || img.Created.After(beforeFilter.Created) { 95 continue 96 } 97 } 98 99 if sinceFilter != nil { 100 if img.Created.Equal(sinceFilter.Created) || img.Created.Before(sinceFilter.Created) { 101 continue 102 } 103 } 104 105 if imageFilters.Contains("label") { 106 // Very old image that do not have image.Config (or even labels) 107 if img.Config == nil { 108 continue 109 } 110 // We are now sure image.Config is not nil 111 if !imageFilters.MatchKVList("label", img.Config.Labels) { 112 continue 113 } 114 } 115 116 // Skip any images with an unsupported operating system to avoid a potential 117 // panic when indexing through the layerstore. Don't error as we want to list 118 // the other images. This should never happen, but here as a safety precaution. 119 if !system.IsOSSupported(img.OperatingSystem()) { 120 continue 121 } 122 123 var size int64 124 if layerID := img.RootFS.ChainID(); layerID != "" { 125 l, err := i.layerStore.Get(layerID) 126 if err != nil { 127 // The layer may have been deleted between the call to `Map()` or 128 // `Heads()` and the call to `Get()`, so we just ignore this error 129 if err == layer.ErrLayerDoesNotExist { 130 continue 131 } 132 return nil, err 133 } 134 135 size, err = l.Size() 136 layer.ReleaseAndLog(i.layerStore, l) 137 if err != nil { 138 return nil, err 139 } 140 } 141 142 summary := newImageSummary(img, size) 143 144 for _, ref := range i.referenceStore.References(id.Digest()) { 145 if imageFilters.Contains("reference") { 146 var found bool 147 var matchErr error 148 for _, pattern := range imageFilters.Get("reference") { 149 found, matchErr = reference.FamiliarMatch(pattern, ref) 150 if matchErr != nil { 151 return nil, matchErr 152 } 153 if found { 154 break 155 } 156 } 157 if !found { 158 continue 159 } 160 } 161 if _, ok := ref.(reference.Canonical); ok { 162 summary.RepoDigests = append(summary.RepoDigests, reference.FamiliarString(ref)) 163 } 164 if _, ok := ref.(reference.NamedTagged); ok { 165 summary.RepoTags = append(summary.RepoTags, reference.FamiliarString(ref)) 166 } 167 } 168 if summary.RepoDigests == nil && summary.RepoTags == nil { 169 if all || len(i.imageStore.Children(id)) == 0 { 170 171 if imageFilters.Contains("dangling") && !danglingOnly { 172 // dangling=false case, so dangling image is not needed 173 continue 174 } 175 if imageFilters.Contains("reference") { // skip images with no references if filtering by reference 176 continue 177 } 178 summary.RepoDigests = []string{"<none>@<none>"} 179 summary.RepoTags = []string{"<none>:<none>"} 180 } else { 181 continue 182 } 183 } else if danglingOnly && len(summary.RepoTags) > 0 { 184 continue 185 } 186 187 if withExtraAttrs { 188 // Lazily init summaryMap and allContainers 189 if summaryMap == nil { 190 summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages)) 191 allContainers = i.containers.List() 192 } 193 194 // Get container count 195 var containers int64 196 for _, c := range allContainers { 197 if c.ImageID == id { 198 containers++ 199 } 200 } 201 // NOTE: By default, Containers is -1, or "not set" 202 summary.Containers = containers 203 204 summaryMap[img] = summary 205 } 206 summaries = append(summaries, summary) 207 } 208 209 if withExtraAttrs { 210 allLayers := i.layerStore.Map() 211 layerRefs := make(map[layer.ChainID]int, len(allLayers)) 212 213 allImages := selectedImages 214 if danglingOnly { 215 // If danglingOnly is true, then selectedImages include only dangling images, 216 // but we need to consider all existing images to correctly perform reference counting. 217 // If danglingOnly is false, selectedImages (and, hence, allImages) is already equal to i.imageStore.Map() 218 // and we can avoid performing an otherwise redundant method call. 219 allImages = i.imageStore.Map() 220 } 221 // Count layer references across all known images 222 for _, img := range allImages { 223 rootFS := *img.RootFS 224 rootFS.DiffIDs = nil 225 for _, id := range img.RootFS.DiffIDs { 226 rootFS.Append(id) 227 layerRefs[rootFS.ChainID()]++ 228 } 229 } 230 231 // Get Shared sizes 232 for img, summary := range summaryMap { 233 rootFS := *img.RootFS 234 rootFS.DiffIDs = nil 235 236 // Indicate that we collected shared size information (default is -1, or "not set") 237 summary.SharedSize = 0 238 for _, id := range img.RootFS.DiffIDs { 239 rootFS.Append(id) 240 chid := rootFS.ChainID() 241 242 if layerRefs[chid] > 1 { 243 if _, ok := allLayers[chid]; !ok { 244 return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) 245 } 246 diffSize, err := allLayers[chid].DiffSize() 247 if err != nil { 248 return nil, err 249 } 250 summary.SharedSize += diffSize 251 } 252 } 253 } 254 } 255 256 sort.Sort(sort.Reverse(byCreated(summaries))) 257 258 return summaries, nil 259 } 260 261 // SquashImage creates a new image with the diff of the specified image and the specified parent. 262 // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between. 263 // The existing image(s) is not destroyed. 264 // If no parent is specified, a new image with the diff of all the specified image's layers merged into a new layer that has no parents. 265 func (i *ImageService) SquashImage(id, parent string) (string, error) { 266 267 var ( 268 img *image.Image 269 err error 270 ) 271 if img, err = i.imageStore.Get(image.ID(id)); err != nil { 272 return "", err 273 } 274 275 var parentImg *image.Image 276 var parentChainID layer.ChainID 277 if len(parent) != 0 { 278 parentImg, err = i.imageStore.Get(image.ID(parent)) 279 if err != nil { 280 return "", errors.Wrap(err, "error getting specified parent layer") 281 } 282 parentChainID = parentImg.RootFS.ChainID() 283 } else { 284 rootFS := image.NewRootFS() 285 parentImg = &image.Image{RootFS: rootFS} 286 } 287 if !system.IsOSSupported(img.OperatingSystem()) { 288 return "", errors.Wrap(err, system.ErrNotSupportedOperatingSystem.Error()) 289 } 290 l, err := i.layerStore.Get(img.RootFS.ChainID()) 291 if err != nil { 292 return "", errors.Wrap(err, "error getting image layer") 293 } 294 defer i.layerStore.Release(l) 295 296 ts, err := l.TarStreamFrom(parentChainID) 297 if err != nil { 298 return "", errors.Wrapf(err, "error getting tar stream to parent") 299 } 300 defer ts.Close() 301 302 newL, err := i.layerStore.Register(ts, parentChainID) 303 if err != nil { 304 return "", errors.Wrap(err, "error registering layer") 305 } 306 defer i.layerStore.Release(newL) 307 308 newImage := *img 309 newImage.RootFS = nil 310 311 rootFS := *parentImg.RootFS 312 rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID()) 313 newImage.RootFS = &rootFS 314 315 for i, hi := range newImage.History { 316 if i >= len(parentImg.History) { 317 hi.EmptyLayer = true 318 } 319 newImage.History[i] = hi 320 } 321 322 now := time.Now() 323 var historyComment string 324 if len(parent) > 0 { 325 historyComment = fmt.Sprintf("merge %s to %s", id, parent) 326 } else { 327 historyComment = fmt.Sprintf("create new from %s", id) 328 } 329 330 newImage.History = append(newImage.History, image.History{ 331 Created: now, 332 Comment: historyComment, 333 }) 334 newImage.Created = now 335 336 b, err := json.Marshal(&newImage) 337 if err != nil { 338 return "", errors.Wrap(err, "error marshalling image config") 339 } 340 341 newImgID, err := i.imageStore.Create(b) 342 if err != nil { 343 return "", errors.Wrap(err, "error creating new image after squash") 344 } 345 return string(newImgID), nil 346 } 347 348 func newImageSummary(image *image.Image, size int64) *types.ImageSummary { 349 summary := &types.ImageSummary{ 350 ParentID: image.Parent.String(), 351 ID: image.ID().String(), 352 Created: image.Created.Unix(), 353 Size: size, 354 VirtualSize: size, 355 // -1 indicates that the value has not been set (avoids ambiguity 356 // between 0 (default) and "not set". We cannot use a pointer (nil) 357 // for this, as the JSON representation uses "omitempty", which would 358 // consider both "0" and "nil" to be "empty". 359 SharedSize: -1, 360 Containers: -1, 361 } 362 if image.Config != nil { 363 summary.Labels = image.Config.Labels 364 } 365 return summary 366 }