c8d: Number of containers using an image

Return the number of containers that use an image if it was asked,
during a `docker system df` call for example.

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
This commit is contained in:
Djordje Lukic 2023-09-18 18:32:53 +02:00
parent 05c60724ed
commit 2394b07bce
No known key found for this signature in database

View file

@ -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
}