fix(sidecar_init): log after spawn in start_all to close SIGTERM race
lint / lint (push) Successful in 2m2s
test / unit (pull_request) Successful in 1m4s
test / integration (pull_request) Successful in 20s
test / coverage (pull_request) Successful in 1m13s

`start_all()` was logging "starting {name}" before appending the
process to `self.procs`, so a SIGTERM arriving between the log write
and the append would call `request_shutdown()` with the new daemon
absent from `self.procs` and therefore never forwarded SIGTERM.
Moving the log after the append eliminates the window.

Signal handlers are also moved before `sup.start_all()` in `main()` so
they are registered before any child is spawned; previously there was a
window where SIGTERM used the default handler (silent process death) if
it arrived between `start_all()` returning and `signal.signal()`.

`TestMainEndToEnd._run` is updated to use a reader thread and a
`wait_for_output` synchronisation barrier instead of a fixed sleep, so
`test_sigterm_clean_shutdown` does not race against slow CI startup time.
This commit is contained in:
2026-07-09 19:57:14 +00:00
parent d0eded11d0
commit a245d8a392
2 changed files with 43 additions and 8 deletions
+2 -3
View File
@@ -167,8 +167,8 @@ class _Supervisor:
def start_all(self) -> None:
for spec in self.specs:
_log(f"starting {spec.name}")
self.procs.append((spec, _spawn(spec)))
_log(f"starting {spec.name}")
def request_shutdown(self, reason: str) -> None:
if self.shutdown_at is not None:
@@ -353,8 +353,6 @@ def main(argv: Sequence[str] | None = None) -> int:
return 0
sup = _Supervisor(specs)
sup.start_all()
signal.signal(signal.SIGTERM, lambda *_: sup.request_shutdown("SIGTERM")) # type: ignore
signal.signal(signal.SIGINT, lambda *_: sup.request_shutdown("SIGINT")) # type: ignore
# SIGHUP reload path: egress_apply.py runs `docker kill
@@ -362,6 +360,7 @@ def main(argv: Sequence[str] | None = None) -> int:
# delivers SIGHUP to PID 1 (this supervisor); forward it to
# mitmdump so it reloads its addon.
signal.signal(signal.SIGHUP, lambda *_: sup.forward_signal(signal.SIGHUP, "egress")) # type: ignore
sup.start_all()
while not sup.tick():
time.sleep(_POLL_INTERVAL)