diff --git a/tools/regression_traffic.py b/tools/regression_traffic.py index 3dd4c6b2f..8ac8e9520 100644 --- a/tools/regression_traffic.py +++ b/tools/regression_traffic.py @@ -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) diff --git a/tools/test_regression_containers.py b/tools/test_regression_containers.py index ff1e35718..56a241227 100644 --- a/tools/test_regression_containers.py +++ b/tools/test_regression_containers.py @@ -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'),