-
Notifications
You must be signed in to change notification settings - Fork 206
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Proposal: add (optional) section on content-negotiation #212
Comments
Hmm, I am not sure I see any reason for conneg in a content addressable store. It kind of makes sense for the old manifest formats, although arguably we should have used a different endpoint. A modern registry should only return what was uploaded, so there is no choice to negotiate. |
@thaJeztah - I think this information is useful, but does not actually change the API defined in the spec (correct me if I'm wrong). It provides a useful framework of how one might leverage the API effectively. Do you take any issue with scheduling this to be included in a 1.1.0 release? |
@jdolitsky this is essentially my first point from #211 described way more completely than I ever could have imagined. As it stands, it's impossible to read this spec and write a client that works with Docker Hub (really, most registries), which seems pretty important. Even if we don't want to do any content negotiation in this spec, we at least need to document something about this "legacy" (in quotes because AFAIK this is in prod for all major registries) content negotiation for 1.0. |
I agree that for
I see it as an optional extension to the API, but would help to improve interoperability, and define host registries can handle content negotiation (what dimensions ( |
I think this is a useful distinction to make. The original spec didn't speak to this at all, and I'm curious about the behavior for other registries. GCR will just 404 if you ask for a manifest by digest without the appropriate accept headers, which had led to some confusion. It would be nice to settle on an expected behavior here. It also seems like not mentioning the Accept headers at all puts registries in a really weird spot: A client author reading this spec will see no indication that they need to supply Accept headers, so they will be missing on manifest fetches. A registry that expects Accept headers so that it can support older clients will be non-conforming by returning a schema 1 manifest, and the client should (rightly, per the spec) complain about the registry. What is a registry operator to do? Do you drop support for older clients or not support newer clients? It should be possible for a registry to implement both the docker spec and the OCI spec, and they are currently mutually exclusive (or the OCI spec is incomplete), per my reading. |
While manifests are not strictly content addressable, they in effect are. Registries must not change them, there may be external signatures (eg Notary) that depend on the bits. Everything needs to be round tripped for this to work correctly, although we have not made this 100% clear in the spec (@stevvooe often says it though!). There is no sane way to support a tag that can Vary based on Accept now, this is purely legacy behaviour. Its actually really weird how some registries return you garbage generated manifests if you don't put the right Accept headers, and we need to phase this out. |
So I had a brief chat with @tonistiigi yesterday, and of course he was able to punch a big gap in my idea w.r.t. content-negotiation for multi-arch (I hate it when he does that 😂); While it can be useful to get just the image/arch you're interested in for a specific request; performing content-negotiation on the registry has the downside that the "root" (manifest-list) is skipped. So while it would still be possible to verify (the digest of) that image-manifest, when consuming multiple architectures, it would not be possible to verify that those images are part of the same manifest-list. For example (simplified, trying to describe what I think the problem is with that);
Now, while both images are valid, they were never part of the same manifest-list; in other words; they contain different versions of Of course this may not be an issue if you're only interested in a single arch, but (as mentioned), could also be an issue if the manifest-list itself is expected to be signed. content-negotiation could still be interesting to store different artifact types in a repository (e.g., store helm-charts and images in the same namespace), although those could of course all be stored in a single manifest-list, and using content-negotiation to pull either one or the other would no longer make this an optional feature (and therefore exclude static registries).
Do you mean
Which would mean: (possibly with an addendum for backward-compat? Not sure how/when it can be phased out). |
Regarding Accept headers and general content-negotiation - can somebody summarize this in 200-300 words or less and suggest which section of the spec to put it in? Alternatively, we can create some external We are presented with a lot of (good) information here with no suggestion for how to proceed. |
Doesn't Docker Hub? And docker/distribution? GCR does this. I would expect that most registries do this. I agree with you that we should phase this out. In my experience, this is the biggest stumbling point for people trying to interact with registries by far. At the same time, not including this in the spec would dramatically reduce the usefulness of the spec until it really is phased out. Registries will probably continue to return schema 1 images if you don't supply the right Accept header unless we specifically require something in this spec about NOT doing content negotiation for OCI media types (or until every customer using an old version of docker has migrated). I see that schema 1 was deprecated about a year ago in distribution/distribution#3000. Is that sufficient warning to break any clients relying on this behavior? With my client and registry maintainer hat on, I'm 100% in favor of no longer supporting schema 1 anywhere; but, if I put on my customer support hat, that would feel irresponsible. I think I would be okay with this:
Someone using this spec to write a client will be quite likely to encounter a docker v2 schema 2 image (you don't really know what's on the other side of a At the very least, we should mention this in the GET manifest section and link to a backwards compatibility doc or the legacy spec.
Registries will be doing this content negotiation already. We should be explicit about what they should be doing for OCI compliance. For example, GCR will not do this conversion for artifacts uploaded with OCI media types; however, we do expect clients to provide these media types in their What is the correct behavior here? |
I don't believe any new registry (ie one you create now, or indeed created in the last few years) needs to do any conneg at all, and should just be round tripping blobs as uploaded, regardless of any Accept headers. Existing registries should be able to start phasing this behaviour out I would think. It is mostly old Docker engines that would have issues, although some API clients that are not sending Accept headers now might get confused, looking at the stats on Docker Hub there are still small numbers of old clients but I rather suspect other registries have none, most are retrieving public images. |
@justincormack just to confirm; in your view all clients should be required to understand manifest lists? (I think that's fair, I just want to be explicit). |
@amouat I think the current practise of redirecting (well actually its not a redirect, that would be better) to the linux/amd64 version is pretty questionable yes, again there is backward compatibility issue but its not something that should be encouraged. If your client is not manifest list aware, you can still point it at the correct location via a different tag/sha. I don't know how many clients break on this (which versions?) but they will already not be working on Arm machines for example, so I don't see how this can be justified... |
I've opened #218, adding a new "content-negotation.md" linking to this issue. I think this doc can be updated over time |
does this proposal have to be resolved before a 1.0.0? |
From #212 Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
From #212 Signed-off-by: Vincent Batts <vbatts@hashbangbash.com>
Related JFrog Artifactory issue: |
Looks like I LGTM'd https://github.com/opencontainers/distribution-spec/blob/main/content-negotiation.md but we might want to make this more "OCI specific". the "manifest.v1" refers to the docker schema1 manifest and that's not entirely clear. How realistic is it to deprecate schema1 at this point? From there, pulls by tag can down convert if a manifest is requested by tag. |
Does that need to be reverted before the 1.1 final release? |
The distribution spec currently does not describe active content negotiation, leaving it up to individual implementations whether (and how) content negotiation should be performed.
This proposal is still a draft, and many "blanks" to fill, but I thought I'd open it, to allow having a discussion on this topic.
1. "Problem" statement
Currently, the client is responsible for picking the right variant for multi-manifest ("multi arch") repositories. While this works well, it requires at multiple steps, and multiple requests to get the image manifest;
GET /v2
)HEAD /v2/<name>/manifests/<reference>
GET /v2/<name>/manifests/<reference>
GET /v2/<name>/manifests/<DIGEST>
/v2/<name>/blobs/<digest>
(repeat for each blob)Content-negotiation can avoid multiple request, which could bring some performance improvements, and (if implemented by the registry), simplify logic in the client.
In addition, content-negotiation can assist in providing backward-compatibility (as outlined in the next section).
2. Existing uses of content-negotiation
Content negotiation is already in use by some registry implementations, such as Docker Hub (mostly for backward-compatibility). Here's some tests against docker hub;
Accept
<not present>
manifest.v1+prettyjws
v1
,linux/amd64
, for backward compatibility with old, non-v2 clients*/*
manifest.v1+prettyjws
v1
,linux/amd64
, for backward compatibility with old, non-v2 clients (same as above)application/json
manifest.v1+prettyjws
v1
,linux/amd64
, for backward compatibility with old, non-v2 clients (same as above)manifest.v1+json
manifest.v1+prettyjws
v1
, matchingAccept
header (linux/amd64
for backward compatibility)manifest.v2+json
manifest.v2+json
v2
, matchingAccept
header (linux/amd64
for backward compatibility)manifest.list.v2+json
manifest.list.v2+json
v2 manifest list
, matchingAccept
headermanifest.list.v2+json
,manifest.v2+json
,manifest.v1+json
manifest.list.v2+json
v2 manifest list
, matching firstAccept
headermanifest.v2+json
,manifest.list.v2+json
,manifest.v1+json
manifest.v2+json
<not present>
manifest.v1+prettyjws
*/*
manifest.v1+prettyjws
application/json
manifest.v1+prettyjws
manifest.v1+json
manifest.v1+prettyjws
manifest.v2+json
manifest.v2+json
manifest.list.v2+json
manifest.list.v2+json
manifest.list.v2+json
,manifest.v2+json
,manifest.v1+json
manifest.list.v2+json
v2 manifest
, matching first "acceptable"Accept
header (by lack of a manifest-list)manifest.v2+json
,manifest.list.v2+json
,manifest.v1+json
manifest.v2+json
v2 manifest
, matching first "acceptable"Accept
header (by lack of a manifest-list)To try these (use repo
library/hello-world
for multi-manifest, andarmhf/hello-world
for single manifest);2.1. Omissions in current implementations
Besides some (edge) cases highlighted (:warning:) in the previous section, current implementations appear to have some omissions;
Vary
header is returned (rfc7231, section 7.14)300
or406
statuses are used3. Proposal: server-side content-negotiation as optional feature
I suggest this feature to be
OPTIONAL
to keep backward compatibility with existing registries, and to facilitate static registries (which would not be able to perform server-side content negotiation).Registries that do not perform content-negotiation, would return either;
3.1. Approaches to content-negotiation
Looking for recommendations, and "correct" approaches, I did some reading-up on the HTTP RFCs. From those, there are multiple alternatives, see (rfc7231, section 5.3 - "Content Negotiation", rfc7231, section 3.4.1 - "Proactive Negotiation", and rfc7231, section 3.4.2 - "Reactive Negotiation"), each with their respective "pros" and "cons".
3.2. "Proactive Negotiation" rfc7231, section 3.4.1
With proactive negotiation, the registry picks the most suitable variant, and in case no acceptable variant can be selected, can return a
406 Not Acceptable
response, allowing the client to select the variant;The RFC does come with some warnings (outlined under "Proactive negotiation has serious disadvantages" in the RFC), although not all of those would apply to the distribution-spec.
If no acceptable match is found, a
406 Not Acceptable
(rfc7231, section 6.5.6) can be returned, as outlined in rfc7231, section 5.3.2Despite the downsides mentioned, this variant;
3.3. "Reactive Negotiation" rfc7231, section 3.4.2
This variant mostly matches the behavior of current registries that do not perform content negotiation, but offers slightly more "assistance":
The above roughly corresponds with the registry returning a manifest-list, although the spec seems to indicate the response can be both the actual content ("best manifest"), and a list of alternatives;
Reactive negotiation can return a
406 Not Acceptable
(rfc7231, section 6.5.6) or300 Multiple Choices
(rfc7231, section 6.4.1) response, the latter including aLocation
header with the "best alternative" as determined by the registry, and which the client can respect. In both cases, the response body SHOULD return a list of alternatives. The spec is unclear what format this list should have, but in case of the registry spec, I think a "manifest-list" would fit this bill.Although this approach somewhat corresponds with the current distribution spec, there are differences, and points of attention;
406 Not Acceptable
or300 Multiple Choices
responses300 Multiple Choices
response has aLocation
header that the client MAY follow, there is a likely possibility that clients automatically follow the redirect.Location
redirect, doing so may (should) strip authentication, making this not a good option.4. Proposal/example: automatic platform selection (os, os-version, arch, variant)
A client can specify multiple variants that are acceptable, optionally with a "Quality" (
q
) parameter to indicate relative weight (see rfc7231, section 5.3.1 and rfc7231, section 5.3.2);If no
q
parameter is passed, variants should be weighted in the order specified;Wildcards should be acceptable, and equivalent to omitting a property. For example
platform=linux/arm/*
andplatform=linux/arm
are equivalent ("any linux arm image").Wildcards can also be useful for OS-versions, particularly for Windows images, which (lacking a stable Windows kernel API) come in variants specific to Kernel versions. Kernel stability has improved with recent versions of Windows, however, and should not be a problem when using "Hyper-V" isolation, in which case any kernel version could be acceptable for clients.
However, using the suggested string representation may require additional changes:
As an alternative to the string representation, separate parameters could be provided for each property, for example:
Accept: application/vnd.docker.distribution.manifest.v2+json;platform.os=linux,platform.architecture=arm,platform.variant=v8
Omitting a property is equivalent to
*
(e.g.platform.variant=*
)5. Backward compatibility
(I think) This proposal would be backward-compatible with existing implementations that perform content-negotiation;
Accept: application/vnd.docker.distribution.manifest.list.v2+json
), or put the manifest-list before the (v2) manifest in the list ofAccept
headers.Accept
header, registries continue to select the "most appropriate" response (see "Backward compatibility (pre-v2 clients)" above)5.1. Backward compatibility (v2 clients)
For backward-compatibility, clients
MUST
include aapplication/vnd.docker.distribution.manifest.list.v2+json
and/or aapplication/vnd.docker.distribution.manifest.v2+json
(withoutplatform
specification orq
parameter) in the list, in order of preference. Theplatform
andq
parameters must be omitted for compatibility with current implementations, which may not expect/handle these.5.2. Backward compatibility (pre-v2 clients)
In case a registry supports both v1 and v2 manifests, and a client does not send an
Accept
header (or sends anAccept: */*
), which would be the case for pre-v1 clients, the registrySHOULD
return the oldest manifest type it supports (v1).Or, as outlined in rfc7231, section 5.3.2
and
The text was updated successfully, but these errors were encountered: