211 lines
10 KiB
TypeScript
211 lines
10 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. plan module for scripts/src/hwlab-node-impl.ts.
|
|
|
|
// Moved mechanically from scripts/src/hwlab-node-impl.ts:440-567 for #903.
|
|
|
|
// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
|
|
// SPEC: PJ2026-01060508 Web哨兵 draft-2026-06-25-p0-web-probe-sentinel.
|
|
// Responsibility: YAML-first node/lane operations, including Workbench observability control commands.
|
|
import { createHash, randomBytes } from "node:crypto";
|
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
import { dirname, join } from "node:path";
|
|
import { repoRoot, rootPath, type Config } from "../config";
|
|
import { runCommand, type CommandResult } from "../command";
|
|
import { startJob } from "../jobs";
|
|
import { classifySshTcpPoolFailure } from "../ssh";
|
|
import { HWLAB_NODE_CONTROL_PLANE_CONFIG_PATH, hwlabNodeControlPlaneInfraHelp, runHwlabNodeControlPlaneInfra } from "../hwlab-node-control-plane";
|
|
import { hwlabRuntimeLaneConfigPath, hwlabRuntimeLaneIds, hwlabRuntimeLaneSpec, hwlabRuntimeLaneSpecForNode, hwlabRuntimeNodeIds, isHwlabRuntimeLane, type HwlabRuntimeLane, type HwlabRuntimeLaneSpec, type HwlabRuntimeObservabilityRecordingRuleSpec, type HwlabRuntimeObservabilitySpec, type HwlabRuntimeObservabilityWarningAlertSpec, type HwlabRuntimePublicExposureSpec, type HwlabRuntimeWebProbeAlertThresholdsSpec, type HwlabRuntimeWebProbeProjectManagementSpec } from "../hwlab-node-lanes";
|
|
import { nodeWebProbeScriptRunnerSource } from "../hwlab-node-web-probe-runner-source";
|
|
import { nodeWebObserveAnalyzerSource } from "../hwlab-node-web-observe-analyzer-source";
|
|
import { nodeWebObserveRunnerSource } from "../hwlab-node-web-observe-runner-source";
|
|
import { nodeWebObserveCollectViewNodeScript, parseNodeWebProbeObserveCollectView, type NodeWebProbeObserveCollectView } from "../hwlab-node-web-observe-collect";
|
|
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 { 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";
|
|
import { runDelegatedHwlabNodeCommand, type DelegatedNodeDomain } from "../hwlab-node-transport";
|
|
import type { RenderedCliResult } from "../output";
|
|
|
|
import { NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS } from "./entry";
|
|
import { publicExposureSummary } from "./public-exposure";
|
|
import { assertNodeId, positiveIntegerOption, requiredOption } from "./utils";
|
|
import { hwlabRuntimeActiveExternalPostgres } from "../hwlab-node-lanes";
|
|
|
|
export function parseNodeScopedDelegatedOptions(domain: DelegatedNodeDomain, args: string[]): {
|
|
domain: DelegatedNodeDomain;
|
|
action: string;
|
|
runtimeImageAction: string | null;
|
|
node: string;
|
|
lane: HwlabRuntimeLane;
|
|
confirm: boolean;
|
|
dryRun: boolean;
|
|
wait: boolean;
|
|
rerun: boolean;
|
|
allowLiveDbRead: boolean;
|
|
timeoutSeconds: number;
|
|
originalArgs: string[];
|
|
spec: HwlabRuntimeLaneSpec;
|
|
} {
|
|
const [actionRaw] = args;
|
|
if (typeof actionRaw !== "string" || actionRaw.startsWith("--")) throw new Error(`${domain} usage: ${domain} ACTION --node NODE --lane vNN [--dry-run|--confirm]`);
|
|
const runtimeImageAction = actionRaw === "runtime-image" && typeof args[1] === "string" && !args[1].startsWith("--") ? args[1] : null;
|
|
const node = requiredOption(args, "--node");
|
|
assertNodeId(node);
|
|
const laneRaw = requiredOption(args, "--lane");
|
|
if (!isHwlabRuntimeLane(laneRaw)) throw new Error(`--lane must be one of v02, v03; got ${laneRaw}`);
|
|
const spec = hwlabRuntimeLaneSpecForNode(laneRaw, node);
|
|
const confirm = args.includes("--confirm");
|
|
const dryRun = args.includes("--dry-run");
|
|
if (confirm && dryRun) throw new Error(`${domain} accepts only one of --confirm or --dry-run`);
|
|
return {
|
|
domain,
|
|
action: actionRaw,
|
|
runtimeImageAction,
|
|
node,
|
|
lane: laneRaw,
|
|
confirm,
|
|
dryRun,
|
|
wait: args.includes("--wait"),
|
|
rerun: args.includes("--rerun"),
|
|
allowLiveDbRead: args.includes("--allow-live-db-read"),
|
|
timeoutSeconds: positiveIntegerOption(args, "--timeout-seconds", NODE_RUNTIME_CICD_WAIT_WARNING_SECONDS, 3600),
|
|
originalArgs: [...args],
|
|
spec,
|
|
};
|
|
}
|
|
|
|
export function nodeRuntimeLocalPostgresExpectedAbsent(spec: HwlabRuntimeLaneSpec): boolean {
|
|
return spec.runtimeStore?.postgres?.mode === "platform-service";
|
|
}
|
|
|
|
export function nodeRuntimeExpected(spec: HwlabRuntimeLaneSpec): Record<string, unknown> {
|
|
const activeExternalPostgres = hwlabRuntimeActiveExternalPostgres(spec);
|
|
return {
|
|
configPath: hwlabRuntimeLaneConfigPath(),
|
|
node: spec.nodeId,
|
|
nodeRoute: spec.nodeRoute,
|
|
nodeKubeRoute: spec.nodeKubeRoute,
|
|
lane: spec.lane,
|
|
sourceBranch: spec.sourceBranch,
|
|
workspace: spec.workspace,
|
|
cicdRepo: spec.cicdRepo,
|
|
git: {
|
|
sourceUrl: spec.gitUrl,
|
|
readUrl: spec.gitReadUrl,
|
|
writeUrl: spec.gitWriteUrl,
|
|
},
|
|
argo: {
|
|
repoURL: spec.argoRepoUrl,
|
|
},
|
|
gitopsBranch: spec.gitopsBranch,
|
|
catalogPath: spec.catalogPath,
|
|
runtimePath: spec.runtimePath,
|
|
runtimeNamespace: spec.runtimeNamespace,
|
|
renderDir: spec.runtimeRenderDir,
|
|
pipeline: spec.pipeline,
|
|
pipelineRunPrefix: spec.pipelineRunPrefix,
|
|
serviceAccount: spec.serviceAccountName,
|
|
argoApplication: spec.app,
|
|
registryPrefix: spec.registryPrefix,
|
|
baseImage: {
|
|
image: spec.baseImage,
|
|
sourceImage: spec.baseImageSource ?? null,
|
|
},
|
|
serviceIds: spec.serviceIds,
|
|
buildkit: spec.buildkit === undefined ? null : {
|
|
sidecarImage: spec.buildkit.sidecarImage,
|
|
sourceImage: spec.buildkit.sourceImage,
|
|
},
|
|
dockerBuildProxy: {
|
|
http: spec.networkProfile.dockerBuildProxy.http,
|
|
https: spec.networkProfile.dockerBuildProxy.https,
|
|
all: spec.networkProfile.dockerBuildProxy.all,
|
|
noProxy: spec.networkProfile.dockerBuildProxy.noProxy,
|
|
},
|
|
stepEnv: spec.stepEnv,
|
|
public: {
|
|
webUrl: spec.publicWebUrl,
|
|
apiUrl: spec.publicApiUrl,
|
|
},
|
|
webProbe: spec.webProbe === undefined ? null : {
|
|
browserProxyMode: spec.webProbe.browserProxyMode ?? null,
|
|
defaultOrigin: spec.webProbe.defaultOrigin ?? null,
|
|
},
|
|
bootstrapAdmin: spec.bootstrapAdmin === undefined ? null : {
|
|
username: spec.bootstrapAdmin.username,
|
|
displayName: spec.bootstrapAdmin.displayName,
|
|
passwordSourceRef: spec.bootstrapAdmin.passwordSourceRef,
|
|
passwordSourceKey: spec.bootstrapAdmin.passwordSourceKey,
|
|
passwordHashTransform: spec.bootstrapAdmin.passwordHashTransform,
|
|
secretName: spec.bootstrapAdmin.secretName,
|
|
secretKey: spec.bootstrapAdmin.secretKey,
|
|
rolloutDeployment: spec.bootstrapAdmin.rolloutDeployment,
|
|
valuesPrinted: false,
|
|
},
|
|
codeAgentRuntime: spec.codeAgentRuntime === undefined ? null : {
|
|
enabled: spec.codeAgentRuntime.enabled,
|
|
adapter: spec.codeAgentRuntime.adapter,
|
|
managerUrl: spec.codeAgentRuntime.managerUrl,
|
|
apiKeySecretName: spec.codeAgentRuntime.apiKeySecretName,
|
|
apiKeySecretKey: spec.codeAgentRuntime.apiKeySecretKey,
|
|
runnerNamespace: spec.codeAgentRuntime.runnerNamespace,
|
|
secretNamespace: spec.codeAgentRuntime.secretNamespace,
|
|
repoUrlFrom: spec.codeAgentRuntime.repoUrlFrom,
|
|
repoUrl: spec.gitReadUrl,
|
|
providerIdFrom: spec.codeAgentRuntime.providerIdFrom,
|
|
providerId: spec.nodeId,
|
|
defaultProviderProfile: spec.codeAgentRuntime.defaultProviderProfile,
|
|
valuesPrinted: false,
|
|
},
|
|
sourceWorkspace: spec.sourceWorkspace === undefined ? null : {
|
|
requiredCommands: spec.sourceWorkspace.requiredCommands,
|
|
requiredFiles: spec.sourceWorkspace.requiredFiles,
|
|
install: spec.sourceWorkspace.install,
|
|
},
|
|
publicExposure: spec.publicExposure === null ? null : publicExposureSummary(spec.publicExposure),
|
|
runtimeStore: spec.runtimeStore ?? null,
|
|
downloadProfile: {
|
|
id: spec.downloadProfileId,
|
|
git: spec.downloadProfile.git,
|
|
npm: spec.downloadProfile.npm,
|
|
},
|
|
observability: spec.observability,
|
|
runtimeImageRewrites: spec.runtimeImageRewrites,
|
|
runtimeImageBuilds: spec.runtimeImageBuilds,
|
|
externalPostgres: spec.externalPostgres === undefined ? null : {
|
|
active: activeExternalPostgres !== undefined,
|
|
provider: spec.externalPostgres.provider,
|
|
configRef: spec.externalPostgres.configRef,
|
|
serviceName: spec.externalPostgres.serviceName,
|
|
endpointAddress: spec.externalPostgres.endpointAddress,
|
|
port: spec.externalPostgres.port,
|
|
runtimeAccess: spec.externalPostgres.runtimeAccess ?? null,
|
|
sslmode: spec.externalPostgres.sslmode,
|
|
database: spec.externalPostgres.database,
|
|
cloudApi: {
|
|
secretName: spec.externalPostgres.cloudApi.secretName,
|
|
secretKey: spec.externalPostgres.cloudApi.secretKey,
|
|
sourceRef: spec.externalPostgres.cloudApi.sourceRef,
|
|
envKey: spec.externalPostgres.cloudApi.envKey,
|
|
role: spec.externalPostgres.cloudApi.role,
|
|
},
|
|
openfga: {
|
|
secretName: spec.externalPostgres.openfga.secretName,
|
|
secretKey: spec.externalPostgres.openfga.secretKey,
|
|
sourceRef: spec.externalPostgres.openfga.sourceRef,
|
|
envKey: spec.externalPostgres.openfga.envKey,
|
|
authnKey: spec.externalPostgres.openfga.authnKey ?? null,
|
|
role: spec.externalPostgres.openfga.role,
|
|
schema: spec.externalPostgres.openfga.schema ?? null,
|
|
},
|
|
valuesPrinted: false,
|
|
},
|
|
localPostgres: {
|
|
shouldRender: spec.runtimeStore?.postgres?.mode !== "platform-service",
|
|
expectedAbsent: spec.runtimeStore?.postgres?.mode === "platform-service",
|
|
},
|
|
};
|
|
}
|