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