From 0727ce59543ca5e9b9089d8808e6ca2bdbba6642 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 9 Sep 2025 13:41:46 -0400 Subject: [PATCH 1/6] Add helper function to generate unique hash for AAP resources --- core/task_runner.py | 4 +-- core/utils/controller/helpers.py | 60 ++++++++++++++++++++++++++++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/core/task_runner.py b/core/task_runner.py index 57a164ce..2c87191a 100644 --- a/core/task_runner.py +++ b/core/task_runner.py @@ -83,10 +83,10 @@ def run_pattern_instance_task(instance_id: int, task_id: int) -> None: task.mark_running({"info": "Creating execution environment"}) ee_id = create_execution_environment(session, instance, pattern_def) task.mark_running({"info": "Creating labels"}) - labels = create_labels(session, instance, pattern_def) + labels = create_labels(session, instance, pattern, pattern_def) task.mark_running({"info": "Creating job templates"}) automations = create_job_templates( - session, instance, pattern_def, project_id, ee_id + session, instance, pattern, pattern_def, project_id, ee_id ) task.mark_running({"info": "Saving instance"}) save_instance_state(instance, project_id, ee_id, labels, automations) diff --git a/core/utils/controller/helpers.py b/core/utils/controller/helpers.py index 852622ac..24e6e78d 100644 --- a/core/utils/controller/helpers.py +++ b/core/utils/controller/helpers.py @@ -1,4 +1,5 @@ import contextlib +import hashlib import logging import os import random @@ -51,6 +52,22 @@ def build_collection_uri(collection_name: str, version: str) -> str: return urljoin(f"{settings.AAP_URL}/", f"{path}/{filename}") +def aap_resource_info_hash( + resource_name: str, + collection_name: str, + pattern_name: str, + version: str, + organization_id: int, +) -> str: + """ + Generates a unique name for a resource. + """ + hash = hashlib.sha256( + f"{collection_name}.{pattern_name}.{version}.{organization_id}".encode() + ).hexdigest()[:8] + return f"{resource_name} {collection_name}.{pattern_name} {hash}" + + @contextlib.contextmanager def download_collection(collection_name: str, version: str) -> Iterator[str]: """ @@ -96,6 +113,14 @@ def create_project( The created project ID. """ project_def = pattern.pattern_definition["aap_resources"]["controller_project"] + # Add unique name to the project definition + project_def["name"] = aap_resource_info_hash( + project_def["name"], + pattern.collection_name, + pattern.pattern_name, + pattern.collection_version, + instance.organization_id, + ) project_def.update( { "organization": instance.organization_id, @@ -123,6 +148,14 @@ def create_execution_environment( """ ee_def = pattern_def["aap_resources"]["controller_execution_environment"] image_name = ee_def.pop("image_name") + # Add unique name to the ee definition + ee_def["name"] = aap_resource_info_hash( + ee_def["name"], + ee_def["collection_name"], + ee_def["pattern_name"], + ee_def["collection_version"], + instance.organization_id, + ) ee_def.update( { "organization": instance.organization_id, @@ -138,7 +171,10 @@ def create_execution_environment( def create_labels( - session: requests.Session, instance: PatternInstance, pattern_def: Dict[str, Any] + session: requests.Session, + instance: PatternInstance, + pattern: Pattern, + pattern_def: Dict[str, Any], ) -> List[ControllerLabel]: """ Creates controller labels and returns model instances. @@ -149,8 +185,19 @@ def create_labels( List of ControllerLabel model instances. """ labels = [] + for name in pattern_def["aap_resources"]["controller_labels"]: - label_def = {"name": name, "organization": instance.organization_id} + # Add unique name to the label definition + label_def = { + "name": aap_resource_info_hash( + name, + pattern.collection_name, + pattern.pattern_name, + pattern.collection_version, + instance.organization_id, + ) + } + label_def.update({"organization": instance.organization_id}) logger.debug(f"Creating label with definition: {label_def}") results = post(session, "/api/controller/v2/labels/", label_def) @@ -163,6 +210,7 @@ def create_labels( def create_job_templates( session: requests.Session, instance: PatternInstance, + pattern: Pattern, pattern_def: Dict[str, Any], project_id: int, ee_id: int, @@ -186,6 +234,14 @@ def create_job_templates( jt_payload = { **jt, + # Add unique name to the job template definition + "name": aap_resource_info_hash( + jt["name"], + pattern.collection_name, + pattern.pattern_name, + pattern.collection_version, + instance.organization_id, + ), "organization": instance.organization_id, "project": project_id, "execution_environment": ee_id, From 43e83c6abd23372093a4732e06eb6d1a0a21a311 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 9 Sep 2025 13:43:37 -0400 Subject: [PATCH 2/6] Update unit tests to ensure unique names --- core/tests/test_controller_helpers.py | 51 ++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/core/tests/test_controller_helpers.py b/core/tests/test_controller_helpers.py index 0fcc2312..5397ee46 100644 --- a/core/tests/test_controller_helpers.py +++ b/core/tests/test_controller_helpers.py @@ -151,7 +151,10 @@ def test_download_collection_failure(mock_download_failure): def test_create_project_builds_payload_and_waits(mock_wait, mock_post, mock_session): instance = MagicMock(organization_id=7, credentials={"project": 123}) pattern = MagicMock( + collection_name="my_namespace.my_collection", collection_version_uri="https://hub/artifacts/collection-1.0.0.tar.gz", + pattern_name="test_pattern", + collection_version="1.0.0", pattern_definition={ "aap_resources": { "controller_project": {"name": "proj", "scm_type": "git"}, @@ -166,6 +169,7 @@ def test_create_project_builds_payload_and_waits(mock_wait, mock_post, mock_sess assert pid == 55 payload = mock_post.call_args.args[2] print("payload", payload) + assert payload["name"] == "proj my_namespace.my_collection.test_pattern 436aa34f" assert payload["organization"] == 7 assert payload["scm_type"] == "archive" assert payload["scm_url"] == "https://hub/artifacts/collection-1.0.0.tar.gz" @@ -178,8 +182,27 @@ def test_create_project_builds_payload_and_waits(mock_wait, mock_post, mock_sess @pytest.mark.parametrize( "ee_def,expected_pull", [ - ({"name": "ee1", "image_name": "ns/repo:tag"}, ""), - ({"name": "ee1", "image_name": "ns/repo:tag", "pull": "always"}, "always"), + ( + { + "name": "ee1", + "collection_name": "collection.example", + "collection_version": "2.0.0", + "pattern_name": "test", + "image_name": "ns/repo:tag", + }, + "", + ), + ( + { + "name": "ee1", + "collection_name": "collection.example", + "collection_version": "2.0.0", + "pattern_name": "test", + "image_name": "ns/repo:tag", + "pull": "always", + }, + "always", + ), ], ) def test_create_execution_environment_pull( @@ -190,6 +213,7 @@ def test_create_execution_environment_pull( mock_post.return_value = {"id": 99} _ = create_execution_environment(mock_session, instance, pattern_def) payload = mock_post.call_args.args[2] + assert payload["name"] == "ee1 collection.example.test 74d0f2c7" assert payload["image"] == "aap.example.com/ns/repo:tag" assert payload["pull"] == expected_pull @@ -198,6 +222,11 @@ def test_create_execution_environment_pull( @patch("core.utils.controller.helpers.post") def test_create_labels(mock_post, MockControllerLabel, mock_session): instance = MagicMock(organization_id=1) + pattern = MagicMock( + collection_name="my_test_namespace.my_test_collection", + pattern_name="tester", + collection_version="1.0.0", + ) pattern_def = {"aap_resources": {"controller_labels": ["L1", "L2"]}} mock_post.side_effect = [ @@ -211,17 +240,25 @@ def test_create_labels(mock_post, MockControllerLabel, mock_session): (label2, False), ] - labels = create_labels(mock_session, instance, pattern_def) + labels = create_labels(mock_session, instance, pattern, pattern_def) assert labels == [label1, label2] # Ensure proper payloads used first_payload = mock_post.call_args_list[0].args[2] - assert first_payload == {"name": "L1", "organization": 1} + assert first_payload == { + "name": "L1 my_test_namespace.my_test_collection.tester 5202431b", + "organization": 1, + } @patch("core.utils.controller.helpers.post") def test_create_job_templates_payload_and_survey(mock_post, mock_session): instance = MagicMock(organization_id=5) + pattern = MagicMock( + collection_name="tester_namespace.test_collection", + pattern_name="demo_pattern", + collection_version="3.0.0", + ) pattern_def = { "name": "mypat", "aap_resources": { @@ -245,7 +282,7 @@ def test_create_job_templates_payload_and_survey(mock_post, mock_session): ] autos = create_job_templates( - mock_session, instance, pattern_def, project_id=10, ee_id=20 + mock_session, instance, pattern, pattern_def, project_id=10, ee_id=20 ) assert autos == [ @@ -261,6 +298,10 @@ def test_create_job_templates_payload_and_survey(mock_post, mock_session): # Verify payload fields for a JT first_jt_payload = mock_post.call_args_list[0].args[2] + assert ( + first_jt_payload["name"] + == "jt1 tester_namespace.test_collection.demo_pattern dc5a3002" + ) assert first_jt_payload["organization"] == 5 assert first_jt_payload["project"] == 10 assert first_jt_payload["execution_environment"] == 20 From 4ce23df83000aa7bdbad462b6eb6b994b242e08d Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 20:02:14 -0400 Subject: [PATCH 3/6] Update doc strings --- core/utils/controller/helpers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/utils/controller/helpers.py b/core/utils/controller/helpers.py index 24e6e78d..bc1decfd 100644 --- a/core/utils/controller/helpers.py +++ b/core/utils/controller/helpers.py @@ -180,6 +180,7 @@ def create_labels( Creates controller labels and returns model instances. Args: instance (PatternInstance): The PatternInstance object. + pattern (Pattern): The related Pattern object. pattern_def (Dict[str, Any]): The pattern definition dictionary. Returns: List of ControllerLabel model instances. @@ -219,6 +220,7 @@ def create_job_templates( Creates job templates and associated surveys. Args: instance (PatternInstance): The PatternInstance object. + pattern (Pattern): The related Pattern object. pattern_def (Dict[str, Any]): The pattern definition dictionary. project_id (int): Controller project ID. ee_id (int): Execution environment ID. From bcaa3bab5f638b26910a26f680751ca4343dde1e Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 20:24:01 -0400 Subject: [PATCH 4/6] Revert the need to utilize 'pattern' as an argument --- core/task_runner.py | 4 ++-- core/tests/test_controller_helpers.py | 24 +++++++++++------------- core/utils/controller/helpers.py | 15 ++++++--------- 3 files changed, 19 insertions(+), 24 deletions(-) diff --git a/core/task_runner.py b/core/task_runner.py index 2c87191a..57a164ce 100644 --- a/core/task_runner.py +++ b/core/task_runner.py @@ -83,10 +83,10 @@ def run_pattern_instance_task(instance_id: int, task_id: int) -> None: task.mark_running({"info": "Creating execution environment"}) ee_id = create_execution_environment(session, instance, pattern_def) task.mark_running({"info": "Creating labels"}) - labels = create_labels(session, instance, pattern, pattern_def) + labels = create_labels(session, instance, pattern_def) task.mark_running({"info": "Creating job templates"}) automations = create_job_templates( - session, instance, pattern, pattern_def, project_id, ee_id + session, instance, pattern_def, project_id, ee_id ) task.mark_running({"info": "Saving instance"}) save_instance_state(instance, project_id, ee_id, labels, automations) diff --git a/core/tests/test_controller_helpers.py b/core/tests/test_controller_helpers.py index 5397ee46..99d371b1 100644 --- a/core/tests/test_controller_helpers.py +++ b/core/tests/test_controller_helpers.py @@ -222,12 +222,12 @@ def test_create_execution_environment_pull( @patch("core.utils.controller.helpers.post") def test_create_labels(mock_post, MockControllerLabel, mock_session): instance = MagicMock(organization_id=1) - pattern = MagicMock( - collection_name="my_test_namespace.my_test_collection", - pattern_name="tester", - collection_version="1.0.0", - ) - pattern_def = {"aap_resources": {"controller_labels": ["L1", "L2"]}} + pattern_def = { + "collection_name": "my_test_namespace.my_test_collection", + "pattern_name": "tester", + "collection_version": "1.0.0", + "aap_resources": {"controller_labels": ["L1", "L2"]} + } mock_post.side_effect = [ {"id": 10}, @@ -240,7 +240,7 @@ def test_create_labels(mock_post, MockControllerLabel, mock_session): (label2, False), ] - labels = create_labels(mock_session, instance, pattern, pattern_def) + labels = create_labels(mock_session, instance, pattern_def) assert labels == [label1, label2] # Ensure proper payloads used @@ -254,13 +254,11 @@ def test_create_labels(mock_post, MockControllerLabel, mock_session): @patch("core.utils.controller.helpers.post") def test_create_job_templates_payload_and_survey(mock_post, mock_session): instance = MagicMock(organization_id=5) - pattern = MagicMock( - collection_name="tester_namespace.test_collection", - pattern_name="demo_pattern", - collection_version="3.0.0", - ) pattern_def = { "name": "mypat", + "collection_name": "tester_namespace.test_collection", + "pattern_name": "demo_pattern", + "collection_version": "3.0.0", "aap_resources": { "controller_job_templates": [ { @@ -282,7 +280,7 @@ def test_create_job_templates_payload_and_survey(mock_post, mock_session): ] autos = create_job_templates( - mock_session, instance, pattern, pattern_def, project_id=10, ee_id=20 + mock_session, instance, pattern_def, project_id=10, ee_id=20 ) assert autos == [ diff --git a/core/utils/controller/helpers.py b/core/utils/controller/helpers.py index bc1decfd..58ef2d63 100644 --- a/core/utils/controller/helpers.py +++ b/core/utils/controller/helpers.py @@ -173,7 +173,6 @@ def create_execution_environment( def create_labels( session: requests.Session, instance: PatternInstance, - pattern: Pattern, pattern_def: Dict[str, Any], ) -> List[ControllerLabel]: """ @@ -192,9 +191,9 @@ def create_labels( label_def = { "name": aap_resource_info_hash( name, - pattern.collection_name, - pattern.pattern_name, - pattern.collection_version, + pattern_def["collection_name"], + pattern_def["pattern_name"], + pattern_def["collection_version"], instance.organization_id, ) } @@ -211,7 +210,6 @@ def create_labels( def create_job_templates( session: requests.Session, instance: PatternInstance, - pattern: Pattern, pattern_def: Dict[str, Any], project_id: int, ee_id: int, @@ -220,7 +218,6 @@ def create_job_templates( Creates job templates and associated surveys. Args: instance (PatternInstance): The PatternInstance object. - pattern (Pattern): The related Pattern object. pattern_def (Dict[str, Any]): The pattern definition dictionary. project_id (int): Controller project ID. ee_id (int): Execution environment ID. @@ -239,9 +236,9 @@ def create_job_templates( # Add unique name to the job template definition "name": aap_resource_info_hash( jt["name"], - pattern.collection_name, - pattern.pattern_name, - pattern.collection_version, + pattern_def["collection_name"], + pattern_def["pattern_name"], + pattern_def["collection_version"], instance.organization_id, ), "organization": instance.organization_id, From c20c7c54e920f239cf76bb9217aaf0aaaa8859f8 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Tue, 16 Sep 2025 20:26:22 -0400 Subject: [PATCH 5/6] Fix linter error --- core/tests/test_controller_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/test_controller_helpers.py b/core/tests/test_controller_helpers.py index 99d371b1..0fdb58ef 100644 --- a/core/tests/test_controller_helpers.py +++ b/core/tests/test_controller_helpers.py @@ -226,7 +226,7 @@ def test_create_labels(mock_post, MockControllerLabel, mock_session): "collection_name": "my_test_namespace.my_test_collection", "pattern_name": "tester", "collection_version": "1.0.0", - "aap_resources": {"controller_labels": ["L1", "L2"]} + "aap_resources": {"controller_labels": ["L1", "L2"]}, } mock_post.side_effect = [ From 72eb05fe5cc7afd2e06ac17afa5182afffb9dd96 Mon Sep 17 00:00:00 2001 From: Bianca Henderson Date: Wed, 17 Sep 2025 13:12:20 -0400 Subject: [PATCH 6/6] Correct doc string --- core/utils/controller/helpers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/core/utils/controller/helpers.py b/core/utils/controller/helpers.py index 58ef2d63..cfec6a8e 100644 --- a/core/utils/controller/helpers.py +++ b/core/utils/controller/helpers.py @@ -179,7 +179,6 @@ def create_labels( Creates controller labels and returns model instances. Args: instance (PatternInstance): The PatternInstance object. - pattern (Pattern): The related Pattern object. pattern_def (Dict[str, Any]): The pattern definition dictionary. Returns: List of ControllerLabel model instances.