fix: switch monitor root via sentinel yaml
This commit is contained in:
@@ -164,6 +164,14 @@ export interface HwlabRuntimeWebProbeSentinelRegistryItemSpec {
|
||||
readonly configRef: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbeMonitorRootSpec {
|
||||
readonly enabled: boolean;
|
||||
readonly sentinelId: string;
|
||||
readonly publicBaseUrl: string;
|
||||
readonly routePrefix: "/";
|
||||
readonly caddyManagedBlockOwner: string;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeWebProbeAlertThresholdsSpec {
|
||||
readonly sameOriginApiSlowMs: number;
|
||||
readonly partialApiSlowMs: number;
|
||||
@@ -206,6 +214,7 @@ export interface HwlabRuntimeObservabilitySpec {
|
||||
export interface HwlabRuntimeObservabilityWebProbeSpec {
|
||||
readonly sentinel?: HwlabRuntimeWebProbeSentinelSpec;
|
||||
readonly sentinels?: readonly HwlabRuntimeWebProbeSentinelRegistryItemSpec[];
|
||||
readonly monitorRoot?: HwlabRuntimeWebProbeMonitorRootSpec;
|
||||
}
|
||||
|
||||
export interface HwlabRuntimeObservabilityMetricsEndpointSpec {
|
||||
@@ -1141,14 +1150,56 @@ function observabilityConfig(value: unknown, path: string): HwlabRuntimeObservab
|
||||
function observabilityWebProbeConfig(value: unknown, path: string): HwlabRuntimeObservabilityWebProbeSpec | undefined {
|
||||
if (value === undefined) return undefined;
|
||||
const raw = asRecord(value, path);
|
||||
const allowed = new Set(["sentinel", "sentinels"]);
|
||||
const allowed = new Set(["sentinel", "sentinels", "monitorRoot"]);
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel/sentinels`);
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; observability.webProbe currently only owns sentinel/sentinels/monitorRoot`);
|
||||
}
|
||||
if (raw.sentinel !== undefined && raw.sentinels !== undefined) throw new Error(`${path} may declare sentinel or sentinels, not both`);
|
||||
const sentinel = raw.sentinel === undefined ? undefined : webProbeSentinelConfig(raw.sentinel, `${path}.sentinel`);
|
||||
const sentinels = raw.sentinels === undefined ? undefined : webProbeSentinelRegistryConfig(raw.sentinels, `${path}.sentinels`);
|
||||
const monitorRoot = raw.monitorRoot === undefined ? undefined : webProbeMonitorRootConfig(raw.monitorRoot, `${path}.monitorRoot`);
|
||||
if (monitorRoot !== undefined) {
|
||||
if (sentinels !== undefined && !sentinels.some((item) => item.id === monitorRoot.sentinelId)) {
|
||||
throw new Error(`${path}.monitorRoot.sentinelId must reference one entry from ${path}.sentinels`);
|
||||
}
|
||||
if (sentinel !== undefined && monitorRoot.sentinelId !== "workbench-dsflash-go-tool-call-10x") {
|
||||
throw new Error(`${path}.monitorRoot.sentinelId must be workbench-dsflash-go-tool-call-10x for legacy sentinel config`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
...(raw.sentinel === undefined ? {} : { sentinel: webProbeSentinelConfig(raw.sentinel, `${path}.sentinel`) }),
|
||||
...(raw.sentinels === undefined ? {} : { sentinels: webProbeSentinelRegistryConfig(raw.sentinels, `${path}.sentinels`) }),
|
||||
...(sentinel === undefined ? {} : { sentinel }),
|
||||
...(sentinels === undefined ? {} : { sentinels }),
|
||||
...(monitorRoot === undefined ? {} : { monitorRoot }),
|
||||
};
|
||||
}
|
||||
|
||||
function webProbeMonitorRootConfig(value: unknown, path: string): HwlabRuntimeWebProbeMonitorRootSpec {
|
||||
const raw = asRecord(value, path);
|
||||
const allowed = new Set(["enabled", "sentinelId", "publicBaseUrl", "routePrefix", "caddyManagedBlockOwner"]);
|
||||
for (const key of Object.keys(raw)) {
|
||||
if (!allowed.has(key)) throw new Error(`${path}.${key} is not allowed; monitorRoot may only contain enabled/sentinelId/publicBaseUrl/routePrefix/caddyManagedBlockOwner`);
|
||||
}
|
||||
const sentinelId = stringField(raw, "sentinelId", path);
|
||||
if (!/^[a-z0-9][a-z0-9-]{1,80}$/u.test(sentinelId)) throw new Error(`${path}.sentinelId must be a stable lowercase sentinel id`);
|
||||
const publicBaseUrl = stringField(raw, "publicBaseUrl", path).replace(/\/+$/u, "");
|
||||
try {
|
||||
const parsed = new URL(publicBaseUrl);
|
||||
if (parsed.protocol !== "https:") throw new Error("must use https");
|
||||
if (parsed.pathname !== "/" || parsed.search.length > 0 || parsed.hash.length > 0) throw new Error("must point to the public origin root");
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(`${path}.publicBaseUrl must be an https origin root URL: ${message}`);
|
||||
}
|
||||
const routePrefix = stringField(raw, "routePrefix", path);
|
||||
if (routePrefix !== "/") throw new Error(`${path}.routePrefix must be / for the monitor root switch`);
|
||||
const caddyManagedBlockOwner = stringField(raw, "caddyManagedBlockOwner", path);
|
||||
if (!/^[a-z0-9][a-z0-9-]{1,100}$/u.test(caddyManagedBlockOwner)) throw new Error(`${path}.caddyManagedBlockOwner must be a stable lowercase owner id`);
|
||||
return {
|
||||
enabled: booleanField(raw, "enabled", path),
|
||||
sentinelId,
|
||||
publicBaseUrl,
|
||||
routePrefix,
|
||||
caddyManagedBlockOwner,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user