Enhance Docker image pull logging with periodic progress updates (#11750)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Tim O'Farrell
2025-11-17 10:15:21 +00:00
committed by GitHub
parent d6fab190bf
commit 1f6ef8175b
3 changed files with 127 additions and 6 deletions

View File

@@ -447,3 +447,85 @@ class TestDockerSandboxSpecServiceInjector:
# Verify no Docker operations were performed
mock_get_docker_client.assert_not_called()
mock_docker_client.images.get.assert_not_called()
@patch('openhands.app_server.sandbox.docker_sandbox_spec_service.get_docker_client')
@patch('openhands.app_server.sandbox.docker_sandbox_spec_service._logger')
async def test_pull_with_progress_logging(
self, mock_logger, mock_get_docker_client, sample_spec
):
"""Test that periodic progress logging occurs during image pull."""
# Setup
mock_docker_client = MagicMock()
mock_get_docker_client.return_value = mock_docker_client
mock_docker_client.images.get.side_effect = ImageNotFound('Image not found')
# Create a future that will be resolved after some delay to simulate slow pull
pull_future = asyncio.Future()
async def delayed_pull_completion():
# Wait for multiple logging intervals to pass
await asyncio.sleep(12) # 12 seconds = 2 logging intervals (5s each)
pull_future.set_result(MagicMock())
# Start the delayed completion task
asyncio.create_task(delayed_pull_completion())
# Mock the executor to return our delayed future
with patch('asyncio.get_running_loop') as mock_get_loop:
mock_loop = MagicMock()
mock_get_loop.return_value = mock_loop
mock_loop.run_in_executor.return_value = pull_future
injector = DockerSandboxSpecServiceInjector()
# Execute
await injector.pull_spec_if_missing(sample_spec)
# Verify that progress logging occurred
# Should have initial pull message, progress messages, and completion message
progress_calls = [
call
for call in mock_logger.info.call_args_list
if '🔄 Downloading Docker Image:' in str(call)
]
# Should have at least 2 progress log messages (every 5 seconds for 12 seconds)
assert len(progress_calls) >= 2
# Verify the progress message format
for call in progress_calls:
assert '🔄 Downloading Docker Image: test-image:latest...' in str(call)
@patch('openhands.app_server.sandbox.docker_sandbox_spec_service.get_docker_client')
@patch('openhands.app_server.sandbox.docker_sandbox_spec_service._logger')
async def test_pull_with_progress_logging_fast_pull(
self, mock_logger, mock_get_docker_client, sample_spec
):
"""Test that no progress logging occurs for fast pulls (< 5 seconds)."""
# Setup
mock_docker_client = MagicMock()
mock_get_docker_client.return_value = mock_docker_client
mock_docker_client.images.get.side_effect = ImageNotFound('Image not found')
# Mock fast pull (completes immediately)
with patch('asyncio.get_running_loop') as mock_get_loop:
mock_loop = MagicMock()
mock_get_loop.return_value = mock_loop
fast_future = asyncio.Future()
fast_future.set_result(MagicMock())
mock_loop.run_in_executor.return_value = fast_future
injector = DockerSandboxSpecServiceInjector()
# Execute
await injector.pull_spec_if_missing(sample_spec)
# Verify that no progress logging occurred (only start/end messages)
progress_calls = [
call
for call in mock_logger.info.call_args_list
if '🔄 Downloading Docker Image:' in str(call)
]
# Should have no progress log messages for fast pulls
assert len(progress_calls) == 0