From bf9c76f0a8ff296c71d32bb46c1ea03f8ec1dd14 Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 22 Jun 2021 19:09:58 +0200 Subject: [PATCH] API, daemon/images: add ImageListOptions and pass context This makes it easier to add more options to the backend without having to change the signature. While we're changing the signature, also adding a context.Context, which is not currently used, but probably should be at some point. Signed-off-by: Roman Volosatovs --- api/server/router/image/backend.go | 2 +- api/server/router/image/image_routes.go | 6 ++- api/types/client.go | 14 ++++++- daemon/disk_usage.go | 6 ++- daemon/images/images.go | 53 +++++++++++++------------ 5 files changed, 50 insertions(+), 31 deletions(-) diff --git a/api/server/router/image/backend.go b/api/server/router/image/backend.go index 5837f9a9bc..624b6c7363 100644 --- a/api/server/router/image/backend.go +++ b/api/server/router/image/backend.go @@ -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) diff --git a/api/server/router/image/image_routes.go b/api/server/router/image/image_routes.go index 728ba6ef09..a2f2a682f8 100644 --- a/api/server/router/image/image_routes.go +++ b/api/server/router/image/image_routes.go @@ -229,13 +229,17 @@ 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) + images, err := s.backend.Images(ctx, types.ImageListOptions{ + Filters: imageFilters, + All: httputils.BoolValue(r, "all"), + }) if err != nil { return err } diff --git a/api/types/client.go b/api/types/client.go index 9dd343ad64..e3c06cef69 100644 --- a/api/types/client.go +++ b/api/types/client.go @@ -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. diff --git a/daemon/disk_usage.go b/daemon/disk_usage.go index 5bec60d174..9e59510aa1 100644 --- a/daemon/disk_usage.go +++ b/daemon/disk_usage.go @@ -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) } diff --git a/daemon/images/images.go b/daemon/images/images.go index dfbeb6f4a7..e756bc334d 100644 --- a/daemon/images/images.go +++ b/daemon/images/images.go @@ -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{"@"} @@ -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))