feat(storage): adjust Cloud CDN middleware to S06 (S06 Step 19)

How to review (please read first)

This MR is split into three commits that squash to a single feat on merge. Review it commit-by-commit, not as the combined diff. The combined diff shows the upstream files fully deleted and the new package fully added — git can't detect renames across the package + dependency rewrite (<30% line similarity), which is exactly why it's split into a reviewable sequence.

  1. refactor(storage): relocate upstream googlecdn middleware to cloudcdn — a pure git mv (100% renames, zero content change). Confirm only paths moved.
  2. feat(storage): port googlecdn middleware to the AR Cloud CDN interface — the functional port, and the commit to spend your review on. The Container Registry StorageDriver (options map, GcsBucketKeyer, CustomParams, storagemiddleware.Register, metrics, clock) is swapped for AR's storage.StorageDriver decorator + a resolved Parameters struct; dcontext.GetRequest/CR log for a package-owned client-IP context carrier + slog; plus the contract-required additions (open-redirect guard, timeout/context-bounded fetch, idempotent stop). It is authored in place against the upstream file, so the upstream declaration order and identifier names are preserved — no reordering or renaming noise here.
  3. refactor(storage): apply AR naming, ordering, and lint — cosmetics only, skim for behavior changes (there are none): renames (eligibleForGCSeligibleForGCP, processAddressappendNetwork, mutexmu, updaterStopChstopCh), declaration reordering, goconst test constants, doc-comment rewrites, and removal of the non-ported README, integration test, and readKeyFile helper.

Notes:

  • The intermediate commits (1 and 2) intentionally do not compile or pass lint in isolation — they exist only to make the review legible and are squashed away. Only the final state is green (CI runs on the squash result).
  • One deliberate exception to the 2/3 boundary: sentinel errors stay in commit 2, because reverting them to inline fmt.Errorf cascades into the test ErrorIs assertions.

What

Promotes the Cloud CDN storage middleware out of the _imported/ Container Registry quarantine into a first-class internal/storage/middleware/cloudcdn/ package and adapts it to S06 (plan Step 19):

  • Wraps a GCS-backed storage.StorageDriver (decorator pattern); URLFor returns a Cloud CDN URL-prefix signed URL, all other methods pass through.
  • Consumes a resolved Parameters struct (NewCloudCDNMiddleware); the signing key arrives as raw bytes resolved by the composition root via the LabKit v2 secret provider.
  • IP-filtering modes "none" / "gcp" with the background GCP IP-range refresh loop (default source: gstatic goog.json).
  • Open-redirect guard on the signed URL (scheme/host allowlist) per the StorageDriver.URLFor contract.
  • Removes the now-promoted googlecdn source from _imported/, mirroring the GCS driver promotion (Step 17, !552 (merged)).

The Cloud CDN URL-prefix signing wire format (base64url URLPrefix, HMAC-SHA1, base64url Signature) is preserved verbatim and pinned by known-answer test vectors.

Reliability hardening (from branch review)

  • The GCP IP-range fetch is bounded by a timeout and a per-fetch context (http.NewRequestWithContext). Without it, a hung gstatic endpoint could stall the synchronous startup fetch indefinitely, and a blocked refresh fetch would wedge the updater goroutine outside its select so it could never observe the stop signal.
  • The IP-filter subsystem now logs through the caller-injected slog logger rather than the process default.
  • Removed orphaned readKeyFile (the middleware takes pre-resolved key bytes).
  • Added direct tests for normalizeBaseURL, cdnObjectKey, both URLFor error wraps, and eligibleForGCP fail-open paths (package coverage 92.9%).

Unpinned surfaces resolved here

  • A package-owned context key carries the client IP (no AR-wide request-IP helper exists yet).
  • CDN object-key derivation from the request path (AR's narrowed StorageDriver has no GcsBucketKeyer).

The cdn_redirects_total metric (OBS-CDN) is deferred to the S06 metrics-framework follow-up (#56 (closed)). This package is not yet wired into the composition root; integration is a later step.

Testing

  • go build ./..., go vet, golangci-lint, goimports: clean.
  • go test -race -short ./internal/storage/middleware/cloudcdn/...: PASS.

Spec: S06 (Cloud CDN middleware) · Plan: storage-layer (Step 19)

Related to #164 (closed)

Edited by Pawel Rozlach

Merge request reports

Loading
Loading