From 7c12aa89f467c843c3d73407ce9d8078ddeebf85 Mon Sep 17 00:00:00 2001 From: Preston Watson Date: Thu, 15 Aug 2024 12:23:42 -0400 Subject: [PATCH] [RHELC-1332] Port PkgManagerConf() to the Action framework (#1321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [RHELC-1332] Port PkgManagerConf() to the Action framework * Add pkg manager config and unit test * Apply suggestions from code review Co-authored-by: Adam Hošek * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Draft PkgManagerConf changes * Updated PkgManagerConfig structure to use instance variable * Apply suggestions from code review Co-authored-by: Freya Gustavsson Co-authored-by: Rodolfo Olivieri * Update class name * Add dependency * Add mocks for write_altered_pkg_manager_conf * Remove PosixPath from unit test * Remove sys import * Update convert2rhel/redhatrelease.py Co-authored-by: Rodolfo Olivieri * Fix changed-yum int test. --------- Co-authored-by: Adam Hošek Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Freya Gustavsson Co-authored-by: Rodolfo Olivieri Co-authored-by: Rodolfo Olivieri --- .../actions/conversion/pkg_manager_config.py | 39 +++++++++++ convert2rhel/main.py | 3 +- convert2rhel/redhatrelease.py | 49 ++++++++------ .../conversion/pkg_manager_config_test.py | 38 +++++++++++ convert2rhel/unit_tests/main_test.py | 3 - convert2rhel/unit_tests/redhatrelease_test.py | 67 ++++++++++--------- .../changed-yum-conf/test_patch_yum_conf.py | 7 +- 7 files changed, 149 insertions(+), 57 deletions(-) create mode 100644 convert2rhel/actions/conversion/pkg_manager_config.py create mode 100644 convert2rhel/unit_tests/actions/conversion/pkg_manager_config_test.py diff --git a/convert2rhel/actions/conversion/pkg_manager_config.py b/convert2rhel/actions/conversion/pkg_manager_config.py new file mode 100644 index 0000000000..cca27f9926 --- /dev/null +++ b/convert2rhel/actions/conversion/pkg_manager_config.py @@ -0,0 +1,39 @@ +# Copyright(C) 2024 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__metaclass__ = type + +import logging + +from convert2rhel import actions, redhatrelease + + +logger = logging.getLogger(__name__) + + +class ConfigurePkgManager(actions.Action): + id = "CONFIGURE_PKG_MANAGER" + dependencies = ("CONVERT_SYSTEM_PACKAGES",) + + def run(self): + """ + Check if the distroverpkg tag inside the package manager config has been modified before the conversion and if so + comment it out and write to the file. + """ + super(ConfigurePkgManager, self).run() + + logger.task("Convert: Patch package manager configuration file") + pmc = redhatrelease.PkgManagerConf() + pmc.patch() diff --git a/convert2rhel/main.py b/convert2rhel/main.py index 04587b0700..dfde484b13 100644 --- a/convert2rhel/main.py +++ b/convert2rhel/main.py @@ -395,8 +395,7 @@ def post_ponr_changes(): def post_ponr_conversion(): """Perform main steps for system conversion.""" - loggerinst.task("Convert: Patch yum configuration file") - redhatrelease.YumConf().patch() + loggerinst.task("Convert: Lock releasever in RHEL repositories") subscription.lock_releasever_in_rhel_repositories() diff --git a/convert2rhel/redhatrelease.py b/convert2rhel/redhatrelease.py index 0d91eeca40..6dda1d9d79 100644 --- a/convert2rhel/redhatrelease.py +++ b/convert2rhel/redhatrelease.py @@ -49,45 +49,56 @@ def get_system_release_content(): loggerinst.critical("%s\n%s file is essential for running this tool." % (err, filepath)) -class YumConf: - _yum_conf_path = "/etc/yum.conf" +class PkgManagerConf: + """ + Check if the config file of the systems package manager has been modified and if it has then + remove those changes before the conversion completes. + .. note:: + The pkg manager config file path only needs to be set to yum.conf as there is a symlink between yum and dnf. + This means that on dnf systems the dnf.conf will be modified even the path is for the yum.conf. + """ - def __init__(self): - self._yum_conf_content = utils.get_file_content(self._yum_conf_path) + _pkg_manager_conf_path = "" # type: str + _pkg_manager_conf_content = "" # type: str + + def __init__(self, config_path=None): # type: (str|None) -> None + if not config_path: + self._pkg_manager_conf_path = "/etc/yum.conf" if pkgmanager.TYPE == "yum" else "/etc/dnf/dnf.conf" + else: + self._pkg_manager_conf_path = config_path + self._pkg_manager_conf_content = utils.get_file_content(self._pkg_manager_conf_path) def patch(self): """Comment out the distroverpkg variable in yum.conf so yum can determine release version ($releasever) based on the installed redhat-release package. """ - if YumConf.is_modified(): - # When the user touches the yum.conf before executing the conversion, then during the conversion yum as a + if self.is_modified(): + # When the user touches the yum/dnf config before executing the conversion, then during the conversion yum/dmf as a # package is replaced but this config file is left unchanged and it keeps the original distroverpkg setting. self._comment_out_distroverpkg_tag() - self._write_altered_yum_conf() - loggerinst.info("%s patched." % self._yum_conf_path) + self._write_altered_pkg_manager_conf() + loggerinst.info("%s patched." % self._pkg_manager_conf_path) else: - loggerinst.info("Skipping patching, yum configuration file not modified") + loggerinst.info("Skipping patching, package manager configuration file has not been modified.") return def _comment_out_distroverpkg_tag(self): - if re.search(r"^distroverpkg=", self._yum_conf_content, re.MULTILINE): - self._yum_conf_content = re.sub(r"\n(distroverpkg=).*", r"\n#\1", self._yum_conf_content) + if re.search(r"^distroverpkg=", self._pkg_manager_conf_content, re.MULTILINE): + self._pkg_manager_conf_content = re.sub(r"\n(distroverpkg=).*", r"\n#\1", self._pkg_manager_conf_content) - def _write_altered_yum_conf(self): - with open(self._yum_conf_path, "w") as file_to_write: - file_to_write.write(self._yum_conf_content) + def _write_altered_pkg_manager_conf(self): + with open(self._pkg_manager_conf_path, "w") as file_to_write: + file_to_write.write(self._pkg_manager_conf_content) - @staticmethod - def is_modified(): + def is_modified(self): """Return true if the YUM/DNF configuration file has been modified by the user.""" - conf = "/etc/yum.conf" if pkgmanager.TYPE == "yum" else "/etc/dnf/dnf.conf" - output, _ = utils.run_subprocess(["rpm", "-Vf", conf], print_output=False) + output, _ = utils.run_subprocess(["rpm", "-Vf", self._pkg_manager_conf_path], print_output=False) # rpm -Vf does not return information about the queried file but about all files owned by the rpm # that owns the queried file. Character '5' on position 3 means that the file was modified. - return True if re.search(r"^.{2}5.*? %s$" % conf, output, re.MULTILINE) else False + return True if re.search(r"^.{2}5.*? %s$" % self._pkg_manager_conf_path, output, re.MULTILINE) else False # Code to be executed upon module import diff --git a/convert2rhel/unit_tests/actions/conversion/pkg_manager_config_test.py b/convert2rhel/unit_tests/actions/conversion/pkg_manager_config_test.py new file mode 100644 index 0000000000..0546ca47b2 --- /dev/null +++ b/convert2rhel/unit_tests/actions/conversion/pkg_manager_config_test.py @@ -0,0 +1,38 @@ +# Copyright(C) 2024 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +__metaclass__ = type +import pytest +import six + +from convert2rhel import redhatrelease +from convert2rhel.actions.conversion import pkg_manager_config + + +six.add_move(six.MovedModule("mock", "mock", "unittest.mock")) +from six.moves import mock + + +@pytest.fixture +def pkg_manager_config_instance(): + return pkg_manager_config.ConfigurePkgManager() + + +def test_pkg_manager_config(pkg_manager_config_instance, monkeypatch): + redhat_release_mock = mock.Mock() + monkeypatch.setattr(redhatrelease.PkgManagerConf, "patch", redhat_release_mock) + pkg_manager_config_instance.run() + + assert redhat_release_mock.call_count == 1 diff --git a/convert2rhel/unit_tests/main_test.py b/convert2rhel/unit_tests/main_test.py index a0c07ec7b7..1e1c02eb22 100644 --- a/convert2rhel/unit_tests/main_test.py +++ b/convert2rhel/unit_tests/main_test.py @@ -208,15 +208,12 @@ def test_show_eula_nonexisting_file(self, caplog, monkeypatch, tmpdir): def test_post_ponr_conversion(monkeypatch): post_ponr_set_efi_configuration_mock = mock.Mock() - yum_conf_patch_mock = mock.Mock() lock_releasever_in_rhel_repositories_mock = mock.Mock() - monkeypatch.setattr(redhatrelease.YumConf, "patch", yum_conf_patch_mock) monkeypatch.setattr(subscription, "lock_releasever_in_rhel_repositories", lock_releasever_in_rhel_repositories_mock) main.post_ponr_conversion() - assert yum_conf_patch_mock.call_count == 1 assert lock_releasever_in_rhel_repositories_mock.call_count == 1 diff --git a/convert2rhel/unit_tests/redhatrelease_test.py b/convert2rhel/unit_tests/redhatrelease_test.py index 41cc1b51c4..a3e9e980ac 100644 --- a/convert2rhel/unit_tests/redhatrelease_test.py +++ b/convert2rhel/unit_tests/redhatrelease_test.py @@ -29,16 +29,16 @@ from convert2rhel import unit_tests # Imports unit_tests/__init__.py from convert2rhel import pkgmanager, redhatrelease, systeminfo, utils -from convert2rhel.redhatrelease import YumConf, get_system_release_filepath +from convert2rhel.redhatrelease import PkgManagerConf, get_system_release_filepath from convert2rhel.systeminfo import system_info -YUM_CONF_WITHOUT_DISTROVERPKG = """[main] +PKG_MANAGER_CONF_WITHOUT_DISTROVERPKG = """[main] installonly_limit=3 # This is the default""" -YUM_CONF_WITH_DISTROVERPKG = """[main] +PKG_MANAGER_CONF_WITH_DISTROVERPKG = """[main] installonly_limit=3 distroverpkg=centos-release @@ -48,40 +48,42 @@ SUPPORTED_RHEL_VERSIONS = [7, 8] -def test_get_yum_conf_content(monkeypatch): - monkeypatch.setattr(redhatrelease.YumConf, "_yum_conf_path", unit_tests.DUMMY_FILE) +@pytest.fixture() +def pkg_manager_conf_instance(): + return PkgManagerConf() - yum_conf = redhatrelease.YumConf() - assert "Dummy file to read" in yum_conf._yum_conf_content +def test_get_pkg_manager_conf_content(monkeypatch): + pkg_manager_conf = redhatrelease.PkgManagerConf(config_path=unit_tests.DUMMY_FILE) + assert "Dummy file to read" in pkg_manager_conf._pkg_manager_conf_content @pytest.mark.parametrize("version", SUPPORTED_RHEL_VERSIONS) -def test_patch_yum_conf_missing_distroverpkg(version, monkeypatch): - monkeypatch.setattr(redhatrelease.YumConf, "_yum_conf_path", unit_tests.DUMMY_FILE) +def test_patch_pkg_manager_conf_missing_distroverpkg(version, monkeypatch, pkg_manager_conf_instance): + monkeypatch.setattr(system_info, "version", version) - yum_conf = redhatrelease.YumConf() - yum_conf._yum_conf_content = YUM_CONF_WITHOUT_DISTROVERPKG + pkg_manager_conf = pkg_manager_conf_instance + pkg_manager_conf._pkg_manager_conf_content = PKG_MANAGER_CONF_WITHOUT_DISTROVERPKG # Call just this function to avoid unmockable built-in write func - yum_conf._comment_out_distroverpkg_tag() + pkg_manager_conf._comment_out_distroverpkg_tag() - assert "distroverpkg=" not in yum_conf._yum_conf_content - assert yum_conf._yum_conf_content.count("distroverpkg=") == 0 + assert "distroverpkg=" not in pkg_manager_conf._pkg_manager_conf_content + assert pkg_manager_conf._pkg_manager_conf_content.count("distroverpkg=") == 0 @pytest.mark.parametrize("version", SUPPORTED_RHEL_VERSIONS) -def test_patch_yum_conf_existing_distroverpkg(version, monkeypatch): - monkeypatch.setattr(redhatrelease.YumConf, "_yum_conf_path", unit_tests.DUMMY_FILE) +def test_patch_pkg_manager_conf_existing_distroverpkg(version, monkeypatch, pkg_manager_conf_instance): + monkeypatch.setattr(system_info, "version", systeminfo.Version(version, 0)) - yum_conf = redhatrelease.YumConf() - yum_conf._yum_conf_content = YUM_CONF_WITH_DISTROVERPKG + pkg_manager_conf = pkg_manager_conf_instance + pkg_manager_conf._pkg_manager_conf_content = PKG_MANAGER_CONF_WITH_DISTROVERPKG # Call just this function to avoid unmockable built-in write func - yum_conf._comment_out_distroverpkg_tag() + pkg_manager_conf._comment_out_distroverpkg_tag() - assert "#distroverpkg=" in yum_conf._yum_conf_content - assert yum_conf._yum_conf_content.count("#distroverpkg=") == 1 + assert "#distroverpkg=" in pkg_manager_conf._pkg_manager_conf_content + assert pkg_manager_conf._pkg_manager_conf_content.count("#distroverpkg=") == 1 @pytest.mark.parametrize( @@ -96,36 +98,39 @@ def test_patch_yum_conf_existing_distroverpkg(version, monkeypatch): ("unknown", "anything", False), ), ) -def test_yum_is_modified(monkeypatch, pkg_type, subprocess_ret, expected_result): +def test_pkg_manager_is_modified(monkeypatch, pkg_type, subprocess_ret, expected_result): monkeypatch.setattr(pkgmanager, "TYPE", value=pkg_type) run_subprocess = unit_tests.RunSubprocessMocked(return_string=subprocess_ret) monkeypatch.setattr(utils, "run_subprocess", value=run_subprocess) + pkg_manager_conf = redhatrelease.PkgManagerConf() - assert YumConf.is_modified() == expected_result + assert pkg_manager_conf.is_modified() == expected_result @pytest.mark.parametrize("modified", (True, False)) -def test_yum_patch(monkeypatch, modified, caplog): +def test_pkg_manager_patch(monkeypatch, modified, caplog, tmp_path): is_modified = mock.Mock(return_value=modified) - monkeypatch.setattr(YumConf, "is_modified", value=is_modified) + monkeypatch.setattr(PkgManagerConf, "is_modified", value=is_modified) _comment_out_distroverpkg_tag = mock.Mock() monkeypatch.setattr( - YumConf, + PkgManagerConf, "_comment_out_distroverpkg_tag", value=_comment_out_distroverpkg_tag, ) - _write_altered_yum_conf = mock.Mock() - monkeypatch.setattr(YumConf, "_write_altered_yum_conf", value=_write_altered_yum_conf) - - YumConf().patch() + monkeypatch.setattr( + PkgManagerConf, + "_pkg_manager_conf_path", + value=tmp_path, + ) + PkgManagerConf(config_path=str(tmp_path / "yum.conf")).patch() if modified: _comment_out_distroverpkg_tag.assert_called_once() assert "patched" in caplog.text else: _comment_out_distroverpkg_tag.assert_not_called() - assert "Skipping patching, yum configuration file not modified" in caplog.text + assert "Skipping patching, package manager configuration file has not been modified" in caplog.text @pytest.mark.parametrize(("is_file", "exception"), ((True, False), (False, True))) diff --git a/tests/integration/tier1/destructive/changed-yum-conf/test_patch_yum_conf.py b/tests/integration/tier1/destructive/changed-yum-conf/test_patch_yum_conf.py index aa784c1e9d..cb1d28adaa 100644 --- a/tests/integration/tier1/destructive/changed-yum-conf/test_patch_yum_conf.py +++ b/tests/integration/tier1/destructive/changed-yum-conf/test_patch_yum_conf.py @@ -1,4 +1,4 @@ -from conftest import TEST_VARS +from conftest import TEST_VARS, SystemInformationRelease def test_yum_conf_patch(convert2rhel, shell): @@ -9,6 +9,9 @@ def test_yum_conf_patch(convert2rhel, shell): expanding the $releasever variable properly. """ shell("echo '#random text' >> /etc/yum.conf") + pkgmanager_conf = "/etc/yum.conf" + if SystemInformationRelease.version.major >= 8: + pkgmanager_conf = "/etc/dnf/dnf.conf" with convert2rhel( "-y --serverurl {} --username {} --password {} --pool {} --debug".format( @@ -18,7 +21,7 @@ def test_yum_conf_patch(convert2rhel, shell): TEST_VARS["RHSM_POOL"], ) ) as c2r: - c2r.expect("/etc/yum.conf patched.") + c2r.expect("{} patched.".format(pkgmanager_conf)) assert c2r.exitstatus == 0 # The tsflags will prevent updating the RHEL-8.5 versions to RHEL-8.6