Initial curated ServiceMap source from 7a0a61232d05ba9097432abef6f7f4102a64d0b8
This commit is contained in:
commit
105251b9c8
96 files changed
+23587
No files matched your search
@@ -0,0 +1,127 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
|
||||
from app.discovery_store import DiscoveryStore
|
||||
from app.models import (
|
||||
DiscoveryConfidence,
|
||||
HealthStatus,
|
||||
MonitoringState,
|
||||
RelationEvidence,
|
||||
RelationType,
|
||||
Service,
|
||||
ServiceCreate,
|
||||
ServiceKind,
|
||||
ServiceSnapshot,
|
||||
TopologySuggestion,
|
||||
)
|
||||
from app.semantic import build_semantic_topology
|
||||
from app.store import Store
|
||||
|
||||
|
||||
def _service(identifier: str, name: str, *, application: str | None = None, category: str = "application") -> Service:
|
||||
now = datetime.now(UTC)
|
||||
return Service(
|
||||
id=identifier,
|
||||
name=name,
|
||||
kind=ServiceKind.HTTP,
|
||||
url=f"https://{identifier}.local",
|
||||
application=application,
|
||||
category=category,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
)
|
||||
|
||||
|
||||
def _snapshot(service: Service, direct: HealthStatus, effective: HealthStatus | None = None) -> ServiceSnapshot:
|
||||
return ServiceSnapshot(
|
||||
service=service,
|
||||
direct_status=direct,
|
||||
dependency_status=effective or direct,
|
||||
effective_status=effective or direct,
|
||||
)
|
||||
|
||||
|
||||
def test_semantic_topology_keeps_singletons_as_distinct_applications() -> None:
|
||||
snapshots = [
|
||||
_snapshot(_service("one", "One", category="application"), HealthStatus.HEALTHY),
|
||||
_snapshot(_service("two", "Two", category="application"), HealthStatus.HEALTHY),
|
||||
]
|
||||
|
||||
topology = build_semantic_topology(snapshots, [])
|
||||
|
||||
assert {app.name for app in topology.applications} == {"One", "Two"}
|
||||
assert all(app.component_count == 1 for app in topology.applications)
|
||||
|
||||
|
||||
def test_semantic_app_health_is_explainable_and_ignores_unmonitored_auxiliaries() -> None:
|
||||
postgres = _service("postgres", "Zabbix PostgreSQL", application="Zabbix", category="database")
|
||||
server = _service("server", "Zabbix Server", application="Zabbix", category="monitoring")
|
||||
agent = _service("agent", "Zabbix Agent", application="Zabbix", category="monitoring")
|
||||
agent.monitoring_enabled = False
|
||||
agent.monitoring_state = MonitoringState.UNMONITORED
|
||||
topology = build_semantic_topology([
|
||||
_snapshot(postgres, HealthStatus.DOWN),
|
||||
_snapshot(server, HealthStatus.HEALTHY),
|
||||
_snapshot(agent, HealthStatus.UNMONITORED),
|
||||
], [], root_service_ids={"postgres"}, affected_service_ids={"server"})
|
||||
|
||||
app = next(item for item in topology.applications if item.name == "Zabbix")
|
||||
assert app.status == HealthStatus.DEGRADED
|
||||
assert app.root_cause is True
|
||||
assert "PostgreSQL" in app.status_explanation
|
||||
assert app.monitored_components == 2
|
||||
assert app.unmonitored_components == 1
|
||||
components = {item.service_id: item for item in topology.components}
|
||||
assert components["postgres"].root_cause is True
|
||||
assert components["server"].root_cause is False
|
||||
assert components["server"].affected is True
|
||||
|
||||
|
||||
def test_logical_relation_merges_independent_evidence_and_improves_confidence(tmp_path: Path) -> None:
|
||||
store = Store(str(tmp_path / "servicemap.db"))
|
||||
source = store.create_service(ServiceCreate(name="API", kind=ServiceKind.HTTP, url="https://api.local"))
|
||||
target = store.create_service(ServiceCreate(name="Database", kind=ServiceKind.TCP, host="db", port=5432))
|
||||
discovery = DiscoveryStore(store)
|
||||
now = datetime.now(UTC)
|
||||
for evidence_type, confidence, explanation in (
|
||||
("compose", DiscoveryConfidence.MEDIUM, "Compose depends_on declares database"),
|
||||
("environment", DiscoveryConfidence.HIGH, "DATABASE_HOST resolves to database"),
|
||||
):
|
||||
discovery.save_evidence(RelationEvidence(
|
||||
id=f"evidence-{evidence_type}",
|
||||
source_service_id=source.id,
|
||||
target_service_id=target.id,
|
||||
relation_type=RelationType.DEPENDS_ON,
|
||||
evidence_type=evidence_type,
|
||||
confidence=confidence,
|
||||
explanation=explanation,
|
||||
source_ref=f"docker:compose:stack:{evidence_type}",
|
||||
first_seen_at=now,
|
||||
last_seen_at=now,
|
||||
))
|
||||
|
||||
relations = discovery.list_logical_relations()
|
||||
|
||||
assert len(relations) == 1
|
||||
assert relations[0].confidence == DiscoveryConfidence.CONFIRMED
|
||||
assert {item.evidence_type for item in relations[0].evidence} == {"compose", "environment"}
|
||||
|
||||
|
||||
def test_repeated_suggestion_merges_changed_evidence_without_a_duplicate(tmp_path: Path) -> None:
|
||||
store = Store(str(tmp_path / "servicemap.db"))
|
||||
discovery = DiscoveryStore(store)
|
||||
run = discovery.start_run()
|
||||
now = datetime.now(UTC)
|
||||
base = {
|
||||
"id": "suggest-rel", "run_id": run.id, "suggestion_type": "relation", "source_ref": "docker:api",
|
||||
"target_ref": "docker:db", "relation_type": RelationType.DEPENDS_ON,
|
||||
"confidence": DiscoveryConfidence.HIGH, "created_at": now, "updated_at": now,
|
||||
}
|
||||
discovery.save_suggestion(TopologySuggestion(**base, evidence=["Compose relationship"]), "canonical-relation")
|
||||
second = {**base, "id": "suggest-rel-second"}
|
||||
saved = discovery.save_suggestion(TopologySuggestion(**second, evidence=["Environment relationship"]), "canonical-relation")
|
||||
|
||||
assert saved.evidence == ["Compose relationship", "Environment relationship"]
|
||||
assert len(discovery.list_suggestions()) == 1
|
||||
Reference in new issue
Block a user