fix(api): route auto repair docker restart through mcp
Some checks failed
CD Pipeline / tests (push) Successful in 1m21s
Code Review / ai-code-review (push) Successful in 13s
run-migration / migrate (push) Successful in 9s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
Some checks failed
CD Pipeline / tests (push) Successful in 1m21s
Code Review / ai-code-review (push) Successful in 13s
run-migration / migrate (push) Successful in 9s
CD Pipeline / post-deploy-checks (push) Has been cancelled
CD Pipeline / build-and-deploy (push) Has been cancelled
This commit is contained in:
@@ -459,6 +459,44 @@ class TestAutoRepairService:
|
||||
|
||||
assert route is None
|
||||
|
||||
def test_legacy_ssh_docker_restart_routes_to_write_mcp_gateway(self, service):
|
||||
"""Safe legacy Docker restart steps use the governed write MCP tool."""
|
||||
incident = create_test_incident(
|
||||
severity=Severity.P2,
|
||||
alert_category="infrastructure",
|
||||
alert_name="DockerContainerUnhealthy",
|
||||
)
|
||||
incident.signals[0].labels.update({
|
||||
"host": "110",
|
||||
"container_name": "minio",
|
||||
})
|
||||
|
||||
route = service._route_legacy_ssh_write_command_to_mcp(
|
||||
incident,
|
||||
'ssh {host} \'docker inspect {container} --format="{{.State.Health.Status}}" && docker restart {container}\'',
|
||||
)
|
||||
|
||||
assert route is not None
|
||||
assert route.tool_name == "ssh_docker_restart"
|
||||
assert route.required_scope == "write"
|
||||
assert route.params == {
|
||||
"host": "192.168.0.110",
|
||||
"container_name": "minio",
|
||||
"trust_score": 0.85,
|
||||
}
|
||||
|
||||
def test_legacy_ssh_complex_restart_stays_blocked(self, service):
|
||||
"""Command substitution/fallback shell must not enter auto write MCP."""
|
||||
incident = create_test_incident(severity=Severity.P2)
|
||||
incident.signals[0].labels.update({"host": "110", "container_name": "node-exporter"})
|
||||
|
||||
route = service._route_legacy_ssh_write_command_to_mcp(
|
||||
incident,
|
||||
'ssh {host} \'docker restart $(docker ps -a --filter name=exporter --format "{{.Names}}" | head -1) || systemctl restart node_exporter\'',
|
||||
)
|
||||
|
||||
assert route is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_legacy_ssh_diagnostic_uses_mcp_gateway(
|
||||
self,
|
||||
@@ -508,6 +546,71 @@ class TestAutoRepairService:
|
||||
assert calls[0]["params"]["container_name"] == "momo-scheduler"
|
||||
assert calls[0]["params"]["_mcp_audit"]["flywheel_node"] == "execute"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_execute_legacy_ssh_docker_restart_uses_write_mcp_gateway(
|
||||
self,
|
||||
service,
|
||||
monkeypatch,
|
||||
):
|
||||
incident = create_test_incident(
|
||||
severity=Severity.P2,
|
||||
alert_category="infrastructure",
|
||||
alert_name="DockerContainerUnhealthy",
|
||||
)
|
||||
incident.signals[0].labels.update({
|
||||
"host": "110",
|
||||
"container_name": "minio",
|
||||
})
|
||||
step = RepairStep(
|
||||
step_number=1,
|
||||
action_type=ActionType.SSH_COMMAND,
|
||||
command='ssh {host} \'docker inspect {container} --format="{{.State.Health.Status}}" && docker restart {container}\'',
|
||||
risk_level=RiskLevel.MEDIUM,
|
||||
requires_approval=False,
|
||||
)
|
||||
calls = []
|
||||
redis_sets = []
|
||||
|
||||
class FakeRedis:
|
||||
async def set(self, key, value, ex=None):
|
||||
redis_sets.append({"key": key, "value": value, "ex": ex})
|
||||
return True
|
||||
|
||||
class FakeGateway:
|
||||
def __init__(self, db: object) -> None:
|
||||
self.db = db
|
||||
|
||||
async def call(self, ctx, params):
|
||||
calls.append({"ctx": ctx, "params": params, "db": self.db})
|
||||
return MCPToolResult(
|
||||
success=True,
|
||||
execution_id="gw-write-ok",
|
||||
output={"stdout": "minio"},
|
||||
)
|
||||
|
||||
monkeypatch.setattr("src.core.redis_client.get_redis", lambda: FakeRedis())
|
||||
monkeypatch.setattr("src.db.base.get_db_context", lambda _project_id: _DbContext())
|
||||
monkeypatch.setattr("src.plugins.mcp.gateway.McpGateway", FakeGateway)
|
||||
|
||||
result = await service._execute_step(incident, step)
|
||||
|
||||
assert result.startswith("SUCCESS: mcp:ssh_docker_restart")
|
||||
assert calls
|
||||
assert redis_sets
|
||||
assert calls[0]["ctx"].agent_id == "auto_repair_executor"
|
||||
assert calls[0]["ctx"].tool_name == "ssh_docker_restart"
|
||||
assert calls[0]["ctx"].required_scope == "write"
|
||||
assert calls[0]["ctx"].is_shadow is False
|
||||
assert calls[0]["ctx"].run_id is not None
|
||||
assert calls[0]["params"]["host"] == "192.168.0.110"
|
||||
assert calls[0]["params"]["container_name"] == "minio"
|
||||
assert calls[0]["params"]["trust_score"] == 0.85
|
||||
assert calls[0]["params"]["_mcp_audit"]["flywheel_node"] == "execute"
|
||||
assert redis_sets[0]["key"].startswith(
|
||||
"mcp_approval:awoooi:auto_repair_executor:ssh_docker_restart:"
|
||||
)
|
||||
assert redis_sets[0]["value"] == "approved:auto_repair_policy"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_evaluate_low_risk_allowed(self, service, mock_playbook_service):
|
||||
"""Test that LOW risk actions are allowed"""
|
||||
|
||||
Reference in New Issue
Block a user