All checks were successful
Bounded-execution contracts / contracts (push) Successful in 1m42s
Build and Publish bounded-executor / build-and-push (push) Successful in 5s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Identity provider journey acceptance / provider (push) Successful in 53s
The first two watch runs after the deploy (2026-11-06 09:20Z, 09:30Z) exited 5 with a bare URLError: k3s programs a new pod's NetworkPolicy a few seconds late (as for the executor, 2026-10-31). The live policies admit the watch pod to both endpoints. NK-WP-0058-T06. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 943911@bnt-lap001 Assistant-Session: fc322940-1c8e-4bed-ab39-86cec99f23a0
134 lines
5.6 KiB
Python
134 lines
5.6 KiB
Python
"""window_watch.py on stubs (NK-WP-0058-T06, contract row O5): rule, both modes, and errors."""
|
|
import datetime as dt
|
|
import importlib.util
|
|
import pathlib
|
|
import sys
|
|
import unittest
|
|
|
|
HERE = pathlib.Path(__file__).resolve().parent
|
|
sys.path.insert(0, str(HERE.parent))
|
|
spec = importlib.util.spec_from_file_location('window_watch', HERE.parent / 'window_watch.py')
|
|
W = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(W)
|
|
OPS = W.OPS
|
|
NOW = dt.datetime(2026, 11, 6, 3, 0, tzinfo=dt.timezone.utc)
|
|
|
|
|
|
def job(minutes_ago, active=0):
|
|
created = (NOW - dt.timedelta(minutes=minutes_ago)).isoformat().replace('+00:00', 'Z')
|
|
return {'metadata': {'creationTimestamp': created}, 'status': {'active': active} if active else {}}
|
|
|
|
|
|
class FakeBao:
|
|
def __init__(self, present=True, delete_works=True):
|
|
self.present, self.delete_works, self.calls = present, delete_works, []
|
|
|
|
def _call(self, method, path, body=None, auth=True):
|
|
self.calls.append((method, path))
|
|
assert path == W.ROLE_PATH, path # nothing but the write role is ever touched
|
|
if method == 'GET':
|
|
if not self.present:
|
|
raise OPS.OperationError('openbao GET 404')
|
|
return {'data': {}}
|
|
if method == 'DELETE':
|
|
self.present = not self.delete_works
|
|
return {}
|
|
raise AssertionError(method)
|
|
|
|
|
|
class FakeKube:
|
|
def __init__(self, jobs):
|
|
self.jobs, self.paths = jobs, []
|
|
|
|
def get(self, path):
|
|
self.paths.append(path)
|
|
return {'items': self.jobs}
|
|
|
|
|
|
class Rule(unittest.TestCase):
|
|
def test_decide(self):
|
|
cases = [
|
|
(False, [], 'ok'), # no role: nothing open
|
|
(True, [], 'stale'), # role, never a run: left open
|
|
(True, [job(3)], 'ok'), # role, run just created: a window in use
|
|
(True, [job(30, active=1)], 'ok'), # role, run still active
|
|
(True, [job(30)], 'stale'), # role, last run finished 30 min ago: left open
|
|
(True, [job(30), job(2)], 'ok'), # the newest run counts
|
|
]
|
|
for present, jobs, verdict in cases:
|
|
with self.subTest(present=present, jobs=len(jobs)):
|
|
self.assertEqual(W.decide(present, jobs, NOW)[0], verdict)
|
|
|
|
def test_run_jobs_selector_excludes_other_components(self):
|
|
kube = FakeKube([])
|
|
W.run_jobs(kube)
|
|
self.assertIn('labelSelector=app.kubernetes.io%2Fname%3Dbounded-executor%2C%21app.kubernetes.io%2Fcomponent',
|
|
kube.paths[0])
|
|
self.assertTrue(kube.paths[0].startswith('/apis/batch/v1/namespaces/sso/jobs?'))
|
|
|
|
|
|
class Modes(unittest.TestCase):
|
|
def test_ok_touches_nothing(self):
|
|
bao = FakeBao(present=False)
|
|
result, code = W.watch(bao, FakeKube([]), 'close', NOW)
|
|
self.assertEqual((result['status'], code), ('ok', 0))
|
|
self.assertEqual([m for m, _ in bao.calls], ['GET'])
|
|
|
|
def test_flag_reads_only_and_fails_the_job(self):
|
|
bao = FakeBao()
|
|
result, code = W.watch(bao, FakeKube([job(30)]), 'flag', NOW)
|
|
self.assertEqual((result['status'], code), ('flagged_stale', 3))
|
|
self.assertEqual([m for m, _ in bao.calls], ['GET'])
|
|
self.assertEqual(result['newest_age_s'], 1800)
|
|
|
|
def test_close_deletes_and_proves_absent(self):
|
|
bao = FakeBao()
|
|
result, code = W.watch(bao, FakeKube([]), 'close', NOW)
|
|
self.assertEqual((result['status'], code), ('closed_stale', 0))
|
|
self.assertEqual([m for m, _ in bao.calls], ['GET', 'DELETE', 'GET'])
|
|
self.assertFalse(bao.present)
|
|
|
|
def test_close_that_does_not_take_is_an_error(self):
|
|
result, code = W.watch(FakeBao(delete_works=False), FakeKube([]), 'close', NOW)
|
|
self.assertEqual((result['status'], result['reason'], code), ('error', 'write_role_still_present', 5))
|
|
|
|
def test_a_window_in_use_is_never_closed(self):
|
|
bao = FakeBao()
|
|
result, code = W.watch(bao, FakeKube([job(1, active=1)]), 'close', NOW)
|
|
self.assertEqual((result['status'], code), ('ok', 0))
|
|
self.assertTrue(bao.present)
|
|
|
|
def test_other_openbao_errors_are_not_read_as_absent(self):
|
|
class Denied(FakeBao):
|
|
def _call(self, method, path, body=None, auth=True):
|
|
raise OPS.OperationError('openbao GET 403')
|
|
with self.assertRaises(OPS.OperationError):
|
|
W.watch(Denied(), FakeKube([]), 'close', NOW)
|
|
|
|
|
|
class Network(unittest.TestCase):
|
|
"""2026-11-06: the first watch runs failed with a bare URLError before the pod's NetworkPolicy was live."""
|
|
|
|
def test_waits_for_both_endpoints_with_the_api_ca(self):
|
|
calls = []
|
|
W.wait_for_network('http://openbao.openbao.svc:8200', wait=lambda addr, **kw: calls.append((addr, kw)))
|
|
self.assertEqual([c[0] for c in calls], ['http://openbao.openbao.svc:8200', W.KUBE_API])
|
|
self.assertEqual(calls[1][1]['ca_file'], W.KUBE_CA)
|
|
self.assertTrue(all(c[1]['timeout'] == 30 for c in calls))
|
|
|
|
def test_an_unreachable_endpoint_is_named_in_the_line(self):
|
|
import contextlib, io, json as js
|
|
original = W.wait_for_network
|
|
|
|
def unreachable(addr, wait=None):
|
|
raise OPS.Unreachable('openbao not reachable')
|
|
W.wait_for_network = unreachable
|
|
self.addCleanup(setattr, W, 'wait_for_network', original)
|
|
with contextlib.redirect_stdout(io.StringIO()) as out:
|
|
code = W.main(['--mode', 'flag'])
|
|
self.assertEqual(code, 5)
|
|
self.assertEqual(js.loads(out.getvalue())['reason'], 'openbao not reachable')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|