From 11855b765ccd4a6586449084ce8823bcf06c487e Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Fri, 22 May 2026 01:31:14 -0700 Subject: [PATCH 1/2] nixos/test-driver: null out self.pid when shutting down the container Without this, we end up with `self.pid` set to the pid of a dead process, and `self.process == None`. This causes us to blow [this assert](https://github.com/jfly/nixpkgs/blob/dd51e81af9226ee80e6f0891f4649e12ea15dfd0/nixos/lib/test-driver/src/test_driver/machine/__init__.py#L1496) in `NspawnMachine.release`. It would probably be cleaner to just get rid of `self.pid`. It's redundant given that we have `self.process`. I'll do that in a followup commit. This was discovered here: https://github.com/NixOS/nixpkgs/pull/522886#discussion_r3285395646, and will be protected against future regression by a test introduced in that PR. --- nixos/lib/test-driver/src/test_driver/machine/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nixos/lib/test-driver/src/test_driver/machine/__init__.py b/nixos/lib/test-driver/src/test_driver/machine/__init__.py index 3dc4f1f8d171..17f19f5d3068 100644 --- a/nixos/lib/test-driver/src/test_driver/machine/__init__.py +++ b/nixos/lib/test-driver/src/test_driver/machine/__init__.py @@ -1721,6 +1721,7 @@ class NspawnMachine(BaseMachine): with self.nested("waiting for the container to power off"): self.process.wait() self.process = None + self.pid = None class MachineDeprecationWrapper: From b0ac1aa86405364fb2baa6bbec8952328424c34c Mon Sep 17 00:00:00 2001 From: Jeremy Fleischman Date: Fri, 22 May 2026 01:36:11 -0700 Subject: [PATCH 2/2] nixos/test-driver: remove redundant `self.pid` state from `NspawnMachine` We can access it via `self.process`, and this avoids bugs where we weren't keeping the 2 in sync. --- .../test-driver/src/test_driver/machine/__init__.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/nixos/lib/test-driver/src/test_driver/machine/__init__.py b/nixos/lib/test-driver/src/test_driver/machine/__init__.py index 17f19f5d3068..790da705cf4f 100644 --- a/nixos/lib/test-driver/src/test_driver/machine/__init__.py +++ b/nixos/lib/test-driver/src/test_driver/machine/__init__.py @@ -1475,7 +1475,6 @@ class NspawnMachine(BaseMachine): self.start_command = start_command self.process = None - self.pid = None self.machine_sock_path = self.tmp_dir / f"{self.name}-nspawn.sock" @@ -1486,14 +1485,13 @@ class NspawnMachine(BaseMachine): return f'ssh -o User=root -o ProxyCommand="{proxy_cmd}" bash' def release(self) -> None: - if self.pid is None: + if self.process is None: return if self.machine_sock: self.machine_sock.close() - self.logger.info(f"kill NspawnMachine (pid {self.pid})") - assert self.process is not None + self.logger.info(f"kill NspawnMachine (pid {self.process.pid})") self.process.terminate() # Wait for the wrapper to finish its context-manager cleanups # (veth/bridge/netns teardown) before returning, so the driver's @@ -1502,7 +1500,7 @@ class NspawnMachine(BaseMachine): self.process.wait(timeout=30) except subprocess.TimeoutExpired: self.logger.error( - f"NspawnMachine {self.name} (pid {self.pid}) did not exit after SIGTERM; sending SIGKILL" + f"NspawnMachine {self.name} (pid {self.process.pid}) did not exit after SIGTERM; sending SIGKILL" ) self.process.kill() self.process.wait() @@ -1694,9 +1692,7 @@ class NspawnMachine(BaseMachine): stdout=subprocess.PIPE, ) - self.pid = self.process.pid - - self.log(f"systemd-nspawn running (pid {self.pid})") + self.log(f"systemd-nspawn running (pid {self.process.pid})") journal_thread = threading.Thread(target=self._stream_journal, daemon=True) journal_thread.start() @@ -1721,7 +1717,6 @@ class NspawnMachine(BaseMachine): with self.nested("waiting for the container to power off"): self.process.wait() self.process = None - self.pid = None class MachineDeprecationWrapper: