From efadc396d7337be7381662e358903d1fc21bf36f Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 12 Jun 2026 16:17:34 +0000 Subject: [PATCH] fix: improve langbot rollout diagnostics --- config/platform-infra/langbot.yaml | 4 +- scripts/src/platform-infra-langbot.ts | 80 ++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/config/platform-infra/langbot.yaml b/config/platform-infra/langbot.yaml index e7a532d1..1de3ed23 100644 --- a/config/platform-infra/langbot.yaml +++ b/config/platform-infra/langbot.yaml @@ -13,7 +13,7 @@ image: pullPolicy: IfNotPresent dependencyImages: - postgresClient: docker.m.daocloud.io/library/postgres:16-alpine + postgresClient: docker.io/library/postgres:16-alpine targets: - id: G14 @@ -32,7 +32,7 @@ targets: deploymentName: langbot-frpc secretName: langbot-frpc-secrets secretKey: frpc.toml - image: 127.0.0.1:5000/hwlab/frpc:v0.68.1 + image: fatedier/frpc:v0.68.1 serverAddr: 82.156.23.220 serverPort: 22000 proxyName: platform-infra-langbot-g14-web diff --git a/scripts/src/platform-infra-langbot.ts b/scripts/src/platform-infra-langbot.ts index 18d7d7ff..c0c930b1 100644 --- a/scripts/src/platform-infra-langbot.ts +++ b/scripts/src/platform-infra-langbot.ts @@ -1273,6 +1273,7 @@ capture networkpolicy kubectl -n ${target.namespace} get networkpolicy allow-all capture ingress kubectl -n ${target.namespace} get ingress capture quota kubectl -n ${target.namespace} get resourcequota capture limitrange kubectl -n ${target.namespace} get limitrange +capture events kubectl -n ${target.namespace} get events --sort-by=.lastTimestamp python3 - "$tmp" <<'PY' import json, os, sys tmp = sys.argv[1] @@ -1290,6 +1291,7 @@ deployments = load("deployments") or {"items": []} pods = load("pods") or {"items": []} services = load("services") or {"items": []} pvc = load("pvc") or {"items": []} +events = load("events") or {"items": []} def deployment_summary(name): for item in deployments.get("items", []): if item.get("metadata", {}).get("name") == name: @@ -1314,6 +1316,66 @@ deployment_ready = ( and plugin_deploy.get("readyReplicas", 0) >= 1 and frpc_deploy.get("readyReplicas", 0) >= 1 ) +watched_names = {"${serviceName}", "${pluginRuntimeServiceName}", "${target.publicExposure.frpc.deploymentName}"} +watched_pod_names = { + item.get("metadata", {}).get("name") + for item in pods.get("items", []) + if item.get("metadata", {}).get("labels", {}).get("app.kubernetes.io/name") in watched_names +} +watched_pvc_names = { + item.get("metadata", {}).get("name") + for item in pvc.get("items", []) + if item.get("metadata", {}).get("labels", {}).get("app.kubernetes.io/part-of") == "platform-infra" + and str(item.get("metadata", {}).get("name", "")).startswith("langbot-") +} +def compact_message(value, limit=500): + if not value: + return None + value = str(value).replace("\\n", " ") + return value if len(value) <= limit else value[:limit] + "...(truncated)" +def state_summary(status): + state = status.get("state") or {} + if not state: + return {"type": "unknown"} + state_type = next(iter(state.keys())) + detail = state.get(state_type) or {} + return { + "type": state_type, + "reason": detail.get("reason"), + "message": compact_message(detail.get("message")), + } +def condition_summary(condition): + return { + "type": condition.get("type"), + "status": condition.get("status"), + "reason": condition.get("reason"), + "message": compact_message(condition.get("message")), + } +def event_summary(event): + involved = event.get("involvedObject", {}) + return { + "type": event.get("type"), + "reason": event.get("reason"), + "message": compact_message(event.get("message"), 700), + "count": event.get("count"), + "lastTimestamp": event.get("lastTimestamp") or event.get("eventTime"), + "involvedObject": { + "kind": involved.get("kind"), + "name": involved.get("name"), + }, + } +relevant_events = [] +for event in events.get("items", []): + involved = event.get("involvedObject", {}) + kind = involved.get("kind") + name = involved.get("name") + if ( + name in watched_pod_names + or name in watched_pvc_names + or name in watched_names + or (kind == "ReplicaSet" and any(str(name or "").startswith(prefix + "-") for prefix in watched_names)) + ): + relevant_events.append(event_summary(event)) payload = { "ok": rc("ns") == 0 and rc("deployments") == 0 and rc("pods") == 0 and deployment_ready, "namespace": "${target.namespace}", @@ -1334,18 +1396,34 @@ payload = { "name": item.get("metadata", {}).get("name"), "phase": item.get("status", {}).get("phase"), "ready": sum(1 for condition in item.get("status", {}).get("conditions", []) if condition.get("type") == "Ready" and condition.get("status") == "True"), + "conditions": [ + condition_summary(condition) + for condition in item.get("status", {}).get("conditions", []) + if condition.get("status") != "True" or condition.get("type") in ("Ready", "ContainersReady", "PodScheduled") + ], + "initContainers": [ + { + "name": c.get("name"), + "ready": c.get("ready"), + "restartCount": c.get("restartCount"), + "state": state_summary(c), + } + for c in item.get("status", {}).get("initContainerStatuses", []) + ], "containers": [ { "name": c.get("name"), "ready": c.get("ready"), "restartCount": c.get("restartCount"), - "state": list((c.get("state") or {}).keys()), + "state": state_summary(c), } for c in item.get("status", {}).get("containerStatuses", []) ], } for item in pods.get("items", []) + if item.get("metadata", {}).get("name") in watched_pod_names ], + "events": relevant_events[-20:], "services": [item.get("metadata", {}).get("name") for item in services.get("items", [])], "pvcs": [item.get("metadata", {}).get("name") for item in pvc.get("items", [])], "secrets": {