feat: wire web probe sentinel validation (#912)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -21,7 +21,7 @@ import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectVie
|
||||
import { withWebObserveCollectRendered, withWebObserveCommandRendered, withWebObserveStatusRendered } from "../hwlab-node-web-observe-render";
|
||||
import { buildWebObserveWrapperForObserveOptions, webObserveWrapperStateDirFromStatus } from "../hwlab-node-web-observe-wrapper";
|
||||
import { renderWebObserveWrapperContract } from "../hwlab-node-web-observe-wrapper-render";
|
||||
import { runWebProbeSentinelCommand, type WebProbeSentinelOptions } from "../hwlab-node-web-sentinel-cicd";
|
||||
import { runWebProbeSentinelCommand, type WebProbeSentinelOptions, type WebProbeSentinelReportView } from "../hwlab-node-web-sentinel-cicd";
|
||||
import { hwlabNodeHelp, hwlabNodeObservabilityHelp, hwlabNodeWebProbeHelp } from "../hwlab-node-help";
|
||||
import { compactWebProbeResult, compactWebProbeScriptResult } from "../hwlab-node-web-probe-summary";
|
||||
import { nodeObservabilityRecordingRuleExpression, nodeObservabilityRecordingRuleSummaries, nodeObservabilityWarningAlertExpression, nodeObservabilityWarningAlertSummaries } from "../hwlab-node-observability-promql";
|
||||
@@ -38,10 +38,29 @@ import { displayRepoPath, readBootstrapAdminPasswordMaterial, sleepSync } from "
|
||||
|
||||
export function parseNodeWebProbeSentinelOptions(args: string[]): NodeWebProbeSentinelOptions {
|
||||
const [sentinelActionRaw] = args;
|
||||
if (sentinelActionRaw !== "plan" && sentinelActionRaw !== "status" && sentinelActionRaw !== "image" && sentinelActionRaw !== "control-plane") {
|
||||
throw new Error("web-probe sentinel usage: sentinel plan|status|image|control-plane --node NODE --lane vNN [--dry-run|--confirm]");
|
||||
if (
|
||||
sentinelActionRaw !== "plan"
|
||||
&& sentinelActionRaw !== "status"
|
||||
&& sentinelActionRaw !== "image"
|
||||
&& sentinelActionRaw !== "control-plane"
|
||||
&& sentinelActionRaw !== "validate"
|
||||
&& sentinelActionRaw !== "maintenance"
|
||||
&& sentinelActionRaw !== "report"
|
||||
) {
|
||||
throw new Error("web-probe sentinel usage: sentinel plan|status|image|control-plane|validate|maintenance|report --node NODE --lane vNN [--dry-run|--confirm]");
|
||||
}
|
||||
assertKnownOptions(args, new Set(["--node", "--lane", "--timeout-seconds"]), new Set(["--dry-run", "--confirm", "--wait"]));
|
||||
assertKnownOptions(args, new Set([
|
||||
"--node",
|
||||
"--lane",
|
||||
"--timeout-seconds",
|
||||
"--release-id",
|
||||
"--reason",
|
||||
"--view",
|
||||
"--run",
|
||||
"--run-id",
|
||||
"--trace-id",
|
||||
"--sample-seq",
|
||||
]), new Set(["--dry-run", "--confirm", "--wait", "--quick-verify", "--raw"]));
|
||||
const node = requiredOption(args, "--node");
|
||||
assertNodeId(node);
|
||||
const lane = requiredOption(args, "--lane");
|
||||
@@ -58,12 +77,49 @@ export function parseNodeWebProbeSentinelOptions(args: string[]): NodeWebProbeSe
|
||||
const imageAction = args[1];
|
||||
if (imageAction !== "status" && imageAction !== "build") throw new Error("web-probe sentinel image usage: image status|build --node NODE --lane vNN [--dry-run|--confirm]");
|
||||
sentinel = { kind: "image", action: imageAction, node, lane, dryRun: imageAction === "build" ? dryRun || !confirm : dryRun, confirm, wait: args.includes("--wait"), timeoutSeconds };
|
||||
} else {
|
||||
} else if (sentinelActionRaw === "control-plane") {
|
||||
const controlPlaneAction = args[1];
|
||||
if (controlPlaneAction !== "plan" && controlPlaneAction !== "apply" && controlPlaneAction !== "status" && controlPlaneAction !== "trigger-current") {
|
||||
throw new Error("web-probe sentinel control-plane usage: control-plane plan|apply|status|trigger-current --node NODE --lane vNN [--dry-run|--confirm]");
|
||||
}
|
||||
sentinel = { kind: "control-plane", action: controlPlaneAction, node, lane, dryRun: controlPlaneAction === "apply" || controlPlaneAction === "trigger-current" ? dryRun || !confirm : dryRun, confirm, wait: args.includes("--wait"), timeoutSeconds };
|
||||
} else if (sentinelActionRaw === "maintenance") {
|
||||
const maintenanceAction = args[1];
|
||||
if (maintenanceAction !== "status" && maintenanceAction !== "start" && maintenanceAction !== "stop") {
|
||||
throw new Error("web-probe sentinel maintenance usage: maintenance status|start|stop --node NODE --lane vNN [--dry-run|--confirm]");
|
||||
}
|
||||
sentinel = {
|
||||
kind: "maintenance",
|
||||
action: maintenanceAction,
|
||||
node,
|
||||
lane,
|
||||
dryRun: maintenanceAction === "status" ? dryRun : dryRun || !confirm,
|
||||
confirm,
|
||||
wait: args.includes("--wait"),
|
||||
timeoutSeconds,
|
||||
releaseId: optionValue(args, "--release-id") ?? null,
|
||||
reason: optionValue(args, "--reason") ?? null,
|
||||
quickVerify: maintenanceAction === "stop" || args.includes("--quick-verify"),
|
||||
};
|
||||
} else if (sentinelActionRaw === "validate") {
|
||||
sentinel = { kind: "validate", action: "validate", node, lane, dryRun, confirm, wait: args.includes("--wait"), timeoutSeconds, quickVerify: args.includes("--quick-verify") };
|
||||
} else {
|
||||
const view = parseWebProbeSentinelReportView(optionValue(args, "--view") ?? "summary");
|
||||
const sampleSeqRaw = optionValue(args, "--sample-seq") ?? null;
|
||||
const sampleSeq = sampleSeqRaw === null ? null : Number(sampleSeqRaw);
|
||||
if (sampleSeq !== null && (!Number.isInteger(sampleSeq) || sampleSeq < 1)) throw new Error("web-probe sentinel report --sample-seq must be a positive integer");
|
||||
sentinel = {
|
||||
kind: "report",
|
||||
action: "report",
|
||||
node,
|
||||
lane,
|
||||
view,
|
||||
runId: optionValue(args, "--run") ?? optionValue(args, "--run-id") ?? null,
|
||||
traceId: optionValue(args, "--trace-id") ?? null,
|
||||
sampleSeq,
|
||||
raw: args.includes("--raw"),
|
||||
timeoutSeconds,
|
||||
};
|
||||
}
|
||||
return {
|
||||
action: "sentinel",
|
||||
@@ -73,6 +129,11 @@ export function parseNodeWebProbeSentinelOptions(args: string[]): NodeWebProbeSe
|
||||
};
|
||||
}
|
||||
|
||||
function parseWebProbeSentinelReportView(value: string): WebProbeSentinelReportView {
|
||||
if (value === "summary" || value === "turn-summary" || value === "findings" || value === "trace-frame") return value;
|
||||
throw new Error(`web-probe sentinel report --view must be summary, turn-summary, findings, or trace-frame; got ${value}`);
|
||||
}
|
||||
|
||||
export function normalizeNodeWebProbeObserveArgs(args: string[]): { args: string[]; id: string | null } {
|
||||
const [observeActionRaw, maybeId, ...rest] = args;
|
||||
if (observeActionRaw !== "start" && maybeId !== undefined && !maybeId.startsWith("--")) {
|
||||
|
||||
Reference in New Issue
Block a user