Skip to content

c8d: Multi-platform image list#47526

Merged
vvoland merged 4 commits intomoby:masterfrom
vvoland:c8d-list-multiplatform
Aug 8, 2024
Merged

c8d: Multi-platform image list#47526
vvoland merged 4 commits intomoby:masterfrom
vvoland:c8d-list-multiplatform

Conversation

@vvoland
Copy link
Contributor

@vvoland vvoland commented Mar 7, 2024

Add the Manifests field to ImageSummary.

CLI initial implementation: docker/cli#4982

- How to verify it

$ docker pull busybox
$ docker images --tree
image

- Description for the changelog

> `GET /images/json` response now includes `Manifests` field, which contains information about the sub-manifests included 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`.
> [!WARNING]  
>
> This is experimental and may change at any time without any backward compatibility.

- A picture of a cute animal (not mandatory but encouraged)
a7c73b012f

@vvoland vvoland added area/api API kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. kind/feature Functionality or other elements that the project doesn't currently have. Features are new and shiny impact/changelog area/images Image Distribution containerd-integration Issues and PRs related to containerd integration labels Mar 7, 2024
@vvoland vvoland added this to the 27.0.0 milestone Mar 7, 2024
@vvoland vvoland self-assigned this Mar 7, 2024
@vvoland vvoland force-pushed the c8d-list-multiplatform branch 2 times, most recently from 9d68e75 to 3507b41 Compare March 7, 2024 19:56
@vvoland vvoland force-pushed the c8d-list-multiplatform branch 4 times, most recently from fc27649 to f7c6a83 Compare April 4, 2024 14:18
@vvoland vvoland changed the title [WIP] c8d: Multi-platform image list c8d: Multi-platform image list Apr 4, 2024
compatibility.
type: "array"
x-nullable: false
x-omitempty: true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm CI fails with:

diff --git a/api/types/image/summary.go b/api/types/image/summary.go
index 4ebbdeadf7..fed2746521 100644
--- a/api/types/image/summary.go
+++ b/api/types/image/summary.go
@@ -54,7 +54,7 @@ type Summary struct {
 	// WARNING: This is experimental and may change at any time without any backward
 	// compatibility.
 	//
-	PlatformImages []PlatformImage `json:"PlatformImages,omitempty"`
+	PlatformImages []PlatformImage `json:"PlatformImages"`
 
 	// List of content-addressable digests of locally available image manifests
 	// that the image is referenced from. Multiple manifests can refer to the

Please update api/swagger.yaml with any API changes, then 
run hack/generate-swagger-api.sh.

Even though I marked this one with x-omitempty 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it's an issue with the outdated go-swagger, see #47827 for update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disabled swagger model generation for this struct: 5eb892a

@vvoland vvoland force-pushed the c8d-list-multiplatform branch from f7c6a83 to ea31ce0 Compare April 4, 2024 15:34
@vvoland vvoland modified the milestones: 26.1.0, 27.0.0 Apr 10, 2024
@vvoland vvoland force-pushed the c8d-list-multiplatform branch 3 times, most recently from 7a67112 to 5eb892a Compare May 14, 2024 12:49
@vvoland
Copy link
Contributor Author

vvoland commented May 15, 2024

// 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"`

Thinking about it a bit more, I think we might want to expose all manifests details, not only the platform-specific images, so we can also provide information about build attestations (and possibly other things in future).

	// 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.
	//
	// WARNING: This is experimental and may change at any time without any backward
	// compatibility.
	//
	Manifests []ImageManifestSummary `json:"Manifests,omitempty"`
type ImageManifestKind string

const (
	ImageManifestKindImage       ImageManifestKind = "image"
	ImageManifestKindAttestation ImageManifestKind = "attestation"
	ImageManifestKindUnknown     ImageManifestKind = "unknown"
)

type ImageManifestSummary struct {
	ID string `json:"Id"`
	Descriptor ocispec.Descriptor `json:"Descriptor"`
	Available bool `json:"Available"`
	ContentSize int64 `json:"ContentSize"`

	Kind ImageManifestKind `json:"Kind"`
	// Fields below are specific to the kind of the image manifest.

	// Present only if Kind == ImageManifestKindImage.
	ImageData ImageProperties `json:"ImageData,omitempty"`

	// Present only if Kind == ImageManifestKindAttestation.
	AttestationData AttestationProperties `json:"AttestationData,omitempty"`
}

