mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 13:47:19 +08:00
Enhance Docker image pull logging with periodic progress updates (#11750)
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user