github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/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 // The layer may have been deleted between the call to `Map()` or 122 // `Heads()` and the call to `Get()`, so we just ignore this error 123 if err == layer.ErrLayerDoesNotExist { 124 continue 125 } 126 return nil, err 127 } 128 129 size, err = l.Size() 130 layer.ReleaseAndLog(daemon.layerStore, l) 131 if err != nil { 132 return nil, err 133 } 134 } 135 136 newImage := newImage(img, size) 137 138 for _, ref := range daemon.referenceStore.References(id.Digest()) { 139 if imageFilters.Include("reference") { 140 var found bool 141 var matchErr error 142 for _, pattern := range imageFilters.Get("reference") { 143 found, matchErr = reference.FamiliarMatch(pattern, ref) 144 if matchErr != nil { 145 return nil, matchErr 146 } 147 } 148 if !found { 149 continue 150 } 151 } 152 if _, ok := ref.(reference.Canonical); ok { 153 newImage.RepoDigests = append(newImage.RepoDigests, reference.FamiliarString(ref)) 154 } 155 if _, ok := ref.(reference.NamedTagged); ok { 156 newImage.RepoTags = append(newImage.RepoTags, reference.FamiliarString(ref)) 157 } 158 } 159 if newImage.RepoDigests == nil && newImage.RepoTags == nil { 160 if all || len(daemon.imageStore.Children(id)) == 0 { 161 162 if imageFilters.Include("dangling") && !danglingOnly { 163 //dangling=false case, so dangling image is not needed 164 continue 165 } 166 if imageFilters.Include("reference") { // skip images with no references if filtering by reference 167 continue 168 } 169 newImage.RepoDigests = []string{"<none>@<none>"} 170 newImage.RepoTags = []string{"<none>:<none>"} 171 } else { 172 continue 173 } 174 } else if danglingOnly && len(newImage.RepoTags) > 0 { 175 continue 176 } 177 178 if withExtraAttrs { 179 // lazily init variables 180 if imagesMap == nil { 181 allContainers = daemon.List() 182 allLayers = daemon.layerStore.Map() 183 imagesMap = make(map[*image.Image]*types.ImageSummary) 184 layerRefs = make(map[layer.ChainID]int) 185 } 186 187 // Get container count 188 newImage.Containers = 0 189 for _, c := range allContainers { 190 if c.ImageID == id { 191 newImage.Containers++ 192 } 193 } 194 195 // count layer references 196 rootFS := *img.RootFS 197 rootFS.DiffIDs = nil 198 for _, id := range img.RootFS.DiffIDs { 199 rootFS.Append(id) 200 chid := rootFS.ChainID() 201 layerRefs[chid]++ 202 if _, ok := allLayers[chid]; !ok { 203 return nil, fmt.Errorf("layer %v was not found (corruption?)", chid) 204 } 205 } 206 imagesMap[img] = newImage 207 } 208 209 images = append(images, newImage) 210 } 211 212 if withExtraAttrs { 213 // Get Shared sizes 214 for img, newImage := range imagesMap { 215 rootFS := *img.RootFS 216 rootFS.DiffIDs = nil 217 218 newImage.SharedSize = 0 219 for _, id := range img.RootFS.DiffIDs { 220 rootFS.Append(id) 221 chid := rootFS.ChainID() 222 223 diffSize, err := allLayers[chid].DiffSize() 224 if err != nil { 225 return nil, err 226 } 227 228 if layerRefs[chid] > 1 { 229 newImage.SharedSize += diffSize 230 } 231 } 232 } 233 } 234 235 sort.Sort(sort.Reverse(byCreated(images))) 236 237 return images, nil 238 } 239 240 // SquashImage creates a new image with the diff of the specified image and the specified parent. 241 // This new image contains only the layers from it's parent + 1 extra layer which contains the diff of all the layers in between. 242 // The existing image(s) is not destroyed. 243 // 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. 244 func (daemon *Daemon) SquashImage(id, parent string) (string, error) { 245 img, err := daemon.imageStore.Get(image.ID(id)) 246 if err != nil { 247 return "", err 248 } 249 250 var parentImg *image.Image 251 var parentChainID layer.ChainID 252 if len(parent) != 0 { 253 parentImg, err = daemon.imageStore.Get(image.ID(parent)) 254 if err != nil { 255 return "", errors.Wrap(err, "error getting specified parent layer") 256 } 257 parentChainID = parentImg.RootFS.ChainID() 258 } else { 259 rootFS := image.NewRootFS() 260 parentImg = &image.Image{RootFS: rootFS} 261 } 262 263 l, err := daemon.layerStore.Get(img.RootFS.ChainID()) 264 if err != nil { 265 return "", errors.Wrap(err, "error getting image layer") 266 } 267 defer daemon.layerStore.Release(l) 268 269 ts, err := l.TarStreamFrom(parentChainID) 270 if err != nil { 271 return "", errors.Wrapf(err, "error getting tar stream to parent") 272 } 273 defer ts.Close() 274 275 newL, err := daemon.layerStore.Register(ts, parentChainID) 276 if err != nil { 277 return "", errors.Wrap(err, "error registering layer") 278 } 279 defer daemon.layerStore.Release(newL) 280 281 var newImage image.Image 282 newImage = *img 283 newImage.RootFS = nil 284 285 var rootFS image.RootFS 286 rootFS = *parentImg.RootFS 287 rootFS.DiffIDs = append(rootFS.DiffIDs, newL.DiffID()) 288 newImage.RootFS = &rootFS 289 290 for i, hi := range newImage.History { 291 if i >= len(parentImg.History) { 292 hi.EmptyLayer = true 293 } 294 newImage.History[i] = hi 295 } 296 297 now := time.Now() 298 var historyComment string 299 if len(parent) > 0 { 300 historyComment = fmt.Sprintf("merge %s to %s", id, parent) 301 } else { 302 historyComment = fmt.Sprintf("create new from %s", id) 303 } 304 305 newImage.History = append(newImage.History, image.History{ 306 Created: now, 307 Comment: historyComment, 308 }) 309 newImage.Created = now 310 311 b, err := json.Marshal(&newImage) 312 if err != nil { 313 return "", errors.Wrap(err, "error marshalling image config") 314 } 315 316 newImgID, err := daemon.imageStore.Create(b) 317 if err != nil { 318 return "", errors.Wrap(err, "error creating new image after squash") 319 } 320 return string(newImgID), nil 321 } 322 323 func newImage(image *image.Image, size int64) *types.ImageSummary { 324 newImage := new(types.ImageSummary) 325 newImage.ParentID = image.Parent.String() 326 newImage.ID = image.ID().String() 327 newImage.Created = image.Created.Unix() 328 newImage.Size = size 329 newImage.VirtualSize = size 330 newImage.SharedSize = -1 331 newImage.Containers = -1 332 if image.Config != nil { 333 newImage.Labels = image.Config.Labels 334 } 335 return newImage 336 }