feat(relay): agent describe 를 PodDescribeDTO 스키마로 확장(로컬 앱 파드 상세)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-29 15:34:01 +09:00
parent 208393b185
commit 761666aade

View File

@ -290,15 +290,51 @@ def _h_kube(action: str, params: dict) -> dict:
if action == "describe":
pod = params["pod"]
p = json.loads(_kubectl(["get", "pod", pod, "-o", "json"]) or "{}")
meta = p.get("metadata", {}) or {}
spec = p.get("spec", {}) or {}
st = p.get("status", {}) or {}
containers = [{"name": c.get("name", ""), "image": c.get("image", ""),
"ready": bool(c.get("ready")), "restarts": int(c.get("restartCount", 0)),
"state": next(iter((c.get("state", {}) or {}).keys()), "")}
for c in (st.get("containerStatuses", []) or [])]
conds = [{"type": c.get("type", ""), "status": c.get("status", "")} for c in (st.get("conditions", []) or [])]
return {"name": pod, "phase": st.get("phase", ""), "node": (p.get("spec", {}) or {}).get("nodeName", ""),
"ip": st.get("podIP", ""), "startedAt": st.get("startTime", ""),
"containers": containers, "conditions": conds}
cs = st.get("containerStatuses", []) or []
spec_by_name = {c.get("name"): c for c in (spec.get("containers", []) or [])}
restarts = sum(int(c.get("restartCount", 0)) for c in cs)
containers = []
for c in cs:
sc = spec_by_name.get(c.get("name"), {}) or {}
res = sc.get("resources", {}) or {}
req, lim = res.get("requests", {}) or {}, res.get("limits", {}) or {}
state = c.get("state", {}) or {}
skey = next(iter(state.keys()), "")
containers.append({
"name": c.get("name", ""), "image": c.get("image"), "image_id": c.get("imageID"),
"ready": bool(c.get("ready")), "restarts": int(c.get("restartCount", 0)),
"state": skey, "state_detail": (state.get(skey, {}) or {}).get("reason"),
"ports": [f"{pt.get('containerPort')}/{pt.get('protocol', 'TCP')}" for pt in (sc.get("ports", []) or [])],
"cpu_request": req.get("cpu"), "mem_request": req.get("memory"),
"cpu_limit": lim.get("cpu"), "mem_limit": lim.get("memory"),
"liveness": "설정됨" if sc.get("livenessProbe") else None,
"readiness": "설정됨" if sc.get("readinessProbe") else None,
"env_from": [], "env": [], # 민감할 수 있어 생략(MVP)
"mounts": [{"path": m.get("mountPath", ""), "name": m.get("name", ""), "ro": bool(m.get("readOnly"))}
for m in (sc.get("volumeMounts", []) or [])],
})
owner = (meta.get("ownerReferences") or [{}])[0]
return {
"summary": {
"name": meta.get("name"), "namespace": meta.get("namespace"), "node": spec.get("nodeName"),
"phase": st.get("phase"), "pod_ip": st.get("podIP"), "host_ip": st.get("hostIP"),
"qos_class": st.get("qosClass"), "service_account": spec.get("serviceAccountName"),
"priority": spec.get("priority"),
"controlled_by": f"{owner.get('kind')}/{owner.get('name')}" if owner.get("kind") else None,
"start_time": st.get("startTime"), "restarts": restarts,
},
"labels": meta.get("labels", {}) or {}, "annotations": {},
"containers": containers,
"conditions": [{"type": c.get("type"), "status": c.get("status"), "reason": c.get("reason"),
"last_transition": c.get("lastTransitionTime")} for c in (st.get("conditions", []) or [])],
"node_selector": spec.get("nodeSelector", {}) or {}, "tolerations": [],
"volumes": [{"name": v.get("name", ""), "type": next(iter([k for k in v.keys() if k != "name"]), "")}
for v in (spec.get("volumes", []) or [])],
"events": [],
}
if action == "workloads":
items = json.loads(_kubectl(["get", "deploy", "-o", "json"]) or "{}").get("items", [])
wls = []