Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for image inspect with containerd-integration #43818

Merged
merged 1 commit into from
Jan 6, 2023

Conversation

thaJeztah
Copy link
Member

@thaJeztah thaJeztah commented Jul 15, 2022

Upstreaming:

These patches were selected by looking at the history for https://github.com/rumpl/moby/commits/dd-4.15.0/daemon/containerd/image.go, and only the relevant parts for this feature were included.

Quite a few of those are touching-up previous commits, so we should consider squashing (some of) them, but keeping them separate for now to somewhat help review.

p 85d6399836 add support for image inspect
p fa430d6686 Implement run using the containerd snapshotter
p d37b797d34 introduce GetImageOpts to manage image inspect data in backend
p 4543499f86 Introduce support for docker commit
p f1213b08bd Fix linting issues
p 2840f63ee6 GetImage to return image tags with details
p 75df7b7a05 list images matching digest to discover all tags
p d9b76c6682 c8d/prune: Handle filters, don't delete used
p a930139556 Add ExposedPorts and Volumes to the image returned
p 8df0a88265 Refactor resolving/getting images
p d58184802b Return the image ID on inspect
p 61ea993343 consider digest and ignore tag when both are set
p eaa810ab98 docker run --platform
p ab6918735d c8d/builder: store untagged images
p 960cff2c71 c8d/commit: Save as dangling name
p f7962b37ef Remove the call to resolveImage
p 9ac5d768c7 Handle multi-platform images when listing them

Also included rumpl#122

p a5a01b5cf7 daemon/containerd: fix compat for "docker run" with image that's not present

This is a squashed version of various PRs (or related code-changes)
to implement image inspect with the containerd-integration;

  • add support for image inspect
  • introduce GetImageOpts to manage image inspect data in backend
  • GetImage to return image tags with details
  • list images matching digest to discover all tags
  • Add ExposedPorts and Volumes to the image returned
  • Refactor resolving/getting images
  • Return the image ID on inspect
  • consider digest and ignore tag when both are set
  • docker run --platform

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

@thaJeztah

This comment was marked as outdated.

@thaJeztah thaJeztah force-pushed the image_inspect branch 2 times, most recently from fe465a1 to 7e7249c Compare July 15, 2022 16:11
@AkihiroSuda
Copy link
Member

Would it be possible to split the "ctx" changes into a separate commit?

@thaJeztah
Copy link
Member Author

moved it to #43828

@rumpl rumpl force-pushed the image_inspect branch 3 times, most recently from 805e2c6 to e35edac Compare December 26, 2022 15:01
}

