Add an offline mode: serve only the cache, contact no upstream

Part of #54 (closed) (milestone 1). Snap install/refresh while offline is the follow-up.

-offline (and GET/PUT /v1/offline on the admin socket, to flip it at run time) makes pkgproxy really offline:

  • Nothing goes upstream. Every side fetches through cmd.upstreamTransport() and CONNECT dials through proxy.Handler.Dial; both refuse with offline.ErrOffline before any DNS lookup or connection while the switch is on.
  • Whatever is cached is served, however old. Every side's ttl() is frozen, and apt's Cache-Control: max-age=0 no longer forces a revalidation. Served as hits, not as stale-after-a-failed-revalidation, so no warning per request.
  • What the cache cannot answer fails fast. offline.Middleware turns the 502 of a failed upstream fetch into a 503 with Retry-After and "pkgproxy is offline".

Commits: the internal/offline package (+ httpheader constants), the wiring and the harness, the admin route. Design: new §21.

Test harness (internal/gateway/offline_test.go): every side (apt, snap, go, registry, lxd) behind the real gateway with its own fake upstream. It warms the caches online, advances a fake clock by a day, goes offline and asserts byte-identical answers and zero upstream requests. http.DefaultTransport is replaced by one that fails the test on any use, so a call site that bypasses the switch is caught. A control run shows the same requests do reach upstream a day later while online (so the test really tests the freezing), and another test pins the 503s for misses (including CONNECT). I checked that disabling the TTL freeze, or the transport refusal, makes them fail.

Known limit, by design for now: snap calls that are relayed uncached (POST /v2/snaps/refresh, find) answer 503, so snap install / refresh do not work offline even for a cached snap. That is the follow-up in #54 (closed).

pkgproxyctl offline [status|on|off|mode <mode>] is the command-line front end to the admin routes. Not done here: an offline indicator in the stats API / setup page.

🤖 Generated with Claude Code

Edited by Zygmunt Krynicki

Merge request reports

Loading
Loading