diff --git a/daemon/containerd/image_list.go b/daemon/containerd/image_list.go index 3543d76303..e4ddbbac35 100644 --- a/daemon/containerd/image_list.go +++ b/daemon/containerd/image_list.go @@ -16,9 +16,11 @@ import ( "github.com/distribution/reference" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/image" + imagetypes "github.com/docker/docker/api/types/image" timetypes "github.com/docker/docker/api/types/time" + "github.com/docker/docker/container" "github.com/docker/docker/errdefs" + "github.com/docker/docker/image" "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/identity" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -86,9 +88,10 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) } var ( - summaries = make([]*types.ImageSummary, 0, len(imgs)) - root []*[]digest.Digest - layers map[digest.Digest]int + allContainers []*container.Container + summaries = make([]*types.ImageSummary, 0, len(imgs)) + root []*[]digest.Digest + layers map[digest.Digest]int ) if opts.SharedSize { root = make([]*[]digest.Digest, 0, len(imgs)) @@ -144,6 +147,10 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) tagsByDigest[dgst] = append(tagsByDigest[dgst], reference.FamiliarString(ref)) } + if opts.ContainerCount { + allContainers = i.containers.List() + } + for _, img := range uniqueImages { err := i.walkImageManifests(ctx, img, func(img *ImageManifest) error { if isPseudo, err := img.IsPseudoImage(ctx); isPseudo || err != nil { @@ -164,7 +171,7 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) return nil } - image, chainIDs, err := i.singlePlatformImage(ctx, contentStore, tagsByDigest[img.RealTarget.Digest], img) + image, chainIDs, err := i.singlePlatformImage(ctx, contentStore, tagsByDigest[img.RealTarget.Digest], img, opts, allContainers) if err != nil { return err } @@ -201,10 +208,10 @@ func (i *ImageService) Images(ctx context.Context, opts types.ImageListOptions) return summaries, nil } -func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore content.Store, repoTags []string, image *ImageManifest) (*types.ImageSummary, []digest.Digest, error) { - diffIDs, err := image.RootFS(ctx) +func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore content.Store, repoTags []string, imageManifest *ImageManifest, opts types.ImageListOptions, allContainers []*container.Container) (*types.ImageSummary, []digest.Digest, error) { + diffIDs, err := imageManifest.RootFS(ctx) if err != nil { - return nil, nil, errors.Wrapf(err, "failed to get rootfs of image %s", image.Name()) + return nil, nil, errors.Wrapf(err, "failed to get rootfs of image %s", imageManifest.Name()) } // TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273 @@ -215,14 +222,14 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con if err != nil { if !cerrdefs.IsNotFound(err) { log.G(ctx).WithError(err).WithFields(log.Fields{ - "image": image.Name(), + "image": imageManifest.Name(), "snapshotID": imageSnapshotID, }).Warn("failed to calculate unpacked size of image") } unpackedUsage = snapshots.Usage{Size: 0} } - contentSize, err := image.Size(ctx) + contentSize, err := imageManifest.Size(ctx) if err != nil { return nil, nil, err } @@ -232,7 +239,7 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con totalSize := contentSize + unpackedUsage.Size var repoDigests []string - rawImg := image.Metadata() + rawImg := imageManifest.Metadata() target := rawImg.Target.Digest logger := log.G(ctx).WithFields(log.Fields{ @@ -260,7 +267,7 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con } } - cfgDesc, err := image.Image.Config(ctx) + cfgDesc, err := imageManifest.Image.Config(ctx) if err != nil { return nil, nil, err } @@ -285,6 +292,17 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con Containers: -1, } + if opts.ContainerCount { + // Get container count + var containers int64 + for _, c := range allContainers { + if c.ImageID == image.ID(target.String()) { + containers++ + } + } + summary.Containers = containers + } + return summary, identity.ChainIDs(diffIDs), nil } @@ -297,13 +315,13 @@ type imageFilterFunc func(image images.Image) bool func (i *ImageService) setupFilters(ctx context.Context, imageFilters filters.Args) (filterFunc imageFilterFunc, outErr error) { var fltrs []imageFilterFunc err := imageFilters.WalkValues("before", func(value string) error { - img, err := i.GetImage(ctx, value, image.GetImageOpts{}) + img, err := i.GetImage(ctx, value, imagetypes.GetImageOpts{}) if err != nil { return err } if img != nil && img.Created != nil { fltrs = append(fltrs, func(candidate images.Image) bool { - cand, err := i.GetImage(ctx, candidate.Name, image.GetImageOpts{}) + cand, err := i.GetImage(ctx, candidate.Name, imagetypes.GetImageOpts{}) if err != nil { return false } @@ -317,13 +335,13 @@ func (i *ImageService) setupFilters(ctx context.Context, imageFilters filters.Ar } err = imageFilters.WalkValues("since", func(value string) error { - img, err := i.GetImage(ctx, value, image.GetImageOpts{}) + img, err := i.GetImage(ctx, value, imagetypes.GetImageOpts{}) if err != nil { return err } if img != nil && img.Created != nil { fltrs = append(fltrs, func(candidate images.Image) bool { - cand, err := i.GetImage(ctx, candidate.Name, image.GetImageOpts{}) + cand, err := i.GetImage(ctx, candidate.Name, imagetypes.GetImageOpts{}) if err != nil { return false }