Merge pull request #42531 from rvolosatovs/image_shared_size
Add support for `shared-size` parameter for images queries
This commit is contained in:
commit
c485e901d9
7 changed files with 65 additions and 31 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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."
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue