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.
refactor(storage): relocate upstream googlecdn middleware to cloudcdn— a puregit mv(100% renames, zero content change). Confirm only paths moved.feat(storage): port googlecdn middleware to the AR Cloud CDN interface— the functional port, and the commit to spend your review on. The Container RegistryStorageDriver(options map,GcsBucketKeyer,CustomParams,storagemiddleware.Register, metrics, clock) is swapped for AR'sstorage.StorageDriverdecorator + a resolvedParametersstruct;dcontext.GetRequest/CRlogfor 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.refactor(storage): apply AR naming, ordering, and lint— cosmetics only, skim for behavior changes (there are none): renames (eligibleForGCS→eligibleForGCP,processAddress→appendNetwork,mutex→mu,updaterStopCh→stopCh), declaration reordering, goconst test constants, doc-comment rewrites, and removal of the non-portedREADME, integration test, andreadKeyFilehelper.
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.Errorfcascades into the testErrorIsassertions.
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);URLForreturns a Cloud CDN URL-prefix signed URL, all other methods pass through. - Consumes a resolved
Parametersstruct (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: gstaticgoog.json). - Open-redirect guard on the signed URL (scheme/host allowlist) per the
StorageDriver.URLForcontract. - Removes the now-promoted
googlecdnsource 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 itsselectso it could never observe the stop signal. - The IP-filter subsystem now logs through the caller-injected
sloglogger rather than the process default. - Removed orphaned
readKeyFile(the middleware takes pre-resolved key bytes). - Added direct tests for
normalizeBaseURL,cdnObjectKey, bothURLForerror wraps, andeligibleForGCPfail-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
StorageDriverhas noGcsBucketKeyer).
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)