Skip to content

fix(oci): cache tag lists and normalize manifest variants - #280

Merged
andrew merged 6 commits into
git-pkgs:mainfrom
abhinavgautam01:fix/oci-offline-cache-249
Sep 2, 2026
Merged

fix(oci): cache tag lists and normalize manifest variants#280
andrew merged 6 commits into
git-pkgs:mainfrom
abhinavgautam01:fix/oci-offline-cache-249

Conversation

@abhinavgautam01

Copy link
Copy Markdown
Contributor

Fixes #249

Summary

  • Cache OCI /v2/<name>/tags/list responses automatically, including when general metadata caching is disabled.
  • Serve stale cached tag lists when the upstream registry or token service is temporarily unavailable.
  • Normalize manifest Accept headers before deriving cache keys, so equivalent media-type lists reuse the same cached variant.
  • Verify a cached manifest's content type is acceptable for the requesting client before serving it.
  • Document OCI manifest and tag-list offline caching behavior.

Validation

  • gofmt
  • go tool golangci-lint run ./...
  • go vet ./...
  • go test -race ./...
  • git diff --check

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR improves OCI offline behavior by caching tag-list responses and making manifest caching more robust across equivalent Accept header variants, so previously warmed caches can be reused when upstream registry/token services are unavailable.

Changes:

  • Add automatic caching (with stale fallback) for OCI /v2/<name>/tags/list responses, independent of general metadata caching.
  • Normalize manifest Accept headers for cache-key derivation and validate cached manifest content types against the caller’s Accept.
  • Add tests and documentation for OCI tag-list + manifest offline caching behavior.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/handler/container.go Routes tag-list requests through the new cached implementation.
internal/handler/container_tags.go Implements tag-list caching, ETag revalidation, and stale fallback behavior.
internal/handler/container_manifest.go Normalizes Accept for cache keys and validates cached manifest content type against request.
internal/handler/container_test.go Adds coverage for stale tag-list fallback and manifest variant normalization/content-type gating.
docs/configuration.md Documents always-cached OCI manifests and tag lists + stale behavior.
docs/architecture.md Updates architecture notes to include tag lists as always-cached metadata.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread internal/handler/container_manifest.go Outdated
Comment thread internal/handler/container_manifest.go

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please address these cache compatibility issues:

  • Preserve reads of existing manifest cache keys. Previous releases hashed the raw Accept value, so the normalized key misses warmed entries after an upgrade. On a normalized-key miss, try the legacy key and rewrite the entry under the normalized key. Add an upgrade test using previous-format data.
  • Apply quality to the most specific matching Accept media range. Accept: application/vnd.oci.image.index.v1+json;q=0, */*;q=1 currently accepts the explicitly excluded index through the wildcard. Add a regression test.

@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Thanks, implemented the legacy raw-Accept cache-key fallback with migration and upgrade coverage. Also fixed specificity-aware Accept matching so an explicit q=0 exclusion overrides a wildcard, with regression coverage.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please preserve mixed-version and rollback cache access. Fresh manifests are now stored only under the normalized Accept key. Older replicas and rollback releases still hash the raw Accept value, so they cannot read entries first cached by the new release. Dual-write the raw key when it differs, including the digest alias. Add a handler-level test that warms through the public route and verifies the previous-format key is written.

@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Implemented dual-write support for normalized and legacy raw-Accept keys, including digest aliases. Added a handler-level warm-route test that verifies both previous-format keys are written.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please address the two cache correctness issues in the inline comments.

Comment thread internal/handler/container_manifest.go Outdated
Comment thread internal/handler/container_tags.go
@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Addressed both cache-correctness issues and added handler-level regression coverage.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please rewrite relative tag-list pagination Link targets for named registries. rewriteContainerTagsLink currently returns non-absolute targets unchanged, so a link such as </v2/owner/repo/tags/list?...> drops the upstream/<registry>/ selector and routes the next page to the default registry. Add a named-registry route test that follows the returned link.

@abhinavgautam01

Copy link
Copy Markdown
Contributor Author

Thanks, Fixed. Relative tag-list pagination links now retain the named-registry selector when rewritten through the proxy, with a route-level test that follows the returned link.

@andrew andrew left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for sticking with this — the relative-Link rewrite now keeps the named-registry selector and the follow-the-link test proves it end to end.

@andrew
andrew merged commit 1e3369c into git-pkgs:main Sep 2, 2026
5 checks passed
andrew added a commit that referenced this pull request Sep 2, 2026
goconst tripped on main after #259 landed on top of #280: five
composite-literal occurrences each of "Content-Length" and
"Content-Type" across container.go, container_manifest.go,
container_tags.go, handler.go, and swift.go crossed the
min-occurrences: 5 threshold. Neither PR hit it alone.

Add headerContentType and headerContentLength beside
headerAcceptEncoding and use them throughout the package rather than
only at the flagged sites, so the next handler that adds one does not
re-trip the check.
andrew added a commit that referenced this pull request Sep 2, 2026
Compute real manifest digests in #280's fixture upstreams so the new
verification accepts them, and add headerETag / headerLastModified to
#301's constant set so goconst passes with the new occurrences.
andrew added a commit that referenced this pull request Sep 3, 2026
Compute real manifest digests in #280's fixture upstreams so the new
verification accepts them, and add headerETag / headerLastModified to
andrew added a commit that referenced this pull request Sep 3, 2026
* Add Homebrew JSON API and bottle proxy support

* Fix Homebrew HEAD offline fallback and non-sha256 OCI manifest handling

Route Homebrew API HEAD requests through ProxyCached so a warm cache
answers without an upstream call and stale entries are served when the
upstream is unreachable. HEAD still reaches upstream as HEAD when
metadata caching is disabled.

Limit OCI manifest digest verification to sha256 references and
Docker-Content-Digest headers so other digest algorithms are proxied
instead of rejected, and log the failing expected value.

* Reconcile with #280 and #301 after rebase

Compute real manifest digests in #280's fixture upstreams so the new
verification accepts them, and add headerETag / headerLastModified to

* Send fixed Accept for Homebrew API and match If-None-Match properly

The Homebrew API cache key does not include Accept, so replaying the
client header could serve one representation under another; the API
does not negotiate anyway. Compare If-None-Match with weak comparison,
list splitting and "*" per RFC 7232 instead of string equality, and
apply the same helper to the metadata and swift responders.

* Reconcile with #298 and #304 after rebase

Move the configureScanning doc comment back to its function after the
auto-merge stacked it on mountProtocolHandlers, and drop the second
ETag/Last-Modified set in writeMetadataCachedResponse now that the
pre-304 set covers both response paths.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve OCI offline caching for tag lists and manifest variants

3 participants