// SPEC: PJ2026-01060307 控制面模块化 draft-2026-06-25-p0. status-script module for scripts/src/platform-infra.ts. // Moved mechanically from scripts/src/platform-infra.ts:3091-3538 for #903. import { createHash, randomBytes } from "node:crypto"; import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"; import { dirname, isAbsolute, join } from "node:path"; import type { UniDeskConfig } from "../config"; import { rootPath } from "../config"; import { startJob } from "../jobs"; import type { RenderedCliResult } from "../output"; import { pk01CaddyMergeManagedBlocksPython, renderCaddyManagedBlock, renderSimpleReverseProxyCaddySiteBlock } from "../pk01-caddy"; import { capture, compactCapture, parseJsonOutput, prepareFrpcSecret, shQuote } from "../platform-infra-public-service"; import { yamlBooleanField, yamlFieldLabel, yamlIntegerField } from "../platform-infra-ops-library"; import { fingerprintSecretValues, parseEnvFile, readEnvSourceFile, readTextFile, redactRepoPath, requiredEnvValue } from "../secrets"; import type { Sub2ApiConfig, Sub2ApiTargetConfig } from "./entry"; import { requiredSecretKeys, serviceName } from "./entry"; import { codexPoolSentinelResourceNames, imageRef, managedResourceCleanupPlan, sub2ApiProxyEnv } from "./manifest"; export function statusScript(sub2api: Sub2ApiConfig, target: Sub2ApiTargetConfig): string { const expectedImage = imageRef(sub2api, target); const expectedUrlAllowlist = sub2api.security.urlAllowlist; const externalPending = target.databaseMode === "external-pending"; const externalActive = target.databaseMode === "external-active"; const proxy = target.egressProxy?.enabled ? target.egressProxy : null; const expectedProxyEnv = sub2ApiProxyEnv(target); const cleanupPlan = managedResourceCleanupPlan(sub2api, target); const appSecretName = sub2api.runtime.database.secretName; const redisService = sub2api.runtime.redis.serviceName; return ` set -u tmp="$(mktemp -d)" trap 'rm -rf "$tmp"' EXIT capture_json() { name="$1" shift "$@" -o json >"$tmp/$name.json" 2>"$tmp/$name.err" rc=$? printf '%s' "$rc" >"$tmp/$name.rc" } capture_json ns kubectl get namespace ${target.namespace} capture_json deployments kubectl -n ${target.namespace} get deployments -l app.kubernetes.io/part-of=platform-infra capture_json statefulsets kubectl -n ${target.namespace} get statefulsets -l app.kubernetes.io/part-of=platform-infra capture_json pods kubectl -n ${target.namespace} get pods -l app.kubernetes.io/part-of=platform-infra capture_json services kubectl -n ${target.namespace} get services -l app.kubernetes.io/part-of=platform-infra capture_json pvc kubectl -n ${target.namespace} get pvc -l app.kubernetes.io/part-of=platform-infra capture_json secrets kubectl -n ${target.namespace} get secret ${appSecretName} capture_json configmap kubectl -n ${target.namespace} get configmap sub2api-config capture_json networkpolicies kubectl -n ${target.namespace} get networkpolicy capture_json cronjobs kubectl -n ${target.namespace} get cronjob -l app.kubernetes.io/part-of=platform-infra capture_json ingresses kubectl -n ${target.namespace} get ingress capture_json quotas kubectl -n ${target.namespace} get resourcequota capture_json limitranges kubectl -n ${target.namespace} get limitrange pod_name="$(kubectl -n ${target.namespace} get pod -l app.kubernetes.io/name=${serviceName},app.kubernetes.io/component=app -o jsonpath='{.items[0].metadata.name}' 2>"$tmp/pod-name.err" || true)" printf '%s' "$pod_name" >"$tmp/pod-name.txt" if [ -n "$pod_name" ]; then kubectl -n ${target.namespace} exec "$pod_name" -- sh -c 'printf "SECURITY_URL_ALLOWLIST_ENABLED=%s\\n" "$SECURITY_URL_ALLOWLIST_ENABLED"; printf "SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP=%s\\n" "$SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP"; printf "SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS=%s\\n" "$SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS"; printf "SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS=%s\\n" "$SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS"; printf "HTTP_PROXY=%s\\n" "$HTTP_PROXY"; printf "HTTPS_PROXY=%s\\n" "$HTTPS_PROXY"; printf "ALL_PROXY=%s\\n" "$ALL_PROXY"; printf "NO_PROXY=%s\\n" "$NO_PROXY"' >"$tmp/pod-env.out" 2>"$tmp/pod-env.err" printf '%s' "$?" >"$tmp/pod-env.rc" else : >"$tmp/pod-env.out" printf '%s\\n' 'sub2api app pod not found' >"$tmp/pod-env.err" printf '%s' "1" >"$tmp/pod-env.rc" fi python3 - "$tmp" <<'PY' import json import os import sys tmp = sys.argv[1] def rc(name): try: return int(open(os.path.join(tmp, f"{name}.rc"), encoding="utf-8").read() or "1") except FileNotFoundError: return 1 def load(name): path = os.path.join(tmp, f"{name}.json") if not os.path.exists(path): return None try: return json.load(open(path, encoding="utf-8")) except json.JSONDecodeError: return None def items(name): data = load(name) if not isinstance(data, dict): return [] return data.get("items") or [] def text(name): path = os.path.join(tmp, name) try: return open(path, encoding="utf-8", errors="replace").read() except FileNotFoundError: return "" def deployment_summary(item): spec = item.get("spec") or {} status = item.get("status") or {} desired = spec.get("replicas", 1) available = status.get("availableReplicas", 0) init_containers = ((spec.get("template") or {}).get("spec") or {}).get("initContainers", []) containers = ((spec.get("template") or {}).get("spec") or {}).get("containers", []) return { "name": item["metadata"]["name"], "desired": desired, "readyReplicas": status.get("readyReplicas", 0), "availableReplicas": available, "updatedReplicas": status.get("updatedReplicas", 0), "ready": available >= desired, "images": [c.get("image") for c in containers], "initImages": [c.get("image") for c in init_containers], } def statefulset_summary(item): spec = item.get("spec") or {} status = item.get("status") or {} desired = spec.get("replicas", 1) ready = status.get("readyReplicas", 0) return { "name": item["metadata"]["name"], "desired": desired, "readyReplicas": ready, "currentReplicas": status.get("currentReplicas", 0), "updatedReplicas": status.get("updatedReplicas", 0), "ready": ready >= desired, "images": [c.get("image") for c in ((spec.get("template") or {}).get("spec") or {}).get("containers", [])], } def pod_summary(item): status = item.get("status") or {} container_statuses = status.get("containerStatuses") or [] return { "name": item["metadata"]["name"], "phase": status.get("phase"), "ready": all((cs.get("ready") is True) for cs in container_statuses) if container_statuses else False, "restarts": sum(int(cs.get("restartCount") or 0) for cs in container_statuses), "nodeName": (item.get("spec") or {}).get("nodeName"), "containers": [ { "name": cs.get("name"), "ready": cs.get("ready"), "restartCount": cs.get("restartCount"), "image": cs.get("image"), "state": list((cs.get("state") or {}).keys()), "stateDetail": cs.get("state") or {}, } for cs in container_statuses ], "initContainers": [ { "name": cs.get("name"), "ready": cs.get("ready"), "restartCount": cs.get("restartCount"), "image": cs.get("image"), "state": list((cs.get("state") or {}).keys()), "stateDetail": cs.get("state") or {}, } for cs in (status.get("initContainerStatuses") or []) ], "conditions": [ { "type": condition.get("type"), "status": condition.get("status"), "reason": condition.get("reason"), "message": condition.get("message"), } for condition in status.get("conditions", []) ], } def service_summary(item): spec = item.get("spec") or {} return { "name": item["metadata"]["name"], "type": spec.get("type", "ClusterIP"), "clusterIP": spec.get("clusterIP"), "ports": [ { "name": p.get("name"), "port": p.get("port"), "targetPort": p.get("targetPort"), "nodePort": p.get("nodePort"), } for p in spec.get("ports", []) ], } def pvc_summary(item): spec = item.get("spec") or {} status = item.get("status") or {} req = (spec.get("resources") or {}).get("requests") or {} return { "name": item["metadata"]["name"], "phase": status.get("phase"), "storageClassName": spec.get("storageClassName"), "requestedStorage": req.get("storage"), } def network_policy_summary(item): spec = item.get("spec") or {} return { "name": item["metadata"]["name"], "podSelector": spec.get("podSelector"), "policyTypes": spec.get("policyTypes") or [], "ingress": spec.get("ingress") or [], "egress": spec.get("egress") or [], } def is_allow_all_network_policy(item): spec = item.get("spec") or {} return ( item.get("metadata", {}).get("name") == "allow-all" and spec.get("podSelector") == {} and set(spec.get("policyTypes") or []) == {"Ingress", "Egress"} and spec.get("ingress") == [{}] and spec.get("egress") == [{}] ) def resource_findings(kind, collection): findings = [] for item in collection: spec = item.get("spec") or {} template_spec = ((spec.get("template") or {}).get("spec") or {}) if template_spec.get("hostNetwork") is True: findings.append({"kind": kind, "name": item["metadata"]["name"], "field": "hostNetwork"}) all_containers = [(container, "containers") for container in template_spec.get("containers", [])] + [(container, "initContainers") for container in template_spec.get("initContainers", [])] for container, container_group in all_containers: resources = container.get("resources") or {} if resources.get("requests"): findings.append({"kind": kind, "name": item["metadata"]["name"], "container": container.get("name"), "containerGroup": container_group, "field": "resources.requests"}) if resources.get("limits"): findings.append({"kind": kind, "name": item["metadata"]["name"], "container": container.get("name"), "containerGroup": container_group, "field": "resources.limits"}) for port in container.get("ports", []): if "hostPort" in port: findings.append({"kind": kind, "name": item["metadata"]["name"], "container": container.get("name"), "containerGroup": container_group, "field": "hostPort", "value": port.get("hostPort")}) return findings deployments = items("deployments") statefulsets = items("statefulsets") services = items("services") pods = items("pods") pvcs = items("pvc") networkpolicies = items("networkpolicies") cronjobs = items("cronjobs") secret = load("secrets") configmap = load("configmap") configmap_data = (configmap or {}).get("data") or {} secret_keys = sorted(((secret or {}).get("data") or {}).keys()) missing_secret_keys = [key for key in ${JSON.stringify(requiredSecretKeys)} if key not in secret_keys] external_pending = ${externalPending ? "True" : "False"} external_active = ${externalActive ? "True" : "False"} expected_app_replicas = ${target.appReplicas} expected_redis_replicas = ${target.redisReplicas} cleanup_plan = json.loads(${JSON.stringify(JSON.stringify(cleanupPlan))}) external_db_state = cleanup_plan["externalDbState"] redis_persistent_state = cleanup_plan["redisPersistentState"] public_exposure_state = cleanup_plan["publicExposure"] egress_proxy_state = cleanup_plan["egressProxy"] service_violations = [] for svc in services: spec = svc.get("spec") or {} if spec.get("type", "ClusterIP") != "ClusterIP": service_violations.append({"name": svc["metadata"]["name"], "type": spec.get("type")}) for port in spec.get("ports", []): if "nodePort" in port: service_violations.append({"name": svc["metadata"]["name"], "nodePort": port.get("nodePort")}) resource_violations = resource_findings("Deployment", deployments) + resource_findings("StatefulSet", statefulsets) expected_image = "${expectedImage}" expected_url_allowlist = json.loads(${JSON.stringify(JSON.stringify(expectedUrlAllowlist))}) sub2api_deployment = next((deployment_summary(item) for item in deployments if item["metadata"]["name"] == "${serviceName}"), None) redis_deployment = next((deployment_summary(item) for item in deployments if item["metadata"]["name"] == "${redisService}"), None) sub2api_desired_aligned = sub2api_deployment is not None and sub2api_deployment.get("desired") == expected_app_replicas redis_desired_aligned = redis_deployment is not None and redis_deployment.get("desired") == expected_redis_replicas image_aligned = sub2api_deployment is not None and expected_image in sub2api_deployment.get("images", []) url_allowlist_runtime = { "enabled": configmap_data.get("SECURITY_URL_ALLOWLIST_ENABLED"), "allowInsecureHttp": configmap_data.get("SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP"), "allowPrivateHosts": configmap_data.get("SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS"), "upstreamHosts": configmap_data.get("SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS"), } pod_env = {} for line in text("pod-env.out").splitlines(): if "=" not in line: continue key, value = line.split("=", 1) pod_env[key] = value url_allowlist_pod_env = { "enabled": pod_env.get("SECURITY_URL_ALLOWLIST_ENABLED"), "allowInsecureHttp": pod_env.get("SECURITY_URL_ALLOWLIST_ALLOW_INSECURE_HTTP"), "allowPrivateHosts": pod_env.get("SECURITY_URL_ALLOWLIST_ALLOW_PRIVATE_HOSTS"), "upstreamHosts": pod_env.get("SECURITY_URL_ALLOWLIST_UPSTREAM_HOSTS"), } expected_url_allowlist_strings = { "enabled": str(expected_url_allowlist.get("enabled")).lower(), "allowInsecureHttp": str(expected_url_allowlist.get("allowInsecureHttp")).lower(), "allowPrivateHosts": str(expected_url_allowlist.get("allowPrivateHosts")).lower(), "upstreamHosts": ",".join(expected_url_allowlist.get("upstreamHosts") or []), } expected_proxy_env = json.loads(${JSON.stringify(JSON.stringify({ enabled: proxy !== null && proxy.applyToSub2Api, httpProxy: expectedProxyEnv.httpProxy, noProxy: expectedProxyEnv.noProxy, deploymentName: proxy?.deploymentName ?? null, serviceName: proxy?.serviceName ?? null, }))}) url_allowlist_configmap_aligned = url_allowlist_runtime == expected_url_allowlist_strings url_allowlist_pod_env_aligned = rc("pod-env") == 0 and url_allowlist_pod_env == expected_url_allowlist_strings url_allowlist_aligned = url_allowlist_configmap_aligned and (url_allowlist_pod_env_aligned or (external_pending and expected_app_replicas == 0)) proxy_env_runtime = { "HTTP_PROXY": pod_env.get("HTTP_PROXY"), "HTTPS_PROXY": pod_env.get("HTTPS_PROXY"), "ALL_PROXY": pod_env.get("ALL_PROXY"), "NO_PROXY": pod_env.get("NO_PROXY"), } if expected_proxy_env.get("enabled"): proxy_env_aligned = ( rc("pod-env") == 0 and proxy_env_runtime.get("HTTP_PROXY") == expected_proxy_env.get("httpProxy") and proxy_env_runtime.get("HTTPS_PROXY") == expected_proxy_env.get("httpProxy") and proxy_env_runtime.get("ALL_PROXY") == expected_proxy_env.get("httpProxy") and proxy_env_runtime.get("NO_PROXY") == expected_proxy_env.get("noProxy") ) else: proxy_env_aligned = True allow_all_network_policy = next((item for item in networkpolicies if item.get("metadata", {}).get("name") == "allow-all"), None) network_policy = { "requiredName": "allow-all", "exists": allow_all_network_policy is not None, "ok": allow_all_network_policy is not None and is_allow_all_network_policy(allow_all_network_policy), "policies": [network_policy_summary(item) for item in networkpolicies], } boundary = { "internalOnly": len(service_violations) == 0 and len(items("ingresses")) == 0, "serviceViolations": service_violations, "ingressCount": len(items("ingresses")), "resourceQuotaCount": len(items("quotas")), "limitRangeCount": len(items("limitranges")), "resourceViolations": resource_violations, } deployment_summaries = [deployment_summary(item) for item in deployments] statefulset_summaries = [statefulset_summary(item) for item in statefulsets] workload_ready = all(d["ready"] for d in deployment_summaries) and all(s["ready"] for s in statefulset_summaries) local_postgres_present = ( any(item.get("metadata", {}).get("name") == external_db_state["postgresStatefulSetName"] for item in statefulsets) or any(item.get("metadata", {}).get("name") == external_db_state["postgresServiceName"] for item in services) or any(item.get("metadata", {}).get("name") in [external_db_state["postgresPvcName"], external_db_state["appDataPvcName"]] for item in pvcs) ) redis_pvc_present = any(item.get("metadata", {}).get("name") == redis_persistent_state["pvcName"] for item in pvcs) public_exposure_deployment_present = any(item.get("metadata", {}).get("name") == public_exposure_state["deploymentName"] for item in deployments) egress_proxy_deployment_present = any(item.get("metadata", {}).get("name") == egress_proxy_state["deploymentName"] for item in deployments) sentinel_cronjob_present = any(item.get("metadata", {}).get("name") == "${codexPoolSentinelResourceNames().cronJobName}" for item in cronjobs) standby_disabled_resources_ok = not external_pending or ( not public_exposure_deployment_present and not egress_proxy_deployment_present and not sentinel_cronjob_present ) secret_ready = len(missing_secret_keys) == 0 secret_ok = secret_ready or external_pending if external_pending: state_model_ok = ( not local_postgres_present and not redis_pvc_present and sub2api_desired_aligned and redis_desired_aligned and expected_app_replicas == 0 and expected_redis_replicas == 0 and standby_disabled_resources_ok ) elif external_active: state_model_ok = ( not local_postgres_present and not redis_pvc_present and sub2api_desired_aligned and redis_desired_aligned and expected_app_replicas == 1 and expected_redis_replicas == 1 ) else: state_model_ok = local_postgres_present and sub2api_desired_aligned and redis_desired_aligned status_label = "pending-external-db" if external_pending else "external-db-active" if external_active else "active" payload = { "ok": rc("ns") == 0 and workload_ready and image_aligned and url_allowlist_aligned and proxy_env_aligned and network_policy["ok"] and boundary["internalOnly"] and len(resource_violations) == 0 and boundary["resourceQuotaCount"] == 0 and boundary["limitRangeCount"] == 0 and secret_ok and state_model_ok, "target": "${target.id}", "route": "${target.route}", "namespace": "${target.namespace}", "status": status_label, "databaseMode": "${target.databaseMode}", "redisMode": "${target.redisMode}", "expectedAppReplicas": expected_app_replicas, "expectedRedisReplicas": expected_redis_replicas, "namespaceExists": rc("ns") == 0, "deployments": deployment_summaries, "statefulsets": statefulset_summaries, "pods": [pod_summary(item) for item in pods], "services": [service_summary(item) for item in services], "pvcs": [pvc_summary(item) for item in pvcs], "networkPolicy": network_policy, "secret": { "name": "${appSecretName}", "exists": rc("secrets") == 0, "requiredKeys": ${JSON.stringify(requiredSecretKeys)}, "missingKeys": missing_secret_keys, "ready": secret_ready, "requiredForPredeployOk": not external_pending, "valuesPrinted": False, }, "stateModel": { "localPostgresPresent": local_postgres_present, "redisPvcPresent": redis_pvc_present, "sub2apiDesiredReplicasAligned": sub2api_desired_aligned, "redisDesiredReplicasAligned": redis_desired_aligned, "standbyDisabledResourcesOk": standby_disabled_resources_ok, "publicExposureDeploymentPresent": public_exposure_deployment_present, "egressProxyDeploymentPresent": egress_proxy_deployment_present, "sentinelCronJobPresent": sentinel_cronjob_present, "ok": state_model_ok, }, "imageControl": { "desiredImage": expected_image, "configPath": "config/platform-infra/sub2api.yaml", "aligned": image_aligned, "runningImages": sub2api_deployment.get("images", []) if sub2api_deployment else [], }, "security": { "urlAllowlist": { "configPath": "config/platform-infra/sub2api.yaml", "expected": expected_url_allowlist, "runtime": url_allowlist_runtime, "podEnv": url_allowlist_pod_env, "aligned": url_allowlist_aligned, "configMapAligned": url_allowlist_configmap_aligned, "podEnvAligned": url_allowlist_pod_env_aligned, "podEnvRequired": not (external_pending and expected_app_replicas == 0), "configMapExists": rc("configmap") == 0, "podEnvProbe": { "podName": text("pod-name.txt"), "exitCode": rc("pod-env"), "stderr": text("pod-env.err")[-1000:], }, } }, "egressProxy": { "enabled": expected_proxy_env.get("enabled"), "deploymentName": expected_proxy_env.get("deploymentName"), "serviceName": expected_proxy_env.get("serviceName"), "expectedHttpProxy": expected_proxy_env.get("httpProxy"), "expectedNoProxy": expected_proxy_env.get("noProxy"), "podEnv": proxy_env_runtime, "aligned": proxy_env_aligned, "valuesPrinted": False, }, "boundary": boundary, "serviceDns": "${serviceName}.${target.namespace}.svc.cluster.local:8080", "next": { "apply": "bun scripts/cli.ts platform-infra sub2api apply --target ${target.id} --confirm", "validate": "bun scripts/cli.ts platform-infra sub2api validate --target ${target.id}", }, } print(json.dumps(payload, ensure_ascii=False, indent=2)) sys.exit(0 if payload["ok"] else 1) PY `; }