From 14ffd70e31c5ec51ab4ad4d6ed23fa6de453ccbe Mon Sep 17 00:00:00 2001 From: pbrassel <52356233+pbrassel@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:14:28 +0100 Subject: [PATCH 1/4] fix(test): retry to lock an analysis if it is not possible yet --- tests/test_core.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/test_core.py b/tests/test_core.py index 1d493d9..71e4df0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -174,7 +174,7 @@ def analysis_bucket_file_includables(): @pytest.fixture() def configured_analysis(core_client, registry, analysis, master_realm, analysis_bucket_file): - # An analysis needs at least one default and one aggregator node. + # An analysis needs at least one default node, one aggregator node, a base image and an entrypoint to be lockable. nodes = [] for node_type in t.get_args(NodeType): new_node = core_client.create_node( @@ -186,8 +186,22 @@ def configured_analysis(core_client, registry, analysis, master_realm, analysis_ nodes.append(new_node) core_client.create_project_node(analysis.project_id, new_node) core_client.create_analysis_node(analysis, new_node) - core_client.send_analysis_command(analysis.id, command="configurationLock") - return analysis + + # It takes some time until an analysis is lockable. + def _check_analysis_lockable(): + try: + core_client.send_analysis_command(analysis.id, command="configurationLock") + except HubAPIError as e: + assert False, e + else: + assert True + + assert_eventually(_check_analysis_lockable) + + yield analysis + + for node in nodes: + core_client.delete_node(node) @pytest.fixture() From 0caaa145f32d956cd5b9d35e3f543e556fe669ea Mon Sep 17 00:00:00 2001 From: pbrassel <52356233+pbrassel@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:09:09 +0100 Subject: [PATCH 2/4] feat: update `Analysis` and `AnalysisNode` Update `Analysis` and `AnalysisNode` models to the v0.8.21 changes on the Hub-side. This implements the `execution_progress` attribute. --- flame_hub/_core_client.py | 32 +++++++++++++++++++++++--------- flame_hub/models.py | 4 ---- flame_hub/types.py | 6 ++---- tests/test_core.py | 13 +++++++++---- 4 files changed, 34 insertions(+), 21 deletions(-) diff --git a/flame_hub/_core_client.py b/flame_hub/_core_client.py index 9197ef1..c1914c2 100644 --- a/flame_hub/_core_client.py +++ b/flame_hub/_core_client.py @@ -215,8 +215,7 @@ class Log(BaseModel): labels: dict[str, str | None] -AnalysisBuildStatus = t.Literal["starting", "started", "stopping", "stopped", "finished", "failed"] -AnalysisRunStatus = t.Literal["starting", "started", "running", "stopping", "stopped", "finished", "failed"] +ProcessStatus = t.Literal["starting", "started", "stopping", "stopped", "finished", "failed"] class CreateAnalysis(BaseModel): @@ -234,10 +233,17 @@ class CreateAnalysis(BaseModel): class Analysis(CreateAnalysis): id: uuid.UUID - configuration_locked: bool nodes: int - build_status: AnalysisBuildStatus | None - run_status: AnalysisRunStatus | None + configuration_locked: bool + configuration_entrypoint_valid: bool + configuration_image_valid: bool + configuration_node_aggregator_valid: bool + configuration_node_default_valid: bool + configuration_nodes_valid: bool + build_status: ProcessStatus | None + execution_status: ProcessStatus | None + distribution_status: ProcessStatus | None + execution_progress: int | None created_at: datetime updated_at: datetime registry: t.Annotated[Registry | None, IsIncludable] = None @@ -278,7 +284,8 @@ class CreateAnalysisNode(BaseModel): class AnalysisNode(CreateAnalysisNode): id: uuid.UUID approval_status: AnalysisNodeApprovalStatus | None - run_status: AnalysisNodeRunStatus | None + execution_status: ProcessStatus | None + execution_progress: int | None comment: str | None artifact_tag: str | None artifact_digest: str | None @@ -293,7 +300,8 @@ class AnalysisNode(CreateAnalysisNode): class UpdateAnalysisNode(BaseModel): comment: str | None | UNSET_T = UNSET approval_status: AnalysisNodeApprovalStatus | None | UNSET_T = UNSET - run_status: AnalysisNodeRunStatus | None | UNSET_T = UNSET + execution_status: ProcessStatus | None | UNSET_T = UNSET + execution_progress: int | None | UNSET_T = UNSET class CreateAnalysisNodeLog(BaseModel): @@ -610,11 +618,17 @@ def update_analysis_node( analysis_node_id: AnalysisNode | uuid.UUID | str, comment: str | None | UNSET_T = UNSET, approval_status: AnalysisNodeApprovalStatus | None | UNSET_T = UNSET, - run_status: AnalysisNodeRunStatus | None | UNSET_T = UNSET, + execution_status: ProcessStatus | None | UNSET_T = UNSET, + execution_progress: int | None | UNSET_T = UNSET, ) -> AnalysisNode: return self._update_resource( AnalysisNode, - UpdateAnalysisNode(comment=comment, approval_status=approval_status, run_status=run_status), + UpdateAnalysisNode( + comment=comment, + approval_status=approval_status, + execution_status=execution_status, + execution_progress=execution_progress, + ), "analysis-nodes", analysis_node_id, ) diff --git a/flame_hub/models.py b/flame_hub/models.py index 5c153e8..253f811 100644 --- a/flame_hub/models.py +++ b/flame_hub/models.py @@ -11,8 +11,6 @@ "UpdateProject", "ProjectNodeApprovalStatus", "ProjectNode", - "AnalysisBuildStatus", - "AnalysisRunStatus", "CreateAnalysis", "Analysis", "UpdateAnalysis", @@ -85,8 +83,6 @@ ProjectNodeApprovalStatus, CreateProjectNode, ProjectNode, - AnalysisBuildStatus, - AnalysisRunStatus, CreateAnalysis, Analysis, UpdateAnalysis, diff --git a/flame_hub/types.py b/flame_hub/types.py index 127f6fa..3ca1b66 100644 --- a/flame_hub/types.py +++ b/flame_hub/types.py @@ -12,8 +12,7 @@ "RegistryProjectType", "MasterImageCommandArgument", "ProjectNodeApprovalStatus", - "AnalysisBuildStatus", - "AnalysisRunStatus", + "ProcessStatus", "AnalysisCommand", "AnalysisNodeApprovalStatus", "AnalysisNodeRunStatus", @@ -44,8 +43,7 @@ RegistryProjectType, MasterImageCommandArgument, ProjectNodeApprovalStatus, - AnalysisBuildStatus, - AnalysisRunStatus, + ProcessStatus, AnalysisCommand, AnalysisNodeApprovalStatus, AnalysisNodeRunStatus, diff --git a/tests/test_core.py b/tests/test_core.py index 71e4df0..c3d6359 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -431,16 +431,21 @@ def test_build_status_analysis(core_client, configured_analysis): def _check_checking_event_in_logs(): logs = core_client.find_analysis_logs(filter={"analysis_id": configured_analysis.id}) - assert "checking" in [log.labels.get("event", None) for log in logs] + assert "configured" in [log.labels.get("event", None) for log in logs] assert_eventually(_check_checking_event_in_logs) -def test_analysis_node_update(core_client, analysis_node): - new_analysis_node = core_client.update_analysis_node(analysis_node.id, run_status="starting") +def test_update_analysis_node(core_client, analysis_node): + new_analysis_node = core_client.update_analysis_node( + analysis_node.id, + execution_status="starting", + execution_progress=1, + ) assert analysis_node != new_analysis_node - assert new_analysis_node.run_status == "starting" + assert new_analysis_node.execution_status == "starting" + assert new_analysis_node.execution_progress == 1 def test_get_analysis_nodes(core_client, analysis_node, analysis_node_includables): From ed2368e1108d89b77845bd8e78ab73f04a8545dd Mon Sep 17 00:00:00 2001 From: pbrassel <52356233+pbrassel@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:13:24 +0100 Subject: [PATCH 3/4] test: update `.env` --- .env.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.test b/.env.test index 88fbebd..77f2c83 100644 --- a/.env.test +++ b/.env.test @@ -7,4 +7,4 @@ PYTEST_ADMIN_PASSWORD=start123 PYTEST_DEFAULT_MASTER_IMAGE=python/base PYTEST_ASYNC_MAX_RETRIES=5 PYTEST_ASYNC_RETRY_DELAY_MILLIS=500 -PYTEST_HUB_VERSION=0.8.19 +PYTEST_HUB_VERSION=0.8.21 From 35c1d0d194142767d326eb92ba0c0b7b691b7bb1 Mon Sep 17 00:00:00 2001 From: pbrassel <52356233+pbrassel@users.noreply.github.com> Date: Tue, 11 Nov 2025 12:21:12 +0100 Subject: [PATCH 4/4] test: randomize `execution_progress` --- tests/helpers.py | 7 +++++++ tests/test_core.py | 7 ++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index 35971a0..5583f97 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -31,5 +31,12 @@ def next_random_string(charset=string.ascii_letters, length: int = 20): return "".join([random.choice(charset) for _ in range(length)]) +def next_random_number(lower=0, upper=1, is_int=False): + if is_int: + return random.randint(lower, upper) + else: + return random.uniform(lower, upper) + + def next_uuid(): return str(uuid.uuid4()) diff --git a/tests/test_core.py b/tests/test_core.py index c3d6359..8f017b7 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -17,7 +17,7 @@ AnalysisBucket, AnalysisBucketFile, ) -from tests.helpers import next_random_string, next_uuid, assert_eventually +from tests.helpers import next_random_string, next_uuid, assert_eventually, next_random_number pytestmark = pytest.mark.integration @@ -437,15 +437,16 @@ def _check_checking_event_in_logs(): def test_update_analysis_node(core_client, analysis_node): + progress = next_random_number(upper=100, is_int=True) new_analysis_node = core_client.update_analysis_node( analysis_node.id, execution_status="starting", - execution_progress=1, + execution_progress=progress, ) assert analysis_node != new_analysis_node assert new_analysis_node.execution_status == "starting" - assert new_analysis_node.execution_progress == 1 + assert new_analysis_node.execution_progress == progress def test_get_analysis_nodes(core_client, analysis_node, analysis_node_includables):