Merge pull request #42531 from rvolosatovs/image_shared_size

Add support for `shared-size` parameter for images queries
This commit is contained in:
Justin Cormack 2021-07-19 14:39:11 +01:00 committed by GitHub
commit c485e901d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 65 additions and 31 deletions

View file

@ -22,7 +22,7 @@ type Backend interface {
type imageBackend interface {
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error)
ImageHistory(imageName string) ([]*image.HistoryResponseItem, error)
Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error)
Images(ctx context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error)
LookupImage(name string) (*types.ImageInspect, error)
TagImage(imageName, repository, tag string) (string, error)
ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)

View file

@ -229,13 +229,24 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
version := httputils.VersionFromContext(ctx)
if versions.LessThan(version, "1.41") {
// NOTE: filter is a shell glob string applied to repository names.
filterParam := r.Form.Get("filter")
if filterParam != "" {
imageFilters.Add("reference", filterParam)
}
}
images, err := s.backend.Images(imageFilters, httputils.BoolValue(r, "all"), false)
var sharedSize bool
if versions.GreaterThanOrEqualTo(version, "1.42") {
// NOTE: Support for the "shared-size" parameter was added in API 1.42.
sharedSize = httputils.BoolValue(r, "shared-size")
}
images, err := s.backend.Images(ctx, types.ImageListOptions{
All: httputils.BoolValue(r, "all"),
Filters: imageFilters,
SharedSize: sharedSize,
})
if err != nil {
return err
}

View file

@ -7202,6 +7202,11 @@ paths:
- `reference`=(`<image-name>[:<tag>]`)
- `since`=(`<image-name>[:<tag>]`, `<image id>` or `<image@digest>`)
type: "string"
- name: "shared-size"
in: "query"
description: "Compute and show shared size as a `SharedSize` field on each image."
type: "boolean"
default: false
- name: "digests"
in: "query"
description: "Show digest information as a `RepoDigests` field on each image."

View file

@ -235,10 +235,20 @@ type ImageImportOptions struct {
Platform string // Platform is the target platform of the image
}
// ImageListOptions holds parameters to filter the list of images with.
// ImageListOptions holds parameters to list images with.
type ImageListOptions struct {
All bool
// All controls whether all images in the graph are filtered, or just
// the heads.
All bool
// Filters is a JSON-encoded set of filter arguments.
Filters filters.Args
// SharedSize indicates whether the shared size of images should be computed.
SharedSize bool
// ContainerCount indicates whether container count should be computed.
ContainerCount bool
}
// ImageLoadResponse returns information to the client about a load process.

View file

@ -26,7 +26,11 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
}
// Get all top images with extra attributes
allImages, err := daemon.imageService.Images(filters.NewArgs(), false, true)
allImages, err := daemon.imageService.Images(ctx, types.ImageListOptions{
Filters: filters.NewArgs(),
SharedSize: true,
ContainerCount: true,
})
if err != nil {
return nil, fmt.Errorf("failed to retrieve image list: %v", err)
}

View file

@ -1,6 +1,7 @@
package images // import "github.com/docker/docker/daemon/images"
import (
"context"
"encoding/json"
"fmt"
"sort"
@ -10,7 +11,6 @@ import (
"github.com/docker/distribution/reference"
"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"
@ -38,22 +38,18 @@ func (i *ImageService) Map() map[image.ID]*image.Image {
return i.imageStore.Map()
}
// Images returns a filtered list of images. filterArgs is a JSON-encoded set
// of filter arguments which will be interpreted by api/types/filters.
// 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 (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
if err := imageFilters.Validate(acceptedImageFilterTags); err != nil {
// Images returns a filtered list of images.
func (i *ImageService) Images(_ context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) {
if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
return nil, err
}
var danglingOnly bool
if imageFilters.Contains("dangling") {
if imageFilters.ExactMatch("dangling", "true") {
if opts.Filters.Contains("dangling") {
if opts.Filters.ExactMatch("dangling", "true") {
danglingOnly = true
} else if !imageFilters.ExactMatch("dangling", "false") {
return nil, invalidFilter{"dangling", imageFilters.Get("dangling")}
} else if !opts.Filters.ExactMatch("dangling", "false") {
return nil, invalidFilter{"dangling", opts.Filters.Get("dangling")}
}
}
@ -61,7 +57,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
beforeFilter, sinceFilter *image.Image
err error
)
err = imageFilters.WalkValues("before", func(value string) error {
err = opts.Filters.WalkValues("before", func(value string) error {
beforeFilter, err = i.GetImage(value, nil)
return err
})
@ -69,7 +65,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
return nil, err
}
err = imageFilters.WalkValues("since", func(value string) error {
err = opts.Filters.WalkValues("since", func(value string) error {
sinceFilter, err = i.GetImage(value, nil)
return err
})
@ -102,13 +98,13 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
}
}
if imageFilters.Contains("label") {
if opts.Filters.Contains("label") {
// Very old image that do not have image.Config (or even labels)
if img.Config == nil {
continue
}
// We are now sure image.Config is not nil
if !imageFilters.MatchKVList("label", img.Config.Labels) {
if !opts.Filters.MatchKVList("label", img.Config.Labels) {
continue
}
}
@ -142,10 +138,10 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
summary := newImageSummary(img, size)
for _, ref := range i.referenceStore.References(id.Digest()) {
if imageFilters.Contains("reference") {
if opts.Filters.Contains("reference") {
var found bool
var matchErr error
for _, pattern := range imageFilters.Get("reference") {
for _, pattern := range opts.Filters.Get("reference") {
found, matchErr = reference.FamiliarMatch(pattern, ref)
if matchErr != nil {
return nil, matchErr
@ -166,13 +162,13 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
}
}
if summary.RepoDigests == nil && summary.RepoTags == nil {
if all || len(i.imageStore.Children(id)) == 0 {
if opts.All || len(i.imageStore.Children(id)) == 0 {
if imageFilters.Contains("dangling") && !danglingOnly {
if opts.Filters.Contains("dangling") && !danglingOnly {
// dangling=false case, so dangling image is not needed
continue
}
if imageFilters.Contains("reference") { // skip images with no references if filtering by reference
if opts.Filters.Contains("reference") { // skip images with no references if filtering by reference
continue
}
summary.RepoDigests = []string{"<none>@<none>"}
@ -184,10 +180,9 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
continue
}
if withExtraAttrs {
// Lazily init summaryMap and allContainers
if summaryMap == nil {
summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages))
if opts.ContainerCount {
// Lazily init allContainers.
if allContainers == nil {
allContainers = i.containers.List()
}
@ -200,13 +195,19 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
}
// NOTE: By default, Containers is -1, or "not set"
summary.Containers = containers
}
if opts.ContainerCount || opts.SharedSize {
// Lazily init summaryMap.
if summaryMap == nil {
summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages))
}
summaryMap[img] = summary
}
summaries = append(summaries, summary)
}
if withExtraAttrs {
if opts.SharedSize {
allLayers := i.layerStore.Map()
layerRefs := make(map[layer.ChainID]int, len(allLayers))

View file

@ -21,6 +21,9 @@ keywords: "API, Docker, rcli, REST, documentation"
was introduced in API 1.31 as part of an experimental feature, and no longer
used since API 1.40.
Use field `BuildCache` instead to track storage used by the builder component.
* `GET /images/json` now accepts query parameter `shared-size`. When set `true`,
images returned will include `SharedSize`, which provides the size on disk shared
with other images present on the system.
## v1.41 API changes