Explorar el Código

Update Images() to allow retrieving specific image size data

Those data include:
 - size of data shared with other images
 - size of data unique to a given image
 - how many containers are using a given image

Signed-off-by: Kenfe-Mickael Laventure <mickael.laventure@gmail.com>
Kenfe-Mickael Laventure hace 8 años
padre
commit
b717de5153

+ 1 - 1
api/server/router/image/backend.go

@@ -25,7 +25,7 @@ type containerBackend interface {
 type imageBackend interface {
 	ImageDelete(imageRef string, force, prune bool) ([]types.ImageDelete, error)
 	ImageHistory(imageName string) ([]*types.ImageHistory, error)
-	Images(filterArgs string, filter string, all bool) ([]*types.Image, error)
+	Images(filterArgs string, filter string, all bool, withExtraAttrs bool) ([]*types.Image, error)
 	LookupImage(name string) (*types.ImageInspect, error)
 	TagImage(imageName, repository, tag string) error
 }

+ 1 - 1
api/server/router/image/image_routes.go

@@ -248,7 +248,7 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
 	}
 
 	// FIXME: The filter parameter could just be a match filter
-	images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"))
+	images, err := s.backend.Images(r.Form.Get("filters"), r.Form.Get("filter"), httputils.BoolValue(r, "all"), false)
 	if err != nil {
 		return err
 	}

+ 2 - 0
api/types/types.go

@@ -95,8 +95,10 @@ type Image struct {
 	RepoDigests []string
 	Created     int64
 	Size        int64
+	SharedSize  int64
 	VirtualSize int64
 	Labels      map[string]string
+	Containers  int64
 }
 
 // GraphDriverData returns Image's graph driver config info

+ 2 - 1
cli/command/formatter/image.go

@@ -225,5 +225,6 @@ func (c *imageContext) CreatedAt() string {
 
 func (c *imageContext) Size() string {
 	c.AddHeader(sizeHeader)
-	return units.HumanSizeWithPrecision(float64(c.i.Size), 3)
+	//NOTE: For backward compatibility we need to return VirtualSize
+	return units.HumanSizeWithPrecision(float64(c.i.VirtualSize), 3)
 }

+ 68 - 4
daemon/images.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
+	"github.com/docker/docker/container"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/layer"
 	"github.com/docker/docker/reference"
@@ -37,7 +38,7 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image {
 // filter is a shell glob string applied to repository names. The argument
 // named all controls whether all images in the graph are filtered, or just
 // the heads.
-func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Image, error) {
+func (daemon *Daemon) Images(filterArgs, filter string, all bool, withExtraAttrs bool) ([]*types.Image, error) {
 	var (
 		allImages    map[image.ID]*image.Image
 		err          error
@@ -83,6 +84,10 @@ func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Imag
 	}
 
 	images := []*types.Image{}
+	var imagesMap map[*image.Image]*types.Image
+	var layerRefs map[layer.ChainID]int
+	var allLayers map[layer.ChainID]layer.Layer
+	var allContainers []*container.Container
 
 	var filterTagged bool
 	if filter != "" {
@@ -171,21 +176,80 @@ func (daemon *Daemon) Images(filterArgs, filter string, all bool) ([]*types.Imag
 			continue
 		}
 
+		if withExtraAttrs {
+			// lazyly init variables
+			if len(allContainers) == 0 {
+				allContainers = daemon.List()
+				allLayers = daemon.layerStore.Map()
+				imagesMap = make(map[*image.Image]*types.Image)
+				layerRefs = make(map[layer.ChainID]int)
+			}
+
+			// Get container count
+			newImage.Containers = 0
+			for _, c := range allContainers {
+				if c.ImageID == id {
+					newImage.Containers++
+				}
+			}
+
+			// count layer references
+			rootFS := *img.RootFS
+			rootFS.DiffIDs = nil
+			for _, id := range img.RootFS.DiffIDs {
+				rootFS.Append(id)
+				chid := rootFS.ChainID()
+				layerRefs[chid]++
+				if _, ok := allLayers[chid]; !ok {
+					return nil, fmt.Errorf("layer %v was not found (corruption?)", chid)
+				}
+			}
+			imagesMap[img] = newImage
+		}
+
 		images = append(images, newImage)
 	}
 
+	if withExtraAttrs {
+		// Get Shared and Unique sizes
+		for img, newImage := range imagesMap {
+			rootFS := *img.RootFS
+			rootFS.DiffIDs = nil
+
+			newImage.Size = 0
+			newImage.SharedSize = 0
+			for _, id := range img.RootFS.DiffIDs {
+				rootFS.Append(id)
+				chid := rootFS.ChainID()
+
+				diffSize, err := allLayers[chid].DiffSize()
+				if err != nil {
+					return nil, err
+				}
+
+				if layerRefs[chid] > 1 {
+					newImage.SharedSize += diffSize
+				} else {
+					newImage.Size += diffSize
+				}
+			}
+		}
+	}
+
 	sort.Sort(sort.Reverse(byCreated(images)))
 
 	return images, nil
 }
 
-func newImage(image *image.Image, size int64) *types.Image {
+func newImage(image *image.Image, virtualSize int64) *types.Image {
 	newImage := new(types.Image)
 	newImage.ParentID = image.Parent.String()
 	newImage.ID = image.ID().String()
 	newImage.Created = image.Created.Unix()
-	newImage.Size = size
-	newImage.VirtualSize = size
+	newImage.Size = -1
+	newImage.VirtualSize = virtualSize
+	newImage.SharedSize = -1
+	newImage.Containers = -1
 	if image.Config != nil {
 		newImage.Labels = image.Config.Labels
 	}