fix(iwooos): scope control plane database read

This commit is contained in:
ogt
2026-07-11 19:57:00 +08:00
parent a21567c991
commit 5e286f25be
2 changed files with 63 additions and 3 deletions

View File

@@ -616,6 +616,7 @@ def build_iwooos_security_asset_control_plane(
ai_trace_row: Any,
siem_row: Any,
audit_row: Any,
source_health: Iterable[dict[str, Any]] | None = None,
generated_at: datetime | None = None,
) -> dict[str, Any]:
"""Build a public-safe aggregate without exposing raw asset or event identities."""
@@ -624,6 +625,20 @@ def build_iwooos_security_asset_control_plane(
coverage_source = list(coverage_rows)
compliance_source = list(compliance_rows)
automation_source = list(automation_rows)
source_health_rows = list(source_health or [])
unavailable_sources = [
row for row in source_health_rows if row.get("status") != "ready"
]
core_source_unavailable = any(
row.get("source_id") in {"asset_discovery", "asset_inventory"}
for row in unavailable_sources
)
if core_source_unavailable:
source_status = "live_database_unavailable"
elif unavailable_sources:
source_status = "live_database_partial"
else:
source_status = "live_database_aggregate"
by_type = {
str(_value(row, "asset_type", "unknown")): int(_value(row, "cnt", 0) or 0)
@@ -831,6 +846,17 @@ def build_iwooos_security_asset_control_plane(
)
work_items: list[dict[str, Any]] = []
if unavailable_sources:
work_items.append(
_work_item(
"AIA-P0-006-00",
"P0",
"source_health",
"修復控制平面資料源",
len(unavailable_sources),
"依 source_health reason code 修復 schema、權限或資料源相容性。",
)
)
if asset_total == 0 or not discovery_fresh:
work_items.append(
_work_item(
@@ -947,9 +973,16 @@ def build_iwooos_security_asset_control_plane(
return {
"schema_version": _SCHEMA_VERSION,
"generated_at": now.isoformat(),
"status": "ready" if asset_total > 0 and discovery_fresh else "degraded",
"source_status": "live_database_aggregate",
"status": (
"ready"
if asset_total > 0 and discovery_fresh and not unavailable_sources
else "degraded"
),
"source_status": source_status,
"summary": {
"source_count": len(source_health_rows),
"ready_source_count": len(source_health_rows) - len(unavailable_sources),
"unavailable_source_count": len(unavailable_sources),
"managed_asset_count": asset_total,
"expected_asset_type_count": len(_ASSET_TYPES),
"present_asset_type_count": len(present_types),
@@ -1010,6 +1043,7 @@ def build_iwooos_security_asset_control_plane(
"same_run_closed_loop_count": 0,
},
"security_audit": audit,
"source_health": source_health_rows,
"work_items": work_items,
"boundaries": {
"aggregate_only": True,
@@ -1239,7 +1273,7 @@ class IwoooSSecurityAssetControlPlaneService:
)
async def _load_snapshot(self) -> dict[str, Any]:
async with get_db_context() as db:
async with get_db_context("awoooi") as db:
discovery_result = await db.execute(
_sql(
"""

View File

@@ -230,6 +230,32 @@ def test_service_redacts_database_exception_details(monkeypatch) -> None:
assert "private-host" not in text
def test_service_uses_canonical_awoooi_project_scope(monkeypatch) -> None:
service = IwoooSSecurityAssetControlPlaneService()
requested_projects: list[str] = []
class FailingContext:
async def __aenter__(self):
raise RuntimeError("stop after project scope capture")
async def __aexit__(self, exc_type, exc, traceback):
return False
def fake_db_context(project_id: str):
requested_projects.append(project_id)
return FailingContext()
monkeypatch.setattr(
"src.services.iwooos_security_asset_control_plane.get_db_context",
fake_db_context,
)
payload = asyncio.run(service.get_snapshot())
assert requested_projects == ["awoooi"]
assert payload["status"] == "degraded"
assert payload["reason_code"] == "database_query_failed"
def test_public_api_returns_live_aggregate(monkeypatch) -> None:
payload = _ready_payload()