c8d/inspect: Add Manifests field

Add `Manifests` field to image inspect (`/images/{name}/json`) response.
This is the same as in `/images/json`.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-02-03 15:12:10 +01:00
parent 639a1214fa
commit 3d37537f75
9 changed files with 90 additions and 10 deletions

View File

@@ -332,7 +332,18 @@ func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter,
}
func (ir *imageRouter) getImagesByName(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
imageInspect, err := ir.backend.ImageInspect(ctx, vars["name"], backend.ImageInspectOpts{})
if err := httputils.ParseForm(r); err != nil {
return err
}
var manifests bool
if r.Form.Get("manifests") != "" && versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.48") {
manifests = httputils.BoolValue(r, "manifests")
}
imageInspect, err := ir.backend.ImageInspect(ctx, vars["name"], backend.ImageInspectOpts{
Manifests: manifests,
})
if err != nil {
return err
}

View File

@@ -2016,6 +2016,21 @@ definitions:
compatibility.
x-nullable: true
$ref: "#/definitions/OCIDescriptor"
Manifests:
description: |
Manifests is a list of image manifests available in this image. It
provides a more detailed view of the platform-specific image manifests or
other image-attached data like build attestations.
Only available if the daemon provides a multi-platform image store
and the `manifests` option is set in the inspect request.
WARNING: This is experimental and may change at any time without any backward
compatibility.
type: "array"
x-nullable: true
items:
$ref: "#/definitions/ImageManifestSummary"
RepoTags:
description: |
List of image names/tags in the local image cache that reference this
@@ -9648,6 +9663,12 @@ paths:
description: "Image name or id"
type: "string"
required: true
- name: "manifests"
in: "query"
description: "Include Manifests in the image summary."
type: "boolean"
default: false
required: false
tags: ["Image"]
/images/{name}/history:
get:

View File

@@ -151,7 +151,9 @@ type GetImageOpts struct {
}
// ImageInspectOpts holds parameters to inspect an image.
type ImageInspectOpts struct{}
type ImageInspectOpts struct {
Manifests bool
}
// CommitConfig is the configuration for creating an image as part of a build.
type CommitConfig struct {

View File

@@ -127,4 +127,14 @@ type InspectResponse struct {
// WARNING: This is experimental and may change at any time without any backward
// compatibility.
Descriptor *ocispec.Descriptor `json:"Descriptor,omitempty"`
// Manifests is a list of image manifests available in this image. It
// provides a more detailed view of the platform-specific image manifests or
// other image-attached data like build attestations.
//
// Only available if the daemon provides a multi-platform image store.
//
// WARNING: This is experimental and may change at any time without any backward
// compatibility.
Manifests []ManifestSummary `json:"Manifests,omitempty"`
}

View File

@@ -103,6 +103,11 @@ type LoadOptions struct {
Platforms []ocispec.Platform
}
type InspectOptions struct {
// Manifests returns the image manifests.
Manifests bool
}
// SaveOptions holds parameters to save images.
type SaveOptions struct {
// Platforms selects the platforms to save if the image is a

View File

@@ -30,6 +30,17 @@ func ImageInspectWithRawResponse(raw *bytes.Buffer) ImageInspectOption {
})
}
// ImageInspectWithManifests sets manifests API option for the image inspect operation.
// This option is only available for API version 1.48 and up.
// With this option set, the image inspect operation response will have the
// [image.InspectResponse.Manifests] field populated if the server is multi-platform capable.
func ImageInspectWithManifests(manifests bool) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
clientOpts.apiOptions.Manifests = manifests
return nil
})
}
// ImageInspectWithAPIOpts sets the API options for the image inspect operation.
func ImageInspectWithAPIOpts(opts image.InspectOptions) ImageInspectOption {
return imageInspectOptionFunc(func(clientOpts *imageInspectOpts) error {
@@ -57,6 +68,13 @@ func (cli *Client) ImageInspect(ctx context.Context, imageID string, inspectOpts
}
query := url.Values{}
if opts.apiOptions.Manifests {
if err := cli.NewVersionError(ctx, "1.48", "manifests"); err != nil {
return image.InspectResponse{}, err
}
query.Set("manifests", "1")
}
serverResp, err := cli.get(ctx, "/images/"+imageID+"/json", query, nil)
defer ensureReaderClosed(serverResp)
if err != nil {

View File

@@ -22,7 +22,7 @@ import (
"golang.org/x/sync/semaphore"
)
func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, _ backend.ImageInspectOpts) (*imagetypes.InspectResponse, error) {
func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, opts backend.ImageInspectOpts) (*imagetypes.InspectResponse, error) {
c8dImg, err := i.resolveImage(ctx, refOrID)
if err != nil {
return nil, err
@@ -90,13 +90,18 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, _ backe
log.G(ctx).WithError(err).Warn("failed to determine Parent property")
}
repoTags, repoDigests := i.collectRepoTagsAndDigests(ctx, tagged)
var manifests []imagetypes.ManifestSummary
if opts.Manifests {
manifests = multi.Manifests
}
repoTags, repoDigests := collectRepoTagsAndDigests(ctx, tagged)
return &imagetypes.InspectResponse{
ID: target.Digest.String(),
RepoTags: repoTags,
Descriptor: &target,
RepoDigests: sliceutil.Dedup(repoDigests),
RepoDigests: repoDigests,
Parent: parent,
Comment: comment,
Created: created,
@@ -108,6 +113,7 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, _ backe
Os: img.OS,
OsVersion: img.OSVersion,
Size: size,
Manifests: manifests,
GraphDriver: storage.DriverData{
Name: i.snapshotter,
Data: nil,
@@ -122,10 +128,7 @@ func (i *ImageService) ImageInspect(ctx context.Context, refOrID string, _ backe
}, nil
}
// collectRepoTagsAndDigests returns repoTags and repoDigests for images with the same target.
func (i *ImageService) collectRepoTagsAndDigests(ctx context.Context, tagged []c8dimages.Image) (
repoTags []string, repoDigests []string,
) {
func collectRepoTagsAndDigests(ctx context.Context, tagged []c8dimages.Image) (repoTags []string, repoDigests []string) {
repoTags = make([]string, 0, len(tagged))
repoDigests = make([]string, 0, len(tagged))
for _, img := range tagged {
@@ -164,7 +167,7 @@ func (i *ImageService) collectRepoTagsAndDigests(ctx context.Context, tagged []c
}
repoDigests = append(repoDigests, reference.FamiliarString(digested))
}
return repoTags, repoDigests
return sliceutil.Dedup(repoTags), sliceutil.Dedup(repoDigests)
}
// size returns the total size of the image's packed resources.

View File

@@ -169,6 +169,9 @@ func (i *ImageService) Images(ctx context.Context, opts imagetypes.ListOptions)
return nil
}
if !opts.Manifests {
image.Manifests = nil
}
resultsMut.Lock()
summaries = append(summaries, image)

View File

@@ -51,6 +51,13 @@ keywords: "API, Docker, rcli, REST, documentation"
image store.
WARNING: This is experimental and may change at any time without any backward
compatibility.
* `GET /images/{name}/json` response now will return the `Manifests` field
containing information about the sub-manifests contained in the image index.
This includes things like platform-specific manifests and build attestations.
The new field will only be populated if the request also sets the `manifests`
query parameter to `true`.
This acts the same as in the `GET /images/json` endpoint.
WARNING: This is experimental and may change at any time without any backward compatibility.
* `GET /containers/{name}/json` now returns an `ImageManifestDescriptor` field
containing the OCI descriptor of the platform-specific image manifest of the
image that was used to create the container.