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