type ImageProperties struct {
	Platform ocispec.Platform `json:"Platform"`
	UnpackedSize int64 `json:"UnpackedSize"`
	Containers int64 `json:"Containers"`
}

type AttestationProperties struct {
	// For - the digest of the image manifest that this attestation is for.
	For digest.Digest `json:"For"`
}

This would also leave the gate open for adding any other manifests types in future.

WDYT?

@vvoland vvoland force-pushed the c8d-list-multiplatform branch from 5eb892a to 1db8418 Compare May 20, 2024 14:54
@vvoland vvoland force-pushed the c8d-list-multiplatform branch 2 times, most recently from e1e2554 to 3adc348 Compare June 5, 2024 08:09
@vvoland vvoland force-pushed the c8d-list-multiplatform branch from eab3e7b to da1fa10 Compare July 30, 2024 13:47
@vvoland vvoland requested a review from thaJeztah July 30, 2024 13:48
@vvoland vvoland force-pushed the c8d-list-multiplatform branch 4 times, most recently from c05ec81 to 49851df Compare August 5, 2024 10:37
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This starts to look good; I left some suggestions on naming, and some other comments; happy to discuss

"digest": target.Digest,
"isPseudo": isPseudo,
}).Debug("pseudo image check failed")
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that here we're returning nil, but for the contentSize, err := img.Size(ctx) error we're logging and continuing; is that intentional?

  • Perhaps this line may need a comment why we don't (or can't) continue in this case. And why we can continue if we get a NotFound error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, added a comment. Also the error checking below should also be ignored (so we don't abort the whole list operation just in case there's some issue with one image).

Comment on lines +285 to +287
if isPseudo {
if img.IsAttestation() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps not for this PR, but perhaps some of these may be good to extract to a separate function at some point. I think it may make the flow clearer as there's some early returns happening that are easy to miss because they're a bit buried in the code to construct the types.

Basically thinking along the lines of

  1. determine the Kind
  2. calling function for Kind to propagate the relevant data

Copy link
Contributor Author

@vvoland vvoland Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it's a bit complex at this point, but splitting it into functions isn't great either as there's a lot of shared state (totalSize, allChainsIDs, containersCount, best).

I can extract it to a separate struct in a follow up (or later, after we decide to settle for this approach).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup indeed. Perhaps there's not better solution, and definitely OK with me to look at in follow-ups

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll definitely try to improve it, but in this case we might need to sacrifice some readability over performance because it's a hot path 😞

@vvoland vvoland force-pushed the c8d-list-multiplatform branch 3 times, most recently from 9a4749b to 1b685f4 Compare August 7, 2024 11:15
Comment on lines +39 to +53
// Content is the size (in bytes) of all the locally present
// content in the content store (e.g. image config, layers)
// referenced by this manifest and its children.
// This only includes blobs in the content store.
Content int64 `json:"Content"`

// Total is the total size (in bytes) of all the locally present
// data (both distributable and non-distributable) that's related to
// this manifest and its children.
// This equal to the sum of [Content] size AND all the sizes in the
// [Size] struct present in the Kind-specific data struct.
// For example, for an image kind (Kind == ManifestKindImage),
// this would include the size of the image content and unpacked
// image snapshots ([Size.Content] + [ImageData.Size.Unpacked]).
Total int64 `json:"Total"`
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if these should be nilable in cases where we fail to determine the size? So we can differentiate between 0 and 🤷🏻 .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that's a good one; I know we used -1 for other situations, but those were -1 means "we didn't check".

Using omitempty also has its own downsides of course (w.r.t. other tools trying to use the JSON as-is) 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we have cases where "one of these" failed? Or would they more likely "both" fail?

(slightly considering if omitting all of Size would work for that), but .. tricky).

We could also consider adding a Warnings[] array somewhere for non-fatal, but still useful failures

Copy link
Contributor Author

