Health-Check: DNS-over-HTTPS-Rückfall bei nicht auflösbarem Hostnamen, DNS-Problem des Admin-Servers nur Warnung (außer strikte Rollouts); COST_INGEST_KEY optional (Schema-getrieben)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -85,7 +85,8 @@ def test_required_secrets_follow_schema_conditions():
|
||||
assert "MISTRAL_API_KEY" not in contract.required_secrets({"stt": {"provider": "cortecs"}})
|
||||
assert "MISTRAL_API_KEY" in contract.required_secrets({"stt": {"provider": "mistral"}})
|
||||
assert "MISTRAL_API_KEY" in contract.required_secrets({})
|
||||
assert {"OIDC_CLIENT_ID", "OIDC_CLIENT_SECRET", "SESSION_SECRET", "COST_INGEST_KEY", "CORTECS_API_KEY"} <= set(contract.required_secrets({}))
|
||||
assert {"OIDC_CLIENT_ID", "OIDC_CLIENT_SECRET", "SESSION_SECRET", "CORTECS_API_KEY"} <= set(contract.required_secrets({}))
|
||||
assert "COST_INGEST_KEY" not in contract.required_secrets({}) # optional: ohne Key bleibt die Kostenerfassung inaktiv
|
||||
|
||||
|
||||
# ── Health-Verifikation & Zustandsabgleich ──────────────────────────────────
|
||||
@@ -560,3 +561,52 @@ def test_wrong_version_or_existing_ingress_still_fails(client, live_env):
|
||||
never = lambda *a, **k: (_ for _ in ()).throw(HealthTimeout("keine Antwort", answered=False)) # noqa: E731
|
||||
d2 = client.get(f"/api/deployments/{deploy.start('hc2', 'deploy', 't', http=WithIngress(), health=never, wait=True)}").json()
|
||||
assert d2["status"] == "failed"
|
||||
|
||||
|
||||
# ── DNS-Rückfall beim Health-Check ──────────────────────────────────────────
|
||||
|
||||
def test_dns_failure_falls_back_to_doh_and_connects_by_ip(monkeypatch):
|
||||
import socket
|
||||
import httpx
|
||||
from app.services import netcheck
|
||||
calls = []
|
||||
|
||||
class Resp:
|
||||
status_code, headers = 200, {"content-type": "application/json"}
|
||||
def __init__(self, body): self._b = body
|
||||
def json(self): return self._b
|
||||
|
||||
def fake_get(url, **kw):
|
||||
calls.append((url, kw.get("headers"), kw.get("extensions")))
|
||||
if url == "https://demo.example.de/health":
|
||||
raise httpx.ConnectError("[Errno -2] Name or service not known") from socket.gaierror(-2, "Name or service not known")
|
||||
if "dns-query" in url:
|
||||
return Resp({"Answer": [{"type": 1, "data": "203.0.113.7"}]})
|
||||
return Resp({"status": "ok", "core": {"version": "abcdef1234567"}})
|
||||
|
||||
monkeypatch.setattr(httpx, "get", fake_get)
|
||||
status, body = netcheck.get_json("https://demo.example.de/health")
|
||||
assert status == 200 and body["_dns_fallback"] and body["core"]["version"].startswith("abcdef1")
|
||||
ip_call = calls[-1]
|
||||
assert ip_call[0] == "https://203.0.113.7/health" and ip_call[1] == {"Host": "demo.example.de"} and ip_call[2] == {"sni_hostname": "demo.example.de"}
|
||||
# Verbindungsfehler ohne DNS-Ursache werden NICHT umgangen
|
||||
monkeypatch.setattr(httpx, "get", lambda url, **kw: (_ for _ in ()).throw(httpx.ConnectError("refused")))
|
||||
with pytest.raises(httpx.ConnectError):
|
||||
netcheck.get_json("https://demo.example.de/health")
|
||||
|
||||
|
||||
def test_unresolvable_host_is_a_warning_for_single_deploys_but_fails_strict_rollouts(client, live_env):
|
||||
from app.services.deploy import HealthTimeout
|
||||
_ready(client, "dn1")
|
||||
class WithIngress(FakeMW):
|
||||
def __call__(self, method, url, headers, body):
|
||||
if method == "GET" and "ingresses" in url:
|
||||
return 200, [{"hostname": "dn1.onyscribe.de", "id": "i1"}], ""
|
||||
return super().__call__(method, url, headers, body)
|
||||
dns = lambda *a, **k: (_ for _ in ()).throw(HealthTimeout("Name or service not known", answered=False, dns=True)) # noqa: E731
|
||||
d = client.get(f"/api/deployments/{deploy.start('dn1', 'deploy', 't', http=WithIngress(), health=dns, wait=True)}").json()
|
||||
assert d["status"] == "warning" and "kann den Hostnamen nicht auflösen" in d["log"] # bestehender Ingress, aber nur DNS-Problem des Admin-Servers
|
||||
# strikt (Core-Rollout): ohne neuen Ingress bleibt es ein Fehler
|
||||
rid = _approved_release(client, "5" * 40)
|
||||
batch, _ = releases.rollout(rid, "t", dry_run=False, http=WithIngress(), health=dns, wait=True)
|
||||
assert [r["status"] for r in client.get(f"/api/deployments?batch={batch}").json() if r["tenant"] == "dn1"] == ["failed"]
|
||||
|
||||
Reference in New Issue
Block a user