Skip to content

Commit

Permalink
Add unit tests for leftover packages actors
Browse files Browse the repository at this point in the history
    * Actors:
        - checkleftoverpackages
        - removeleftoverpackages
        - reportleftoverpackages

    * Refactor actors code from el7toel8 to common
    * Put their processes into library
    * Create unit tests for actors

Jira: OAMG-1254
  • Loading branch information
tomasfratrik committed Apr 15, 2024
1 parent 4526ea2 commit 3f4f395
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 111 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import re
from leapp.libraries.common.rpms import get_installed_rpms
from leapp.libraries.common.config.version import get_source_major_version
from leapp.models import InstalledUnsignedRPM, LeftoverPackages, RPM
from leapp.libraries.stdlib import api

Expand All @@ -19,15 +21,20 @@ def process():
continue
name, version, release, epoch, packager, arch, pgpsig = rpm.split('|')

if 'el7' in release and name not in set(unsigned + LEAPP_PACKAGES):
to_remove.items.append(RPM(
name=name,
version=version,
epoch=epoch,
packager=packager,
arch=arch,
release=release,
pgpsig=pgpsig
))
version_pattern = r'el(\d+)'
match = re.search(version_pattern, release)

if match:
RHEL_version = match.group(1)
if int(RHEL_version) <= int(get_source_major_version()) and name not in LEAPP_PACKAGES + unsigned:
to_remove.items.append(RPM(
name=name,
version=version,
epoch=epoch,
packager=packager,
arch=arch,
release=release,
pgpsig=pgpsig
))

api.produce(to_remove)
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,45 @@


@pytest.mark.parametrize(
('rpm_name', 'release', 'expected_to_be_removed'),
('source_major_version', 'rpm_name', 'release', 'expected_to_be_removed'),
(
# el7
('sed', '7.el7', True),
('leapp', '1.el7', False),
('unsigned', '1.el7', False),
(7, 'sed', '7.el7', True),
(8, 'sed', '7.el7', True),
(8, 'sed', '8.el7', True),
# el8
('leapp-repository', '1.el8', False),
('gnutls', '8.el8_9.1', False),
(7, 'leapp', '1.el7', False),
(7, 'unsigned', '1.el7', False),
# other
('whois-nls', '1.fc39', False),
(7, 'leapp-repository', '1.el8', False),
(7, 'gnutls', '8.el8_9.1', False),
)
)
def test_package_to_be_removed(monkeypatch, rpm_name, release, expected_to_be_removed):
def test_package_to_be_removed(monkeypatch, source_major_version, rpm_name, release, expected_to_be_removed):
rpm_details = {
'version': '0.1',
'epoch': '0',
'packager': 'packager',
'arch': 'noarch',
'pgpsig': 'OTHER_SIG'
}

def get_installed_rpms_mocked():
return [f'{rpm_name}|0.1|{release}|0|packager|noarch|OTHER_SIG']

UnsignedRPM = RPM(name='unsigned', version='0.1', release=f'{release}', epoch='0', packager='packager', arch='noarch',
pgpsig='OTHER_SIG')
return ['{}|{}|{}|{}|{}|{}|{}'.format(rpm_name, rpm_details['version'], release, rpm_details['epoch'],
rpm_details['packager'], rpm_details['arch'], rpm_details['pgpsig'])]

UnsignedRPM = RPM(name='unsigned', version='0.1', release=release, epoch='0',
packager='packager', arch='noarch', pgpsig='OTHER_SIG')

monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[InstalledUnsignedRPM(items=[UnsignedRPM])]))
monkeypatch.setattr(checkleftoverpackages, 'get_installed_rpms', get_installed_rpms_mocked)
monkeypatch.setattr(checkleftoverpackages, 'get_source_major_version', lambda: source_major_version)
monkeypatch.setattr(api, 'produce', produce_mocked())

checkleftoverpackages.process()

expected_output = LeftoverPackages()
if expected_to_be_removed:
expected_output.items.append(RPM(name=f'{rpm_name}', version='0.1', release=f'{release}', epoch='0',
packager='packager', arch='noarch', pgpsig='OTHER_SIG'))
expected_output.items.append(RPM(name=rpm_name, release=release, **rpm_details))

