feat(harness): P1 lock-lifetime hardening — PDEATHSIG + SIGTERM/SIGALRM teardown funnel + 60-min hard deadline
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- new harness/lifetime.py: install_lifetime_guards() arms PR_SET_PDEATHSIG(SIGTERM) (with post-prctl ppid==1 orphan refusal), a SIGTERM handler raising SystemExit through the run's finally: teardown funnel (exit 143), and signal.alarm(3600) funnelling SIGALRM the same way with a distinct deadline log line (exit 142). Re-entrant signals during teardown are logged and ignored (begin_teardown guard) so a second signal can't abort the running cleanup. - run_recipe_ci.main(): guards installed first thing, before any abra call/lock; both teardown finally: blocks (cold + quick) mark begin_teardown(). - .drone.yml recipe-ci step: harness runs under setsid in its own process group; a trap forwards the step shell's TERM/EXIT to the whole group so drone cancel reaches the harness instead of leaking it (docs/concurrency.md §8.1). - PEP 446 note on the recipe-lock open(): the fd is non-inheritable, children never carry it.
This commit is contained in:
@ -47,6 +47,7 @@ from harness import ( # noqa: E402
|
||||
discovery,
|
||||
generic,
|
||||
lifecycle,
|
||||
lifetime,
|
||||
naming,
|
||||
warm,
|
||||
warmsnap,
|
||||
@ -658,6 +659,8 @@ def run_quick(
|
||||
results["upgrade"] = "fail"
|
||||
results["custom"] = "skip"
|
||||
finally:
|
||||
# Teardown funnel running: further SIGTERM/SIGALRM are logged + ignored (lifetime.py).
|
||||
lifetime.begin_teardown()
|
||||
# F2-11 skip count (read before deciding pass/fail)
|
||||
requires_deps_skipped = 0
|
||||
try:
|
||||
@ -821,6 +824,9 @@ def promote_canonical(recipe: str, head_ref: str | None) -> None:
|
||||
|
||||
|
||||
def main() -> int:
|
||||
# P1 lock-lifetime hardening: PDEATHSIG + SIGTERM/SIGALRM teardown funnel + 60-min hard
|
||||
# deadline, armed before ANY abra call or lock acquisition (see harness/lifetime.py).
|
||||
lifetime.install_lifetime_guards()
|
||||
recipe = os.environ.get("RECIPE")
|
||||
if not recipe:
|
||||
print("RECIPE env is required", file=sys.stderr)
|
||||
@ -1123,6 +1129,9 @@ def main() -> int:
|
||||
if op in stages:
|
||||
results[op] = "skip"
|
||||
finally:
|
||||
# From here the teardown funnel runs: a SIGTERM/SIGALRM landing now is logged + ignored
|
||||
# (lifetime.py) so a second signal can't abort the cleanup the first one asked for.
|
||||
lifetime.begin_teardown()
|
||||
# Teardown the recipe under test FIRST, then deps in reverse declaration order.
|
||||
# Parent verify=False (Phase 1d): keep as-is so a parent residual doesn't mask a tier
|
||||
# failure. Dep teardown uses verify=True via teardown_deps (F2-5 fix); failures are
|
||||
|
||||
Reference in New Issue
Block a user