github.com/morganxf/moby@v1.13.1/daemon/images.go (about) 1 package daemon 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 ) 18 19 var acceptedImageFilterTags = map[string]bool{ 20 "dangling": true, 21 "label": true, 22 "before": true, 23 "since": true, 24 "reference": true, 25 } 26 27 // byCreated is a temporary type used to sort a list of images by creation 28 // time. 29 type byCreated []*types.ImageSummary 30 31 func (r byCreated) Len() int { return len(r) } 32 func (r byCreated) Swap(i, j int) { r[i], r[j] = r[j], r[i] } 33 func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created } 34 35 // Map returns a map of all images in the ImageStore 36 func (daemon *Daemon) Map() map[image.ID]*image.Image { 37 return daemon.imageStore.Map() 38 } 39 40 // Images returns a filtered list of images. filterArgs is a JSON-encoded set 41 // of filter arguments which will be interpreted by api/types/filters. 42 // filter is a shell glob string applied to repository names. The argument 43 // named all controls whether all images in the graph are filtered, or just 44 // the heads. 45 func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { 46 var ( 47 allImages map[image.ID]*image.Image 48 err error 49 danglingOnly = false 50 ) 51 52 if err := imageFilters.Validate(acceptedImageFilterTags); err != nil { 53 return nil, err 54 } 55 56 if imageFilters.Include("dangling") { 57 if imageFilters.ExactMatch("dangling", "true") { 58 danglingOnly = true 59 } else if !imageFilters.ExactMatch("dangling", "false") { 60 return nil, fmt.Errorf("Invalid filter 'dangling=%s'", imageFilters.Get("dangling")) 61 } 62 } 63 if danglingOnly { 64 allImages = daemon.imageStore.Heads() 65 } else { 66 allImages = daemon.imageStore.Map() 67 } 68 69 var beforeFilter, sinceFilter *image.Image 70 err = imageFilters.WalkValues("before", func(value string) error { 71 beforeFilter, err = daemon.GetImage(value) 72 return err 73 }) 74 if err != nil { 75 return nil, err 76 } 77 78 err = imageFilters.WalkValues("since", func(value string) error { 79 sinceFilter, err = daemon.GetImage(value) 80 return err 81 }) 82 if err != nil { 83 return nil, err 84 } 85 86 images := []*types.ImageSummary{} 87 var imagesMap map[*image.Image]*types.ImageSummary 88 var layerRefs map[layer.ChainID]int 89 var allLayers map[layer.ChainID]layer.Layer 90 var allContainers []*container.Container 91 92 for id, img := range allImages { 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.Include("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 layerID := img.RootFS.ChainID() 117 var size int64 118 if layerID != "" { 119 l, err := daemon.layerStore.Get(layerID) 120 if err != nil { 121 return nil, err 122 } 123 124 size, err = l.Size() 125 layer.ReleaseAndLog(daemon.layerStore, l) 126 if err != nil { 127 return nil, err 128 } 129 } 130 131 newImage := newImage(img, size) 132 133 for _, ref := range daemon.referenceStore.References(id.Digest()) { 134 if imageFilters.Include("reference") { 135 var found bool 136 var matchErr error 137 for _, pattern := range imageFilters.Get("reference") { 138 found, matchErr = reference.Match(pattern, ref) 139 if matchErr != nil { 140 return nil, matchErr 141 } 142 } 143 if !found { 144 continue 145 } 146 } 147 if _, ok := ref.(reference.Canonical); ok { 148 newImage.RepoDigests = append(newImage.RepoDigests, ref.String()) 149 } 150 if _, ok := ref.(reference.NamedTagged); ok { 151 newImage.RepoTags = append(newImage.RepoTags, ref.String()) 152 } 153 } 154 if newImage.RepoDigests == nil && newImage.RepoTags == nil { 155 if all || len(daemon.imageStore.Children(id)) == 0 { 156 157 if imageFilters.Include("dangling") && !danglingOnly { 158 //dangling=false case, so dangling image is not needed 159 continue 160 } 161 if imageFilters.Include("reference") { // skip images with no references if filtering by reference 162 continue 163 } 164 newImage.RepoDigests = []string{"<none>@<none>"} 165 newImage.RepoTags = []string{"<none>:<none>"} 166 } else { 167 continue 168 } 169 } else if danglingOnly && len(newImage.RepoTags) > 0 { 170 continue 171 } 172 173 if withExtraAttrs { 174 // lazyly init variables 175 if imagesMap == nil { 176 allContainers = daemon.List() 177 allLayers = daemon.layerStore.Map() 178 imagesMap = make(map[*image.Image]*types.ImageSummary) 179 layerRefs = make(map[layer.ChainID]int) 180 } 181 182 // Get container count 183 newImage.Containers = 0 184 for _, c := range allContainers { 185 if c.ImageID == id { 186 newImage.Containers++ 187 } 188 } 189 190 // count layer references 191 rootFS := *img.RootFS 192 rootFS.DiffIDs = nil 193 for _, id := range img.RootFS.DiffIDs { 194 rootFS.Append(id) 195 chid := rootFS.ChainID() 196 layerRefs[chid]++ 197 if _, ok := allLayers[chid]; !ok { 198 return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) 199 } 200 } 201 imagesMap[img] = newImage 202 } 203 204 images = append(images, newImage) 205 } 206 207 if withExtraAttrs { 208 // Get Shared sizes 209 for img, newImage := range imagesMap { 210 rootFS := *img.RootFS 211 rootFS.DiffIDs = nil 212 213 newImage.SharedSize = 0 214 for _, id := range img.RootFS.DiffIDs { 215 rootFS.Append(id) 216 chid := rootFS.ChainID() 217 218 diffSize, err := allLayers[chid].DiffSize() 219 if err != nil { 220 return nil, err 221 } 222 223 if layerRefs[chid] > 1 { 224 newImage.SharedSize += diffSize 225 } 226 } 227 } 228 } 229 230 sort.Sort(sort.Reverse(byCreated(images))) 231 232 return images, nil 233 } 234 235 // SquashImage creates a new image with the diff of the specified image and the specified parent. 236 // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between. 237 // The existing image(s) is not destroyed. 238 // 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. 239 func (daemon *Daemon) SquashImage(id, parent string) (string, error) { 240 img, err := daemon.imageStore.Get(image.ID(id)) 241 if err != nil { 242 return "", err 243 } 244 245 var parentImg *image.Image 246 var parentChainID layer.ChainID 247 if len(parent) != 0 { 248 parentImg, err = daemon.imageStore.Get(image.ID(parent)) 249 if err != nil { 250 return "", errors.Wrap(err, "error getting specified parent layer") 251 } 252 parentChainID = parentImg.RootFS.ChainID() 253 } else { 254 rootFS := image.NewRootFS() 255 parentImg = &image.Image{RootFS: rootFS} 256 } 257 258 l, err := daemon.layerStore.Get(img.RootFS.ChainID()) 259 if err != nil { 260 return "", errors.Wrap(err, "error getting image layer") 261 } 262 defer daemon.layerStore.Release(l) 263 264 ts, err := l.TarStreamFrom(parentChainID) 265 if err != nil { 266 return "", errors.Wrapf(err, "error getting tar stream to parent") 267 } 268 defer ts.Close() 269 270 newL, err := daemon.layerStore.Register(ts, parentChainID) 271 if err != nil { 272 return "", errors.Wrap(err, "error registering layer") 273 } 274 defer daemon.layerStore.Release(newL) 275 276 var newImage image.Image 277 newImage = *img 278 newImage.RootFS = nil 279 280 var rootFS image.RootFS 281 rootFS = *parentImg.RootFS 282 rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID()) 283 newImage.RootFS = &rootFS 284 285 for i, hi := range newImage.History { 286 if i >= len(parentImg.History) { 287 hi.EmptyLayer = true 288 } 289 newImage.History[i] = hi 290 } 291 292 now := time.Now() 293 var historyComment string 294 if len(parent) > 0 { 295 historyComment = fmt.Sprintf("merge %s to %s", id, parent) 296 } else { 297 historyComment = fmt.Sprintf("create new from %s", id) 298 } 299 300 newImage.History = append(newImage.History, image.History{ 301 Created: now, 302 Comment: historyComment, 303 }) 304 newImage.Created = now 305 306 b, err := json.Marshal(&newImage) 307 if err != nil { 308 return "", errors.Wrap(err, "error marshalling image config") 309 } 310 311 newImgID, err := daemon.imageStore.Create(b) 312 if err != nil { 313 return "", errors.Wrap(err, "error creating new image after squash") 314 } 315 return string(newImgID), nil 316 } 317 318 func newImage(image *image.Image, virtualSize int64) *types.ImageSummary { 319 newImage := new(types.ImageSummary) 320 newImage.ParentID = image.Parent.String() 321 newImage.ID = image.ID().String() 322 newImage.Created = image.Created.Unix() 323 newImage.Size = virtualSize 324 newImage.VirtualSize = virtualSize 325 newImage.SharedSize = -1 326 newImage.Containers = -1 327 if image.Config != nil { 328 newImage.Labels = image.Config.Labels 329 } 330 return newImage 331 }