assert api.produce.called == 1
assert api.produce.model_instances[0] == expected_output
44 changes: 5 additions & 39 deletions repos/system_upgrade/common/actors/removeleftoverpackages/actor.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from leapp.actors import Actor
from leapp.libraries import stdlib
from leapp.libraries.common import rhsm
from leapp.libraries.common.rpms import get_installed_rpms
from leapp.models import LeftoverPackages, RemovedPackages, RPM
from leapp.libraries.actor import removeleftoverpackages
from leapp.models import LeftoverPackages, RemovedPackages
from leapp.reporting import Report
from leapp.tags import ExperimentalTag, IPUWorkflowTag, RPMUpgradePhaseTag


class RemoveLeftoverPackages(Actor):
"""
Remove el7 packages left on the system after the upgrade to RHEL 8.
Remove packages left on the system after the upgrade to higher major version of RHEL.
Removal of el7 packages is necessary in order to keep the machine in supported state.
Removal of packages is necessary in order to keep the machine in supported state.
Actor generates report telling users what packages have been removed.
"""

Expand All @@ -21,36 +19,4 @@ class RemoveLeftoverPackages(Actor):
tags = (RPMUpgradePhaseTag, IPUWorkflowTag, ExperimentalTag)

def process(self):
leftover_packages = next(self.consume(LeftoverPackages), LeftoverPackages())
if not leftover_packages.items:
self.log.info('No leftover packages, skipping...')
return

installed_rpms = get_installed_rpms()

to_remove = ['-'.join([pkg.name, pkg.version, pkg.release]) for pkg in leftover_packages.items]
cmd = ['dnf', 'remove', '-y', '--noautoremove'] + to_remove
if rhsm.skip_rhsm():
# ensure we don't use suscription-manager when it should be skipped
cmd += ['--disableplugin', 'subscription-manager']
try:
stdlib.run(cmd)
except stdlib.CalledProcessError:
error = 'Failed to remove packages: {}'.format(', '.join(to_remove))
self.log.error(error)
return

removed_packages = RemovedPackages()
removed = list(set(installed_rpms) - set(get_installed_rpms()))
for pkg in removed:
name, version, release, epoch, packager, arch, pgpsig = pkg.split('|')
removed_packages.items.append(RPM(
name=name,
version=version,
epoch=epoch,
packager=packager,
arch=arch,
release=release,
pgpsig=pgpsig
))
self.produce(removed_packages)
removeleftoverpackages.process()
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from leapp.libraries.stdlib import api, run, CalledProcessError
from leapp.libraries.common.rpms import get_installed_rpms
from leapp.libraries.common.rhsm import skip_rhsm
from leapp.models import LeftoverPackages, RemovedPackages, RPM


def get_leftover_packages():
leftover_packages = next(api.consume(LeftoverPackages), LeftoverPackages())
if not leftover_packages.items:
api.current_logger().info('No leftover packages, skipping...')
return None
return leftover_packages


def _get_removed_packages(installed_rpms):
return list(set(installed_rpms) - set(get_installed_rpms()))


def get_removed_packages(installed_rpms):
removed_packages = RemovedPackages()
removed = _get_removed_packages(installed_rpms)

for pkg in removed:
name, version, release, epoch, packager, arch, pgpsig = pkg.split('|')
removed_packages.items.append(RPM(
name=name,
version=version,
epoch=epoch,
packager=packager,
arch=arch,
release=release,
pgpsig=pgpsig
))
return removed_packages


def process():
leftover_packages = get_leftover_packages()
if not leftover_packages:
return

installed_rpms = get_installed_rpms()

to_remove = ['-'.join([pkg.name, pkg.version, pkg.release]) for pkg in leftover_packages.items]
cmd = ['dnf', 'remove', '-y', '--noautoremove'] + to_remove
if skip_rhsm():
# ensure we don't use suscription-manager when it should be skipped
cmd += ['--disableplugin', 'subscription-manager']
try:
run(cmd)
except (CalledProcessError, OSError):
error = 'Failed to remove packages: {}'.format(', '.join(to_remove))
api.current_logger().error(error)
return

api.produce(get_removed_packages(installed_rpms))
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import pytest

from leapp.libraries.common.testutils import produce_mocked, CurrentActorMocked, logger_mocked
from leapp.libraries.stdlib import api, CalledProcessError
from leapp.libraries.actor import removeleftoverpackages
from leapp.models import LeftoverPackages, RemovedPackages, RPM


def test_get_leftover_packages(monkeypatch):
rpm = RPM(name='rpm', version='1.0', release='1.el7', epoch='0', packager='foo', arch='noarch', pgpsig='SIG')
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[LeftoverPackages(items=[rpm])]))
monkeypatch.setattr(api, 'current_logger', logger_mocked())

