c8d/list: Embed platform specific information
Add the PlatformImages field to `ImageSummary` which describes each platform-specific manifest in that image. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
eecb36e822
commit
ea31ce0fce
6 changed files with 176 additions and 24 deletions
|
@ -407,9 +407,10 @@ func (ir *imageRouter) getImagesJSON(ctx context.Context, w http.ResponseWriter,
|
|||
}
|
||||
|
||||
images, err := ir.backend.Images(ctx, imagetypes.ListOptions{
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Filters: imageFilters,
|
||||
SharedSize: sharedSize,
|
||||
All: httputils.BoolValue(r, "all"),
|
||||
Filters: imageFilters,
|
||||
SharedSize: sharedSize,
|
||||
ContainerCount: true,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -1988,6 +1988,19 @@ definitions:
|
|||
x-nullable: false
|
||||
type: "integer"
|
||||
example: 2
|
||||
PlatformImages:
|
||||
description: |
|
||||
Platform-specific images available for this image.
|
||||
|
||||
Only present with the containerd integration enabled.
|
||||
|
||||
WARNING: This is experimental and may change at any time without any backward
|
||||
compatibility.
|
||||
type: "array"
|
||||
x-nullable: false
|
||||
x-omitempty: true
|
||||
items:
|
||||
$ref: "#/definitions/PlatformImage"
|
||||
|
||||
AuthConfig:
|
||||
type: "object"
|
||||
|
@ -6200,6 +6213,50 @@ definitions:
|
|||
additionalProperties:
|
||||
type: "string"
|
||||
|
||||
PlatformImage:
|
||||
x-nullable: false
|
||||
required: [Id, Descriptor, Available, Platform, ContentSize, UnpackedSize, Containers]
|
||||
description: |
|
||||
PlatformImage represents a platform-specific image that is part of a
|
||||
multi-platform image.
|
||||
type: "object"
|
||||
properties:
|
||||
Id:
|
||||
description: |
|
||||
Content-addressable ID of an image derived from the platform-specific
|
||||
image manifest.
|
||||
type: "string"
|
||||
example: "sha256:95869fbcf224d947ace8d61d0e931d49e31bb7fc67fffbbe9c3198c33aa8e93f"
|
||||
Descriptor:
|
||||
$ref: "#/definitions/OCIDescriptor"
|
||||
Available:
|
||||
description: Indicates whether the image is locally available.
|
||||
type: "boolean"
|
||||
example: true
|
||||
Platform:
|
||||
$ref: "#/definitions/OCIPlatform"
|
||||
ContentSize:
|
||||
description: |
|
||||
The size of the available distributable (possibly compressed) image content
|
||||
in bytes.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
example: 3987495
|
||||
UnpackedSize:
|
||||
description: |
|
||||
The size of the unpacked and uncompressed image content (needed for
|
||||
the image to be useable by containers) in bytes.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
example: 3987495
|
||||
Containers:
|
||||
description: |
|
||||
The number of containers that are using this specific platform image.
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
example: 2
|
||||
|
||||
|
||||
paths:
|
||||
/containers/json:
|
||||
get:
|
||||
|
|
43
api/types/image/platform_image.go
Normal file
43
api/types/image/platform_image.go
Normal file
|
@ -0,0 +1,43 @@
|
|||
package image
|
||||
|
||||
import (
|
||||
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
type PlatformImage struct {
|
||||
// ID is the content-addressable ID of an image and is the same as the
|
||||
// digest of the platform-specific image manifest.
|
||||
//
|
||||
// Required: true
|
||||
ID string `json:"Id"`
|
||||
|
||||
// Descriptor is the OCI descriptor of the image.
|
||||
//
|
||||
// Required: true
|
||||
Descriptor ocispec.Descriptor `json:"Descriptor"`
|
||||
|
||||
// Available indicates whether the image is locally available.
|
||||
//
|
||||
// Required: true
|
||||
Available bool `json:"Available"`
|
||||
|
||||
// Platform is the platform of the image
|
||||
//
|
||||
// Required: true
|
||||
Platform ocispec.Platform `json:"Platform"`
|
||||
|
||||
// ContentSize is the size of all the locally available distributable content size.
|
||||
//
|
||||
// Required: true
|
||||
ContentSize int64 `json:"ContentSize"`
|
||||
|
||||
// UnpackedSize is the size of the image when unpacked.
|
||||
//
|
||||
// Required: true
|
||||
UnpackedSize int64 `json:"UnpackedSize"`
|
||||
|
||||
// Containers is the number of containers created from this image.
|
||||
//
|
||||
// Required: true
|
||||
Containers int64 `json:"Containers"`
|
||||
}
|
|
@ -47,6 +47,15 @@ type Summary struct {
|
|||
// Required: true
|
||||
ParentID string `json:"ParentId"`
|
||||
|
||||
// Platform-specific images available for this image.
|
||||
//
|
||||
// Only present with the containerd integration enabled.
|
||||
//
|
||||
// WARNING: This is experimental and may change at any time without any backward
|
||||
// compatibility.
|
||||
//
|
||||
PlatformImages []PlatformImage `json:"PlatformImages,omitempty"`
|
||||
|
||||
// List of content-addressable digests of locally available image manifests
|
||||
// that the image is referenced from. Multiple manifests can refer to the
|
||||
// same image.
|
||||
|
|
|
@ -210,6 +210,7 @@ func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions)
|
|||
func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platformMatcher platforms.MatchComparer,
|
||||
opts imagetypes.ListOptions, tagsByDigest map[digest.Digest][]string,
|
||||
) (_ *imagetypes.Summary, allChainIDs []digest.Digest, _ error) {
|
||||
var platformImages []imagetypes.PlatformImage
|
||||
|
||||
// Total size of the image including all its platform
|
||||
var totalSize int64
|
||||
|
@ -224,9 +225,20 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
var best *ImageManifest
|
||||
var bestPlatform ocispec.Platform
|
||||
|
||||
err := i.walkImageManifests(ctx, img, func(img *ImageManifest) error {
|
||||
err := i.walkReachableImageManifests(ctx, img, func(img *ImageManifest) error {
|
||||
target := img.Target()
|
||||
|
||||
if isPseudo, err := img.IsPseudoImage(ctx); isPseudo || err != nil {
|
||||
return nil
|
||||
log.G(ctx).WithFields(log.Fields{
|
||||
"error": err,
|
||||
"image": img.Name(),
|
||||
"digest": target.Digest,
|
||||
"isPseudo": isPseudo,
|
||||
}).Debug("skipping pseudo image")
|
||||
|
||||
if !errdefs.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
available, err := img.CheckContentAvailable(ctx)
|
||||
|
@ -239,7 +251,19 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
return nil
|
||||
}
|
||||
|
||||
platformSummary := imagetypes.PlatformImage{
|
||||
ID: target.Digest.String(),
|
||||
Available: available,
|
||||
Descriptor: target,
|
||||
Containers: -1,
|
||||
}
|
||||
|
||||
if target.Platform != nil {
|
||||
platformSummary.Platform = *target.Platform
|
||||
}
|
||||
|
||||
if !available {
|
||||
platformImages = append(platformImages, platformSummary)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -253,7 +277,9 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
return err
|
||||
}
|
||||
|
||||
target := img.Target()
|
||||
if target.Platform == nil {
|
||||
platformSummary.Platform = dockerImage.Platform
|
||||
}
|
||||
|
||||
diffIDs, err := img.RootFS(ctx)
|
||||
if err != nil {
|
||||
|
@ -262,40 +288,45 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
|
||||
chainIDs := identity.ChainIDs(diffIDs)
|
||||
|
||||
ts, _, err := i.singlePlatformSize(ctx, img)
|
||||
unpackedSize, contentSize, err := i.singlePlatformSize(ctx, img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
totalSize += ts
|
||||
totalSize += unpackedSize + contentSize
|
||||
allChainsIDs = append(allChainsIDs, chainIDs...)
|
||||
|
||||
platformSummary.ContentSize = contentSize
|
||||
platformSummary.UnpackedSize = unpackedSize
|
||||
|
||||
if opts.ContainerCount {
|
||||
i.containers.ApplyAll(func(c *container.Container) {
|
||||
if c.ImageManifest != nil && c.ImageManifest.Digest == target.Digest {
|
||||
containersCount++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
var platform ocispec.Platform
|
||||
if target.Platform != nil {
|
||||
platform = *target.Platform
|
||||
} else {
|
||||
platform = dockerImage.Platform
|
||||
platformSummary.Containers = containersCount
|
||||
}
|
||||
|
||||
// Filter out platforms that don't match the requested platform. Do it
|
||||
// after the size, container count and chainIDs are summed up to have
|
||||
// the single combined entry still represent the whole multi-platform
|
||||
// image.
|
||||
if !platformMatcher.Match(platform) {
|
||||
if !platformMatcher.Match(platformSummary.Platform) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if best == nil || platformMatcher.Less(platform, bestPlatform) {
|
||||
// If the platform is available, prepend it to the list of platforms
|
||||
// otherwise append it at the end.
|
||||
if platformSummary.Available {
|
||||
platformImages = append([]imagetypes.PlatformImage{platformSummary}, platformImages...)
|
||||
} else {
|
||||
platformImages = append(platformImages, platformSummary)
|
||||
}
|
||||
|
||||
if best == nil || platformMatcher.Less(platformSummary.Platform, bestPlatform) {
|
||||
best = img
|
||||
bestPlatform = platform
|
||||
bestPlatform = platformSummary.Platform
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -317,6 +348,7 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
return nil, nil, err
|
||||
}
|
||||
image.Size = totalSize
|
||||
image.PlatformImages = platformImages
|
||||
|
||||
if opts.ContainerCount {
|
||||
image.Containers = containersCount
|
||||
|
@ -324,7 +356,7 @@ func (i *ImageService) imageSummary(ctx context.Context, img images.Image, platf
|
|||
return image, allChainsIDs, nil
|
||||
}
|
||||
|
||||
func (i *ImageService) singlePlatformSize(ctx context.Context, imgMfst *ImageManifest) (totalSize int64, contentSize int64, _ error) {
|
||||
func (i *ImageService) singlePlatformSize(ctx context.Context, imgMfst *ImageManifest) (unpackedSize int64, contentSize int64, _ error) {
|
||||
// TODO(thaJeztah): do we need to take multiple snapshotters into account? See https://github.com/moby/moby/issues/45273
|
||||
snapshotter := i.snapshotterService(i.snapshotter)
|
||||
|
||||
|
@ -350,10 +382,7 @@ func (i *ImageService) singlePlatformSize(ctx context.Context, imgMfst *ImageMan
|
|||
return -1, -1, err
|
||||
}
|
||||
|
||||
// totalSize is the size of the image's packed layers and snapshots
|
||||
// (unpacked layers) combined.
|
||||
totalSize = contentSize + unpackedUsage.Size
|
||||
return totalSize, contentSize, nil
|
||||
return unpackedUsage.Size, contentSize, nil
|
||||
}
|
||||
|
||||
func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore content.Store, repoTags []string, imageManifest *ImageManifest) (*imagetypes.Summary, error) {
|
||||
|
@ -395,11 +424,15 @@ func (i *ImageService) singlePlatformImage(ctx context.Context, contentStore con
|
|||
return nil, err
|
||||
}
|
||||
|
||||
totalSize, _, err := i.singlePlatformSize(ctx, imageManifest)
|
||||
unpackedSize, contentSize, err := i.singlePlatformSize(ctx, imageManifest)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to calculate size of image %s", imageManifest.Name())
|
||||
}
|
||||
|
||||
// totalSize is the size of the image's packed layers and snapshots
|
||||
// (unpacked layers) combined.
|
||||
totalSize := contentSize + unpackedSize
|
||||
|
||||
summary := &imagetypes.Summary{
|
||||
ParentID: rawImg.Labels[imageLabelClassicBuilderParent],
|
||||
ID: target.String(),
|
||||
|
|
|
@ -13,6 +13,15 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
will be rejected.
|
||||
-->
|
||||
|
||||
## v1.46 API changes
|
||||
|
||||
[Docker Engine API v1.46](https://docs.docker.com/engine/api/v1.46/) documentation
|
||||
|
||||
* `GET /images/json` response now includes `PlatformImages` field, which contains
|
||||
information about the platform-specific manifests available for the image.
|
||||
WARNING: This is experimental and may change at any time without any backward
|
||||
compatibility.
|
||||
|
||||
## v1.45 API changes
|
||||
|
||||
[Docker Engine API v1.45](https://docs.docker.com/engine/api/v1.45/) documentation
|
||||
|
|
Loading…
Add table
Reference in a new issue