From bf72d60012708ee77dd4d861014bc51c05568d0b Mon Sep 17 00:00:00 2001 From: Roman Prilipskii Date: Tue, 21 Jul 2026 08:52:01 -0400 Subject: [PATCH] target_userspace_creator: allow erasing conflicting packages during cloudlinux-release localinstall The preliminary `dnf localinstall cloudlinux-release` in prepare_target_userspace runs against the source overlay, so the source rpmdb is in scope. The target release's intentional Conflicts (e.g. cloudlinux-release-8 `Conflicts: rhn-client-tools < 2.11.5`, added by CLOS-3472 for the rhn hooks support) have no upgrade candidate on the source OS, so without --allowerasing dnf cannot resolve them and target_userspace_creator crashes on CL7->CL8 for any host carrying the default el7 rhn-client-tools 2.0.2 (ZD 287724). Add --allowerasing so dnf resolves the conflict by erasing the legacy package in the throwaway overlay, matching the real upgrade transaction which already runs with allow_erasing=True. Also covers the sibling cloudlinux-ea4-release < 1-11 conflict. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../libraries/userspacegen.py | 10 +++- .../tests/unit_test_targetuserspacecreator.py | 52 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py index 2d40d19a8e..15fa3a859d 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py @@ -266,7 +266,15 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): 'https://repo.cloudlinux.com/cloudlinux/migrate/release-files' '/cloudlinux/{version}/x86_64/cloudlinux{version}-release-current.x86_64.rpm' ).format(version=target_major_version) - context.call(['dnf', '-y', 'localinstall', cloudlinux_release_url], + # This localinstall runs against the (throwaway) source overlay, so the + # source rpmdb is in scope here. The target cloudlinux-release + # intentionally Conflicts with legacy packages that have no upgrade + # candidate on the source OS (e.g. rhn-client-tools < 2.11.5 on CL7, + # superseded by the CL8 build). Without --allowerasing dnf cannot + # resolve those conflicts and target_userspace_creator crashes (ZD + # 287724). Erasing here only touches the discarded overlay; the real + # upgrade transaction already resolves with allow_erasing=True. + context.call(['dnf', '-y', 'localinstall', '--allowerasing', cloudlinux_release_url], callback_raw=utils.logging_handler) # cloudlinux 9 does not have modular packages diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py index aac13fc67d..2ba78f5f20 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/tests/unit_test_targetuserspacecreator.py @@ -1224,3 +1224,55 @@ def test_perform_ok(monkeypatch): assert userspacegen.api.produce.model_instances[1] == msg_target_repos # this one is full of constants, so it's safe to check just the instance assert isinstance(userspacegen.api.produce.model_instances[2], models.TargetUserSpaceInfo) + + +class _RecordingContext(object): + """Minimal context stub that records the commands passed to call().""" + base_dir = '/base' + + def __init__(self): + self.commands = [] + + def call(self, cmd, **dummy_kwargs): + self.commands.append(cmd) + return {'stdout': '', 'stderr': ''} + + +class _NoopBindMount(object): + def __init__(self, **dummy_kwargs): + pass + + def __enter__(self): + return self + + def __exit__(self, *dummy_args): + return False + + +def test_prepare_target_userspace_allows_erasing_conflicts(monkeypatch): + """ + The preliminary cloudlinux-release localinstall runs against the source + overlay, where legacy packages the target release Conflicts with (e.g. + rhn-client-tools < 2.11.5 on CL7) are still present with no upgrade + candidate. It must pass --allowerasing so dnf can resolve the conflict in + the throwaway overlay instead of crashing target_userspace_creator + (ZD 287724). + """ + context = _RecordingContext() + monkeypatch.setattr(userspacegen, '_backup_to_persistent_package_cache', lambda *a, **k: None) + monkeypatch.setattr(userspacegen, '_restore_persistent_package_cache', lambda *a, **k: None) + monkeypatch.setattr(userspacegen, '_create_target_userspace_directories', lambda *a, **k: None) + monkeypatch.setattr(userspacegen, 'run', lambda *a, **k: None) + monkeypatch.setattr(userspacegen, 'is_nogpgcheck_set', lambda: True) + monkeypatch.setattr(userspacegen, 'enable_spacewalk_module', lambda ctx: None) + monkeypatch.setattr(userspacegen, 'get_target_major_version', lambda: '8') + monkeypatch.setattr(userspacegen.mounting, 'BindMount', _NoopBindMount) + monkeypatch.setattr(userspacegen.rhsm, 'skip_rhsm', lambda: False) + monkeypatch.setattr(userspacegen.api, 'current_actor', CurrentActorMocked(dst_ver='8.10')) + monkeypatch.setattr(userspacegen.api, 'current_logger', logger_mocked()) + + userspacegen.prepare_target_userspace(context, '/tmp/userspace', ['cloudlinux8-baseos'], ['pkg']) + + localinstall_cmds = [c for c in context.commands if 'localinstall' in c] + assert localinstall_cmds, 'expected a dnf localinstall of cloudlinux-release' + assert '--allowerasing' in localinstall_cmds[0]