Initial curated Unraid-AppOps-Gateway source from 3bd6f6fee310125bfbb4d4650389a817f498724d

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:35:57 +02:00
commit 1dfbca08a1
528 files changed
+131054

No files matched your search

@@ -0,0 +1,400 @@
from __future__ import annotations
from sqlmodel import Session, SQLModel, create_engine
from backend.app.db.models import Policy
from backend.app.schemas.deployment import DeploymentPlanV1
from backend.app.services.deployment_control_plane import DeploymentControlPlane
from backend.app.services.secret_store import set_secret
def _plan() -> DeploymentPlanV1:
return DeploymentPlanV1.model_validate(
{
"application_id": "itworx-mcp-hub",
"repository": "Jens/ITWorx_MCP_Hub",
"revision": "a" * 40,
"release_version": "1.0.0",
"image_repository": "itworx-mcp-hub",
"image_tag": "1.0.0",
"topology": {
"compose_project": "itworx-mcp-hub",
"containers": ["itworx-mcp-hub"],
"proxy_network": "itworx-npm",
"internal_port": 8080,
},
"proxy": {"hostname": "mcp-test.example.com"},
"oidc": None,
"broker_secret_ref": "MCP_HUB_TEST_DEPLOYMENT_BROKER",
}
)
class _Broker:
def __init__(self, rollback_status: str = "rolled_back") -> None:
self.rollback_status = rollback_status
self.rollback_calls: list[str] = []
def preflight(self, plan, *, secret):
return {"status": "preflight_ok"}
def apply(self, plan, *, secret, idempotency_key):
return {"status": "deployed", "request": "a" * 64}
def rollback(self, plan, *, secret, idempotency_key):
self.rollback_calls.append(idempotency_key)
return {"status": self.rollback_status, "request": "b" * 64}
def status(self, request_id, *, secret):
return {"status": "rolled_back", "request": request_id}
class _Portainer:
def list_containers(self):
return {"status": "ok", "containers": []}
class _FailingNpm:
def proxy_hosts(self):
return {"success": True, "items": []}
def ensure_managed_proxy_host(self, **kwargs):
return {"success": False, "status": "failed"}
class _Unraid:
def get_share_summary(self):
return {"status": "ok"}
class _Authentik:
def test_connection(self):
return {"status": "connected"}
def applications(self):
return {"success": True, "items": []}
def _session(tmp_path) -> Session:
engine = create_engine(f"sqlite:///{tmp_path / 'auto-rollback.db'}")
SQLModel.metadata.create_all(engine)
return Session(engine)
def _policy(action: str) -> Policy:
return Policy(
id=f"allow-mcp-hub-{action.replace('.', '-')}",
action=action,
target="itworx-mcp-hub",
voice="blocked",
manual="allowed",
risk="high",
reason="Exact-target no-regression deployment test policy.",
)
def _plane(broker: _Broker) -> DeploymentControlPlane:
return DeploymentControlPlane(
broker=broker,
portainer=_Portainer(),
npm=_FailingNpm(),
authentik=_Authentik(),
unraid=_Unraid(),
)
def _configure(session: Session, *, rollback_policy: bool = True) -> None:
set_secret(session, "MCP_HUB_TEST_DEPLOYMENT_BROKER", "x" * 32)
session.add(_policy("deployment.apply"))
if rollback_policy:
session.add(_policy("deployment.rollback"))
session.commit()
def test_default_compose_file_survives_durable_plan_roundtrip():
plan = _plan()
restored = DeploymentPlanV1.model_validate(plan.model_dump(mode="json"))
assert restored.topology.compose_file == ".itworx/deployment.yml"
def test_post_apply_verification_failure_auto_rolls_back_with_exact_policy(tmp_path):
broker = _Broker("rolled_back")
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0001",
operation="apply",
)
assert result["status"] == "rolled_back"
assert result["stage"] == "auto_rollback_verified"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert result["evidence"]["autoRollback"]["status"] == "rolled_back"
assert broker.rollback_calls == [f"auto-rollback:{result['id']}"]
def test_post_apply_verification_failure_does_not_rollback_without_exact_policy(tmp_path):
broker = _Broker("rolled_back")
with _session(tmp_path) as session:
_configure(session, rollback_policy=False)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0002",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["autoRollback"]["status"] == "blocked"
assert broker.rollback_calls == []
def test_post_apply_verification_failure_keeps_failure_when_controller_rejects_rollback(tmp_path):
broker = _Broker("rejected")
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0003",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "auto_rollback_failed"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert result["evidence"]["autoRollback"]["status"] == "rejected"
assert len(broker.rollback_calls) == 1
def test_queued_auto_rollback_is_polled_and_finishes_as_auto_rollback(tmp_path):
broker = _Broker("accepted")
plane = _plane(broker)
with _session(tmp_path) as session:
_configure(session)
initial = plane.execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0004",
operation="apply",
)
assert initial["status"] == "rolling_back"
assert initial["stage"] == "auto_rollback_queued"
finished = plane.refresh(session, initial["id"])
assert finished["status"] == "rolled_back"
assert finished["stage"] == "auto_rollback_verified"
assert finished["evidence"]["autoRollback"]["status"] == "rolled_back"
def test_successful_post_apply_verification_never_invokes_auto_rollback(tmp_path):
class _HealthyNpm(_FailingNpm):
def ensure_managed_proxy_host(self, **kwargs):
return {"success": True, "status": "created", "hostId": 42}
broker = _Broker("rolled_back")
plane = DeploymentControlPlane(
broker=broker,
portainer=_Portainer(),
npm=_HealthyNpm(),
authentik=_Authentik(),
unraid=_Unraid(),
)
with _session(tmp_path) as session:
_configure(session)
result = plane.execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0005",
operation="apply",
)
assert result["status"] == "succeeded"
assert result["stage"] == "controller_and_edge_verified"
assert broker.rollback_calls == []
def test_auto_rollback_is_scoped_to_exact_mcp_hub_target(tmp_path):
broker = _Broker("rolled_back")
plan = _plan().model_copy(update={"repository": "jens/other-repository"})
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=plan,
idempotency_key="mcp-hub-apply-0006",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert "autoRollback" not in result["evidence"]
assert broker.rollback_calls == []
def test_auto_rollback_rejects_same_repository_with_different_application(tmp_path):
broker = _Broker("rolled_back")
plan = _plan().model_copy(
update={
"application_id": "itworx-mcp-hub-copy",
"topology": _plan().topology.model_copy(
update={
"compose_project": "itworx-mcp-hub-copy",
"containers": ("itworx-mcp-hub-copy",),
}
),
}
)
with _session(tmp_path) as session:
set_secret(session, "MCP_HUB_TEST_DEPLOYMENT_BROKER", "x" * 32)
session.add(
Policy(
id="allow-copy-apply",
action="deployment.apply",
target="itworx-mcp-hub-copy",
voice="blocked",
manual="allowed",
risk="high",
reason="Test-only exact target.",
)
)
session.add(
Policy(
id="allow-copy-rollback",
action="deployment.rollback",
target="itworx-mcp-hub-copy",
voice="blocked",
manual="allowed",
risk="high",
reason="Test-only exact target.",
)
)
session.commit()
result = _plane(broker).execute(
session,
plan=plan,
idempotency_key="mcp-hub-apply-0007",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["postApplyVerification"]["status"] == "failed"
assert "autoRollback" not in result["evidence"]
assert broker.rollback_calls == []
def test_queued_auto_rollback_rejects_deployed_status_as_rollback_proof(tmp_path):
class _AmbiguousRollbackBroker(_Broker):
def status(self, request_id, *, secret):
return {"status": "deployed", "request": request_id}
broker = _AmbiguousRollbackBroker("accepted")
plane = _plane(broker)
with _session(tmp_path) as session:
_configure(session)
initial = plane.execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0008",
operation="apply",
)
assert initial["status"] == "rolling_back"
finished = plane.refresh(session, initial["id"])
assert finished["status"] == "failed"
assert finished["stage"] == "auto_rollback_failed"
assert finished["evidence"]["autoRollback"]["status"] == "accepted"
assert "deployed state" in finished["result"]
def test_manual_rollback_requires_rolled_back_terminal_proof(tmp_path):
broker = _Broker("deployed")
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-rollback-0009",
operation="rollback",
)
assert result["status"] == "rollback_failed"
assert result["stage"] == "controller_rejected"
def test_apply_rejects_rolled_back_terminal_status(tmp_path):
class _RolledBackApplyBroker(_Broker):
def apply(self, plan, *, secret, idempotency_key):
return {"status": "rolled_back", "request": "c" * 64}
broker = _RolledBackApplyBroker()
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0010",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "controller_rejected"
assert broker.rollback_calls == []
def test_auto_rollback_refuses_duplicate_exact_rollback_policies(tmp_path):
broker = _Broker("rolled_back")
with _session(tmp_path) as session:
_configure(session)
session.add(
Policy(
id="duplicate-rollback-policy",
action="deployment.rollback",
target="itworx-mcp-hub",
voice="blocked",
manual="allowed",
risk="high",
reason="Duplicate exact rollback policy.",
)
)
session.commit()
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0011",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "external_integration_failed"
assert result["evidence"]["autoRollback"] == {
"status": "blocked",
"reason": "exact-target-policy",
}
assert broker.rollback_calls == []
def test_auto_rollback_rejects_unpollable_accepted_response(tmp_path):
class _MissingRequestBroker(_Broker):
def rollback(self, plan, *, secret, idempotency_key):
self.rollback_calls.append(idempotency_key)
return {"status": "accepted"}
broker = _MissingRequestBroker()
with _session(tmp_path) as session:
_configure(session)
result = _plane(broker).execute(
session,
plan=_plan(),
idempotency_key="mcp-hub-apply-0012",
operation="apply",
)
assert result["status"] == "failed"
assert result["stage"] == "auto_rollback_failed"
assert result["evidence"]["autoRollback"] == {
"status": "accepted",
"reason": "missing-controller-request",
}
assert len(broker.rollback_calls) == 1