feat(grafana-metric-gateway): slice 1 — backend gateway transport

Route all prometheus widget queries through Grafana /api/ds/query instead of
direct Prom HTTP. PrometheusConfig: drop base_url, add grafana_url +
datasource_uid; secret grafana_api_key (required). PrometheusWidgetSource →
MetricSource with _gateway_query POST method. normalize_grafana_frames
recovered from 65bae95 + shared _dedup_label helper. Gateway-path status
check. Startup old-config validation. CHANGELOG migration note. All adapter
tests rewritten for POST /api/ds/query + Grafana frames mock. Backend: 331
pytest pass, ruff clean. Frontend: build green (unchanged in S1).
This commit is contained in:
Developer
2026-07-09 21:24:44 +00:00
parent 798196ffc7
commit df80c68f89
10 changed files with 573 additions and 250 deletions
+16 -11
View File
@@ -751,11 +751,15 @@ class TestPrometheusStatus:
def test_prometheus_status_when_unreachable(self, test_client):
service = ServiceRecord(
id="p1", service_type="prometheus", name="Prometheus", config={"base_url": "http://prometheus:9090"}
id="p1",
service_type="prometheus",
name="Prometheus",
config={"grafana_url": "http://grafana:3000", "datasource_uid": "prometheus"},
secrets={"grafana_api_key": "key"},
)
with (
patch(f"{_MON}.resolve_service_record", return_value=service),
patch(f"{_MON}.requests.get", side_effect=Exception("refused")),
patch(f"{_MON}.requests.post", side_effect=__import__("requests").ConnectionError("refused")),
):
response = test_client.get("/api/monitoring/prometheus-status")
assert response.status_code == 200
@@ -763,22 +767,23 @@ class TestPrometheusStatus:
assert data["up"] is False
assert data["error"] == "prometheus_unreachable"
def test_prometheus_status_returns_version(self, test_client):
def test_prometheus_status_returns_ok(self, test_client):
service = ServiceRecord(
id="p1", service_type="prometheus", name="Prometheus", config={"base_url": "http://prometheus:9090"}
id="p1",
service_type="prometheus",
name="Prometheus",
config={"grafana_url": "http://grafana:3000", "datasource_uid": "prometheus"},
secrets={"grafana_api_key": "key"},
)
health = MagicMock()
health.raise_for_status = MagicMock()
build_info = MagicMock()
build_info.raise_for_status = MagicMock()
build_info.json.return_value = {"status": "success", "data": {"version": "2.55.1"}}
gateway_resp = MagicMock()
gateway_resp.raise_for_status = MagicMock()
with (
patch(f"{_MON}.resolve_service_record", return_value=service),
patch(f"{_MON}.requests.get", side_effect=[health, build_info]),
patch(f"{_MON}.requests.post", return_value=gateway_resp),
):
response = test_client.get("/api/monitoring/prometheus-status")
assert response.status_code == 200
data = response.json()
assert data["up"] is True
assert data["version"] == "2.55.1"
assert data["version"] == "ok"
assert data["service_id"] == "p1"