feat(api): Phase 13.2 #80 Kubernetes MCP Tool real implementation
- Integrate real ActionExecutor instead of mock responses - kubectl_get: Execute real kubectl get with JSON output - kubectl_delete: Dry-run validation + actual pod deletion - kubectl_scale: Real kubectl scale command - kubectl_restart: Deployment rollout restart with validation - Database query placeholder for #81 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -305,6 +305,19 @@ class MCPBridge:
|
||||
},
|
||||
server_name=server.name,
|
||||
),
|
||||
MCPTool(
|
||||
name="kubectl_restart",
|
||||
description="Restart Kubernetes deployment (rollout restart)",
|
||||
input_schema={
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"deployment": {"type": "string"},
|
||||
"namespace": {"type": "string"},
|
||||
},
|
||||
"required": ["deployment"],
|
||||
},
|
||||
server_name=server.name,
|
||||
),
|
||||
],
|
||||
"database": [
|
||||
MCPTool(
|
||||
@@ -465,18 +478,112 @@ class MCPBridge:
|
||||
tool_name: str,
|
||||
parameters: dict[str, Any],
|
||||
) -> Any:
|
||||
"""HTTP 方式執行工具 (Mock 實作)"""
|
||||
# Phase 3: Mock 執行,實際連接待 MCP Server 部署
|
||||
logger.info(f"[MOCK] HTTP call to {server.endpoint}: {tool_name}({parameters})")
|
||||
"""
|
||||
HTTP 方式執行工具
|
||||
|
||||
# 模擬不同工具的回傳
|
||||
mock_responses = {
|
||||
"kubectl_get": {"items": [{"name": "pod-1"}, {"name": "pod-2"}]},
|
||||
"kubectl_delete": {"deleted": True, "resource": parameters.get("name")},
|
||||
"kubectl_scale": {"scaled": True, "replicas": parameters.get("replicas")},
|
||||
"query": {"rows": [], "affected": 0},
|
||||
}
|
||||
return mock_responses.get(tool_name, {"status": "ok"})
|
||||
Phase 13.2: 整合真實 K8s Executor (不再是 Mock)
|
||||
"""
|
||||
# =============================================
|
||||
# Kubernetes: 使用真實 ActionExecutor
|
||||
# =============================================
|
||||
if server.name == "kubernetes":
|
||||
from src.services.executor import get_executor
|
||||
|
||||
executor = get_executor()
|
||||
|
||||
if tool_name == "kubectl_get":
|
||||
# 使用 kubectl 指令查詢
|
||||
namespace = parameters.get("namespace", "awoooi-prod")
|
||||
resource = parameters.get("resource", "pods")
|
||||
name = parameters.get("name", "")
|
||||
cmd = f"kubectl get {resource} {name} -n {namespace} -o json".strip()
|
||||
result = await executor.execute_kubectl_command(cmd)
|
||||
if result.success and result.k8s_response:
|
||||
return result.k8s_response.get("stdout", "")
|
||||
return {"error": result.error}
|
||||
|
||||
elif tool_name == "kubectl_delete":
|
||||
namespace = parameters.get("namespace", "awoooi-prod")
|
||||
resource = parameters.get("resource", "pod")
|
||||
name = parameters.get("name", "")
|
||||
if not name:
|
||||
return {"error": "Missing 'name' parameter"}
|
||||
|
||||
# Dry-run 驗證
|
||||
if resource == "pod":
|
||||
dry_run = await executor.validate_pod_exists(name, namespace)
|
||||
else:
|
||||
dry_run = await executor.validate_deployment_exists(name, namespace)
|
||||
|
||||
if not dry_run.passed:
|
||||
return {"error": dry_run.message, "dry_run": False}
|
||||
|
||||
# 執行刪除
|
||||
if resource == "pod":
|
||||
result = await executor.delete_pod(name, namespace)
|
||||
else:
|
||||
# deployment 不支援直接刪除,改用 restart
|
||||
return {"error": "Direct deployment deletion not supported, use restart"}
|
||||
|
||||
return {
|
||||
"success": result.success,
|
||||
"message": result.message,
|
||||
"duration_ms": result.duration_ms,
|
||||
}
|
||||
|
||||
elif tool_name == "kubectl_scale":
|
||||
namespace = parameters.get("namespace", "awoooi-prod")
|
||||
deployment = parameters.get("deployment", "")
|
||||
replicas = parameters.get("replicas", 1)
|
||||
if not deployment:
|
||||
return {"error": "Missing 'deployment' parameter"}
|
||||
|
||||
cmd = f"kubectl scale deployment/{deployment} --replicas={replicas} -n {namespace}"
|
||||
result = await executor.execute_kubectl_command(cmd)
|
||||
return {
|
||||
"success": result.success,
|
||||
"scaled": result.success,
|
||||
"replicas": replicas,
|
||||
"message": result.message,
|
||||
}
|
||||
|
||||
elif tool_name == "kubectl_restart":
|
||||
namespace = parameters.get("namespace", "awoooi-prod")
|
||||
deployment = parameters.get("deployment", "")
|
||||
if not deployment:
|
||||
return {"error": "Missing 'deployment' parameter"}
|
||||
|
||||
dry_run = await executor.validate_deployment_exists(deployment, namespace)
|
||||
if not dry_run.passed:
|
||||
return {"error": dry_run.message, "dry_run": False}
|
||||
|
||||
result = await executor.restart_deployment(deployment, namespace)
|
||||
return {
|
||||
"success": result.success,
|
||||
"restarted": result.success,
|
||||
"message": result.message,
|
||||
"duration_ms": result.duration_ms,
|
||||
}
|
||||
|
||||
else:
|
||||
return {"error": f"Unknown kubernetes tool: {tool_name}"}
|
||||
|
||||
# =============================================
|
||||
# Database: 查詢 incident/approval 歷史 (Phase 13.2 #81)
|
||||
# =============================================
|
||||
elif server.name == "database":
|
||||
if tool_name == "query":
|
||||
# TODO: 整合真實 PostgreSQL 查詢
|
||||
logger.info(f"[TODO] Database query: {parameters.get('sql', '')[:50]}")
|
||||
return {"rows": [], "affected": 0, "note": "Phase 13.2 #81 待實作"}
|
||||
else:
|
||||
return {"error": f"Unknown database tool: {tool_name}"}
|
||||
|
||||
# =============================================
|
||||
# Mock fallback
|
||||
# =============================================
|
||||
logger.info(f"[MOCK] HTTP call to {server.endpoint}: {tool_name}")
|
||||
return {"status": "ok", "mock": True}
|
||||
|
||||
async def _execute_stdio(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user