refactor(hermes-autotriage): repair via ConfigMap patch, not PVC write
Longhorn RWO attach latency and per-node engine availability made the PVC fixture unreliable for a fast repeatable demo. The fixture is now a ConfigMap; the repair Job runs bitnami/kubectl under the least-privilege hermes-demo-repair SA and patches state=healthy. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
8314bd1843
commit
1cbed3f6a5
@ -477,7 +477,7 @@ def _repair_config() -> dict[str, Any]:
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"namespace": settings.hermes_demo_namespace,
|
"namespace": settings.hermes_demo_namespace,
|
||||||
"fixture_pvc": settings.hermes_demo_fixture_pvc,
|
"fixture_configmap": settings.hermes_demo_fixture_configmap,
|
||||||
"image": settings.hermes_repair_image,
|
"image": settings.hermes_repair_image,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ _REPAIR_MESSAGE = "fixture state reset to healthy"
|
|||||||
def execute_repair(cfg: dict, incident_id: str, build_number: int) -> dict[str, Any]:
|
def execute_repair(cfg: dict, incident_id: str, build_number: int) -> dict[str, Any]:
|
||||||
"""Run the hardcoded demo-fixture repair Job and wait for its outcome.
|
"""Run the hardcoded demo-fixture repair Job and wait for its outcome.
|
||||||
|
|
||||||
Inputs: `cfg` with namespace, fixture_pvc, and image (plus optional
|
Inputs: `cfg` with namespace, fixture_configmap, and image (plus optional
|
||||||
wait_timeout_seconds); the incident id and the failed build number that
|
wait_timeout_seconds); the incident id and the failed build number that
|
||||||
names the Job. Outputs: {"job_name", "succeeded", "error"}; a 409 on
|
names the Job. Outputs: {"job_name", "succeeded", "error"}; a 409 on
|
||||||
creation is reported as a "duplicate job" failure. Never raises.
|
creation is reported as a "duplicate job" failure. Never raises.
|
||||||
@ -80,7 +80,13 @@ def _job_payload(cfg: dict, job_name: str, incident_id: str) -> dict[str, Any]:
|
|||||||
{"event": "hermes_demo_repair", "incident_id": incident_id, "message": _REPAIR_MESSAGE},
|
{"event": "hermes_demo_repair", "incident_id": incident_id, "message": _REPAIR_MESSAGE},
|
||||||
separators=(",", ":"),
|
separators=(",", ":"),
|
||||||
)
|
)
|
||||||
script = f"printf 'healthy' > /fixture/state && echo '{marker}'"
|
namespace = str(cfg.get("namespace") or "")
|
||||||
|
fixture = str(cfg.get("fixture_configmap") or "hermes-triage-demo-fixture")
|
||||||
|
patch = '{"data":{"state":"healthy"}}'
|
||||||
|
script = (
|
||||||
|
f"kubectl -n {namespace} patch configmap {fixture} "
|
||||||
|
f"--type merge -p '{patch}' && echo '{marker}'"
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"apiVersion": "batch/v1",
|
"apiVersion": "batch/v1",
|
||||||
"kind": "Job",
|
"kind": "Job",
|
||||||
@ -99,19 +105,13 @@ def _job_payload(cfg: dict, job_name: str, incident_id: str) -> dict[str, Any]:
|
|||||||
"metadata": {"labels": {"app.kubernetes.io/part-of": "hermes-triage-demo"}},
|
"metadata": {"labels": {"app.kubernetes.io/part-of": "hermes-triage-demo"}},
|
||||||
"spec": {
|
"spec": {
|
||||||
"restartPolicy": "Never",
|
"restartPolicy": "Never",
|
||||||
|
"serviceAccountName": "hermes-demo-repair",
|
||||||
"nodeSelector": {"node-role.kubernetes.io/worker": "true"},
|
"nodeSelector": {"node-role.kubernetes.io/worker": "true"},
|
||||||
"containers": [
|
"containers": [
|
||||||
{
|
{
|
||||||
"name": "repair",
|
"name": "repair",
|
||||||
"image": str(cfg.get("image") or ""),
|
"image": str(cfg.get("image") or ""),
|
||||||
"command": ["sh", "-c", script],
|
"command": ["sh", "-c", script],
|
||||||
"volumeMounts": [{"name": "fixture", "mountPath": "/fixture"}],
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"volumes": [
|
|
||||||
{
|
|
||||||
"name": "fixture",
|
|
||||||
"persistentVolumeClaim": {"claimName": str(cfg.get("fixture_pvc") or "")},
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -190,7 +190,7 @@ class Settings:
|
|||||||
hermes_api_key: str
|
hermes_api_key: str
|
||||||
hermes_run_timeout_seconds: float
|
hermes_run_timeout_seconds: float
|
||||||
hermes_demo_namespace: str
|
hermes_demo_namespace: str
|
||||||
hermes_demo_fixture_pvc: str
|
hermes_demo_fixture_configmap: str
|
||||||
hermes_repair_image: str
|
hermes_repair_image: str
|
||||||
|
|
||||||
vaultwarden_namespace: str
|
vaultwarden_namespace: str
|
||||||
|
|||||||
@ -277,8 +277,8 @@ def _hermes_autotriage_config() -> dict[str, Any]:
|
|||||||
"hermes_api_key": _env("ARIADNE_HERMES_API_KEY", ""),
|
"hermes_api_key": _env("ARIADNE_HERMES_API_KEY", ""),
|
||||||
"hermes_run_timeout_seconds": _env_float("ARIADNE_HERMES_RUN_TIMEOUT_SECONDS", 420.0),
|
"hermes_run_timeout_seconds": _env_float("ARIADNE_HERMES_RUN_TIMEOUT_SECONDS", 420.0),
|
||||||
"hermes_demo_namespace": _env("ARIADNE_HERMES_DEMO_NAMESPACE", "hermes-triage-demo"),
|
"hermes_demo_namespace": _env("ARIADNE_HERMES_DEMO_NAMESPACE", "hermes-triage-demo"),
|
||||||
"hermes_demo_fixture_pvc": _env("ARIADNE_HERMES_DEMO_FIXTURE_PVC", "hermes-triage-demo-fixture"),
|
"hermes_demo_fixture_configmap": _env("ARIADNE_HERMES_DEMO_FIXTURE_CONFIGMAP", "hermes-triage-demo-fixture"),
|
||||||
"hermes_repair_image": _env("ARIADNE_HERMES_REPAIR_IMAGE", "busybox:1.37"),
|
"hermes_repair_image": _env("ARIADNE_HERMES_REPAIR_IMAGE", "bitnami/kubectl@sha256:554ab88b1858e8424c55de37ad417b16f2a0e65d1607aa0f3fe3ce9b9f10b131"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,7 @@ def _settings(**overrides) -> SimpleNamespace: # type: ignore[no-untyped-def]
|
|||||||
"hermes_api_key": "key",
|
"hermes_api_key": "key",
|
||||||
"hermes_run_timeout_seconds": 420.0,
|
"hermes_run_timeout_seconds": 420.0,
|
||||||
"hermes_demo_namespace": "hermes-triage-demo",
|
"hermes_demo_namespace": "hermes-triage-demo",
|
||||||
"hermes_demo_fixture_pvc": "hermes-triage-demo-fixture",
|
"hermes_demo_fixture_configmap": "hermes-triage-demo-fixture",
|
||||||
"hermes_repair_image": "busybox:1.37",
|
"hermes_repair_image": "busybox:1.37",
|
||||||
"jenkins_base_url": "https://ci.example",
|
"jenkins_base_url": "https://ci.example",
|
||||||
"jenkins_api_user": "user",
|
"jenkins_api_user": "user",
|
||||||
@ -261,7 +261,7 @@ def test_new_failure_full_happy_path(monkeypatch) -> None:
|
|||||||
(
|
(
|
||||||
{
|
{
|
||||||
"namespace": "hermes-triage-demo",
|
"namespace": "hermes-triage-demo",
|
||||||
"fixture_pvc": "hermes-triage-demo-fixture",
|
"fixture_configmap": "hermes-triage-demo-fixture",
|
||||||
"image": "busybox:1.37",
|
"image": "busybox:1.37",
|
||||||
},
|
},
|
||||||
INCIDENT_ID,
|
INCIDENT_ID,
|
||||||
|
|||||||
@ -29,8 +29,8 @@ class FakeResponse:
|
|||||||
def _cfg(**overrides) -> dict: # type: ignore[no-untyped-def]
|
def _cfg(**overrides) -> dict: # type: ignore[no-untyped-def]
|
||||||
base = {
|
base = {
|
||||||
"namespace": "hermes-triage-demo",
|
"namespace": "hermes-triage-demo",
|
||||||
"fixture_pvc": "hermes-triage-demo-fixture",
|
"fixture_configmap": "hermes-triage-demo-fixture",
|
||||||
"image": "busybox:1.37",
|
"image": "bitnami/kubectl@sha256:554ab88b1858e8424c55de37ad417b16f2a0e65d1607aa0f3fe3ce9b9f10b131",
|
||||||
}
|
}
|
||||||
base.update(overrides)
|
base.update(overrides)
|
||||||
return base
|
return base
|
||||||
@ -93,18 +93,18 @@ def test_repair_job_payload_contract(monkeypatch) -> None:
|
|||||||
pod = spec["template"]["spec"]
|
pod = spec["template"]["spec"]
|
||||||
assert pod["restartPolicy"] == "Never"
|
assert pod["restartPolicy"] == "Never"
|
||||||
assert pod["nodeSelector"] == {"node-role.kubernetes.io/worker": "true"}
|
assert pod["nodeSelector"] == {"node-role.kubernetes.io/worker": "true"}
|
||||||
|
assert pod["serviceAccountName"] == "hermes-demo-repair"
|
||||||
container = pod["containers"][0]
|
container = pod["containers"][0]
|
||||||
assert container["image"] == "busybox:1.37"
|
assert container["image"].startswith("bitnami/kubectl@sha256:")
|
||||||
assert container["command"][:2] == ["sh", "-c"]
|
assert container["command"][:2] == ["sh", "-c"]
|
||||||
script = container["command"][2]
|
script = container["command"][2]
|
||||||
assert "printf 'healthy' > /fixture/state" in script
|
assert "kubectl -n hermes-triage-demo patch configmap hermes-triage-demo-fixture" in script
|
||||||
|
assert '{"data":{"state":"healthy"}}' in script
|
||||||
assert '"event":"hermes_demo_repair"' in script
|
assert '"event":"hermes_demo_repair"' in script
|
||||||
assert f'"incident_id":"{INCIDENT_ID}"' in script
|
assert f'"incident_id":"{INCIDENT_ID}"' in script
|
||||||
assert '"message":"fixture state reset to healthy"' in script
|
assert '"message":"fixture state reset to healthy"' in script
|
||||||
assert container["volumeMounts"] == [{"name": "fixture", "mountPath": "/fixture"}]
|
assert "volumeMounts" not in container
|
||||||
assert pod["volumes"] == [
|
assert "volumes" not in pod
|
||||||
{"name": "fixture", "persistentVolumeClaim": {"claimName": "hermes-triage-demo-fixture"}}
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def test_execute_repair_waits_through_active_polls(monkeypatch) -> None:
|
def test_execute_repair_waits_through_active_polls(monkeypatch) -> None:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user