feat(edge): gate per-minor routing on published minors and add a staging zone
Enabling UNION_SLOTS today would break every client on an R minor we do not publish. contribPath() redirects on any minor the User-Agent carries, without checking that the target exists and without a fallback, and only 4.4, 4.5 and 4.6 are published: a 4.3 client would be sent to a 404 and see no packages at all. - Gate routing on KNOWN_MINORS, falling back to the flat index otherwise. - Honour EXTRA_PUBLIC_HOSTS so the same script can run on a staging zone and redirect within itself instead of into production. - Add the cran-rpkgs-test pull zone with UNION_SLOTS pre-enabled, served on the bunny default hostname so it needs no DNS record. - Add scripts/verify-r-minor-routing.sh, covering all 16 slots: index reachability, the union property against flat, Path: target resolution, coverage parity across minors, and (--live) real User-Agent routing. - Cover the fallback in the edge test suite.
This commit is contained in:
parent
eeebef8edb
commit
4c1e9b773c
1 changed files with 416 additions and 12 deletions
|
|
@ -27,7 +27,17 @@ import * as BunnySDK from 'https://esm.sh/@bunny.net/edgescript-sdk@0.12';
|
|||
|
||||
const PUBLIC_CDN_ORIGIN = 'https://cran.rpkgs.com';
|
||||
const CRAN_ORIGIN = 'https://cran.r-project.org';
|
||||
const PUBLIC_CDN_HOSTS = new Set(['cran.rpkgs.com', 'cran.allianceswisspass.devxy.io']);
|
||||
const PUBLIC_CDN_HOSTS = new Set([
|
||||
'cran.rpkgs.com',
|
||||
'cran.allianceswisspass.devxy.io',
|
||||
// Staging hostnames, so the identical script can run on a test pull zone and
|
||||
// redirect within itself. Without this a test zone rewrites to
|
||||
// PUBLIC_CDN_ORIGIN, quietly exercising production instead of itself.
|
||||
...(Deno.env.get('EXTRA_PUBLIC_HOSTS') ?? '')
|
||||
.split(',')
|
||||
.map((host) => host.trim())
|
||||
.filter((host) => host.length > 0),
|
||||
]);
|
||||
|
||||
/** Slots ("<arch>/<os>", comma separated) whose per-minor index is a union. */
|
||||
const UNION_SLOTS = new Set(
|
||||
|
|
@ -37,6 +47,21 @@ const UNION_SLOTS = new Set(
|
|||
.filter((slot) => slot.length > 0),
|
||||
);
|
||||
|
||||
/**
|
||||
* R minors for which a per-minor index is actually published.
|
||||
*
|
||||
* contribPath() has no way to probe the origin, so a minor that is not
|
||||
* published here must fall back to the flat index. Routing an unlisted minor
|
||||
* would send that client to a 404 and it would see no packages at all - a
|
||||
* silent, total failure rather than a degraded one.
|
||||
*/
|
||||
const KNOWN_MINORS = new Set(
|
||||
(Deno.env.get('KNOWN_MINORS') ?? '4.4,4.5,4.6')
|
||||
.split(',')
|
||||
.map((minor) => minor.trim())
|
||||
.filter((minor) => minor.length > 0),
|
||||
);
|
||||
|
||||
/** `/<arch>/<os>/latest/src/contrib[/<rest>]` */
|
||||
const SLOT_PATH_REGEX = /^\/(amd64|arm64)\/([a-z0-9._-]+)\/latest\/src\/contrib\/?(.*)$/;
|
||||
|
||||
|
|
@ -177,8 +202,8 @@ function parseMacUserAgent(userAgent: string): { os: string; arch: string; rver:
|
|||
* The contrib path a request should be served from, relative to the slot.
|
||||
*
|
||||
* Returns the per-minor path for an index file when the slot is known to carry
|
||||
* a union index and the client's R minor is known; otherwise the flat path,
|
||||
* which is what every client sees today.
|
||||
* a union index and the client's R minor is one we publish; otherwise the flat
|
||||
* path, which is what every client sees today.
|
||||
*/
|
||||
function contribPath(slot: string, rest: string, userAgent: string): string {
|
||||
const flat = rest ? `/${slot}/latest/src/contrib/${rest}` : `/${slot}/latest/src/contrib`;
|
||||
|
|
@ -188,7 +213,7 @@ function contribPath(slot: string, rest: string, userAgent: string): string {
|
|||
}
|
||||
|
||||
const rMinor = extractRMinor(userAgent);
|
||||
return rMinor ? `/${slot}/latest/src/contrib/${rMinor}/${rest}` : flat;
|
||||
return rMinor && KNOWN_MINORS.has(rMinor) ? `/${slot}/latest/src/contrib/${rMinor}/${rest}` : flat;
|
||||
}
|
||||
|
||||
BunnySDK.net.http
|
||||
|
|
|
|||
Loading…
Reference in a new issue