1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

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 <roman.volosatovs@docker.com>
This commit is contained in:
Roman Volosatovs 2021-06-22 19:09:58 +02:00
parent bf78e25fe5
commit bf9c76f0a8
No known key found for this signature in database
GPG key ID: 216DD5F8CA6618A1
5 changed files with 50 additions and 31 deletions

View file

@ -22,7 +22,7 @@ type Backend interface {
type imageBackend interface { type imageBackend interface {
ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error) ImageDelete(imageRef string, force, prune bool) ([]types.ImageDeleteResponseItem, error)
ImageHistory(imageName string) ([]*image.HistoryResponseItem, 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) LookupImage(name string) (*types.ImageInspect, error)
TagImage(imageName, repository, tag string) (string, error) TagImage(imageName, repository, tag string) (string, error)
ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error) ImagesPrune(ctx context.Context, pruneFilters filters.Args) (*types.ImagesPruneReport, error)

View file

@ -229,13 +229,17 @@ func (s *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
version := httputils.VersionFromContext(ctx) version := httputils.VersionFromContext(ctx)
if versions.LessThan(version, "1.41") { if versions.LessThan(version, "1.41") {
// NOTE: filter is a shell glob string applied to repository names.
filterParam := r.Form.Get("filter") filterParam := r.Form.Get("filter")
if filterParam != "" { if filterParam != "" {
imageFilters.Add("reference", 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 { if err != nil {
return err return err
} }

View file

@ -235,10 +235,20 @@ type ImageImportOptions struct {
Platform string // Platform is the target platform of the image 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 { 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 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. // 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 // 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 { if err != nil {
return nil, fmt.Errorf("failed to retrieve image list: %v", err) 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" package images // import "github.com/docker/docker/daemon/images"
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"sort" "sort"
@ -10,7 +11,6 @@ import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/container" "github.com/docker/docker/container"
"github.com/docker/docker/image" "github.com/docker/docker/image"
"github.com/docker/docker/layer" "github.com/docker/docker/layer"
@ -38,22 +38,18 @@ func (i *ImageService) Map() map[image.ID]*image.Image {
return i.imageStore.Map() return i.imageStore.Map()
} }
// Images returns a filtered list of images. filterArgs is a JSON-encoded set // Images returns a filtered list of images.
// of filter arguments which will be interpreted by api/types/filters. func (i *ImageService) Images(_ context.Context, opts types.ImageListOptions) ([]*types.ImageSummary, error) {
// filter is a shell glob string applied to repository names. The argument if err := opts.Filters.Validate(acceptedImageFilterTags); err != nil {
// 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 {
return nil, err return nil, err
} }
var danglingOnly bool var danglingOnly bool
if imageFilters.Contains("dangling") { if opts.Filters.Contains("dangling") {
if imageFilters.ExactMatch("dangling", "true") { if opts.Filters.ExactMatch("dangling", "true") {
danglingOnly = true danglingOnly = true
} else if !imageFilters.ExactMatch("dangling", "false") { } else if !opts.Filters.ExactMatch("dangling", "false") {
return nil, invalidFilter{"dangling", imageFilters.Get("dangling")} 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 beforeFilter, sinceFilter *image.Image
err error 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) beforeFilter, err = i.GetImage(value, nil)
return err return err
}) })
@ -69,7 +65,7 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
return nil, err 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) sinceFilter, err = i.GetImage(value, nil)
return err 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) // Very old image that do not have image.Config (or even labels)
if img.Config == nil { if img.Config == nil {
continue continue
} }
// We are now sure image.Config is not nil // 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 continue
} }
} }
@ -142,10 +138,10 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
summary := newImageSummary(img, size) summary := newImageSummary(img, size)
for _, ref := range i.referenceStore.References(id.Digest()) { for _, ref := range i.referenceStore.References(id.Digest()) {
if imageFilters.Contains("reference") { if opts.Filters.Contains("reference") {
var found bool var found bool
var matchErr error var matchErr error
for _, pattern := range imageFilters.Get("reference") { for _, pattern := range opts.Filters.Get("reference") {
found, matchErr = reference.FamiliarMatch(pattern, ref) found, matchErr = reference.FamiliarMatch(pattern, ref)
if matchErr != nil { if matchErr != nil {
return nil, matchErr 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 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 // dangling=false case, so dangling image is not needed
continue 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 continue
} }
summary.RepoDigests = []string{"<none>@<none>"} summary.RepoDigests = []string{"<none>@<none>"}
@ -184,10 +180,9 @@ func (i *ImageService) Images(imageFilters filters.Args, all bool, withExtraAttr
continue continue
} }
if withExtraAttrs { if opts.ContainerCount {
// Lazily init summaryMap and allContainers // Lazily init allContainers.
if summaryMap == nil { if allContainers == nil {
summaryMap = make(map[*image.Image]*types.ImageSummary, len(selectedImages))
allContainers = i.containers.List() 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" // NOTE: By default, Containers is -1, or "not set"
summary.Containers = containers 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 summaryMap[img] = summary
} }
summaries = append(summaries, summary) summaries = append(summaries, summary)
} }
if withExtraAttrs { if opts.SharedSize {
allLayers := i.layerStore.Map() allLayers := i.layerStore.Map()
layerRefs := make(map[layer.ChainID]int, len(allLayers)) layerRefs := make(map[layer.ChainID]int, len(allLayers))