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,322 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from app.discovery import DiscoveryManager
|
||||
from app.discovery_store import DiscoveryStore
|
||||
from app.docker_discovery import _kind_and_endpoint, _preflight_candidate
|
||||
from app.models import (
|
||||
DiscoveryCategory,
|
||||
DiscoveryConfidence,
|
||||
DiscoveryRunStatus,
|
||||
DockerCandidate,
|
||||
DockerPort,
|
||||
DockerSuggestion,
|
||||
HealthStatus,
|
||||
MonitoringState,
|
||||
ProbeEndpointCandidate,
|
||||
ProbeEndpointSource,
|
||||
ProbeReachability,
|
||||
ProbeResult,
|
||||
ServiceCreate,
|
||||
ServiceKind,
|
||||
)
|
||||
from app.store import Store
|
||||
|
||||
|
||||
def _manager(tmp_path: Path) -> DiscoveryManager:
|
||||
store = Store(str(tmp_path / "servicemap.db"))
|
||||
return DiscoveryManager(store, DiscoveryStore(store))
|
||||
|
||||
|
||||
def _candidate(
|
||||
name: str,
|
||||
reference: str,
|
||||
*,
|
||||
project: str | None = None,
|
||||
compose_service: str | None = None,
|
||||
labels: dict[str, str] | None = None,
|
||||
networks: list[str] | None = None,
|
||||
) -> DockerCandidate:
|
||||
return DockerCandidate(
|
||||
container_id=reference[-12:],
|
||||
name=name,
|
||||
image="example/service:1",
|
||||
state="running",
|
||||
networks=networks or [],
|
||||
ports=[],
|
||||
labels=labels or {},
|
||||
stable_source_ref=reference,
|
||||
compose_project=project,
|
||||
compose_service=compose_service,
|
||||
category=DiscoveryCategory.APPLICATION,
|
||||
)
|
||||
|
||||
|
||||
def test_internal_docker_dns_is_evidence_not_an_active_probe() -> None:
|
||||
kind, url, host, port, evidence = _kind_and_endpoint(
|
||||
"postgres",
|
||||
[DockerPort(container_port="5432/tcp")],
|
||||
{},
|
||||
)
|
||||
|
||||
assert kind == ServiceKind.TCP
|
||||
assert url is None
|
||||
assert host == "postgres"
|
||||
assert port == 5432
|
||||
assert "internal container endpoint candidate" in evidence
|
||||
|
||||
|
||||
def test_published_port_requires_an_explicit_probe_host() -> None:
|
||||
ports = [DockerPort(container_port="8080/tcp", host_ip="0.0.0.0", host_port=1226)]
|
||||
labels = {"servicemap.probe": "http"}
|
||||
_, unsafe_url, unsafe_host, _, _ = _kind_and_endpoint("app", ports, labels)
|
||||
kind, url, host, port, evidence = _kind_and_endpoint("app", ports, labels, "monitoring-host")
|
||||
|
||||
assert unsafe_url is None
|
||||
assert unsafe_host == "app"
|
||||
assert kind == ServiceKind.HTTP
|
||||
assert url == "http://monitoring-host:1226"
|
||||
assert host is None and port is None
|
||||
assert "host-published endpoint" in evidence
|
||||
|
||||
|
||||
def test_running_container_without_reachable_endpoint_is_runtime_observed(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
candidate = _candidate("worker", "docker:name:worker")
|
||||
candidate.suggestions = [DockerSuggestion(name="worker", kind=ServiceKind.TCP, host="worker", port=9000)]
|
||||
candidate.probe_candidates = [
|
||||
ProbeEndpointCandidate(
|
||||
source=ProbeEndpointSource.CONTAINER_INTERNAL,
|
||||
kind=ServiceKind.TCP,
|
||||
host="worker",
|
||||
port=9000,
|
||||
reachability=ProbeReachability.INTERNAL_ONLY,
|
||||
)
|
||||
]
|
||||
|
||||
payload = manager._payload_for_candidate(candidate)
|
||||
|
||||
assert payload.monitoring_enabled is False
|
||||
assert payload.monitoring_state.value == "observed"
|
||||
assert payload.probe_reachability == ProbeReachability.INTERNAL_ONLY
|
||||
|
||||
|
||||
def test_docker_healthcheck_observation_does_not_enable_network_probe(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
candidate = _candidate("database", "docker:name:database")
|
||||
candidate.healthcheck_present = True
|
||||
candidate.probe_candidates = [
|
||||
ProbeEndpointCandidate(
|
||||
source=ProbeEndpointSource.DOCKER_HEALTHCHECK,
|
||||
kind=ServiceKind.TCP,
|
||||
reachability=ProbeReachability.VERIFIED,
|
||||
confidence=DiscoveryConfidence.HIGH,
|
||||
)
|
||||
]
|
||||
|
||||
payload = manager._payload_for_candidate(candidate)
|
||||
|
||||
assert payload.monitoring_enabled is False
|
||||
assert payload.monitoring_state.value == "docker_health"
|
||||
assert payload.probe_source == ProbeEndpointSource.DOCKER_HEALTHCHECK
|
||||
|
||||
|
||||
def test_appops_application_health_is_provenance_not_component_health(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
candidate = _candidate("application", "docker:name:application")
|
||||
candidate.healthcheck_present = True
|
||||
candidate.appops_health = "healthy"
|
||||
candidate.appops_app_id = "northstar"
|
||||
candidate.appops_discovery_confidence = 0.9
|
||||
|
||||
payload = manager._payload_for_candidate(candidate)
|
||||
|
||||
assert payload.monitoring_enabled is False
|
||||
assert payload.monitoring_state == MonitoringState.DOCKER_HEALTH
|
||||
assert payload.probe_source == ProbeEndpointSource.DOCKER_HEALTHCHECK
|
||||
assert payload.runtime_health is None
|
||||
|
||||
service = manager.store.create_service(payload)
|
||||
manager._record_candidate_evidence(candidate, service)
|
||||
resolution = manager.store.health_resolution(service.id)
|
||||
|
||||
assert resolution.selected_evidence_id is None
|
||||
assert resolution.evidence[0].scope.value == "application"
|
||||
assert "not used as component" in resolution.explanation
|
||||
|
||||
|
||||
def test_compose_reference_prefers_same_project_over_duplicate_stack_name(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
run = manager.discovery_store.start_run()
|
||||
source = _candidate(
|
||||
"api-a",
|
||||
"docker:compose:stack-a:api",
|
||||
project="stack-a",
|
||||
compose_service="api",
|
||||
labels={"com.docker.compose.depends_on": "postgres:service_healthy:false"},
|
||||
)
|
||||
postgres_a = _candidate("postgres-a", "docker:compose:stack-a:postgres", project="stack-a", compose_service="postgres")
|
||||
postgres_b = _candidate("postgres-b", "docker:compose:stack-b:postgres", project="stack-b", compose_service="postgres")
|
||||
|
||||
suggestions = manager._relation_suggestions(run, [source, postgres_a, postgres_b], {})
|
||||
|
||||
assert len(suggestions) == 1
|
||||
assert suggestions[0].target_ref == postgres_a.stable_source_ref
|
||||
assert suggestions[0].confidence == DiscoveryConfidence.HIGH
|
||||
|
||||
|
||||
def test_ambiguous_global_name_stays_review_only(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
run = manager.discovery_store.start_run()
|
||||
source = _candidate(
|
||||
"api",
|
||||
"docker:name:api",
|
||||
labels={"servicemap.depends_on": "redis"},
|
||||
)
|
||||
redis_a = _candidate("redis-a", "docker:compose:a:redis", project="a", compose_service="redis")
|
||||
redis_b = _candidate("redis-b", "docker:compose:b:redis", project="b", compose_service="redis")
|
||||
|
||||
suggestions = manager._relation_suggestions(run, [source, redis_a, redis_b], {})
|
||||
|
||||
assert len(suggestions) == 1
|
||||
assert suggestions[0].target_ref is None
|
||||
assert suggestions[0].confidence == DiscoveryConfidence.LOW
|
||||
assert "Manual review required" in " ".join(suggestions[0].evidence)
|
||||
|
||||
|
||||
def test_reachability_repair_is_dry_run_first_and_preserves_probe_history(tmp_path: Path) -> None:
|
||||
manager = _manager(tmp_path)
|
||||
candidate = _candidate("legacy-db", "docker:name:legacy-db")
|
||||
candidate.suggestions = [DockerSuggestion(name="legacy-db", kind=ServiceKind.TCP, host="legacy-db", port=5432)]
|
||||
candidate.probe_candidates = [
|
||||
ProbeEndpointCandidate(
|
||||
source=ProbeEndpointSource.CONTAINER_INTERNAL,
|
||||
kind=ServiceKind.TCP,
|
||||
host="legacy-db",
|
||||
port=5432,
|
||||
reachability=ProbeReachability.INTERNAL_ONLY,
|
||||
)
|
||||
]
|
||||
legacy = manager.store.create_service(
|
||||
ServiceCreate(
|
||||
name="legacy-db",
|
||||
kind=ServiceKind.TCP,
|
||||
host="legacy-db",
|
||||
port=5432,
|
||||
source_type="docker",
|
||||
source_ref=candidate.stable_source_ref,
|
||||
)
|
||||
)
|
||||
manager.store.save_probe(
|
||||
ProbeResult(
|
||||
id="legacy-probe",
|
||||
service_id=legacy.id,
|
||||
status=HealthStatus.DOWN,
|
||||
latency_ms=None,
|
||||
detail="legacy unreachable internal DNS target",
|
||||
checked_at=legacy.created_at,
|
||||
)
|
||||
)
|
||||
run = manager.discovery_store.start_run()
|
||||
source = manager.discovery_store.save_source(
|
||||
source_type="docker",
|
||||
source_id=candidate.stable_source_ref or "",
|
||||
source_name=candidate.name,
|
||||
lifecycle=candidate.lifecycle,
|
||||
metadata={"candidate": candidate.model_dump(mode="json")},
|
||||
)
|
||||
manager.discovery_store.save_observation(
|
||||
run_id=run.id,
|
||||
source=source,
|
||||
source_name=candidate.name,
|
||||
lifecycle=candidate.lifecycle,
|
||||
change_type="changed",
|
||||
metadata={"candidate": candidate.model_dump(mode="json")},
|
||||
)
|
||||
manager.discovery_store.finish_run(run.id, status=DiscoveryRunStatus.COMPLETED, source_counts={}, diff=run.diff)
|
||||
|
||||
preview = manager.repair_probe_reachability(run_id=run.id, dry_run=True)
|
||||
assert preview.changed == 1
|
||||
assert manager.store.get_service(legacy.id).monitoring_enabled is True # type: ignore[union-attr]
|
||||
|
||||
applied = manager.repair_probe_reachability(run_id=run.id, dry_run=False)
|
||||
updated = manager.store.get_service(legacy.id)
|
||||
assert applied.disabled_unsafe == 1
|
||||
assert updated is not None and updated.monitoring_enabled is False
|
||||
assert updated.monitoring_state.value == "observed"
|
||||
assert len(manager.store.probe_history(legacy.id)) == 1
|
||||
|
||||
|
||||
def test_container_http_port_is_not_misclassified_by_a_custom_host_port() -> None:
|
||||
kind, url, host, port, evidence = _kind_and_endpoint(
|
||||
"adminer",
|
||||
[DockerPort(container_port="8080/tcp", host_ip="0.0.0.0", host_port=8070)],
|
||||
{},
|
||||
"127.0.0.1",
|
||||
)
|
||||
|
||||
assert kind == ServiceKind.HTTP
|
||||
assert url == "http://127.0.0.1:8070"
|
||||
assert host is None and port is None
|
||||
assert "HTTP-like container port" in evidence
|
||||
|
||||
|
||||
def test_published_preflight_requires_a_real_tcp_connection(monkeypatch) -> None:
|
||||
candidate = ProbeEndpointCandidate(
|
||||
source=ProbeEndpointSource.PUBLISHED_PORT,
|
||||
kind=ServiceKind.TCP,
|
||||
host="127.0.0.1",
|
||||
port=3306,
|
||||
)
|
||||
|
||||
class _Connection:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_: object) -> None:
|
||||
return None
|
||||
|
||||
monkeypatch.setattr("app.docker_discovery.socket.create_connection", lambda *_args, **_kwargs: _Connection())
|
||||
verified = _preflight_candidate(candidate)
|
||||
|
||||
assert verified.primary is True
|
||||
assert verified.reachability == ProbeReachability.VERIFIED
|
||||
assert verified.preflight_detail == "TCP connect succeeded"
|
||||
|
||||
|
||||
def test_http_preflight_records_response_without_scanning_paths(monkeypatch) -> None:
|
||||
candidate = ProbeEndpointCandidate(
|
||||
source=ProbeEndpointSource.PUBLISHED_PORT,
|
||||
kind=ServiceKind.HTTP,
|
||||
url="http://127.0.0.1:8070",
|
||||
)
|
||||
|
||||
class _Connection:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_: object) -> None:
|
||||
return None
|
||||
|
||||
class _Response:
|
||||
status_code = 204
|
||||
|
||||
class _Client:
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, *_: object) -> None:
|
||||
return None
|
||||
|
||||
def request(self, method: str, url: str) -> _Response:
|
||||
assert method == "HEAD"
|
||||
assert url == candidate.url
|
||||
return _Response()
|
||||
|
||||
monkeypatch.setattr("app.docker_discovery.socket.create_connection", lambda *_args, **_kwargs: _Connection())
|
||||
monkeypatch.setattr("app.docker_discovery.httpx.Client", lambda **_kwargs: _Client())
|
||||
verified = _preflight_candidate(candidate)
|
||||
|
||||
assert verified.primary is True
|
||||
assert verified.preflight_detail == "HTTP 204 responded"
|
||||
Reference in new issue
Block a user