diff --git a/core/tests/test_controller_helpers.py b/core/tests/test_controller_helpers.py index 0fcc2312..0fdb58ef 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,7 +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_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}, @@ -216,7 +245,10 @@ def test_create_labels(mock_post, MockControllerLabel, mock_session): 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") @@ -224,6 +256,9 @@ def test_create_job_templates_payload_and_survey(mock_post, mock_session): instance = MagicMock(organization_id=5) pattern_def = { "name": "mypat", + "collection_name": "tester_namespace.test_collection", + "pattern_name": "demo_pattern", + "collection_version": "3.0.0", "aap_resources": { "controller_job_templates": [ { @@ -261,6 +296,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 diff --git a/core/utils/controller/helpers.py b/core/utils/controller/helpers.py index 852622ac..cfec6a8e 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,9 @@ def create_execution_environment( def create_labels( - session: requests.Session, instance: PatternInstance, pattern_def: Dict[str, Any] + session: requests.Session, + instance: PatternInstance, + pattern_def: Dict[str, Any], ) -> List[ControllerLabel]: """ Creates controller labels and returns model instances. @@ -149,8 +184,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_def["collection_name"], + pattern_def["pattern_name"], + pattern_def["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) @@ -186,6 +232,14 @@ def create_job_templates( jt_payload = { **jt, + # Add unique name to the job template definition + "name": aap_resource_info_hash( + jt["name"], + pattern_def["collection_name"], + pattern_def["pattern_name"], + pattern_def["collection_version"], + instance.organization_id, + ), "organization": instance.organization_id, "project": project_id, "execution_environment": ee_id,