assert removeleftoverpackages.get_leftover_packages() == LeftoverPackages(items=[rpm])


def test_no_leftover_packages(monkeypatch):
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[LeftoverPackages()]))
monkeypatch.setattr(api, 'current_logger', logger_mocked())

removeleftoverpackages.get_leftover_packages()

assert api.current_logger.infomsg == ['No leftover packages, skipping...']


def test_remove_lefover_packages_error(monkeypatch):
def get_leftover_pkgs():
return LeftoverPackages(items=[RPM(name='rpm', version='1.0', release='1.el7', epoch='0',
packager='packager', arch='noarch', pgpsig='SIG')])

def mocked_run(cmd):
raise CalledProcessError(command=cmd,
message='mocked error',
result={'stdout': 'out', 'stderr': 'err', 'exit_code': 1, 'signal': 0})

monkeypatch.setattr(api, 'current_logger', logger_mocked())
monkeypatch.setattr(api, 'produce', produce_mocked())
monkeypatch.setattr(removeleftoverpackages, 'get_leftover_packages', get_leftover_pkgs)
monkeypatch.setattr(removeleftoverpackages, 'get_installed_rpms', lambda: [])
monkeypatch.setattr(removeleftoverpackages, 'skip_rhsm', lambda: False)
monkeypatch.setattr(removeleftoverpackages, 'run', mocked_run)

removeleftoverpackages.process()

assert api.produce.called == 0
assert api.current_logger.errmsg == ['Failed to remove packages: rpm-1.0-1.el7']


def test__get_removed_packages(monkeypatch):
pkg1 = 'rpm|1.0|1.el7|0|packager|noarch|SIG'
pkg2 = 'rpm2|1.0|1.el7|0|packager|noarch|SIG'
installed_packages = [pkg1, pkg2]
monkeypatch.setattr(removeleftoverpackages, 'get_installed_rpms', lambda: [pkg1])

assert removeleftoverpackages._get_removed_packages(installed_packages) == [pkg2]


@pytest.mark.parametrize(
('installed_rpms'),
(
([]),
(['rpm1']),
(['rpm1', 'rpm2']),
)
)
def test_get_removed_packages(monkeypatch, installed_rpms):
rpm_details = {
'version': '1.0',
'release': '1.el7',
'epoch': '0',
'packager': 'packager',
'arch': 'noarch',
'pgpsig': 'SIG'
}
rpm_details_composed = '|'.join([rpm_details[key] for key in ['version', 'release', 'epoch',
'packager', 'arch', 'pgpsig']])
mocked_installed_rpms = ['{}|{}'.format(rpm, rpm_details_composed) for rpm in installed_rpms]

monkeypatch.setattr(removeleftoverpackages, '_get_removed_packages', lambda _: mocked_installed_rpms)

removed_packages = removeleftoverpackages.get_removed_packages(mocked_installed_rpms)
expected_output = [RPM(name=rpm, **rpm_details) for rpm in installed_rpms]

assert removed_packages == RemovedPackages(items=expected_output)


@pytest.mark.parametrize(
('removed_packages'),
(
([]),
(['rpm1']),
(['rpm1', 'rpm2']),
)
)
def test_process(monkeypatch, removed_packages):
def get_leftover_pkgs():
return LeftoverPackages()

removed_pkgs = RemovedPackages(items=[RPM(name=pkg, version='1.0', release='1.el7', epoch='0',
packager='packager', arch='noarch', pgpsig='SIG')
for pkg in removed_packages])

def mocked_get_removed_packages(installed_rpms):
return removed_pkgs

monkeypatch.setattr(api, 'produce', produce_mocked())
monkeypatch.setattr(removeleftoverpackages, 'get_leftover_packages', get_leftover_pkgs)
monkeypatch.setattr(removeleftoverpackages, 'get_installed_rpms', lambda: [])
monkeypatch.setattr(removeleftoverpackages, 'run', lambda _: None)
monkeypatch.setattr(removeleftoverpackages, 'skip_rhsm', lambda: False)
monkeypatch.setattr(removeleftoverpackages, 'get_removed_packages', mocked_get_removed_packages)

removeleftoverpackages.process()

assert api.produce.called == 1
assert api.produce.model_instances == [removed_pkgs]
Loading

0 comments on commit 3f4f395

Please sign in to comment.