diff --git a/requirements.txt b/requirements.txt index 9cb45f0..d8080bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,6 +4,7 @@ libvirt-python requests peewee +fedora-distro-aliases>=1.5 #guestfs - after it is on pypi ( https://bugzilla.redhat.com/show_bug.cgi?id=1075594 ) # Test suite requirements diff --git a/setup.py b/setup.py index 62c270d..2878068 100644 --- a/setup.py +++ b/setup.py @@ -67,6 +67,7 @@ def run(self): "peewee", "requests", "packaging", + "fedora-distro-aliases>=1.5", ], extras_require={"image_resolve_caching": ["requests_cache>=1.2"]}, ) diff --git a/test/test_fedora.py b/test/test_fedora.py new file mode 100644 index 0000000..c678291 --- /dev/null +++ b/test/test_fedora.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Copyright 2026, Red Hat, Inc. +# License: GPL-2.0+ +# See the LICENSE file for more details on Licensing + +"""Tests for Fedora version resolution via fedora-distro-aliases.""" + +from munch import Munch + +from testcloud.distro_utils import fedora + + +def _distro(version, version_number): + return Munch(version=version, version_number=version_number) + + +def _aliases(development, latest_stable): + return { + "fedora-development": development, + "fedora-latest-stable": latest_stable, + } + + +class TestResolveFedoraVersion: + """``_resolve_fedora_version`` normalizes version strings using the + aliases returned by ``fedora-distro-aliases``.""" + + def test_numeric_rawhide(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert fedora._resolve_fedora_version("45", aliases) == "rawhide" + + def test_numeric_branched(self): + aliases = _aliases( + development=[_distro("44", "44"), _distro("rawhide", "45")], + latest_stable=[_distro("43", "43")], + ) + assert fedora._resolve_fedora_version("44", aliases) == "branched" + + def test_latest_resolves_to_stable(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert fedora._resolve_fedora_version("latest", aliases) == "44" + + def test_branched_falls_back_to_rawhide(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert fedora._resolve_fedora_version("branched", aliases) == "rawhide" + + def test_rawhide_passthrough(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert fedora._resolve_fedora_version("rawhide", aliases) == "rawhide" + + def test_numeric_passthrough(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert fedora._resolve_fedora_version("43", aliases) == "43" + + def test_latest_without_stable(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[], + ) + assert fedora._resolve_fedora_version("latest", aliases) == "latest" diff --git a/testcloud/distro_utils/fedora.py b/testcloud/distro_utils/fedora.py index c5e6b0e..7c3d6c9 100644 --- a/testcloud/distro_utils/fedora.py +++ b/testcloud/distro_utils/fedora.py @@ -7,6 +7,8 @@ import re import requests +from fedora_distro_aliases import get_distro_aliases, Cache + from testcloud import config from testcloud import exceptions from testcloud.distro_utils.misc import get_requests_session @@ -15,6 +17,43 @@ config_data = config.get_config() +def _get_fedora_aliases(): + """Fetch Fedora release aliases from Bodhi with optional caching.""" + cache = None + if config_data.CACHE_IMAGES: + cache = Cache( + path="{}/fedora_distro_aliases_cache.json".format(config_data.DATA_DIR), + ttl=config_data.TRUST_DEADLINE * 60 * 60 * 24, + ) + return get_distro_aliases(cache=cache) + + +def _resolve_fedora_version(version, aliases): + """ + Normalize a Fedora version string against live release data. + + Maps numeric versions that match a development release to their alias + (e.g. ``"45"`` -> ``"rawhide"``) and resolves ``"latest"`` to the + current stable release number. + """ + devel = aliases["fedora-development"] + stable = aliases["fedora-latest-stable"] + + for distro in devel: + if version == str(distro.version_number): + version = "rawhide" if distro.version == "rawhide" else "branched" + break + + if version == "branched" and all(d.version == "rawhide" for d in devel): + log.warning("Branched release currently doesn't exist, using rawhide...") + version = "rawhide" + + if version == "latest" and stable: + version = str(stable[0].version_number) + + return version + + def _process_coreos_url(version: str, arch: str, platform: str) -> str: """ Returns an CoreOS url in either qemu or openstack format @@ -78,21 +117,12 @@ def get_fedora_image_url(version: str, arch: str) -> str: # get Fedora Cloud url try: - oraculum_releases = session.get("https://packager-dashboard.fedoraproject.org/api/v1/releases").json() - except (ConnectionError, IndexError, requests.exceptions.JSONDecodeError): - log.error("Couldn't fetch Fedora releases from oraculum...") + aliases = _get_fedora_aliases() + version = _resolve_fedora_version(version, aliases) + except Exception: + log.error("Couldn't fetch Fedora releases...") raise exceptions.TestcloudImageError - if oraculum_releases["fedora"]["branched"] and version == str(oraculum_releases["fedora"]["branched"]): - version = "branched" - - if not oraculum_releases["fedora"]["branched"] and version == "branched": - log.warning("Branched release currently doesn't exist, using rawhide...") - version = "rawhide" - - if version == str(oraculum_releases["fedora"]["rawhide"]): - version = "rawhide" - if version == "qa-matrix": if arch != "x86_64": log.error("non-x86_64 architecture is not supported with Fedora qa-matrix.") @@ -122,9 +152,6 @@ def get_fedora_image_url(version: str, arch: str) -> str: raise exceptions.TestcloudImageError return str(url) - if version == "latest": - version = str(oraculum_releases["fedora"]["stable"]) - try: releases = session.get("https://getfedora.org/releases.json").json() except (ConnectionError, requests.exceptions.JSONDecodeError):