// 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, 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 { 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 `, }, }; } export async function apply(config: UniDeskConfig, options: ApplyOptions): Promise> { 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> { 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> { 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 : 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 }), }; }