NK-WP-0056-T06: executor waits for every endpoint; errors named by phase, never mislabelled as no-effect
All checks were successful
All checks were successful
Two live runs of BEA-2026-0002 ended setup:URLError while four read-only diagnostic pods (same image, labels, mounts and config) reached every endpoint. Likely cause: a fresh Job pod reaches OpenBao before the sso/approval-engine ingress rules admit it (NetworkPolicy is programmed per destination). Now: - build waits until OpenBao, KeyCape, approval-engine, audit-core and LLDAP all answer HTTP (one shared 30 s budget); an unreachable one is named: setup:<name> not reachable. - a preflight error is a refusal named preflight_error:<Type> (preflight has no effect). - an error in the consume read-back is outcome unknown, consume_unconfirmed:<Type>, alerted; before, any exception after setup was reported as 'refused, no effect' even after consume. - any other escaping error after setup is outcome unknown (exit 5), never 'no effect'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 523928@bnt-lap001 Assistant-Session: 6c737afb-9c14-4152-9f02-477214b9c350
This commit is contained in:
parent
a962bc8ecc
commit
c2a0f2f4e0
5 changed files with 116 additions and 9 deletions
|
|
@ -402,6 +402,30 @@ class FailureTests(Base):
|
|||
self.assertIn("restore-kv", self.ops())
|
||||
self.assertNotIn("deliver", self.ops())
|
||||
|
||||
def test_preflight_error_is_a_named_refusal(self):
|
||||
def broken(_id):
|
||||
raise ConnectionRefusedError()
|
||||
self.w.carrier.get = broken
|
||||
result = self.run_it()
|
||||
self.assertEqual((result["outcome"], result["reason"], result["effects"]),
|
||||
("refused", "preflight_error:ConnectionRefusedError", False))
|
||||
self.assertEqual(self.w.carrier.consumes, 0)
|
||||
|
||||
def test_consume_read_back_error_is_unknown_never_no_effect(self):
|
||||
real_get, calls = self.w.carrier.get, []
|
||||
def get(approval_id):
|
||||
calls.append(approval_id)
|
||||
if len(calls) > 1: # preflight reads once; the consume read-back fails
|
||||
raise ConnectionResetError()
|
||||
return real_get(approval_id)
|
||||
self.w.carrier.get = get
|
||||
self.w.carrier.raise_on_consume = True
|
||||
result = self.run_it()
|
||||
self.assertEqual((result["outcome"], result["reason"], result["effects"]),
|
||||
("unknown", "consume_unconfirmed:ConnectionResetError", "consume-unknown"))
|
||||
self.assertIn("consume_unconfirmed", self.w.alerts)
|
||||
self.assertEqual(self.w.events, [])
|
||||
|
||||
def test_stop_before_consume_spends_nothing(self):
|
||||
self.w.stop_after = 0
|
||||
result = self.run_it()
|
||||
|
|
|
|||
|
|
@ -92,14 +92,19 @@ class CliLanes(Base):
|
|||
return code, json.loads(out.getvalue())
|
||||
|
||||
def cfg(self, journey_role="journey-read"):
|
||||
# lldap is missing on purpose: setup fails after both logins
|
||||
# every endpoint answers (the fake answers any GET); setup fails after both logins, at the Kubernetes client
|
||||
# (no in-cluster service-account CA outside a pod)
|
||||
return {"openbao": {"addr": self.addr, "k8s_role": "bind-write", "jwt_file": str(self.root / "jwt")},
|
||||
"journey": {"kv": "x", "k8s_role": journey_role, "config": {}}}
|
||||
"journey": {"kv": "x", "k8s_role": journey_role, "config": {}},
|
||||
"lldap": {"url": self.addr}, "audit_core": {"url": self.addr, "token_file": str(self.root / "jwt")},
|
||||
"approval_engine": {"url": self.addr, "token_file": str(self.root / "jwt")}}
|
||||
|
||||
def test_two_lanes_two_logins_both_revoked_when_setup_fails(self):
|
||||
code, result = self.run_cli(self.cfg())
|
||||
self.assertEqual((code, result["reason"]), (4, "setup:KeyError"))
|
||||
self.assertEqual((code, result["reason"]), (4, "setup:FileNotFoundError"))
|
||||
self.assertEqual([role for role, _ in self.bao.logins], ["bind-write", "journey-read"])
|
||||
for path in ("/v1/sys/health", "/", "/healthz"): # every endpoint was waited for before any login
|
||||
self.assertIn(path, [p for p, _ in self.bao.reads])
|
||||
self.assertEqual(sorted(self.bao.revoked), ["tok-bind-write-1", "tok-journey-read-2"])
|
||||
|
||||
def test_one_role_for_both_lanes_is_refused_before_any_login(self):
|
||||
|
|
@ -244,3 +249,39 @@ class JourneyUsesItsOwnLane(unittest.TestCase):
|
|||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class EveryEndpointReachable(Base):
|
||||
def test_unreachable_endpoint_is_named_and_nothing_logs_in(self):
|
||||
closed = socket_port()
|
||||
cfg = {"openbao": {"addr": self.addr, "k8s_role": "bind-write", "jwt_file": str(self.root / "jwt")},
|
||||
"journey": {"kv": "x", "k8s_role": "journey-read", "config": {}},
|
||||
"lldap": {"url": self.addr}, "audit_core": {"url": self.addr, "token_file": "x"},
|
||||
"approval_engine": {"url": self.addr, "client": {"token_url": f"http://127.0.0.1:{closed}/token",
|
||||
"client_id": "c", "k8s_role": "a", "kv": "k"}}}
|
||||
original = OPS.wait_reachable
|
||||
self.addCleanup(setattr, OPS, "wait_reachable", original)
|
||||
OPS.wait_reachable = lambda *a, **k: original(*a, **{**k, "timeout": 0.2, "interval": 0.05})
|
||||
(self.root / "cfg.json").write_text(json.dumps(cfg))
|
||||
admission = CLI.X.REPO / "examples/bounded-execution-admission/bind-rotation-svc-authelia.yaml"
|
||||
with contextlib.redirect_stdout(io.StringIO()) as out:
|
||||
code = CLI.main(["--admission", str(admission), "--approval-id", "apr-1", "--config", str(self.root / "cfg.json")])
|
||||
result = json.loads(out.getvalue())
|
||||
self.assertEqual((code, result["reason"], result["effects"]), (4, "setup:keycape not reachable", False))
|
||||
self.assertEqual(self.bao.logins, [])
|
||||
|
||||
def test_endpoints_cover_every_service_the_run_uses(self):
|
||||
cfg = {"openbao": {"addr": "http://bao:8200"}, "lldap": {"url": "http://lldap:17170"},
|
||||
"audit_core": {"url": "http://audit:8080"},
|
||||
"approval_engine": {"url": "http://ae:8080", "client": {"token_url": "http://kc:8080/token"}}}
|
||||
self.assertEqual(CLI.run_endpoints(cfg), {
|
||||
"openbao": ("http://bao:8200", "/v1/sys/health"), "lldap": ("http://lldap:17170", "/"),
|
||||
"audit-core": ("http://audit:8080", "/healthz"), "approval-engine": ("http://ae:8080", "/healthz"),
|
||||
"keycape": ("http://kc:8080", "/healthz")})
|
||||
|
||||
|
||||
def socket_port():
|
||||
import socket
|
||||
with socket.socket() as sock:
|
||||
sock.bind(("127.0.0.1", 0))
|
||||
return sock.getsockname()[1]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue