From 70cd7cdf62cb388140c5284d2e0691d1dabe6cb1 Mon Sep 17 00:00:00 2001 From: Unnati Solanki Date: Tue, 10 Feb 2026 13:54:13 +0530 Subject: [PATCH 1/3] [patch] fallback for master catalog --- src/mas/devops/data/__init__.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/mas/devops/data/__init__.py b/src/mas/devops/data/__init__.py index 3101b32c..1fdff4dc 100644 --- a/src/mas/devops/data/__init__.py +++ b/src/mas/devops/data/__init__.py @@ -24,21 +24,23 @@ class NoSuchCatalogError(Exception): pass -def getCatalog(name: str) -> dict: +def getCatalog(name: str) -> dict | None: """ Load a specific IBM Operator Catalog definition by name. This function reads a catalog YAML file from the catalogs directory and returns - its contents as a dictionary. + its contents as a dictionary. Special handling for "master" catalogs: returns None + to allow fallback to the newest catalog via Ansible playbook logic. Args: - name (str): The catalog name/tag (e.g., "v9-241205-amd64", "v8-240528-amd64"). + name (str): The catalog name/tag (e.g., "v9-241205-amd64", "v8-240528-amd64", "v9-master-amd64"). Returns: dict: The catalog definition dictionary containing operator versions and metadata. + Returns None for master catalogs to trigger fallback logic. Raises: - NoSuchCatalogError: If the specified catalog does not exist. + NoSuchCatalogError: If the specified catalog does not exist (except for master catalogs). """ moduleFile = path.abspath(__file__) modulePath = path.dirname(moduleFile) @@ -46,6 +48,10 @@ def getCatalog(name: str) -> dict: pathToCatalog = path.join(modulePath, "catalogs", catalogFileName) if not path.exists(pathToCatalog): + # Special handling for master catalogs - return None to trigger fallback + if "master" in name.lower(): + return None + raise NoSuchCatalogError( f"Catalog {name} is unknown: {pathToCatalog} does not exist" ) @@ -185,4 +191,7 @@ def getCatalogEditorial(catalogTag: str) -> dict | None: """ catalog = getCatalog(catalogTag) + if catalog is None: + return None + return catalog.get("editorial") From 0d5f6a692169fe8cc055fabefb4e718ec4d31a99 Mon Sep 17 00:00:00 2001 From: Unnati Solanki Date: Tue, 10 Feb 2026 15:01:55 +0530 Subject: [PATCH 2/3] [patch] fallback with latest catalog --- src/mas/devops/data/__init__.py | 49 +++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/src/mas/devops/data/__init__.py b/src/mas/devops/data/__init__.py index 1fdff4dc..8ef1d7bf 100644 --- a/src/mas/devops/data/__init__.py +++ b/src/mas/devops/data/__init__.py @@ -24,23 +24,24 @@ class NoSuchCatalogError(Exception): pass -def getCatalog(name: str) -> dict | None: +def getCatalog(name: str) -> dict: """ Load a specific IBM Operator Catalog definition by name. This function reads a catalog YAML file from the catalogs directory and returns - its contents as a dictionary. Special handling for "master" catalogs: returns None - to allow fallback to the newest catalog via Ansible playbook logic. + its contents as a dictionary. Special handling for dev/master catalogs: if a catalog + doesn't exist and matches dev patterns (master, branch names), automatically resolves + to the newest catalog for that architecture. Args: name (str): The catalog name/tag (e.g., "v9-241205-amd64", "v8-240528-amd64", "v9-master-amd64"). Returns: dict: The catalog definition dictionary containing operator versions and metadata. - Returns None for master catalogs to trigger fallback logic. + For dev catalogs, returns the newest catalog data for that architecture. Raises: - NoSuchCatalogError: If the specified catalog does not exist (except for master catalogs). + NoSuchCatalogError: If the specified catalog does not exist and is not a dev catalog pattern. """ moduleFile = path.abspath(__file__) modulePath = path.dirname(moduleFile) @@ -48,13 +49,30 @@ def getCatalog(name: str) -> dict | None: pathToCatalog = path.join(modulePath, "catalogs", catalogFileName) if not path.exists(pathToCatalog): - # Special handling for master catalogs - return None to trigger fallback - if "master" in name.lower(): - return None - - raise NoSuchCatalogError( - f"Catalog {name} is unknown: {pathToCatalog} does not exist" - ) + # Check if this looks like a dev catalog (master or branch name pattern) + parts = name.split("-") + if len(parts) >= 3: + middle_part = parts[1] # e.g., "master", "branchname", or "241205" + arch = parts[-1] # e.g., "amd64" + + # Validate catalog format + is_dev_catalog = not (middle_part.isdigit() and len(middle_part) == 6) + + if is_dev_catalog: + try: + newestCatalog = getNewestCatalogTag(arch) + catalogFileName = f"{newestCatalog}.yaml" + pathToCatalog = path.join(modulePath, "catalogs", catalogFileName) + except NoSuchCatalogError: + raise NoSuchCatalogError( + f"Catalog {name} appears to be a dev catalog, but no catalogs found for architecture {arch}" + ) + + # Final check if the resolved path exists + if not path.exists(pathToCatalog): + raise NoSuchCatalogError( + f"Catalog {name} is unknown: {pathToCatalog} does not exist" + ) with open(pathToCatalog) as stream: return yaml.safe_load(stream) @@ -186,12 +204,7 @@ def getCatalogEditorial(catalogTag: str) -> dict | None: Returns: dict: Dictionary with 'whats_new' and 'known_issues' keys containing - structured lists. Returns None if catalog doesn't exist - or has no editorial content. + structured lists. Returns None if catalog has no editorial content. """ catalog = getCatalog(catalogTag) - - if catalog is None: - return None - return catalog.get("editorial") From 71fb285ea86406e2ab4b482f1a2b894257c026fd Mon Sep 17 00:00:00 2001 From: Unnati Solanki Date: Tue, 10 Feb 2026 16:22:18 +0530 Subject: [PATCH 3/3] [patch] Resolve master catalog --- src/mas/devops/data/__init__.py | 6 +++--- test/src/test_data.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/mas/devops/data/__init__.py b/src/mas/devops/data/__init__.py index 8ef1d7bf..750ad4a3 100644 --- a/src/mas/devops/data/__init__.py +++ b/src/mas/devops/data/__init__.py @@ -29,9 +29,8 @@ def getCatalog(name: str) -> dict: Load a specific IBM Operator Catalog definition by name. This function reads a catalog YAML file from the catalogs directory and returns - its contents as a dictionary. Special handling for dev/master catalogs: if a catalog - doesn't exist and matches dev patterns (master, branch names), automatically resolves - to the newest catalog for that architecture. + its contents as a dictionary. Dev/master catalogs that don't exist will automatically + resolve to the newest catalog for that architecture. Args: name (str): The catalog name/tag (e.g., "v9-241205-amd64", "v8-240528-amd64", "v9-master-amd64"). @@ -59,6 +58,7 @@ def getCatalog(name: str) -> dict: is_dev_catalog = not (middle_part.isdigit() and len(middle_part) == 6) if is_dev_catalog: + # This is a dev catalog, resolve to newest catalog for this architecture try: newestCatalog = getNewestCatalogTag(arch) catalogFileName = f"{newestCatalog}.yaml" diff --git a/test/src/test_data.py b/test/src/test_data.py index cbf0f152..7ce42ab9 100644 --- a/test/src/test_data.py +++ b/test/src/test_data.py @@ -43,3 +43,27 @@ def test_get_newest_catalog_tag_fail(): def test_get_catalog_fail(): with pytest.raises(NoSuchCatalogError, match="Catalog nonexistent-catalog is unknown"): getCatalog("nonexistent-catalog") + + +def test_get_dev_catalog_master(): + """Test that master dev catalogs automatically resolve to newest catalog""" + catalogData = getCatalog("v9-master-amd64") + # Should resolve to newest catalog + newestCatalog = getCatalog(getNewestCatalogTag("amd64")) + assert catalogData == newestCatalog + + +def test_get_dev_catalog_branch(): + """Test that branch dev catalogs automatically resolve to newest catalog""" + catalogData = getCatalog("v9-feature-branch-amd64") + # Should resolve to newest catalog + newestCatalog = getCatalog(getNewestCatalogTag("amd64")) + assert catalogData == newestCatalog + + +def test_get_dev_catalog_s390x(): + """Test that dev catalogs work for different architectures""" + catalogData = getCatalog("v9-master-s390x") + # Should resolve to newest s390x catalog + newestCatalog = getCatalog(getNewestCatalogTag("s390x")) + assert catalogData == newestCatalog