mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-11-01 05:15:22 +08:00
fix(regression): confirm Box removal before skipping inspect errors
Confirm Box container removal before tolerating docker inspect races.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user