From 3c74cd2e237df1646b73238de51bd0d4e019ec82 Mon Sep 17 00:00:00 2001 From: Matej Matuska Date: Tue, 7 Oct 2025 17:57:19 +0200 Subject: [PATCH 65/69] cs->rhel: Handle potentially unreleased target version On CS to RHEL upgrades, particularly on 9->10, there is only one upgrade path defined, CS 9 -> latest RHEL 10 (10.X). However a situation may occur, in which the latest RHEL version has not yet been publicly released, e.g. in pre-release builds. This is problematic because the upgrade fails if the content is not yet available. If this happens the user is informed via a hint in the error message to specify the latest available RHEL version (the previous minor version) manually using the --target-version CLI option. To make leapp consider the previous minor version as supported without adding it to upgrade_paths.json (which would affect RHEL->RHEL upgrades), it is added to the IPUConfig message after processing the json. Also, in the error message, move the existing proxy hints from 'details' to 'hint', otherwise the original exeception message, which is in 'details' is overwritten. Jira: RHEL-110563 --- .../libraries/ipuworkflowconfig.py | 50 +++++++++++++++++++ .../tests/test_ipuworkflowconfig.py | 47 ++++++++++++++++- .../libraries/userspacegen.py | 46 +++++++++++------ 3 files changed, 127 insertions(+), 16 deletions(-) diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py index 0c05640a..828eee88 100644 --- a/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py +++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/libraries/ipuworkflowconfig.py @@ -189,6 +189,53 @@ def construct_models_for_paths_matching_source_major( return multipaths_matching_source +def _centos_to_rhel_supported_version_workaround(exposed_supported_paths): + """ + Add target version one minor version lower than the latest version + + On CS to RHEL upgrades, particularly on 9->10, there is only one upgrade + path defined, CS 9 -> latest RHEL 10 (10.X). However a situation may occur, + in which the latest RHEL version has not yet been publicly released, e.g. + in pre-release builds. + + This is problematic because the upgrade fails if the content is not yet + available. If this happens the user is informed (by code elsewhere) to + specify the latest available RHEL version (the previous minor version) + manually using the --target-version CLI option. + However the previous minor version is not a supported target version. This + function adds it as one by appending it to exposed_supported_paths[0].target_versions. + The version is not appended if already present or if the defined latest is X.0. + + :param exposed_supported_paths: The supported upgrade paths. Length is expected to be 1. + :type exposed_supported_paths: list[IPUSourceToPossibleTargets] + """ + + if len(exposed_supported_paths) != 1: + raise StopActorExecutionError( + "Expected only 1 IPUSourceToPossibleTargets model on CS->RHEL upgrade" + ) + path = exposed_supported_paths[0] + + major, minor = max(path.target_versions).split('.') + if not minor or minor == '0': + api.current_logger().debug( + "Skipping centos->rhel supported versions workaround, the latest target minor version is 0." + ) + return + + new_minor = int(minor) - 1 + to_add = "{}.{}".format(major, new_minor) + + if to_add not in path.target_versions: + msg = "Adding {} as a supported target version for centos->rhel upgrade.".format(to_add) + path.target_versions.append(to_add) + else: + msg = "Skipping adding {} as a target version for centos->rhel upgrade, already present.".format( + to_add + ) + api.current_logger().debug(msg) + + def produce_ipu_config(actor): flavour = os.environ.get('LEAPP_UPGRADE_PATH_FLAVOUR') target_version = os.environ.get('LEAPP_UPGRADE_PATH_TARGET_RELEASE') @@ -219,6 +266,9 @@ def produce_ipu_config(actor): raw_upgrade_paths, source_major_version ) + if exposed_supported_paths and os_release.release_id == 'centos' and target_distro == 'rhel': + _centos_to_rhel_supported_version_workaround(exposed_supported_paths) + actor.produce(IPUConfig( leapp_env_vars=get_env_vars(), os_release=os_release, diff --git a/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py b/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py index c0deaf0e..583cdfc5 100644 --- a/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py +++ b/repos/system_upgrade/common/actors/ipuworkflowconfig/tests/test_ipuworkflowconfig.py @@ -1,10 +1,12 @@ import os +from copy import deepcopy import pytest from leapp.exceptions import StopActorExecutionError from leapp.libraries.actor import ipuworkflowconfig -from leapp.libraries.stdlib import CalledProcessError +from leapp.libraries.common.testutils import logger_mocked +from leapp.libraries.stdlib import api, CalledProcessError from leapp.models import IPUSourceToPossibleTargets, OSRelease CUR_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -260,3 +262,46 @@ def test_load_raw_upgrade_paths_for_distro_and_flavour(monkeypatch, distro, flav def test_virtual_version_construction(construction_params, expected_versions): result = ipuworkflowconfig.get_virtual_version(TEST_UPGRADE_PATHS, *construction_params) assert result == expected_versions + + +def _make_path(source_ver, target_vers): + return IPUSourceToPossibleTargets(source_version=source_ver, target_versions=target_vers) + + +@pytest.mark.parametrize( + "paths,to_add,logmsg", + [ + ( + [_make_path("9.8", ["10.2"])], + ["10.1"], + "Adding 10.1 as a supported target version for centos->rhel upgrade." + ), + ( + [_make_path("9.10", ["10.10"])], + ["10.9"], + "Adding 10.9 as a supported target version for centos->rhel upgrade." + ), + # already present + ( + [_make_path("8.10", ["9.6", "9.7"])], + [], + "Skipping adding 9.6 as a target version for centos->rhel upgrade, already present." + ), + # lowest minor + ( + [_make_path("9.6", ["10.0"])], + [], + "Skipping centos->rhel supported versions workaround, the latest target minor version is 0." + ), + ], +) +def test_centos_to_rhel_supported_version_workaround(monkeypatch, paths, to_add, logmsg): + logger = logger_mocked() + monkeypatch.setattr(api, 'current_logger', logger) + + original = deepcopy(paths[0]) + ipuworkflowconfig._centos_to_rhel_supported_version_workaround(paths) + + assert paths[0].source_version == original.source_version + assert paths[0].target_versions == original.target_versions + to_add + assert logmsg in logger.dbgmsg[0] diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py index 9df83ef8..c825c731 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py @@ -7,8 +7,8 @@ from leapp import reporting from leapp.exceptions import StopActorExecution, StopActorExecutionError from leapp.libraries.actor import constants from leapp.libraries.common import distro, dnfplugin, mounting, overlaygen, repofileutils, rhsm, utils -from leapp.libraries.common.config import get_env, get_product_type, get_target_distro_id -from leapp.libraries.common.config.version import get_target_major_version +from leapp.libraries.common.config import get_env, get_product_type, get_source_distro_id, get_target_distro_id +from leapp.libraries.common.config.version import get_target_major_version, get_target_version from leapp.libraries.common.gpg import get_path_to_gpg_certs, is_nogpgcheck_set from leapp.libraries.stdlib import api, CalledProcessError, config, run from leapp.models import RequiredTargetUserspacePackages # deprecated @@ -250,7 +250,9 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): try: context.call(cmd, callback_raw=utils.logging_handler) except CalledProcessError as exc: - message = 'Unable to install RHEL {} userspace packages.'.format(target_major_version) + message = 'Unable to install target \'{}\' {} userspace packages.'.format( + get_target_distro_id(), target_major_version + ) details = {'details': str(exc), 'stderr': exc.stderr} if 'more space needed on the' in exc.stderr: @@ -263,26 +265,40 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): # failed since leapp does not support updates behind proxy yet. for manager_info in api.consume(PkgManagerInfo): if manager_info.configured_proxies: - details['details'] = ( - "DNF failed to install userspace packages, likely due to the proxy " - "configuration detected in the YUM/DNF configuration file. " - "Make sure the proxy is properly configured in /etc/dnf/dnf.conf. " - "It's also possible the proxy settings in the DNF configuration file are " - "incompatible with the target system. A compatible configuration can be " - "placed in /etc/leapp/files/dnf.conf which, if present, will be used during " - "the upgrade instead of /etc/dnf/dnf.conf. " - "In such case the configuration will also be applied to the target system." + details['hint'] = ( + 'DNF failed to install userspace packages, likely due to the proxy ' + 'configuration detected in the YUM/DNF configuration file. ' + 'Make sure the proxy is properly configured in /etc/dnf/dnf.conf. ' + 'It\'s also possible the proxy settings in the DNF configuration file are ' + 'incompatible with the target system. A compatible configuration can be ' + 'placed in /etc/leapp/files/dnf.conf which, if present, will be used during ' + 'the upgrade instead of /etc/dnf/dnf.conf. ' + 'In such case the configuration will also be applied to the target system.' ) # Similarly if a proxy was set specifically for one of the repositories. for repo_facts in api.consume(RepositoriesFacts): for repo_file in repo_facts.repositories: if any(repo_data.proxy and repo_data.enabled for repo_data in repo_file.data): - details['details'] = ( - "DNF failed to install userspace packages, likely due to the proxy " - "configuration detected in a repository configuration file." + details['hint'] = ( + 'DNF failed to install userspace packages, likely due to the proxy ' + 'configuration detected in a repository configuration file.' ) + if get_source_distro_id() == 'centos' and get_target_distro_id() == 'rhel': + check_rhel_release_hint = ( + 'When upgrading and converting from Centos Stream to Red Hat Enterprise Linux' + ' (RHEL), the automatically determined latest target version of RHEL \'{}\' might' + ' not yet have been released. If so, specify the latest released RHEL version' + ' manually using the --target-version commandline option.' + ).format(get_target_version()) + + if details.get('hint'): + # keep the proxy hint, we don't know which one is the problem + details['hint'] = f"{details['hint']}\n\n{check_rhel_release_hint}" + else: + details['hint'] = check_rhel_release_hint + raise StopActorExecutionError(message=message, details=details) -- 2.51.1