-
Notifications
You must be signed in to change notification settings - Fork 210
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 HEAD endpoints #207
Comments
What exactly is the addition recommended here? I am +1 for calling out explicitly that HEAD should be supported though and having that in conformance tests. There have been issues in the past of registries returning different response code or headers for |
There was some convo yesterday about this - containerd uses HEAD and falls back to GET. So the plan is to add to conformance, and also add it to the endpoints table |
Given DockerHub's recent implementation of rate limits, an interesting issue surfaced around the behavior of HEAD. If you do a HEAD request against a manifest-list, the Docker, and I assume other container environments as well, store the manifest list's digest as the actual image digest, without resolving the digest of the manifest it's redirected t, it would probably make sense if a HEAD request against a manifest list would return the digest of that list, matching what's being stored locally. There is already a way around this by supplying a I get that the ideal thing here would be to change the digests stored by docker-engine, but given the wide adoption of docker-ce, and the wide array of versions being used, any changes like that would probably take years to propagate fully into the wild. Some repros using DockerHub. I appreciate any challenges to my understanding as I may very well have gotten it wrong.
Curl response with the header added:
and without, defaulting to
The result of this currently seems to be that all HEAD requests against multi-arch/manifest-lists are considered to indicate that the digest has changed, leading to an actual |
The current draft of the OCI spec has removed mention of the I understand we probably want to remove the word |
I would expect the specs to describe a If it does describe the |
Thanks @thaJeztah - to be honest I wanted to say the same and gave up trying to word it correctly! 🤦 |
Hi all - this conversation has split into two separate threads. Please see the conclusions here: #208 (comment) |
Even worse than that, it's down-converted to a schema 1 image. Even more worse, there used to be a bug where you'd see the manifest list digest even if it returned a schema 2 image (based on your accept headers). That's luckily now been fixed: distribution/distribution#2395
I think you mean
We should make sure we haven't left out any other details like this. It should be possible to produce a client that works against existing registries just from reading the spec (especially docker hub). |
Hey @jonjohnsonjr - in response to
We can use your knowledge and experience here. The goal is to make things digestible to newcomers to this specification. If things were left out that you view to be vital, we are probably missing some context. Please reach out via slack/email, I'd like to setup some time to meet and address any concerns. |
Apologies for that last remark -- I am genuinely confused here, and didn't intend for that to come off so rudely. I know this is a lot of work and it's challenging to tackle a document of this size, so please don't take it personally :) I'll find you on slack to follow up. |
No, I mean The behavior is not obvious and kind of counter-intuitive currently, as it's not returning the type that matches my request the closest, in this case: a tag that happens to point at a manifest list. |
For the Docker Hub case, both have been done to remain backward-compatible;
@simskij see my comment above; for the Docker Hub case, this is done to remain backward compatible. I also was reading through the HTTP RFCs for content negotiation Yesterday (more below), and both variants are "valid". However, in case of the "no Testing some variations against Docker Hub, here's how it currently handles content-negotiation: ✅ For a multi-arch repository, specifying multiple export token="$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/hello-world:pull" | jq --raw-output '.token')";
curl -X HEAD -I -fsSL -H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.v1+json' \
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest"
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.distribution.manifest.list.v2+json
... ✅ Using export token="$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/hello-world:pull" | jq --raw-output '.token')";
curl -X HEAD -I -fsSL -H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.docker.distribution.manifest.v1+json' \
"https://registry-1.docker.io/v2/library/hello-world/manifests/latest"
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
... ✅ On a single-arch repository ( export token="$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:armhf/hello-world:pull" | jq --raw-output '.token')";
curl -X HEAD -I -fsSL -H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.docker.distribution.manifest.v2+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
-H 'Accept: application/vnd.docker.distribution.manifest.v1+json' \
"https://registry-1.docker.io/v2/armhf/hello-world/manifests/latest"
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.distribution.manifest.v2+json
...
This is one is odd, and feels like a bug/omission. Based on the presence of a export token="$(curl -fsSL "https://auth.docker.io/token?service=registry.docker.io&scope=repository:armhf/hello-world:pull" | jq --raw-output '.token')";
curl -X HEAD -I -fsSL -H "Authorization: Bearer $token" \
-H 'Accept: application/vnd.docker.distribution.manifest.list.v2+json' \
"https://registry-1.docker.io/v2/armhf/hello-world/manifests/latest"
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.distribution.manifest.v1+prettyjws
... However, content-negotiation is not described in the specification currently, and because of that would currently be up to each implementation how to handle Docker Hub (and other registries that existed before introduction of the v2 schemas) may be in a slightly special position as they have to preserve backward compatibility for that reason. Other registries could make other choices when dealing with active (server-side) content-negation. That said, I do think it would be good to have content-negotiation added to the specs, but as OPTIONAL. The reason for making it optional is that it should be possible to host a static registry (which would not be able to perform active content-negotiation). In situations where no active content-negotiation is performed by the registry, it should be handled by the client (in case of a manifest-list, the client is responsible for picking the right variant from the list). I started writing up a draft / some ideas Yesterday, and will try to post an initial proposal for discussion (also to perform active content-negotiation for multi-arch manifests, which could benefit (e.g.) ARM architectures, where a client could be able to support multiple (v5/v6/v7/v8) variants: allowing the client to set a list of accepted architectures, with priority would save extra roundtrips to fetch the manifest list and pick the variant on the client side). |
@thaJeztah are there still active clients that only work with v1 manifests? I guess they belong to bespoke CI/CD systems and the like? It would simplify life if we could drop v1 support. I would agree that we should a section on content-type negotiation to the specification. It's a bit frustrating though, as the way you've worded it will result in different registries returning significantly different results for the same requests (as I guess happens at the minute with the Docker Hub). |
@amouat I don't have numbers at hand, but with billions of pulls, we definitely get old (or plain "weird") clients that connect.
I wonder if this can be avoided. Today's |
Personally, I'd rather never return a v1 manifest. I actually removed all support for v1 from Trow. Supporting v1 manifests is a lot more work than v2. One option might be to refuse v1 uploads but automatically convert v2 manifests to v1 if requested. |
Actually, I may be mixing up v2, v1, and v2 schema1 (I always get confused by those); I think v1 has already been removed (https://www.docker.com/blog/docker-hub-deprecation-1-5/), but v2 schema1 is still "supported" by Hub (current versions of docker will produce a warning (19.03) or error (20.10) to recommend users to pull the schema 2 v1 manifest, and push as schema 2 v2) (moby/moby#39365, moby/moby#41295); docker 20.10 will produce;
I think the schema 2 v1 manifests are auto-generated by Hub though (again, would have to check) |
Cleaned up my draft a bit, and posted it as #212 |
Yes @thaJeztah - I was confused as well and also talking about schema 2 v1 (which is vastly different to v2). |
Ah yeah, I see what you mean. That makes sense for this spec in isolation, but unfortunately (as @thaJeztah described in great detail) the FWIW, the way we "solved" this in GCR was to do what you expect if clients send
So you get what you would expect with a manifest list:
But clients that send no
This is a somewhat janky solution because it relies on some specific client behavior, but it seems to fall within the spirit of the spec and seems to work for both curl and docker 🤷♂️ |
https://docs.docker.com/registry/spec/api/#existing-manifests
"Docker’s use of
HEAD
is also has aDocker-Content-Digest
in the response headers so you canHEAD
against a tag to determine the digest that the tag currently points to."The text was updated successfully, but these errors were encountered: