-
Notifications
You must be signed in to change notification settings - Fork 5
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 knownDigest
to ociclient
's Resolve*
methods
#29
Conversation
Brilliant, thanks for catching this! I'd suggest one other little thing to keep us honest: I added So I'd suggest changing Thanks again for the fix. |
Yeah, absolutely! Happy to update. I figured I'd just develop the test update on main before applying it to my PR, but I followed what you've explained and it doesn't seem to fail the tests -- are they invoking $ git log -1 --oneline
f3720d0 (HEAD -> main, origin/main, origin/HEAD) ociclient,ociauth: breaking change: use standard http.Transport $ git --no-pager diff
diff --git a/ociregistry/ociserver/reader.go b/ociregistry/ociserver/reader.go
index bd5952a..851e7bb 100644
--- a/ociregistry/ociserver/reader.go
+++ b/ociregistry/ociserver/reader.go
@@ -147,7 +147,9 @@ func (r *registry) handleManifestHead(ctx context.Context, resp http.ResponseWri
if err != nil {
return err
}
- resp.Header().Set("Docker-Content-Digest", string(desc.Digest))
+ if !r.opts.OmitDigestFromTagGetResponse {
+ resp.Header().Set("Docker-Content-Digest", string(desc.Digest))
+ }
resp.Header().Set("Content-Type", desc.MediaType)
resp.Header().Set("Content-Length", fmt.Sprint(desc.Size))
resp.WriteHeader(http.StatusOK) $ go test ./... && echo pass || echo fail
? cuelabs.dev/go/oci/ociregistry [no test files]
? cuelabs.dev/go/oci/ociregistry/internal/exp/constraints [no test files]
? cuelabs.dev/go/oci/ociregistry/internal/exp/maps [no test files]
? cuelabs.dev/go/oci/ociregistry/internal/exp/slices [no test files]
ok cuelabs.dev/go/oci/ociregistry/internal/ocirequest 0.004s
? cuelabs.dev/go/oci/ociregistry/ocidebug [no test files]
? cuelabs.dev/go/oci/ociregistry/ocitest [no test files]
ok cuelabs.dev/go/oci/ociregistry/ociauth 1.128s
ok cuelabs.dev/go/oci/ociregistry/ociclient 0.008s
ok cuelabs.dev/go/oci/ociregistry/ocifilter 0.004s
ok cuelabs.dev/go/oci/ociregistry/ocimem 0.005s
ok cuelabs.dev/go/oci/ociregistry/ociref 0.006s
ok cuelabs.dev/go/oci/ociregistry/ociserver 0.028s
ok cuelabs.dev/go/oci/ociregistry/ociunify 0.003s
pass |
afe0e68
to
ed38c98
Compare
(Rebased and committed the |
Ohhhhh, the conformance tests are a separate module, so my Edit: there we go! $ go test ./ociregistry/internal/conformance/...
--- FAIL: TestClientAsProxyWithOmittedTagGetDigest (0.51s)
--- FAIL: TestClientAsProxyWithOmittedTagGetDigest/extra (0.07s)
--- FAIL: TestClientAsProxyWithOmittedTagGetDigest/extra/largeManifest (0.03s)
quicktest.go:12:
error:
values are not equal
comment:
response body: "{\"errors\":[{\"code\":\"UNKNOWN\",\"message\":\"no digest header found in response\"}]}"
got:
int(500)
want:
int(200)
stack:
/wd/ociregistry/internal/conformance/conformance_test.go:387
qt.Assert(t, qt.Equals(resp.StatusCode, http.StatusOK), qt.Commentf("response body: %q", data))
/wd/ociregistry/internal/conformance/conformance_test.go:231
test.run(t, client)
FAIL
FAIL cuelabs.dev/go/oci/ociregistry/internal/conformance 4.217s
FAIL |
ed38c98
to
1096806
Compare
Ok, that helped me find another line that needed the same fix, but it doesn't actually fix the conformance tests because they apparently do a |
oci/ociregistry/ociclient/reader.go Lines 153 to 157 in 1096806
Perhaps we should update this code for if the |
I think in this case we are probably justified in requiring the response to contain a digest, despite the wording of the spec. By way of justification, at least one other client library fails if the response doesn't contain the digest. With that in mind, perhaps the
WDYT? |
Sounds good to me! 👍 |
4634745
to
7d32429
Compare
Updated with the new conditional that accounts for tag, your suggested comment, and a rebase on main for good measure. 🚀 |
Hmm, on the less positive side, making just the |
In the OCI spec, having `HEAD` requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via the `knownDigest` parameter to `descriptorFromResponse`, but it was (accidentally?) set to `""` explicitly for all the `resolve` methods (even though 2 out of 3 of them are by-digest): https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry > A HEAD request to an existing blob or manifest URL MUST return `200 OK`. A successful response SHOULD contain the digest of the uploaded blob in the header `Docker-Content-Digest`. A successful response SHOULD contain the size in bytes of the uploaded blob in the header `Content-Length`. As a result, my attempts at using `ResolveBlob` to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK: ```console $ curl -H "$(crane auth token --header busybox)" --head -L https://registry-1.docker.io/v2/library/busybox/blobs/sha256:7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c HTTP/1.1 307 Temporary Redirect content-type: application/octet-stream docker-distribution-api-version: registry/2.0 location: https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/7b/7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c/data?verify=1709871346-z5HS81nN1Ov9OEB3RdN60eJ2MjA%3D date: Fri, 08 Mar 2024 03:25:46 GMT strict-transport-security: max-age=31536000 HTTP/2 200 date: Fri, 08 Mar 2024 03:25:46 GMT content-type: application/octet-stream content-length: 2152262 cf-ray: 860fb84bfd2f2b58-LAX cf-cache-status: HIT accept-ranges: bytes age: 170784 cache-control: public, max-age=14400 etag: "2aeecf61106ed1c6e3a774e3db220ca7" expires: Fri, 08 Mar 2024 07:25:46 GMT last-modified: Wed, 06 Mar 2024 00:07:43 GMT vary: Accept-Encoding x-amz-id-2: GIEh93BaQl88NHOZYTVLgwDtBjOzY96VsTcyP2qql4lrK98VY+vcj1wOxHAT47ChDa+wRCKv7Pk= x-amz-request-id: 8YB4XDY06KMSY2GN x-amz-server-side-encryption: AES256 x-amz-version-id: oxJ9x0zrl0ICqBGgSAIDsycrCQ9qVxw5 server: cloudflare ``` I have verified that this fixes the issue and allows the `ResolveBlob` call to complete successfully against Docker Hub. Signed-off-by: Tianon Gravi <admwiggin@gmail.com>
7d32429
to
b652d61
Compare
@rogpeppe thoughts on this one? 🙇 It's necessary for using this API against Docker Hub, and I think the only hesitation is that the tests don't actually have good coverage on it. 😞 (I'm currently relying on this PR's branch in my project, so that's why I've rebased above so I can pull in and benefit from your other changes 😄) |
I looked into this a bit more. It seems the tests do cover this, but the client was being unwarrantedly lax about requiring digests in some cases. I've made some adjustments that demonstrate that this fix is covered by the tests, but I'll add that in another CL. For now, this LGTM, thanks! |
In the OCI spec, having `HEAD` requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via the `knownDigest` parameter to `descriptorFromResponse`, but it was (accidentally?) set to `""` explicitly for all the `resolve` methods (even though 2 out of 3 of them are by-digest): https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry > A HEAD request to an existing blob or manifest URL MUST return `200 OK`. A successful response SHOULD contain the digest of the uploaded blob in the header `Docker-Content-Digest`. A successful response SHOULD contain the size in bytes of the uploaded blob in the header `Content-Length`. As a result, my attempts at using `ResolveBlob` to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK: ```console $ curl -H "$(crane auth token --header busybox)" --head -L https://registry-1.docker.io/v2/library/busybox/blobs/sha256:7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c HTTP/1.1 307 Temporary Redirect content-type: application/octet-stream docker-distribution-api-version: registry/2.0 location: https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/7b/7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c/data?verify=1709871346-z5HS81nN1Ov9OEB3RdN60eJ2MjA%3D date: Fri, 08 Mar 2024 03:25:46 GMT strict-transport-security: max-age=31536000 HTTP/2 200 date: Fri, 08 Mar 2024 03:25:46 GMT content-type: application/octet-stream content-length: 2152262 cf-ray: 860fb84bfd2f2b58-LAX cf-cache-status: HIT accept-ranges: bytes age: 170784 cache-control: public, max-age=14400 etag: "2aeecf61106ed1c6e3a774e3db220ca7" expires: Fri, 08 Mar 2024 07:25:46 GMT last-modified: Wed, 06 Mar 2024 00:07:43 GMT vary: Accept-Encoding x-amz-id-2: GIEh93BaQl88NHOZYTVLgwDtBjOzY96VsTcyP2qql4lrK98VY+vcj1wOxHAT47ChDa+wRCKv7Pk= x-amz-request-id: 8YB4XDY06KMSY2GN x-amz-server-side-encryption: AES256 x-amz-version-id: oxJ9x0zrl0ICqBGgSAIDsycrCQ9qVxw5 server: cloudflare ``` I have verified that this fixes the issue and allows the `ResolveBlob` call to complete successfully against Docker Hub. Closes #29 as merged as of commit b652d61. Signed-off-by: Tianon Gravi <admwiggin@gmail.com> Change-Id: I47bec2e50528d7fb566eeaddfafdf9d44291b13a
In the OCI spec, having `HEAD` requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via the `knownDigest` parameter to `descriptorFromResponse`, but it was (accidentally?) set to `""` explicitly for all the `resolve` methods (even though 2 out of 3 of them are by-digest): https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry > A HEAD request to an existing blob or manifest URL MUST return `200 OK`. A successful response SHOULD contain the digest of the uploaded blob in the header `Docker-Content-Digest`. A successful response SHOULD contain the size in bytes of the uploaded blob in the header `Content-Length`. As a result, my attempts at using `ResolveBlob` to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK: ```console $ curl -H "$(crane auth token --header busybox)" --head -L https://registry-1.docker.io/v2/library/busybox/blobs/sha256:7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c HTTP/1.1 307 Temporary Redirect content-type: application/octet-stream docker-distribution-api-version: registry/2.0 location: https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/7b/7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c/data?verify=1709871346-z5HS81nN1Ov9OEB3RdN60eJ2MjA%3D date: Fri, 08 Mar 2024 03:25:46 GMT strict-transport-security: max-age=31536000 HTTP/2 200 date: Fri, 08 Mar 2024 03:25:46 GMT content-type: application/octet-stream content-length: 2152262 cf-ray: 860fb84bfd2f2b58-LAX cf-cache-status: HIT accept-ranges: bytes age: 170784 cache-control: public, max-age=14400 etag: "2aeecf61106ed1c6e3a774e3db220ca7" expires: Fri, 08 Mar 2024 07:25:46 GMT last-modified: Wed, 06 Mar 2024 00:07:43 GMT vary: Accept-Encoding x-amz-id-2: GIEh93BaQl88NHOZYTVLgwDtBjOzY96VsTcyP2qql4lrK98VY+vcj1wOxHAT47ChDa+wRCKv7Pk= x-amz-request-id: 8YB4XDY06KMSY2GN x-amz-server-side-encryption: AES256 x-amz-version-id: oxJ9x0zrl0ICqBGgSAIDsycrCQ9qVxw5 server: cloudflare ``` I have verified that this fixes the issue and allows the `ResolveBlob` call to complete successfully against Docker Hub. Closes #29 as merged as of commit b652d61. Signed-off-by: Tianon Gravi <admwiggin@gmail.com> Change-Id: I47bec2e50528d7fb566eeaddfafdf9d44291b13a
In PR #29, I suggested that we should verify that the tests fail when the fix was not applied, but the tests did not fail in that case. It turns out that's because the client was being a bit too slack about requiring digests in responses, meaning that the client succeeded even when it should have failed. This change makes `ociclient.descriptorFromResponse` a little more stringent about digests, and I've explicitly verified that when the changes in #29 are reverted, the tests do in fact fail as expected. Signed-off-by: Roger Peppe <rogpeppe@gmail.com> Change-Id: Ic7352b572593a60dc2209193fdd64bf2e4235af9
In the OCI spec, having `HEAD` requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via the `knownDigest` parameter to `descriptorFromResponse`, but it was (accidentally?) set to `""` explicitly for all the `resolve` methods (even though 2 out of 3 of them are by-digest): https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry > A HEAD request to an existing blob or manifest URL MUST return `200 OK`. > A successful response SHOULD contain the digest of the uploaded blob in > the header `Docker-Content-Digest`. A successful response SHOULD contain > the size in bytes of the uploaded blob in the header `Content-Length`. As a result, my attempts at using `ResolveBlob` to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK: ```console $ curl -H "$(crane auth token --header busybox)" --head -L https://registry-1.docker.io/v2/library/busybox/blobs/sha256:7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c HTTP/1.1 307 Temporary Redirect content-type: application/octet-stream docker-distribution-api-version: registry/2.0 location: https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/7b/7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c/data?verify=1709871346-z5HS81nN1Ov9OEB3RdN60eJ2MjA%3D date: Fri, 08 Mar 2024 03:25:46 GMT strict-transport-security: max-age=31536000 HTTP/2 200 date: Fri, 08 Mar 2024 03:25:46 GMT content-type: application/octet-stream content-length: 2152262 cf-ray: 860fb84bfd2f2b58-LAX cf-cache-status: HIT accept-ranges: bytes age: 170784 cache-control: public, max-age=14400 etag: "2aeecf61106ed1c6e3a774e3db220ca7" expires: Fri, 08 Mar 2024 07:25:46 GMT last-modified: Wed, 06 Mar 2024 00:07:43 GMT vary: Accept-Encoding x-amz-id-2: GIEh93BaQl88NHOZYTVLgwDtBjOzY96VsTcyP2qql4lrK98VY+vcj1wOxHAT47ChDa+wRCKv7Pk= x-amz-request-id: 8YB4XDY06KMSY2GN x-amz-server-side-encryption: AES256 x-amz-version-id: oxJ9x0zrl0ICqBGgSAIDsycrCQ9qVxw5 server: cloudflare ``` I have verified that this fixes the issue and allows the `ResolveBlob` call to complete successfully against Docker Hub. Closes #29 as merged as of commit b652d61. Signed-off-by: Tianon Gravi <admwiggin@gmail.com> Change-Id: I47bec2e50528d7fb566eeaddfafdf9d44291b13a
In PR #29, I suggested that we should verify that the tests fail when the fix was not applied, but the tests did not fail in that case. It turns out that's because the client was being a bit too slack about requiring digests in responses, meaning that the client succeeded even when it should have failed. This change makes `ociclient.descriptorFromResponse` a little more stringent about digests, and I've explicitly verified that when the changes in #29 are reverted, the tests do in fact fail as expected. Signed-off-by: Roger Peppe <rogpeppe@gmail.com> Change-Id: Ic7352b572593a60dc2209193fdd64bf2e4235af9
In the OCI spec, having `HEAD` requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via the `knownDigest` parameter to `descriptorFromResponse`, but it was (accidentally?) set to `""` explicitly for all the `resolve` methods (even though 2 out of 3 of them are by-digest): https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry > A HEAD request to an existing blob or manifest URL MUST return `200 > OK`. A successful response SHOULD contain the digest of the uploaded > blob in the header `Docker-Content-Digest`. A successful response > SHOULD contain the size in bytes of the uploaded blob in the header > `Content-Length`. As a result, my attempts at using `ResolveBlob` to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK: ```console $ curl -H "$(crane auth token --header busybox)" --head -L https://registry-1.docker.io/v2/library/busybox/blobs/sha256:7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c HTTP/1.1 307 Temporary Redirect content-type: application/octet-stream docker-distribution-api-version: registry/2.0 location: https://production.cloudflare.docker.com/registry-v2/docker/registry/v2/blobs/sha256/7b/7b2699543f22d5b8dc8d66a5873eb246767bca37232dee1e7a3b8c9956bceb0c/data?verify=1709871346-z5HS81nN1Ov9OEB3RdN60eJ2MjA%3D date: Fri, 08 Mar 2024 03:25:46 GMT strict-transport-security: max-age=31536000 HTTP/2 200 date: Fri, 08 Mar 2024 03:25:46 GMT content-type: application/octet-stream content-length: 2152262 cf-ray: 860fb84bfd2f2b58-LAX cf-cache-status: HIT accept-ranges: bytes age: 170784 cache-control: public, max-age=14400 etag: "2aeecf61106ed1c6e3a774e3db220ca7" expires: Fri, 08 Mar 2024 07:25:46 GMT last-modified: Wed, 06 Mar 2024 00:07:43 GMT vary: Accept-Encoding x-amz-id-2: GIEh93BaQl88NHOZYTVLgwDtBjOzY96VsTcyP2qql4lrK98VY+vcj1wOxHAT47ChDa+wRCKv7Pk= x-amz-request-id: 8YB4XDY06KMSY2GN x-amz-server-side-encryption: AES256 x-amz-version-id: oxJ9x0zrl0ICqBGgSAIDsycrCQ9qVxw5 server: cloudflare ``` I have verified that this fixes the issue and allows the `ResolveBlob` call to complete successfully against Docker Hub. Closes #29 as merged as of commit b652d61. Signed-off-by: Tianon Gravi <admwiggin@gmail.com> Change-Id: I47bec2e50528d7fb566eeaddfafdf9d44291b13a
In PR #29, I suggested that we should verify that the tests fail when the fix was not applied, but the tests did not fail in that case. It turns out that's because the client was being a bit too slack about requiring digests in responses, meaning that the client succeeded even when it should have failed. This change makes `ociclient.descriptorFromResponse` a little more stringent about digests, and I've explicitly verified that when the changes in #29 are reverted, the tests do in fact fail as expected. Signed-off-by: Roger Peppe <rogpeppe@gmail.com> Change-Id: Ic7352b572593a60dc2209193fdd64bf2e4235af9
In PR #29, I suggested that we should verify that the tests fail when the fix was not applied, but the tests did not fail in that case. It turns out that's because the client was being a bit too slack about requiring digests in responses, meaning that the client succeeded even when it should have failed. This change makes `ociclient.descriptorFromResponse` a little more stringent about digests, and I've explicitly verified that when the changes in #29 are reverted, the tests do in fact fail as expected. Signed-off-by: Roger Peppe <rogpeppe@gmail.com> Change-Id: Ic7352b572593a60dc2209193fdd64bf2e4235af9
In PR #29, I suggested that we should verify that the tests fail when the fix was not applied, but the tests did not fail in that case. It turns out that's because the client was being a bit too slack about requiring digests in responses, meaning that the client succeeded even when it should have failed. This change makes `ociclient.descriptorFromResponse` a little more stringent about digests, and I've explicitly verified that when the changes in #29 are reverted, the tests do in fact fail as expected. Signed-off-by: Roger Peppe <rogpeppe@gmail.com> Change-Id: Ic7352b572593a60dc2209193fdd64bf2e4235af9 Reviewed-on: https://review.gerrithub.io/c/cue-labs/oci/+/1191111 TryBot-Result: CUE porcuepine <cue.porcuepine@gmail.com> Reviewed-by: Daniel Martí <mvdan@mvdan.cc>
Very nice, thank you!! 🚀 |
In the OCI spec, having
HEAD
requests return the digest header is technically only a "SHOULD" (not a "MUST"), and it turns out that Docker Hub is one of the registries on the sad side of that "SHOULD" -- the code elsewhere was already accounting for this via theknownDigest
parameter todescriptorFromResponse
, but it was (accidentally?) set to""
explicitly for all theresolve
methods (even though 2 out of 3 of them are by-digest):https://github.com/opencontainers/distribution-spec/blob/v1.1.0/spec.md#checking-if-content-exists-in-the-registry
As a result, my attempts at using
ResolveBlob
to check if a specific blob exists on Docker Hub were failing with "invalid descriptor in response: no digest found in response" even though the proper digest was in the request and the response was 200 OK:curl on Docker Hub showing it being on the sad side of that SHOULD:
I have verified that this fixes the issue and allows the
ResolveBlob
call to complete successfully against Docker Hub.FWIW, I implemented this to mimic other uses of the
descriptorFromResponse
function, such as:oci/ociregistry/ociclient/reader.go
Line 138 in f3720d0
(but I'm happy to rebase, amend, adjust, etc ❤️)