From 46b354f5040dc97d4e6068cb10e24b73ffb48c33 Mon Sep 17 00:00:00 2001 From: Adam Hosek Date: Fri, 19 Jan 2024 13:24:31 +0100 Subject: [PATCH] Fix the issue --- .../data/7/ppc64/configs/centos-7-ppc64.cfg | 12 ++-- .../data/7/x86_64/configs/centos-7-x86_64.cfg | 12 ++-- .../data/7/x86_64/configs/oracle-7-x86_64.cfg | 5 +- .../7/x86_64/configs/scientific-7-x86_64.cfg | 9 ++- .../8/x86_64/configs/almalinux-8-x86_64.cfg | 11 +++- .../data/8/x86_64/configs/centos-8-x86_64.cfg | 11 +++- .../data/8/x86_64/configs/oracle-8-x86_64.cfg | 12 +++- .../data/8/x86_64/configs/rocky-8-x86_64.cfg | 11 +++- .../9/x86_64/configs/almalinux-9-x86_64.cfg | 12 +++- .../data/9/x86_64/configs/oracle-9-x86_64.cfg | 11 +++- .../data/9/x86_64/configs/rocky-9-x86_64.cfg | 11 +++- .../pkgmanager/handlers/dnf/__init__.py | 22 +++++++ .../pkgmanager/handlers/yum/__init__.py | 25 +++++++- convert2rhel/systeminfo.py | 33 +++++++++++ .../pkgmanager/handlers/dnf/dnf_test.py | 40 ++++++++++++- .../pkgmanager/handlers/yum/yum_test.py | 40 ++++++++++++- convert2rhel/unit_tests/systeminfo_test.py | 58 +++++++++++++++++++ 17 files changed, 297 insertions(+), 38 deletions(-) diff --git a/convert2rhel/data/7/ppc64/configs/centos-7-ppc64.cfg b/convert2rhel/data/7/ppc64/configs/centos-7-ppc64.cfg index c1c215ba8c..1ccd8a0d92 100644 --- a/convert2rhel/data/7/ppc64/configs/centos-7-ppc64.cfg +++ b/convert2rhel/data/7/ppc64/configs/centos-7-ppc64.cfg @@ -10,11 +10,13 @@ gpg_fingerprints = # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - centos-bookmarks - centos-logos - centos-indexhtml - libreport-centos - libreport-plugin-mantisbt + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + centos-logos | redhat-logos + centos-indexhtml | redhat-indexhtml + centos-bookmarks | redhat-bookmarks + libreport-centos | libreport-rhel # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/7/x86_64/configs/centos-7-x86_64.cfg b/convert2rhel/data/7/x86_64/configs/centos-7-x86_64.cfg index 0153c9cd33..3fcdd93b32 100644 --- a/convert2rhel/data/7/x86_64/configs/centos-7-x86_64.cfg +++ b/convert2rhel/data/7/x86_64/configs/centos-7-x86_64.cfg @@ -9,20 +9,22 @@ gpg_fingerprints = 24c6a8a7f4a80eb5 # Delimited by any whitespace(s). excluded_pkgs = anaconda-dracut - centos-bookmarks - centos-indexhtml - centos-logos cockpit-subscriptions geoipupdate kmod-kvdo - libreport-centos - libreport-plugin-mantisbt mod_ldap mod_proxy_html rhn* yum-rhn-plugin gnome-documents-libs +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + centos-logos | redhat-logos + centos-indexhtml | redhat-indexhtml + centos-bookmarks | redhat-bookmarks + libreport-centos | libreport-rhel + # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). repofile_pkgs = diff --git a/convert2rhel/data/7/x86_64/configs/oracle-7-x86_64.cfg b/convert2rhel/data/7/x86_64/configs/oracle-7-x86_64.cfg index c191c326a6..cf19f1745a 100644 --- a/convert2rhel/data/7/x86_64/configs/oracle-7-x86_64.cfg +++ b/convert2rhel/data/7/x86_64/configs/oracle-7-x86_64.cfg @@ -11,7 +11,6 @@ gpg_fingerprints = 72f97b74ec551f03 excluded_pkgs = akonadi-mysql anaconda-dracut - oracle-logos oracle-rdbms-server-* oracleasm-support rhn-check @@ -26,6 +25,10 @@ excluded_pkgs = gnome-documents-libs shim-x64 +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + oracle-logos | redhat-logos + # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # The rhn-client-tools package contains repofiles on OL 7.6 and older, but when it's installed it's not possible to # install subscription-manager. That means that the conversion using RHSM is not possible on OL 7.6 and older. diff --git a/convert2rhel/data/7/x86_64/configs/scientific-7-x86_64.cfg b/convert2rhel/data/7/x86_64/configs/scientific-7-x86_64.cfg index 6550f6940d..039996df4c 100644 --- a/convert2rhel/data/7/x86_64/configs/scientific-7-x86_64.cfg +++ b/convert2rhel/data/7/x86_64/configs/scientific-7-x86_64.cfg @@ -9,9 +9,6 @@ gpg_fingerprints = b0b4183f192a7d7d # Delimited by any whitespace(s). excluded_pkgs = anaconda-dracut - sl-bookmarks - sl-indexhtml - sl-logos sl-release-notes sl7-upgrade cockpit-subscriptions @@ -23,6 +20,12 @@ excluded_pkgs = rhn* yum-rhn-plugin +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + sl-logos | redhat-logos + sl-indexhtml | redhat-indexhtml + sl-bookmarks | redhat-bookmarks + # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). repofile_pkgs = diff --git a/convert2rhel/data/8/x86_64/configs/almalinux-8-x86_64.cfg b/convert2rhel/data/8/x86_64/configs/almalinux-8-x86_64.cfg index 89e168b914..4391491d35 100644 --- a/convert2rhel/data/8/x86_64/configs/almalinux-8-x86_64.cfg +++ b/convert2rhel/data/8/x86_64/configs/almalinux-8-x86_64.cfg @@ -10,10 +10,15 @@ gpg_fingerprints = # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - almalinux-logos* - almalinux-indexhtml almalinux-gpg-keys - almalinux-backgrounds* + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + almalinux-logos | redhat-logos + almalinux-logos-httpd | redhat-logos-httpd + almalinux-logos-ipa | redhat-logos-ipa + almalinux-indexhtml | redhat-indexhtml + almalinux-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/8/x86_64/configs/centos-8-x86_64.cfg b/convert2rhel/data/8/x86_64/configs/centos-8-x86_64.cfg index ae3870b4e1..9e61c3c158 100644 --- a/convert2rhel/data/8/x86_64/configs/centos-8-x86_64.cfg +++ b/convert2rhel/data/8/x86_64/configs/centos-8-x86_64.cfg @@ -8,11 +8,16 @@ gpg_fingerprints = 05b555b38483c65d # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - centos-logos* - centos-indexhtml centos-obsolete-packages centos-gpg-keys - centos-backgrounds + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + centos-logos | redhat-logos + centos-logos-httpd | redhat-logos-httpd + centos-logos-ipa | redhat-logos-ipa + centos-indexhtml | redhat-indexhtml + centos-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/8/x86_64/configs/oracle-8-x86_64.cfg b/convert2rhel/data/8/x86_64/configs/oracle-8-x86_64.cfg index 63764a3632..7d34715aa8 100644 --- a/convert2rhel/data/8/x86_64/configs/oracle-8-x86_64.cfg +++ b/convert2rhel/data/8/x86_64/configs/oracle-8-x86_64.cfg @@ -8,9 +8,15 @@ gpg_fingerprints = 82562ea9ad986da3 # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - oracle-logos* - oracle-indexhtml - oracle-backgrounds + + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + oracle-logos | redhat-logos + oracle-logos-httpd | redhat-logos-httpd + oracle-logos-ipa | redhat-logos-ipa + oracle-indexhtml | redhat-indexhtml + oracle-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/8/x86_64/configs/rocky-8-x86_64.cfg b/convert2rhel/data/8/x86_64/configs/rocky-8-x86_64.cfg index 77bfe2af77..cae9922bcf 100644 --- a/convert2rhel/data/8/x86_64/configs/rocky-8-x86_64.cfg +++ b/convert2rhel/data/8/x86_64/configs/rocky-8-x86_64.cfg @@ -8,11 +8,16 @@ gpg_fingerprints = 15af5dac6d745a60 # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - rocky-logos* - rocky-indexhtml rocky-obsolete-packages rocky-gpg-keys - rocky-backgrounds + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + rocky-logos | redhat-logos + rocky-logos-httpd | redhat-logos-httpd + rocky-logos-ipa | redhat-logos-ipa + rocky-indexhtml | redhat-indexhtml + rocky-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/9/x86_64/configs/almalinux-9-x86_64.cfg b/convert2rhel/data/9/x86_64/configs/almalinux-9-x86_64.cfg index ab29fe22fd..42e22638b2 100644 --- a/convert2rhel/data/9/x86_64/configs/almalinux-9-x86_64.cfg +++ b/convert2rhel/data/9/x86_64/configs/almalinux-9-x86_64.cfg @@ -8,10 +8,16 @@ gpg_fingerprints = d36cb86cb86b3716 # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - almalinux-logos* - almalinux-indexhtml almalinux-gpg-keys - almalinux-backgrounds* + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + almalinux-logos | redhat-logos + almalinux-logos-httpd | redhat-logos-httpd + almalinux-logos-ipa | redhat-logos-ipa + almalinux-indexhtml | redhat-indexhtml + almalinux-backgrounds | redhat-backgrounds + # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/9/x86_64/configs/oracle-9-x86_64.cfg b/convert2rhel/data/9/x86_64/configs/oracle-9-x86_64.cfg index ee3712c416..14c60aefaf 100644 --- a/convert2rhel/data/9/x86_64/configs/oracle-9-x86_64.cfg +++ b/convert2rhel/data/9/x86_64/configs/oracle-9-x86_64.cfg @@ -10,9 +10,14 @@ gpg_fingerprints = # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - oracle-logos* - oracle-indexhtml - oracle-backgrounds + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + oracle-logos | redhat-logos + oracle-logos-httpd | redhat-logos-httpd + oracle-logos-ipa | redhat-logos-ipa + oracle-indexhtml | redhat-indexhtml + oracle-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/data/9/x86_64/configs/rocky-9-x86_64.cfg b/convert2rhel/data/9/x86_64/configs/rocky-9-x86_64.cfg index deba3e967f..1c0ba86617 100644 --- a/convert2rhel/data/9/x86_64/configs/rocky-9-x86_64.cfg +++ b/convert2rhel/data/9/x86_64/configs/rocky-9-x86_64.cfg @@ -8,11 +8,16 @@ gpg_fingerprints = 702d426d350d275d # List of packages to be removed before the system conversion starts. # Delimited by any whitespace(s). excluded_pkgs = - rocky-logos* - rocky-indexhtml rocky-obsolete-packages rocky-gpg-keys - rocky-backgrounds + +# Mapping of packages that need to be swapped during the transaction +swap_pkgs = + rocky-logos | redhat-logos + rocky-logos-httpd | redhat-logos-httpd + rocky-logos-ipa | redhat-logos-ipa + rocky-indexhtml | redhat-indexhtml + rocky-backgrounds | redhat-backgrounds # List of packages that either contain repofiles or affect variables in the repofiles (e.g. $releasever). # Delimited by any whitespace(s). diff --git a/convert2rhel/pkgmanager/handlers/dnf/__init__.py b/convert2rhel/pkgmanager/handlers/dnf/__init__.py index f3ea0aa220..6e5c1d2ee5 100644 --- a/convert2rhel/pkgmanager/handlers/dnf/__init__.py +++ b/convert2rhel/pkgmanager/handlers/dnf/__init__.py @@ -111,6 +111,26 @@ def _enable_repos(self): diagnosis="Loading repository metadata failed with error %s." % (str(e)), ) + def _swap_base_os_specific_packages(self): + """Swap base os specific packages for their RHEL counterparts in the transaction. + + Some packages need to be manually injected in the transaction as a + "swap", since those packages are not always able to be installed + automatically by yum if they don't exist in the system anymore, this + can cause problems during the transaction as missing dependencies. + """ + # Related issue: https://issues.redhat.com/browse/RHELC-1130, see comments + # to get more proper description of solution + for old_package, new_package in system_info.swap_pkgs.items(): + loggerinst.debug("Checking if %s installed for later swap." % old_package) + is_installed = system_info.is_rpm_installed(old_package) + if is_installed: + loggerinst.debug("Package %s will be swapped to %s during conversion." % (old_package, new_package)) + # Order of commands based on DNF implementation of swap, different from YUM order: + # https://github.com/rpm-software-management/dnf/blob/master/dnf/cli/commands/swap.py#L60 + self._base.install(pkg_spec=new_package) + self._base.remove(pkg_spec=old_package) + def _perform_operations(self): """Perform the necessary operations in the transaction. @@ -146,6 +166,8 @@ def _perform_operations(self): except pkgmanager.exceptions.PackagesNotInstalledError: loggerinst.warning("Package %s not available in RHEL repositories.", pkg) + self._swap_base_os_specific_packages() + def _resolve_dependencies(self): """Resolve the dependencies for the transaction. diff --git a/convert2rhel/pkgmanager/handlers/yum/__init__.py b/convert2rhel/pkgmanager/handlers/yum/__init__.py index 6225b37a00..cbf5ba83ff 100644 --- a/convert2rhel/pkgmanager/handlers/yum/__init__.py +++ b/convert2rhel/pkgmanager/handlers/yum/__init__.py @@ -163,6 +163,26 @@ def _enable_repos(self): diagnosis="Loading repository metadata failed with error %s." % (str(e)), ) + def _swap_base_os_specific_packages(self): + """Swap base os specific packages for their RHEL counterparts in the transaction. + + Some packages need to be manually injected in the transaction as a + "swap", since those packages are not always able to be installed + automatically by yum if they don't exist in the system anymore, this + can cause problems during the transaction as missing dependencies. + """ + # Related issue: https://issues.redhat.com/browse/RHELC-1130, see comments + # to get more proper description of solution + for old_package, new_package in system_info.swap_pkgs.items(): + loggerinst.debug("Checking if %s installed for later swap." % old_package) + is_installed = system_info.is_rpm_installed(old_package) + if is_installed: + loggerinst.debug("Package %s will be swapped to %s during conversion." % (old_package, new_package)) + # Order of operations based on YUM implementation of swap: + # https://github.com/rpm-software-management/yum/blob/master/yumcommands.py#L3488 + self._base.remove(pattern=old_package) + self._base.install(pattern=new_package) + def _perform_operations(self): """Perform the necessary operations in the transaction. @@ -187,7 +207,6 @@ def _perform_operations(self): # being outdated in the system after the conversion. if can_update: continue - try: self._base.reinstall(pattern=pkg) except (pkgmanager.Errors.ReinstallInstallError, pkgmanager.Errors.ReinstallRemoveError): @@ -199,6 +218,10 @@ def _perform_operations(self): pkgmanager.Errors.DowngradeError, ): loggerinst.warning("Package %s not available in RHEL repositories.", pkg) + + # Swapping the packages needs to be after the operations + # If not, swapped packages are removed from transaction as obsolete + self._swap_base_os_specific_packages() except pkgmanager.Errors.NoMoreMirrorsRepoError as e: loggerinst.debug("Got the following exception message: %s", e) loggerinst.critical_no_exit("There are no suitable mirrors available for the loaded repositories.") diff --git a/convert2rhel/systeminfo.py b/convert2rhel/systeminfo.py index 6684716ff4..60c8f7961d 100644 --- a/convert2rhel/systeminfo.py +++ b/convert2rhel/systeminfo.py @@ -93,6 +93,8 @@ def __init__(self): self.eus_system = None # Packages to be removed before the system conversion self.excluded_pkgs = [] + # Packages that need to perform a swap in the transaction + self.swap_pkgs = {} # Release packages to be removed before the system conversion self.repofile_pkgs = [] self.cfg_filename = None @@ -124,6 +126,7 @@ def resolve_system_info(self): self.cfg_filename = self._get_cfg_filename() self.cfg_content = self._get_cfg_content() self.excluded_pkgs = self._get_excluded_pkgs() + self.swap_pkgs = self._get_swap_pkgs() self.repofile_pkgs = self._get_repofile_pkgs() self.default_rhsm_repoids = self._get_default_rhsm_repoids() self.eus_rhsm_repoids = self._get_eus_rhsm_repoids() @@ -251,6 +254,36 @@ def _get_gpg_key_fingerprints(self): def _get_excluded_pkgs(self): return self._get_cfg_opt("excluded_pkgs").split() + def _get_swap_pkgs(self): + pkgs_to_swap = {} + + try: + lines = self._get_cfg_opt("swap_pkgs").strip().split("\n") + for line in lines: + old_package, new_package = tuple(line.split("|")) + + old_package = old_package.strip() + new_package = new_package.strip() + + if old_package in pkgs_to_swap: + self.logger.warning( + "Package {old_package} redefined in swap packages list.\n" + "Old package {old_package} will be swapped by {newest_package} instead of {new_package}.".format( + old_package=old_package, new_package=pkgs_to_swap[old_package], newest_package=new_package + ) + ) + pkgs_to_swap.update({old_package: new_package}) + + except ValueError: + # Leave the swap packages dict empty, packages for swap aren't defined + self.logger.debug("Leaving the swap package list empty. No packages defined.") + + except AttributeError: + # Leave the swap packages dict empty, missing swap_pkgs in config file + self.logger.warning("Leaving the swap package list empty. Missing swap_pkgs key in configuration file.") + + return pkgs_to_swap + def _get_repofile_pkgs(self): return self._get_cfg_opt("repofile_pkgs").split() diff --git a/convert2rhel/unit_tests/pkgmanager/handlers/dnf/dnf_test.py b/convert2rhel/unit_tests/pkgmanager/handlers/dnf/dnf_test.py index 11df1bb604..177019e9d6 100644 --- a/convert2rhel/unit_tests/pkgmanager/handlers/dnf/dnf_test.py +++ b/convert2rhel/unit_tests/pkgmanager/handlers/dnf/dnf_test.py @@ -125,6 +125,8 @@ def _mock_dnf_api_calls(self, monkeypatch): monkeypatch.setattr(pkgmanager.Base, "do_transaction", value=mock.Mock()) monkeypatch.setattr(pkgmanager.Base, "transaction", value=mock.Mock()) monkeypatch.setattr(pkgmanager.Base, "sack", value=SackMock()) + monkeypatch.setattr(pkgmanager.Base, "install", value=mock.Mock()) + monkeypatch.setattr(pkgmanager.Base, "remove", value=mock.Mock()) @centos8 def test_set_up_base(self, pretend_os): @@ -215,17 +217,21 @@ def test_enable_repos_repo_error_exception( @centos8 def test_perform_operations(self, pretend_os, caplog, monkeypatch): + swap_base_os_specific_packages = mock.Mock() + monkeypatch.setattr( pkghandler, "get_installed_pkg_information", value=lambda: SYSTEM_PACKAGES, ) + monkeypatch.setattr(DnfTransactionHandler, "_swap_base_os_specific_packages", swap_base_os_specific_packages) instance = DnfTransactionHandler() instance._set_up_base() instance._perform_operations() assert pkgmanager.Base.reinstall.call_count == len(SYSTEM_PACKAGES) assert pkgmanager.Base.downgrade.call_count == 0 + assert swap_base_os_specific_packages.call_count == 1 @centos8 def test_package_marked_for_update(self, pretend_os, monkeypatch): @@ -277,7 +283,7 @@ def test_perform_operations_downgrade_exception(self, pretend_os, caplog, monkey assert pkgmanager.Base.reinstall.call_count == len(SYSTEM_PACKAGES) assert pkgmanager.Base.downgrade.call_count == len(SYSTEM_PACKAGES) - assert "not available in RHEL repositories" in caplog.records[-1].message + assert "not available in RHEL repositories" in caplog.text @centos8 def test_resolve_dependencies(self, pretend_os, caplog, monkeypatch): @@ -364,3 +370,35 @@ def test_run_transaction(self, pretend_os, validate_transaction, caplog, monkeyp assert instance._perform_operations.call_count == 1 assert instance._resolve_dependencies.call_count == 1 assert instance._process_transaction.call_count == 1 + + @centos8 + @pytest.mark.parametrize( + ("installed_pkgs", "swap_pkgs", "swaps"), + ( + (["pkg0", "pkg1"], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 2), + (["pkg1"], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 1), + ([], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 0), + (["pkg0", "pkg1", "pkg2"], {}, 0), + ([], {}, 0), + ), + ) + def test_swap_problematic_packages( + self, monkeypatch, installed_pkgs, swap_pkgs, _mock_dnf_api_calls, pretend_os, swaps + ): + def return_installed(pkg): + """Dynamically change the return value.""" + return True if pkg in installed_pkgs else False + + is_rpm_installed = mock.Mock(side_effect=return_installed) + + monkeypatch.setattr(system_info, "is_rpm_installed", value=is_rpm_installed) + monkeypatch.setattr(system_info, "swap_pkgs", value=swap_pkgs) + + instance = DnfTransactionHandler() + # Need to setup the base, in the production code it's done in upper level + instance._set_up_base() + + instance._swap_base_os_specific_packages() + + assert pkgmanager.Base.install.call_count == swaps + assert pkgmanager.Base.remove.call_count == swaps diff --git a/convert2rhel/unit_tests/pkgmanager/handlers/yum/yum_test.py b/convert2rhel/unit_tests/pkgmanager/handlers/yum/yum_test.py index 9b95c94823..0718a19b19 100644 --- a/convert2rhel/unit_tests/pkgmanager/handlers/yum/yum_test.py +++ b/convert2rhel/unit_tests/pkgmanager/handlers/yum/yum_test.py @@ -107,6 +107,8 @@ def _mock_yum_api_calls(self, monkeypatch): monkeypatch.setattr(pkgmanager.YumBase, "downgrade", value=mock.Mock()) monkeypatch.setattr(pkgmanager.YumBase, "resolveDeps", value=mock.Mock(return_value=(0, "Success."))) monkeypatch.setattr(pkgmanager.YumBase, "processTransaction", value=mock.Mock()) + monkeypatch.setattr(pkgmanager.YumBase, "install", value=mock.Mock()) + monkeypatch.setattr(pkgmanager.YumBase, "remove", value=mock.Mock()) @centos7 def test_set_up_base(self, pretend_os): @@ -144,7 +146,10 @@ def test_enable_repos_repo_error(self, pretend_os, enabled_rhel_repos, caplog, m @centos7 def test_perform_operations(self, pretend_os, caplog, monkeypatch): + swap_base_os_specific_packages = mock.Mock() + monkeypatch.setattr(pkghandler, "get_installed_pkg_information", lambda: SYSTEM_PACKAGES) + monkeypatch.setattr(YumTransactionHandler, "_swap_base_os_specific_packages", swap_base_os_specific_packages) instance = YumTransactionHandler() instance._perform_operations() @@ -152,6 +157,7 @@ def test_perform_operations(self, pretend_os, caplog, monkeypatch): assert pkgmanager.YumBase.update.call_count == len(SYSTEM_PACKAGES) assert pkgmanager.YumBase.reinstall.call_count == len(SYSTEM_PACKAGES) assert pkgmanager.YumBase.downgrade.call_count == 0 + assert swap_base_os_specific_packages.call_count == 1 @centos7 def test_perform_operations_reinstall_exception(self, pretend_os, caplog, monkeypatch): @@ -176,7 +182,7 @@ def test_perform_operations_downgrade_exception(self, pretend_os, caplog, monkey assert pkgmanager.YumBase.reinstall.call_count == len(SYSTEM_PACKAGES) assert pkgmanager.YumBase.downgrade.call_count == len(SYSTEM_PACKAGES) - assert "not available in RHEL repositories." in caplog.records[-1].message + assert "not available in RHEL repositories." in caplog.text @centos7 def test_perform_operations_no_more_mirrors_repo_exception(self, pretend_os, monkeypatch): @@ -324,6 +330,38 @@ def test_package_marked_for_update(self, pretend_os, monkeypatch): assert pkgmanager.YumBase.reinstall.call_count == 0 assert pkgmanager.YumBase.downgrade.call_count == 0 + @centos7 + @pytest.mark.parametrize( + ("installed_pkgs", "swap_pkgs", "swaps"), + ( + (["pkg0", "pkg1"], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 2), + (["pkg1"], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 1), + ([], {"pkg0": "new_pkg0", "pkg1": "new_pkg1"}, 0), + (["pkg0", "pkg1", "pkg2"], {}, 0), + ([], {}, 0), + ), + ) + def test_swap_problematic_packages( + self, monkeypatch, installed_pkgs, swap_pkgs, _mock_yum_api_calls, pretend_os, swaps + ): + def return_installed(pkg): + """Dynamically change the return value.""" + return True if pkg in installed_pkgs else False + + is_rpm_installed = mock.Mock(side_effect=return_installed) + + monkeypatch.setattr(system_info, "is_rpm_installed", value=is_rpm_installed) + monkeypatch.setattr(system_info, "swap_pkgs", value=swap_pkgs) + + instance = YumTransactionHandler() + # Need to setup the base, in the production code it's done in upper level + instance._set_up_base() + + instance._swap_base_os_specific_packages() + + assert pkgmanager.YumBase.remove.call_count == swaps + assert pkgmanager.YumBase.install.call_count == swaps + @centos7 @pytest.mark.parametrize( diff --git a/convert2rhel/unit_tests/systeminfo_test.py b/convert2rhel/unit_tests/systeminfo_test.py index 630e1e8c24..a7b729b5e9 100644 --- a/convert2rhel/unit_tests/systeminfo_test.py +++ b/convert2rhel/unit_tests/systeminfo_test.py @@ -407,3 +407,61 @@ def test_print_system_information(pretend_os, caplog): assert "8.5" in caplog.records[-3].message assert "x86_64" in caplog.records[-2].message assert "centos-8-x86_64.cfg" in caplog.records[-1].message + + +@pytest.mark.parametrize( + ("file_content", "expected", "message"), + ( + ( + """ +[system_info] +swap_pkgs = + centos-logos | redhat-logos + centos-indexhtml | redhat-indexhtml + +""", + {"centos-indexhtml": "redhat-indexhtml", "centos-logos": "redhat-logos"}, + "", + ), + ( + """ +[system_info] +swap_pkgs = +""", + {}, + "Leaving the swap package list empty. No packages defined.", + ), + ( + """ +[system_info] +swap_pkgs = + centos-logos | redhat-logos + centos-logos | redhat-logos2 +""", + {"centos-logos": "redhat-logos2"}, + "Old package centos-logos will be swapped by redhat-logos2 instead of redhat-logos.", + ), + ( + """ +[system_info] +""", + {}, + "Leaving the swap package list empty. Missing swap_pkgs key in configuration file.", + ), + ), +) +def test_get_swap_pkgs(monkeypatch, file_content, tmpdir, expected, message, caplog): + cfg_filename = "test.cfg" + cfg_path = tmpdir.mkdir("configs").join(cfg_filename) + cfg_path.write(file_content) + + monkeypatch.setattr(utils, "DATA_DIR", str(tmpdir)) + monkeypatch.setattr(system_info, "cfg_filename", cfg_filename) + monkeypatch.setattr(system_info, "cfg_content", system_info._get_cfg_content()) + + assert system_info._get_swap_pkgs() == expected + + if message: + assert message in caplog.text + else: + assert "" == caplog.text