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..bbb4640 --- /dev/null +++ b/test/test_fedora.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +# Copyright 2026, Red Hat, Inc. +# License: GPL-2.0+ +# See the LICENSE file for more details on Licensing + +"""Tests for Fedora release alias resolution via fedora-distro-aliases.""" + +from unittest.mock import patch + +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 TestGetFedoraReleases: + """``get_fedora_releases`` maps fedora-distro-aliases data to the + oraculum-shaped ``{"rawhide", "branched", "stable"}`` dict.""" + + def _run(self, aliases): + with patch.object(fedora.config_data, "CACHE_IMAGES", False): + with patch.object(fedora, "get_distro_aliases", return_value=aliases): + return fedora.get_fedora_releases() + + def test_without_branched(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[_distro("44", "44")], + ) + assert self._run(aliases) == {"rawhide": 45, "branched": None, "stable": 44} + + def test_with_branched(self): + aliases = _aliases( + development=[_distro("44", "44"), _distro("rawhide", "45")], + latest_stable=[_distro("43", "43")], + ) + assert self._run(aliases) == {"rawhide": 45, "branched": 44, "stable": 43} + + def test_without_stable(self): + aliases = _aliases( + development=[_distro("rawhide", "45")], + latest_stable=[], + ) + assert self._run(aliases) == {"rawhide": 45, "branched": None, "stable": None} diff --git a/testcloud/distro_utils/fedora.py b/testcloud/distro_utils/fedora.py index c5e6b0e..addbf98 100644 --- a/testcloud/distro_utils/fedora.py +++ b/testcloud/distro_utils/fedora.py @@ -7,6 +7,9 @@ import re import requests +from fedora_distro_aliases import get_distro_aliases, Cache +from fedora_distro_aliases.cache import BadCache + from testcloud import config from testcloud import exceptions from testcloud.distro_utils.misc import get_requests_session @@ -15,6 +18,38 @@ config_data = config.get_config() +def get_fedora_releases() -> dict: + """ + Resolve current Fedora rawhide/branched/stable release numbers via Bodhi + (using the fedora-distro-aliases library). + + Returns a dict shaped like the old oraculum ``releases["fedora"]`` payload: + ``{"rawhide": int, "branched": int | None, "stable": int | None}``. + """ + 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, + ) + + aliases = get_distro_aliases(cache=cache) + + # The highest development release is Rawhide (its ``version`` is overridden to + # "rawhide" while ``version_number`` keeps the numeric value); any remaining + # development release is the branched one. + devel = aliases["fedora-development"] + rawhide = next(distro for distro in devel if distro.version == "rawhide") + branched = [distro for distro in devel if distro.version != "rawhide"] + stable = aliases["fedora-latest-stable"] + + return { + "rawhide": int(rawhide.version_number), + "branched": int(branched[-1].version_number) if branched else None, + "stable": int(stable[0].version_number) if stable else None, + } + + def _process_coreos_url(version: str, arch: str, platform: str) -> str: """ Returns an CoreOS url in either qemu or openstack format @@ -78,19 +113,19 @@ 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...") + fedora_releases = get_fedora_releases() + except (requests.exceptions.RequestException, BadCache, StopIteration, KeyError): + log.error("Couldn't fetch Fedora releases...") raise exceptions.TestcloudImageError - if oraculum_releases["fedora"]["branched"] and version == str(oraculum_releases["fedora"]["branched"]): + if fedora_releases["branched"] and version == str(fedora_releases["branched"]): version = "branched" - if not oraculum_releases["fedora"]["branched"] and version == "branched": + if not fedora_releases["branched"] and version == "branched": log.warning("Branched release currently doesn't exist, using rawhide...") version = "rawhide" - if version == str(oraculum_releases["fedora"]["rawhide"]): + if version == str(fedora_releases["rawhide"]): version = "rawhide" if version == "qa-matrix": @@ -123,7 +158,7 @@ def get_fedora_image_url(version: str, arch: str) -> str: return str(url) if version == "latest": - version = str(oraculum_releases["fedora"]["stable"]) + version = str(fedora_releases["stable"]) try: releases = session.get("https://getfedora.org/releases.json").json()