Files
pikasTech-unidesk/scripts/src/platform-infra-observability/trace-script.ts
T
2026-06-25 16:16:25 +00:00

321 lines
9.7 KiB
TypeScript

// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. trace-script module for scripts/src/platform-infra-observability.ts.
// Moved mechanically from scripts/src/platform-infra-observability.ts:1575-1813 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 { ObservabilityConfig, ObservabilityTarget } from "./types";
import { tempoService } from "./search-script";
import { imageReference, indent } from "./summary";
export function asArray(value: unknown): unknown[] {
return Array.isArray(value) ? value : [];
}
export function limitArray(value: unknown, limit: number): unknown[] {
return asArray(value).slice(0, limit);
}
export function renderManifest(observability: ObservabilityConfig, target: ObservabilityTarget): string {
const collectorImage = imageReference(observability.images.collector);
const tempoImage = imageReference(observability.images.tempo);
return [
target.createNamespace ? namespaceManifest(target) : "",
allowAllNetworkPolicy(target),
collectorConfigMap(observability, target),
collectorDeployment(observability, target, collectorImage),
collectorService(observability, target),
tempoConfigMap(observability, target),
tempoDeployment(observability, target, tempoImage),
tempoService(observability, target),
].filter((item) => item.trim().length > 0).join("\n---\n");
}
export function namespaceManifest(target: ObservabilityTarget): string {
return `apiVersion: v1
kind: Namespace
metadata:
name: ${target.namespace}
labels:
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
unidesk.ai/runtime-node: ${target.id}
`;
}
export function allowAllNetworkPolicy(target: ObservabilityTarget): string {
return `apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
namespace: ${target.namespace}
labels:
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
ingress:
- {}
egress:
- {}
`;
}
export function collectorConfigMap(observability: ObservabilityConfig, target: ObservabilityTarget): string {
const config = `receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:${observability.collector.otlp.grpcPort}
http:
endpoint: 0.0.0.0:${observability.collector.otlp.httpPort}
processors:
batch: {}
exporters:
otlp/tempo:
endpoint: ${observability.traceBackend.serviceName}.${target.namespace}.svc.cluster.local:${observability.traceBackend.otlp.grpcPort}
tls:
insecure: true
extensions:
health_check:
endpoint: 0.0.0.0:${observability.collector.healthPort}
service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [otlp/tempo]
`;
return `apiVersion: v1
kind: ConfigMap
metadata:
name: ${observability.collector.configMapName}
namespace: ${target.namespace}
labels:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
annotations:
unidesk.ai/spec: "${observability.metadata.spec}"
data:
collector.yaml: |
${indent(config, 4)}
`;
}
export function collectorDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
return `apiVersion: apps/v1
kind: Deployment
metadata:
name: ${observability.collector.deploymentName}
namespace: ${target.namespace}
labels:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
spec:
replicas: ${observability.collector.replicas}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
template:
metadata:
labels:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
app.kubernetes.io/part-of: platform-infra
annotations:
unidesk.ai/spec: "${observability.metadata.spec}"
spec:
containers:
- name: collector
image: ${image}
imagePullPolicy: ${observability.images.collector.pullPolicy}
args:
- --config=/etc/otelcol/collector.yaml
ports:
- name: otlp-grpc
containerPort: ${observability.collector.otlp.grpcPort}
- name: otlp-http
containerPort: ${observability.collector.otlp.httpPort}
- name: health
containerPort: ${observability.collector.healthPort}
readinessProbe:
httpGet:
path: /
port: health
volumeMounts:
- name: config
mountPath: /etc/otelcol/collector.yaml
subPath: collector.yaml
readOnly: true
volumes:
- name: config
configMap:
name: ${observability.collector.configMapName}
`;
}
export function collectorService(observability: ObservabilityConfig, target: ObservabilityTarget): string {
return `apiVersion: v1
kind: Service
metadata:
name: ${observability.collector.serviceName}
namespace: ${target.namespace}
labels:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: ${observability.collector.deploymentName}
app.kubernetes.io/component: tracing
ports:
- name: otlp-grpc
port: ${observability.collector.otlp.grpcPort}
targetPort: otlp-grpc
- name: otlp-http
port: ${observability.collector.otlp.httpPort}
targetPort: otlp-http
- name: health
port: ${observability.collector.healthPort}
targetPort: health
`;
}
export function tempoConfigMap(observability: ObservabilityConfig, _target: ObservabilityTarget): string {
const config = `server:
http_listen_port: ${observability.traceBackend.httpPort}
distributor:
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:${observability.traceBackend.otlp.grpcPort}
http:
endpoint: 0.0.0.0:${observability.traceBackend.otlp.httpPort}
ingester:
trace_idle_period: 10s
max_block_duration: 5m
compactor:
compaction:
block_retention: ${observability.traceBackend.storage.retention}
storage:
trace:
backend: local
wal:
path: /var/tempo/wal
local:
path: /var/tempo/traces
`;
return `apiVersion: v1
kind: ConfigMap
metadata:
name: ${observability.traceBackend.configMapName}
namespace: ${_target.namespace}
labels:
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
app.kubernetes.io/component: trace-backend
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
annotations:
unidesk.ai/spec: "${observability.metadata.spec}"
data:
tempo.yaml: |
${indent(config, 4)}
`;
}
export function tempoDeployment(observability: ObservabilityConfig, target: ObservabilityTarget, image: string): string {
return `apiVersion: apps/v1
kind: Deployment
metadata:
name: ${observability.traceBackend.deploymentName}
namespace: ${target.namespace}
labels:
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
app.kubernetes.io/component: trace-backend
app.kubernetes.io/part-of: platform-infra
app.kubernetes.io/managed-by: unidesk
spec:
replicas: ${observability.traceBackend.replicas}
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
selector:
matchLabels:
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
app.kubernetes.io/component: trace-backend
template:
metadata:
labels:
app.kubernetes.io/name: ${observability.traceBackend.deploymentName}
app.kubernetes.io/component: trace-backend
app.kubernetes.io/part-of: platform-infra
annotations:
unidesk.ai/spec: "${observability.metadata.spec}"
spec:
containers:
- name: tempo
image: ${image}
imagePullPolicy: ${observability.images.tempo.pullPolicy}
args:
- -config.file=/etc/tempo/tempo.yaml
ports:
- name: http
containerPort: ${observability.traceBackend.httpPort}
- name: otlp-grpc
containerPort: ${observability.traceBackend.otlp.grpcPort}
- name: otlp-http
containerPort: ${observability.traceBackend.otlp.httpPort}
readinessProbe:
httpGet:
path: ${observability.probes.readinessPath}
port: http
volumeMounts:
- name: config
mountPath: /etc/tempo/tempo.yaml
subPath: tempo.yaml
readOnly: true
- name: data
mountPath: /var/tempo
volumes:
- name: config
configMap:
name: ${observability.traceBackend.configMapName}
- name: data
emptyDir: {}
`;
}