A remote's base_url may now be a single string OR a list of upstream
mirrors. When it is a list the shared proxy engine load-balances across
them round-robin and, on an upstream error/timeout/5xx, fails over to the
next mirror before returning an error. Because the selection happens in
the engine (not per provider), it applies to every remote package type.
Backward compatible: a bare-string base_url behaves exactly as before.
- add models.StringOrSlice (string-or-array JSON) and custom Remote
(Un)MarshalJSON: base_url populates BaseURLs (full list) + BaseURL
(active/first); marshals a single mirror back to a bare string
- add Remote.BaseURLList / ValidateBaseURLs; validate list is non-empty
and every entry is an http/https URL in the v2 create/update handlers
- persist the full list in a new base_urls TEXT[] column (additive
migration), keeping base_url in sync for old readers; only write
base_urls for genuinely multi-mirror remotes
- engine: per-remote round-robin cursor + attempt ordering; wrap the
fetch/head/revalidate upstream calls in a failover loop that narrows
the remote to one selected mirror per attempt; only network errors and
5xx fail over (404/403/... are returned as-is); circuit breaker stays
keyed per remote and trips only after all mirrors fail
- add unit tests (JSON round-trip, engine round-robin/failover/single-URL,
DB multi-URL round-trip) and a docker acceptance suite: round-robin
distribution across two mock upstreams, failover past a dead primary,
single-base_url regression, and a real dnf makecache+install through a
two-mirror rpm remote whose primary is dead
Least-connections and a per-remote strategy selector are a follow-up PR.
Fixes#72
## Why
`compilePatterns` silently discards any pattern that fails to compile. A typo in a blocklist entry therefore turns a deny rule into a no-op — a fail-open with security impact.
## Changes
- Add `Remote.ValidatePatterns`, which compiles every pattern list (patterns, blocklist, mutable/immutable patterns, ban_tags) and returns an error on the first invalid regex.
- Reject invalid patterns with 400 at remote create and update time.
- Unit test for valid and invalid patterns.
## Validation
- `go test ./pkg/models/` and `make e2e` pass.
Reviewed-on: #87
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>