From 6a7521dc588a018486170386c31bdd5aac0c12be Mon Sep 17 00:00:00 2001 From: Antonio Rivero Date: Thu, 5 Feb 2026 11:34:48 +0100 Subject: [PATCH 1/2] chore(aws): Increase healthcheck timeout to 20 mins --- showtime/__init__.py | 2 +- showtime/core/aws.py | 3 +- tests/unit/test_aws_health_check.py | 59 +++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/unit/test_aws_health_check.py diff --git a/showtime/__init__.py b/showtime/__init__.py index 1747151..12b555a 100644 --- a/showtime/__init__.py +++ b/showtime/__init__.py @@ -4,7 +4,7 @@ Circus tent emoji state tracking for Apache Superset ephemeral environments. """ -__version__ = "0.6.11" +__version__ = "0.6.12" __author__ = "Maxime Beauchemin" __email__ = "maximebeauchemin@gmail.com" diff --git a/showtime/core/aws.py b/showtime/core/aws.py index f12b2d7..1110681 100644 --- a/showtime/core/aws.py +++ b/showtime/core/aws.py @@ -176,8 +176,9 @@ def create_environment( return EnvironmentResult(success=False, error="Service failed to become stable") # Step 7: Health check the new service (longer timeout for Superset + examples) + # Note: Superset example loading takes more tha 10 mins in some cases print(f"🏥 Health checking service {service_name}...") - if not self._health_check_service(service_name, max_attempts=20): # 10 minutes total + if not self._health_check_service(service_name, max_attempts=40): # 20 minutes total return EnvironmentResult(success=False, error="Service failed health checks") # Step 8: Get IP after health checks pass diff --git a/tests/unit/test_aws_health_check.py b/tests/unit/test_aws_health_check.py new file mode 100644 index 0000000..c719678 --- /dev/null +++ b/tests/unit/test_aws_health_check.py @@ -0,0 +1,59 @@ +""" +Tests for health check retry behavior. +""" + +from unittest.mock import MagicMock, patch + +import httpx +import pytest + +from showtime.core.aws import AWSInterface + + +@pytest.fixture +def aws() -> AWSInterface: + """AWSInterface with mocked clients""" + return AWSInterface( + ecs_client=MagicMock(), + ecr_client=MagicMock(), + ec2_client=MagicMock(), + ) + + +class TestHealthCheckRetries: + """Core retry behavior tests""" + + def test_succeeds_on_healthy_response(self, aws: AWSInterface) -> None: + """Returns True when health endpoint responds 200""" + with patch.object(aws, "get_environment_ip", return_value="1.2.3.4"): + with patch("httpx.Client") as mock_client: + mock_response = MagicMock(status_code=200) + mock_client.return_value.__enter__.return_value.get.return_value = mock_response + + assert aws._health_check_service("test-service", max_attempts=3) is True + + def test_retries_and_succeeds(self, aws: AWSInterface) -> None: + """Retries on failure and succeeds when health check eventually passes""" + with patch.object(aws, "get_environment_ip", return_value="1.2.3.4"): + with patch("httpx.Client") as mock_client: + mock_instance = mock_client.return_value.__enter__.return_value + # Fail twice (health + fallback each), then succeed + mock_instance.get.side_effect = [ + httpx.RequestError("refused"), MagicMock(status_code=503), # attempt 1 + httpx.RequestError("refused"), MagicMock(status_code=503), # attempt 2 + MagicMock(status_code=200), # attempt 3 succeeds + ] + + with patch("time.sleep"): + assert aws._health_check_service("test-service", max_attempts=5) is True + + def test_fails_after_max_attempts_exhausted(self, aws: AWSInterface) -> None: + """Returns False after all attempts fail""" + with patch.object(aws, "get_environment_ip", return_value="1.2.3.4"): + with patch("httpx.Client") as mock_client: + mock_client.return_value.__enter__.return_value.get.side_effect = ( + httpx.RequestError("refused") + ) + + with patch("time.sleep"): + assert aws._health_check_service("test-service", max_attempts=3) is False From f2db21840a77c147b6f7f838bc7dd86da1b3bb68 Mon Sep 17 00:00:00 2001 From: Antonio Rivero Date: Thu, 5 Feb 2026 11:54:27 +0100 Subject: [PATCH 2/2] - Down to 15 mins --- showtime/core/aws.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/showtime/core/aws.py b/showtime/core/aws.py index 1110681..24e1177 100644 --- a/showtime/core/aws.py +++ b/showtime/core/aws.py @@ -178,7 +178,7 @@ def create_environment( # Step 7: Health check the new service (longer timeout for Superset + examples) # Note: Superset example loading takes more tha 10 mins in some cases print(f"🏥 Health checking service {service_name}...") - if not self._health_check_service(service_name, max_attempts=40): # 20 minutes total + if not self._health_check_service(service_name, max_attempts=30): # 15 minutes total return EnvironmentResult(success=False, error="Service failed health checks") # Step 8: Get IP after health checks pass