fix(asset_scanner): kubectl 改 subprocess — K8sProvider 不支援 --all-namespaces
All checks were successful
CD Pipeline / build-and-deploy (push) Successful in 9m9s

5b9b36f 部署後 asset_scanner 跑 3 次但 total=0, new=0:
  - asset_inventory 仍 0 筆
  - Pod 手動 kubectl get pods --all-namespaces -o json 可取 JSON
  - 真因: K8sProvider._kubectl_get 把 namespace 參數塞進 '-n $ns',
    所以 '--all-namespaces' 變成 '-n --all-namespaces' (kubectl 拒絕)

修復:
  - 不走 K8sProvider,直接 asyncio.create_subprocess_exec
  - kubectl get pods --all-namespaces -o json
  - 30s timeout,rc != 0 拋 RuntimeError 觸發 aol status='failed'

驗證: 部署後 asset_inventory 應在 1 分鐘內開始有 pods 寫入

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
OG T
2026-04-19 16:31:26 +08:00
parent 4259a104f5
commit 02263445c2

View File

@@ -169,34 +169,30 @@ async def scan_once(
async def _collect_k8s_assets() -> list[dict[str, Any]]:
"""
用 K8sProvider 列出全 namespace 的 pods,轉成 asset_inventory 結構。
直接 subprocess 執行 kubectl get pods --all-namespaces.
2026-04-19 ogt + Claude Opus 4.7 v2: 不走 K8sProvider._kubectl_get,
因為它把 namespace 參數當 -n 旗標,無法處理 '--all-namespaces'.
直接 subprocess 最可靠 + Pod 內 /usr/local/bin/kubectl 已確認可用.
回傳每筆: {asset_key, asset_type, host, namespace, name, metadata, tags}
"""
from src.plugins.mcp.providers.k8s_provider import K8sProvider
provider = K8sProvider()
result = await asyncio.wait_for(
provider.execute(
tool_name="kubectl_get",
parameters={"namespace": "--all-namespaces", "resource": "pods"},
proc = await asyncio.wait_for(
asyncio.create_subprocess_exec(
"kubectl", "get", "pods", "--all-namespaces", "-o", "json",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE,
),
timeout=_KUBECTL_TIMEOUT_SEC,
)
if not result.success:
raise RuntimeError(f"kubectl get pods failed: {result.error}")
stdout, stderr = await asyncio.wait_for(proc.communicate(), timeout=_KUBECTL_TIMEOUT_SEC)
if proc.returncode != 0:
raise RuntimeError(f"kubectl failed rc={proc.returncode}: {stderr.decode('utf-8', errors='replace')[:500]}")
raw = result.output
# k8s_provider _kubectl_get 回傳 stdout 字串 (line 299)
if isinstance(raw, str):
try:
payload = _json.loads(raw)
except _json.JSONDecodeError as e:
raise RuntimeError(f"kubectl JSON parse failed: {e}") from e
elif isinstance(raw, dict):
payload = raw
else:
raise RuntimeError(f"unexpected kubectl output type: {type(raw)}")
try:
payload = _json.loads(stdout.decode("utf-8", errors="replace"))
except _json.JSONDecodeError as e:
raise RuntimeError(f"kubectl JSON parse failed: {e}") from e
items = payload.get("items", []) if isinstance(payload, dict) else []
assets: list[dict[str, Any]] = []