edfddd2445
* docs: specify cicd yaml target governance * fix: resolve cicd targets from yaml --------- Co-authored-by: Codex <codex@noreply.local>
164 lines
8.3 KiB
TypeScript
164 lines
8.3 KiB
TypeScript
// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. actions module for scripts/src/platform-infra-observability.ts.
|
|
// SPEC: PJ2026-01060308 cicd-yaml-targets draft-2026-06-25-cicd-yaml-targets.
|
|
|
|
// Moved mechanically from scripts/src/platform-infra-observability.ts:515-623 for #903.
|
|
|
|
// SPEC: PJ2026-01060501 OTel追踪 draft-2026-06-19-p0.
|
|
// Responsibility: YAML-first platform-infra OpenTelemetry tracing control commands.
|
|
import { Buffer } from "node:buffer";
|
|
import { readFileSync } from "node:fs";
|
|
import type { UniDeskConfig } from "../config";
|
|
import { rootPath } from "../config";
|
|
import type { RenderedCliResult } from "../output";
|
|
import {
|
|
compactCapture,
|
|
createYamlFieldReader,
|
|
numberField,
|
|
parseJsonOutput,
|
|
redactSensitiveUnknown,
|
|
shQuote,
|
|
capture,
|
|
} from "../platform-infra-ops-library";
|
|
|
|
import type { ApplyOptions, CommonOptions, ObservabilityConfig, ObservabilityTarget, StatusEndpoint } from "./types";
|
|
import { readObservabilityConfig } from "./config";
|
|
import { applyScript, statusScript } from "./search-script";
|
|
import { compactStatus, configSummary, manifestObjectSummary, policyChecks, statusSummary, targetSummary } from "./summary";
|
|
import { renderManifest } from "./trace-script";
|
|
import { apiPathField, configLabel, kubernetesNameField, stringField } from "./types";
|
|
|
|
export function parseStatusEndpoint(record: Record<string, unknown>, index: number): StatusEndpoint {
|
|
const path = `probes.statusEndpoints[${index}]`;
|
|
return {
|
|
name: stringField(record, "name", path),
|
|
service: kubernetesNameField(record, "service", path),
|
|
portName: stringField(record, "portName", path),
|
|
path: apiPathField(record, "path", path),
|
|
};
|
|
}
|
|
|
|
export function assertKnownEnabledTarget(targets: ObservabilityTarget[], targetId: string, path: string): void {
|
|
const target = targets.find((item) => item.id.toLowerCase() === targetId.toLowerCase());
|
|
if (target === undefined) throw new Error(`${configLabel}.${path} references unknown target ${targetId}; known targets: ${targets.map((item) => item.id).join(", ")}`);
|
|
if (!target.enabled) throw new Error(`${configLabel}.${path} references disabled target ${target.id}`);
|
|
}
|
|
|
|
export function resolveTarget(observability: ObservabilityConfig, targetId: string | null): ObservabilityTarget {
|
|
const resolved = targetId ?? observability.defaults.targetId;
|
|
const target = observability.targets.find((item) => item.id.toLowerCase() === resolved.toLowerCase());
|
|
if (target === undefined) throw new Error(`unknown observability target ${resolved}; known targets: ${observability.targets.map((item) => item.id).join(", ")}`);
|
|
if (!target.enabled) throw new Error(`observability target ${target.id} is disabled in ${configLabel}`);
|
|
return target;
|
|
}
|
|
|
|
export function plan(options: CommonOptions): Record<string, unknown> {
|
|
const observability = readObservabilityConfig();
|
|
const target = resolveTarget(observability, options.targetId);
|
|
const yaml = renderManifest(observability, target);
|
|
const policy = policyChecks(yaml, target);
|
|
return {
|
|
ok: policy.every((check) => check.ok),
|
|
action: "platform-infra-observability-plan",
|
|
mutation: false,
|
|
config: configSummary(observability, target),
|
|
renderPlan: {
|
|
target: targetSummary(target),
|
|
objects: manifestObjectSummary(yaml),
|
|
otlp: {
|
|
collectorGrpcEndpoint: `${observability.collector.serviceName}.${target.namespace}.svc.cluster.local:${observability.collector.otlp.grpcPort}`,
|
|
collectorHttpEndpoint: `http://${observability.collector.serviceName}.${target.namespace}.svc.cluster.local:${observability.collector.otlp.httpPort}`,
|
|
backendGrpcEndpoint: `${observability.traceBackend.serviceName}.${target.namespace}.svc.cluster.local:${observability.traceBackend.otlp.grpcPort}`,
|
|
},
|
|
instrumentation: observability.instrumentation.serviceConnections,
|
|
resourceAttributes: observability.resourceAttributes,
|
|
},
|
|
policy,
|
|
next: {
|
|
dryRun: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --dry-run`,
|
|
apply: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --confirm`,
|
|
status: `bun scripts/cli.ts platform-infra observability status --target ${target.id}`,
|
|
validate: `bun scripts/cli.ts platform-infra observability validate --target ${target.id}`,
|
|
trace: `bun scripts/cli.ts platform-infra observability trace --target ${target.id} --trace-id <traceId>`,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function apply(config: UniDeskConfig, options: ApplyOptions): Promise<Record<string, unknown>> {
|
|
const observability = readObservabilityConfig();
|
|
const target = resolveTarget(observability, options.targetId);
|
|
const yaml = renderManifest(observability, target);
|
|
const policy = policyChecks(yaml, target);
|
|
if (!policy.every((check) => check.ok)) return { ok: false, action: "platform-infra-observability-apply", mode: "policy-blocked", policy };
|
|
const result = await capture(config, target.route, ["sh"], applyScript({
|
|
yaml,
|
|
target,
|
|
dryRun: options.dryRun,
|
|
wait: options.wait,
|
|
collectorDeploymentName: observability.collector.deploymentName,
|
|
backendDeploymentName: observability.traceBackend.deploymentName,
|
|
}));
|
|
const parsed = parseJsonOutput(result.stdout);
|
|
return {
|
|
ok: result.exitCode === 0 && parsed?.ok === true,
|
|
action: "platform-infra-observability-apply",
|
|
mode: options.dryRun ? "dry-run" : "confirmed",
|
|
mutation: !options.dryRun,
|
|
target: targetSummary(target),
|
|
policy,
|
|
remote: parsed ?? compactCapture(result, { full: true }),
|
|
};
|
|
}
|
|
|
|
export async function status(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
|
|
const observability = readObservabilityConfig();
|
|
const target = resolveTarget(observability, options.targetId);
|
|
const result = await capture(config, target.route, ["sh"], statusScript(observability, target, options.full));
|
|
const parsed = parseJsonOutput(result.stdout);
|
|
const summary = parsed === null ? null : statusSummary(parsed);
|
|
return {
|
|
ok: result.exitCode === 0 && summary?.ready === true,
|
|
action: "platform-infra-observability-status",
|
|
mutation: false,
|
|
target: targetSummary(target),
|
|
config: configSummary(observability, target),
|
|
summary,
|
|
remote: options.raw ? parsed : compactStatus(parsed, options.full) ?? compactCapture(result, { full: true }),
|
|
next: {
|
|
plan: `bun scripts/cli.ts platform-infra observability plan --target ${target.id}`,
|
|
apply: `bun scripts/cli.ts platform-infra observability apply --target ${target.id} --confirm`,
|
|
validate: `bun scripts/cli.ts platform-infra observability validate --target ${target.id}`,
|
|
},
|
|
};
|
|
}
|
|
|
|
export async function validate(config: UniDeskConfig, options: CommonOptions): Promise<Record<string, unknown>> {
|
|
const observability = readObservabilityConfig();
|
|
const target = resolveTarget(observability, options.targetId);
|
|
const result = await capture(config, target.route, ["sh"], statusScript(observability, target, options.full, true));
|
|
const parsed = parseJsonOutput(result.stdout);
|
|
const summary = parsed === null ? null : statusSummary(parsed);
|
|
const ready = summary?.ready === true;
|
|
const validationTrace = parsed !== null && typeof parsed.validationTrace === "object" && parsed.validationTrace !== null && !Array.isArray(parsed.validationTrace)
|
|
? parsed.validationTrace as Record<string, unknown>
|
|
: null;
|
|
const validationTraceId = typeof validationTrace?.traceId === "string" ? validationTrace.traceId : null;
|
|
const validationTraceOk = validationTrace?.ok === true;
|
|
return {
|
|
ok: result.exitCode === 0 && ready && validationTraceOk,
|
|
action: "platform-infra-observability-validate",
|
|
mutation: false,
|
|
target: targetSummary(target),
|
|
config: configSummary(observability, target),
|
|
summary,
|
|
validation: {
|
|
readiness: ready ? "passed" : "failed",
|
|
testTrace: validationTrace ?? "not-generated-runtime-not-ready",
|
|
traceQuery: validationTraceId !== null
|
|
? `bun scripts/cli.ts platform-infra observability trace --target ${target.id} --trace-id ${validationTraceId}`
|
|
: "blocked-until-validation-trace-generated",
|
|
metricsBoundary: "Prometheus/RUM remains outside this trace readiness check.",
|
|
},
|
|
remote: options.raw ? parsed : compactStatus(parsed, options.full) ?? compactCapture(result, { full: true }),
|
|
};
|
|
}
|