Hotfix: stale-container cleanup must not delete infra containers

Worker startup cleanup previously deleted every container whose
name started with codex-, including the codex-postgres database
container. On the external host the first worker restart after
the prod-grade deploy deleted codex-postgres, leaving the
control plane unable to connect to Postgres (connection refused
on 127.0.0.1:5432, server auto-restart loop).
Fix: cleanup only deletes codex-<32-hex task id> containers,
which is exactly the name DockerExecutor gives task containers;
infrastructure names like codex-postgres never match and are
never deleted. Regression test covers postgres + non-hex names.
This commit is contained in:
host committed 2026-10-26 03:59:54 +08:00
1 parent 5a6d136ce4
commit 29dcae4513
2 files changed
+26 -7

No files matched your search

+14 -6
View File
@@ -212,18 +212,26 @@ class ContainerCleanupTestCase(unittest.TestCase):
executor = DockerExecutor(runner=runner)
return executor, calls
def test_cleanup_lists_by_prefix_and_removes(self):
def test_cleanup_only_task_containers_never_infra(self):
# 回归:2026-10-26 实测,按 codex- 前缀全删会把数据库容器
# codex-postgres 当成残留任务容器杀掉,导致控制面连不上
# Postgres。清理只能删 codex-<32 位 hex 任务 id>。
executor, calls = self._docker_executor(
ps_stdout="codex-abc\nother\ncodex-def\n")
ps_stdout="codex-postgres\n"
"codex-3689cf40d671492992c6bfedd18b004c\n"
"codex-abc\nother\ncodex-app-server\n")
cleaned = executor.cleanup_stale_containers()
self.assertEqual(cleaned, ["codex-abc", "codex-def"])
self.assertEqual(
cleaned, ["codex-3689cf40d671492992c6bfedd18b004c"])
flat = [c for call in calls for c in call]
self.assertNotIn("codex-postgres", flat)
self.assertNotIn("codex-abc", flat)
self.assertNotIn("other", flat)
# kill + rm 各调一次(每个容器)。
# kill + rm 各调一次(仅那个任务容器)。
self.assertEqual(
sum(1 for call in calls if call[1] == "kill"), 2)
sum(1 for call in calls if call[1] == "kill"), 1)
self.assertEqual(
sum(1 for call in calls if call[1] == "rm"), 2)
sum(1 for call in calls if call[1] == "rm"), 1)
def test_cleanup_failure_does_not_crash_worker(self):
executor, _ = self._docker_executor(fail_ps=True)