Skip to content

Commit

Permalink
[RHELC-899] Prevent packages marked for update to reinstall (#833)
Browse files Browse the repository at this point in the history
* Prevent packages marked for update to reinstall

During our transaction handler, some packages were trying to be
reinstalled/downgraded on the system without necessity, as they were
already marked for update.

This patch prevents those packages marked for update to be reinstalled or
downgraded in further cases.

Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>

* Add integration tests

Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>

* Modify the test

* change the test to be able to install tracked packages

Signed-off-by: Daniel Diblik <ddiblik@redhat.com>

* Try yum check-update

* try yum check update to verify if package lands on latest version
* add list of packages relevant for Oracle Linux 7

Signed-off-by: Daniel Diblik <ddiblik@redhat.com>

* Override releasever for CentOS 8.5

* we need to point the yum check-update to the correct minor

Signed-off-by: Daniel Diblik <ddiblik@redhat.com>

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Signed-off-by: Rodolfo Olivieri <rolivier@redhat.com>
Signed-off-by: Daniel Diblik <ddiblik@redhat.com>
Co-authored-by: Daniel Diblik <ddiblik@redhat.com>
Co-authored-by: Daniel Diblik <8378124+danmyway@users.noreply.github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored May 25, 2023
1 parent 639151a commit 62a6492
Show file tree
Hide file tree
Showing 9 changed files with 274 additions and 324 deletions.
15 changes: 14 additions & 1 deletion convert2rhel/pkgmanager/handlers/dnf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,24 @@ def _perform_operations(self):
`PackagesNotAvailableError`.
"""
original_os_pkgs = get_system_packages_for_replacement()
upgrades = self._base.sack.query().upgrades().latest()

loggerinst.info("Adding %s packages to the dnf transaction set.", system_info.name)

for pkg in original_os_pkgs:
self._base.upgrade(pkg_spec=pkg)
# Splitting the name and arch so we can filter it out in the list
# of packages to upgrade.
name, arch = tuple(pkg.rsplit("."))
upgrade_pkg = next(iter(upgrades.filter(name=name, arch=arch)), None)

# If a package is marked for update, then we don't need to
# proceed with reinstall, and possibly, the downgrade of this
# package. This is an inconsistency that could lead to packages
# being outdated in the system after the conversion.
if upgrade_pkg:
self._base.upgrade(pkg_spec=pkg)
continue

try:
self._base.reinstall(pkg_spec=pkg)
except pkgmanager.exceptions.PackagesNotAvailableError:
Expand Down
10 changes: 9 additions & 1 deletion convert2rhel/pkgmanager/handlers/yum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,15 @@ def _perform_operations(self):

try:
for pkg in original_os_pkgs:
self._base.update(pattern=pkg)
can_update = self._base.update(pattern=pkg)

# If a package is marked for update, then we don't need to
# proceed with reinstall, and possibly, the downgrade of this
# package. This is an inconsistency that could lead to packages
# 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):
Expand Down
247 changes: 97 additions & 150 deletions convert2rhel/unit_tests/pkgmanager/handlers/dnf/dnf_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.

import pytest
import six

from convert2rhel import pkgmanager
from convert2rhel import pkghandler, pkgmanager
from convert2rhel.pkgmanager.handlers.dnf import DnfTransactionHandler
from convert2rhel.pkgmanager.handlers.dnf.callback import DependencySolverProgressIndicatorCallback
from convert2rhel.systeminfo import system_info
Expand All @@ -45,6 +44,66 @@ def enable(self):
self.disabled = False


class SackMock:
def __init__(self, packages=None):
if not packages:
packages = []

self.packages = packages

def __call__(self, *args, **kwds):
return self

def query(self):
return self

def upgrades(self):
return self

def latest(self):
return self

def filter(self, *args, **kwargs):
return self.packages


SYSTEM_PACKAGES = [
create_pkg_information(
packager="test",
vendor="test",
name="pkg-1",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="05b555b38483c65d",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-2",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="05b555b38483c65d",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-3",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="05b555b38483c65d",
signature="test",
),
]


@pytest.mark.skipif(
pkgmanager.TYPE != "dnf",
reason="No dnf module detected on the system, skipping it.",
Expand All @@ -63,6 +122,7 @@ def _mock_dnf_api_calls(self, monkeypatch):
monkeypatch.setattr(pkgmanager.Base, "download_packages", value=mock.Mock())
monkeypatch.setattr(pkgmanager.Base, "do_transaction", value=mock.Mock())
monkeypatch.setattr(pkgmanager.Base, "transaction", value=mock.Mock())
monkeypatch.setattr(pkgmanager.Base, "sack", value=SackMock())

@centos8
def test_set_up_base(self, pretend_os):
Expand Down Expand Up @@ -154,172 +214,59 @@ def test_enable_repos_repo_error_exception(
assert "Failed to populate repository metadata." in caplog.records[-1].message

@centos8
@pytest.mark.parametrize(
("system_packages"),
(
(
[
create_pkg_information(
packager="test",
vendor="test",
name="pkg-1",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-2",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-3",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
]
),
),
)
def test_perform_operations(self, pretend_os, system_packages, _mock_dnf_api_calls, caplog, monkeypatch):
def test_perform_operations(self, pretend_os, _mock_dnf_api_calls, caplog, monkeypatch):
monkeypatch.setattr(
pkgmanager.handlers.dnf,
"get_system_packages_for_replacement",
value=lambda: system_packages,
pkghandler,
"get_installed_pkg_information",
value=lambda: SYSTEM_PACKAGES,
)
instance = DnfTransactionHandler()
instance._set_up_base()
instance._perform_operations()

assert pkgmanager.Base.upgrade.call_count == len(system_packages)
assert pkgmanager.Base.reinstall.call_count == len(system_packages)
assert pkgmanager.Base.reinstall.call_count == len(SYSTEM_PACKAGES)
assert pkgmanager.Base.downgrade.call_count == 0

@centos8
@pytest.mark.parametrize(
("system_packages"),
(
(
[
create_pkg_information(
packager="test",
vendor="test",
name="pkg-1",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-2",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-3",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
]
),
),
)
def test_perform_operations_reinstall_exception(
self, pretend_os, system_packages, _mock_dnf_api_calls, caplog, monkeypatch
):
def test_package_marked_for_update(self, pretend_os, _mock_dnf_api_calls, monkeypatch):
"""
Test that if a package is marked for update, we won't call reinstall or
downgrade after that.
This comes from: https://issues.redhat.com/browse/RHELC-899
"""
monkeypatch.setattr(pkghandler, "get_installed_pkg_information", value=lambda: SYSTEM_PACKAGES)
monkeypatch.setattr(pkgmanager.Base, "sack", value=SackMock(packages=SYSTEM_PACKAGES))
instance = DnfTransactionHandler()
instance._set_up_base()
instance._perform_operations()

assert pkgmanager.Base.upgrade.call_count == len(SYSTEM_PACKAGES)
assert pkgmanager.Base.reinstall.call_count == 0
assert pkgmanager.Base.downgrade.call_count == 0

@centos8
def test_perform_operations_reinstall_exception(self, pretend_os, _mock_dnf_api_calls, caplog, monkeypatch):
monkeypatch.setattr(
pkgmanager.handlers.dnf,
"get_system_packages_for_replacement",
value=lambda: system_packages,
pkghandler,
"get_installed_pkg_information",
value=lambda: SYSTEM_PACKAGES,
)
pkgmanager.Base.reinstall.side_effect = pkgmanager.exceptions.PackagesNotAvailableError
instance = DnfTransactionHandler()
instance._set_up_base()
instance._perform_operations()

assert pkgmanager.Base.reinstall.call_count == len(system_packages)
assert pkgmanager.Base.downgrade.call_count == len(system_packages)
assert pkgmanager.Base.reinstall.call_count == len(SYSTEM_PACKAGES)
assert pkgmanager.Base.downgrade.call_count == len(SYSTEM_PACKAGES)
assert "not available in RHEL repositories" not in caplog.records[-1].message

@centos8
@pytest.mark.parametrize(
("system_packages"),
(
(
[
create_pkg_information(
packager="test",
vendor="test",
name="pkg-1",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-2",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
create_pkg_information(
packager="test",
vendor="test",
name="pkg-3",
epoch="0",
version="1.0.0",
release="1",
arch="x86_64",
fingerprint="test",
signature="test",
),
]
),
),
)
def test_perform_operations_downgrade_exception(
self, pretend_os, system_packages, _mock_dnf_api_calls, caplog, monkeypatch
):
def test_perform_operations_downgrade_exception(self, pretend_os, _mock_dnf_api_calls, caplog, monkeypatch):
monkeypatch.setattr(
pkgmanager.handlers.dnf,
"get_system_packages_for_replacement",
value=lambda: system_packages,
pkghandler,
"get_installed_pkg_information",
value=lambda: SYSTEM_PACKAGES,
)
pkgmanager.Base.reinstall.side_effect = pkgmanager.exceptions.PackagesNotAvailableError
pkgmanager.Base.downgrade.side_effect = pkgmanager.exceptions.PackagesNotInstalledError
Expand All @@ -328,8 +275,8 @@ def test_perform_operations_downgrade_exception(
instance._set_up_base()
instance._perform_operations()

assert pkgmanager.Base.reinstall.call_count == len(system_packages)
assert pkgmanager.Base.downgrade.call_count == len(system_packages)
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

@centos8
Expand Down
Loading

0 comments on commit 62a6492

Please sign in to comment.