fix(regression): confirm Box removal before skipping inspect errors

Confirm Box container removal before tolerating docker inspect races.
This commit is contained in:
huanghuoguoguo
2026-10-31 09:47:02 +08:00
committed by GitHub
parent 566952c0f7
commit 306e154256
2 changed files with 23 additions and 7 deletions
+10 -7
View File
@@ -38,6 +38,13 @@ def missing_containers(error, candidates):
return {messages[line] for line in lines} if lines and all(line in messages for line in lines) else set()
def container_is_absent(container, checked):
try:
return not checked(['docker', 'ps', '-a', '--filter', f'id={container}', '--format', '{{.ID}}']).strip()
except Exception:
return False
def owned_box_containers(data_root, checked=None):
"""Discover sandboxes mounted below one run's data root, including newly created groups."""
if checked is None:
@@ -54,15 +61,11 @@ def owned_box_containers(data_root, checked=None):
try:
description = checked(['docker', 'inspect', '--format', '{{json .}}', container])
except subprocess.CalledProcessError as exc:
if (
exc.returncode != 1
or (exc.output or '').strip()
or not missing_containers(exc.stderr or '', {container})
):
if exc.returncode != 1 or (exc.output or '').strip() or not container_is_absent(container, checked):
raise
continue # A sandbox may expire between discovery and inspection.
except RuntimeError as exc:
if not missing_containers(str(exc), {container}):
except RuntimeError:
if not container_is_absent(container, checked):
raise
continue
entry = json.loads(description)
+13
View File
@@ -100,12 +100,25 @@ class ContainerSamplingTests(unittest.TestCase):
side_effect=[
'expired live foreign',
error,
'',
description('live', '/owned/box/session'),
description('foreign', '/owned/box-other/session'),
]
)
self.assertEqual(owned_box_containers('/owned/box', checked), ['live'])
def test_unknown_inspect_error_skips_only_after_relist_confirms_removal(self):
error = subprocess.CalledProcessError(1, ['docker', 'inspect'], '', '')
checked = Mock(side_effect=['expired', error, ''])
self.assertEqual(owned_box_containers('/owned/box', checked), [])
def test_unknown_inspect_error_is_preserved_when_container_still_exists(self):
error = subprocess.CalledProcessError(1, ['docker', 'inspect'], '', 'permission denied')
checked = Mock(side_effect=['live', error, 'live'])
with self.assertRaises(subprocess.CalledProcessError) as caught:
owned_box_containers('/owned/box', checked)
self.assertIs(caught.exception, error)
def test_inspect_errors_are_not_reported_as_expiry(self):
for error in (
subprocess.CalledProcessError(1, [], '', 'permission denied'),