if len(digests) > 1 {
return containerdimages.Image{}, errdefs.NotFound(errors.New("ambiguous reference"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe errdefs.Conflict would be more suitable?

Copy link
Member Author

@thaJeztah thaJeztah Jan 1, 2023

Choose a reason for hiding this comment

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

Conflict (409) is mostly intended if there's a conflict with another request (e.g. update a container that is in the process of being deleted); that said; "it's hard", as those status codes weren't really designed with APIs in mind. Also see the discussion on #44685

This page has some flow-charts that have been quite useful; https://www.codetinkerer.com/2015/12/04/choosing-an-http-status-code.html

Maybe a 422 would make sense, but it's not about the body (although that may be semantics)

@@ -20,6 +20,7 @@ import (
"github.com/docker/docker/image"
"github.com/docker/docker/layer"
"github.com/docker/go-connections/nat"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm still not comfortable with having our internal types defined by another project; see the discussion on rumpl#121 (comment)

If that type is implementing the image "v1" spec defined in this repository (https://github.com/moby/moby/tree/master/image/spec), and we don't have a type for that in this repository, we should define it here

Copy link
Member

Choose a reason for hiding this comment

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

I removed the commit

exposedPorts[nat.Port(k)] = v
}

img := image.NewImage(image.IDFromDigest(ctrdimg.Target.Digest))
Copy link
Member Author

Choose a reason for hiding this comment

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

Just recalled #44426 - we should update this to

Suggested change
img := image.NewImage(image.IDFromDigest(ctrdimg.Target.Digest))
img := image.NewImage(image.ID(ctrdimg.Target.Digest))

Copy link
Member

Choose a reason for hiding this comment

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

Done 👍

@rumpl rumpl force-pushed the image_inspect branch 2 times, most recently from 6228c7a to 1d1988c Compare January 2, 2023 16:41
@rumpl rumpl requested review from corhere and vvoland January 2, 2023 16:49
Comment on lines 220 to 234
if platform != nil {
cs := i.client.ContentStore()
imgPlatforms, err := containerdimages.Platforms(ctx, cs, img.Target)
if err != nil {
return img.Target, err
}

comparer := platforms.Only(*platform)
for _, p := range imgPlatforms {
if comparer.Match(p) {
return img.Target, nil
}
}
return img.Target, errdefs.NotFound(errors.Errorf("%s: platform %s not supported by image", refOrID, platforms.Format(*platform)))
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should be extracted from resolveDescriptor to GetImage. Accepting platform as an argument to this function gives an impression that the returned descriptor will be platform specific and it doesn't.
The platform is relevant only in the GetImage context, which uses it to pick the correct image config.

Copy link
Member

Choose a reason for hiding this comment

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

👍 I moved the code


cs := i.client.ContentStore()

platform := platforms.Default()
Copy link
Contributor

Choose a reason for hiding this comment

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

Hm, this won't work, if we have image for the non-default platform only (like if we only do docker pull --platform linux/arm64 alpine on non-arm64 host).
Is this expected?

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 it makes sense that when no --platform flag is given then we consider this as the user asking for the current platform

Copy link
Contributor

Choose a reason for hiding this comment

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

That bugs me - if the user doesn't set the --platform then we don't know what he's asking for, consider this:

$ docker pull --platform linux/arm64 alpine
$ docker inspect alpine
alpine: platform linux/amd64 not supported by image

seems a bit harsh, especially for doing an inspect which should allow me to get information about an image. The user might not remember which platform the image was pulled for and currently there's no way for him to find out that.

Copy link
Member

Choose a reason for hiding this comment

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

What should we show in this case then?

$ docker pull --platform linux/arm64 alpine
$ docker pull --platform linux/s390x alpine
$ docker inspect alpine

Copy link
Member Author

Choose a reason for hiding this comment

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

The "old" behavior would be to pick the first platform that's found (but of course that would always be the only platform). Ideally we would show all platforms for the image, but that brings us to the discussion in #44582. If we must show a single architecture, but multiple architectures are present, it could follow the same method of selecting as a docker pull would do (basically, use a platform-matcher?)

Copy link
Member

Choose a reason for hiding this comment

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

Consider the API currently shows whatever image is currently there regardless of platform.

We could error out with like ambiguous image reference: multiple platforms are available, you must specify a platform: available platforms <platform1>,<platform2>. It would be nice, though, if the API's json error could list those platforms so clients can parse it nicely (without having to parse the error string) or pick the first one... I'm inclined to error in case of ambiguity here, though.

Alternatively... we could update the API to return a list of objects
The docker CLI at least already outputs a json array.
There wouldn't be much too change to support the updated API.

... or change it to return the image index (with the appropriate media type on the response headers).
This does mean larger changes would be needed on the client.

Copy link
Member

Choose a reason for hiding this comment

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

On the last point... we could gate this by accept header.

Copy link
Member

Choose a reason for hiding this comment

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

Talking with @thaJeztah and we decided to go with a AllPlatformsWithPreference that returns the preferred platform first and then the others in order that they are defined by containerd.

Copy link
Member Author

Choose a reason for hiding this comment

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

^^ That approach is the "first step" for this PR, to keep the behavior (mostly) consistent with what we have pre-containers, and to get us going, but we should indeed continue the discussion on UX (which could be return multiple platforms if there's multiple variations present)

Copy link
Member

@rumpl rumpl Jan 5, 2023

Choose a reason for hiding this comment

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

Definitely, the discussion is only getting started. Way back when we talked about snapshotters we decided that the way to go was to make everything as close as possible to the existing and after that we can shape the things in different ways


platform := platforms.Default()
if desc.Platform != nil {
platform = platforms.OnlyStrict(*desc.Platform)
Copy link
Contributor

Choose a reason for hiding this comment

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

We use OnlyStrict here, but Only when doing the previous check which will make some cases pass the check but fail on getting the config. Maybe we could get rid of the previous platform check completely, and rely on containerdimages.Config returning an error when we don't have that platform present?

@cpuguy83
Copy link
Member

cpuguy83 commented Jan 3, 2023

Outside of the discussion around multiple platforms being available, the rest of the code LGTM.

@rumpl rumpl force-pushed the image_inspect branch 2 times, most recently from 40f184f to 82430d6 Compare January 5, 2023 14:18
Copy link
Contributor

@vvoland vvoland left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Member Author

@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

Comment on lines 26 to 28
type ErrImageDoesNotExist struct {
ref reference.Reference
Ref reference.Reference
}
Copy link
Member Author

Choose a reason for hiding this comment

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

For a follow-up; we need to check if we really need this error-type, as it already implements errdefs.NotFound, perhaps we don't need it (with the possible exception of the "special" No such ... prefix, which may be needed for older CLIs).

Copy link
Member

Choose a reason for hiding this comment

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

Yeah this one might not be needed, the only difference is that this error type formats the message with "No such image: %s", ref whereas the errdefs.NotFound doesn't have that extra data

daemon/containerd/image.go Outdated Show resolved Hide resolved
This is a squashed version of various PRs (or related code-changes)
to implement image inspect with the containerd-integration;

- add support for image inspect
- introduce GetImageOpts to manage image inspect data in backend
- GetImage to return image tags with details
- list images matching digest to discover all tags
- Add ExposedPorts and Volumes to the image returned
- Refactor resolving/getting images
- Return the image ID on inspect
- consider digest and ignore tag when both are set
- docker run --platform

Signed-off-by: Djordje Lukic <djordje.lukic@docker.com>
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
Copy link
Member Author

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

still LGTM

@thaJeztah
Copy link
Member Author

Let me bring this one in 👍

@thaJeztah thaJeztah merged commit 9374912 into moby:master Jan 6, 2023
containerd integration automation moved this from In progress to Done Jan 6, 2023
@thaJeztah thaJeztah deleted the image_inspect branch January 6, 2023 16:40
}

func (c allPlatformsWithPreferenceMatcher) Less(p1, p2 ocispec.Platform) bool {
return c.preferred.Less(p1, p2)
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is to live in a pkg package, we should make sure it's robust for any preferred matcher, including ones whose Less implementation may not necessarily sort platforms which Match less than platforms it would not.

Suggested change
return c.preferred.Less(p1, p2)
m1, m2 := c.preferred.Match(p1), c.preferred.Match(p2)
if m1 && m2 {
return c.preferred.Less(p1, p2)
}
return m1 // Not totally-ordered

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/images containerd-integration Issues and PRs related to containerd integration status/2-code-review
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

7 participants