@vvoland vvoland Aug 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1 is a bit meh to me, as it's really easy to get unnoticed, especially if you only add/subtract it.
Yeah I'd be against making it omitempty actually - not sure how other languages could handle it, but in pure JSON I think it's fine to just use explicit null instead of having us use the omitempty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the Warnings would be nice too IMO.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOL, we'll just make it a string and put "NaN" in it. Problem solved

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"LGTM"

I left some last few nits/notes, and wasn't sure if you were planning to squash the last touch-up commits.

Otherwise, I think this one should be good to get in from my side ❤️

Comment on lines 6665 to 6668
ImageManifestSummary:
description: |
ImageManifestSummary represents a summary of an image manifest.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can either rename the definition here, or add a x-go-name: "ManifestSummary"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Went with the x-go-name

Comment on lines 6344 to 6716
Kind:
description: |
The kind of the manifest.
type: "string"
example: "image"
enum:
- "image"
- "attestation"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably fine to touch-up in a follow-up, but if you're planning to still document these, I think we can either use a bullet-list, like this one (but that one should have a white-space before the list 😂)

moby/api/swagger.yaml

Lines 6484 to 6489 in 1dd102e

description: |
The published state of the volume.
* `pending-publish` The volume should be published to this node, but the call to the controller plugin to do so has not yet been successfully completed.
* `published` The volume is published successfully to the node.
* `pending-node-unpublish` The volume should be unpublished from the node, and the manager is awaiting confirmation from the worker that it has done so.
* `pending-controller-unpublish` The volume is successfully unpublished from the node, but has not yet been successfully unpublished on the controller.

Or even a small table, like

moby/api/swagger.yaml

Lines 4302 to 4310 in 1dd102e

node attribute | matches | example
---------------------|--------------------------------|-----------------------------------------------
`node.id` | Node ID | `node.id==2ivku8v2gvtg4`
`node.hostname` | Node hostname | `node.hostname!=node-2`
`node.role` | Node role (`manager`/`worker`) | `node.role==manager`
`node.platform.os` | Node operating system | `node.platform.os==windows`
`node.platform.arch` | Node architecture | `node.platform.arch==x86_64`
`node.labels` | User-defined node labels | `node.labels.security==high`
`engine.labels` | Docker Engine's labels | `engine.labels.operatingsystem==ubuntu-24.04`

Comment on lines +6728 to +6735
Platform:
$ref: "#/definitions/OCIPlatform"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also fine for a follow-up, and I THINK this is a limitation of the options we have in swagger, but wondering if we can somehow document how this Platform differs from the Descriptor.Platform (one from the descriptor, the other from the image config).

Not sure if it's possible (I don't think swagger accepts combination of description and $ref

Screenshot 2024-08-07 at 13 19 29

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, it's not rendered. However, I think it doesn't seem like it's an error to actually include the description?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and added it regardless of whether it's rendered or not 😅

Comment on lines +6727 to +6734
properties:
Platform:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also still contemplating if we want to add image ID (digest of the image-config) somewhere, but that one is probably fine for a follow-up discussion as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it has any use currently (without exposing the content store to allow to fetch it), so I think we can add it later if needed.

vvoland added 4 commits August 7, 2024 13:48
Our version of go-swagger doesn't handle the `omitempty` correctly for
the new field.

Signed-off-by: Paweł Gronowski <[email protected]>
Signed-off-by: Paweł Gronowski <[email protected]>
Use a helper from `ImageManifest` which reads the config instead.

Signed-off-by: Paweł Gronowski <[email protected]>
Add `Manifests` field to `ImageSummary` which exposes all image
manifests (which includes other blobs using the image media type, like
buildkit attestations).

There's also a new `manifests` query field that needs to be set in order
for the response to contain the new information.

Signed-off-by: Paweł Gronowski <[email protected]>
@vvoland vvoland force-pushed the c8d-list-multiplatform branch from 1b685f4 to 050afe1 Compare August 7, 2024 11:49
Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/api API area/images Image Distribution containerd-integration Issues and PRs related to containerd integration impact/changelog kind/enhancement Enhancements are not bugs or new features but can improve usability or performance. kind/feature Functionality or other elements that the project doesn't currently have. Features are new and shiny process/cherry-picked

Projects

Development

Successfully merging this pull request may close these issues.

5 participants