Fix HEAD on cached container virtual registry manifests

What does this MR do and why?

Fixes cached manifest HEAD responses in the container virtual registry. containerd rejects them, so the first pull works and every later pull fails.

On a cache hit, HEAD returned Content-Length: 0 and no Docker-Content-Digest. containerd fails with "reports content size of zero". Two customers are blocked, and one is a POC. A cache miss works because we proxy the upstream response.

Cause

  • No real Content-Length: on HEAD, the controller still called send_upload for the cached file. The body was stripped, so the response had no real Content-Length. It also had a Content-Disposition header.
  • Missing digest: we built Docker-Content-Digest from the upstream ETag. Upstreams like quay.io send no ETag, or send one that is not the digest. We dropped the header.

Fix

  • Controller: on HEAD for a cached file, set Content-Length from the stored size. Set Content-Type and Docker-Content-Digest. Return head :ok and do not send the file. The digest comes from the stored digest, then falls back to upstream_etag.
  • Cache entry service: when we cache a manifest, store sha256:<file sha256> as the digest. A manifest digest is the sha256 of its bytes. Workhorse already computes this on upload. The order is: digest from path, then manifest content digest, then ETag digest.
  • File request service: pass digest and size in the download payload.

There is no migration. The digest column already exists and has no uniqueness constraint.

Existing cache entries

  • Old entries get the right Content-Length at once.
  • They get a digest after we re-fetch them from upstream. That happens after the cache validity period, which is 24 hours by default.
  • Upstreams with cache validity 0 never re-fetch. Their old entries need a cache clear.

Not yet tested

I have not validated this end to end in GDK with Workhorse in front and a real quay.io upstream. Request specs cover the controller behavior. Reviewers, please note this when you review.

References

Screenshots or screen recordings

Not applicable. This MR has no UI change.

How to set up and validate locally

  1. Create a container virtual registry with upstream https://quay.io in a top-level group.
  2. Pull an image by tag through it once. This is a cache miss.
  3. Run curl -I with a bearer token against /v2/virtual_registries/container/<registry_id>/<image>/manifests/<tag> twice. Check that the second response has a non-zero Content-Length and a Docker-Content-Digest.
  4. Pull by digest with containerd twice, for example with crictl pull or nerdctl pull. Confirm both pulls succeed.

Run the specs:

bin/rspec ee/spec/requests/virtual_registries/container_controller_spec.rb ee/spec/services/virtual_registries/container/cache/entries/create_or_update_service_spec.rb ee/spec/services/virtual_registries/container/handle_file_request_service_spec.rb

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

🤖 Generated with Claude Code

Edited by Tim Rizzi

Merge request reports

Loading
Loading