Skip to content

Commit

Permalink
Introduce two functions for listing which packages are leapp packages.
Browse files Browse the repository at this point in the history
* rpms.get_leapp_packages() library function returns the leapp packages which ship the leapp
  components.
* rpms.get_leapp_deps_packages() library function returns the leapp deps meta packages which list
  the requirements of the associated leapp packages.

This function can be used as leapp-installation-packages-getter.
Refactoring of other actors using it will be done later.
  • Loading branch information
fernflower authored and pirat89 committed Jan 9, 2024
1 parent 4968bec commit 118133a
Show file tree
Hide file tree
Showing 2 changed files with 205 additions and 1 deletion.
139 changes: 139 additions & 0 deletions repos/system_upgrade/common/libraries/rpms.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,47 @@
from leapp.libraries import stdlib
from leapp.libraries.common.config.version import get_source_major_version
from leapp.models import InstalledRPM


class LeappComponents(object):
"""
Supported component values to be used with get_packages_function:
* FRAMEWORK - the core of the leapp project: the leapp executable and
associated leapp libraries
* REPOSITORY - the leapp-repository project
* COCKPIT - the cockpit-leapp project
* TOOLS - miscellaneous tooling like snactor
"""
FRAMEWORK = 'framework'
REPOSITORY = 'repository'
COCKPIT = 'cockpit'
TOOLS = 'tools'


_LEAPP_PACKAGES_MAP = {
LeappComponents.FRAMEWORK: {'7': {'pkgs': ['leapp', 'python2-leapp'],
'deps': ['leapp-deps']},
'8': {'pkgs': ['leapp', 'python3-leapp'],
'deps': ['leapp-deps']}
},
LeappComponents.REPOSITORY: {'7': {'pkgs': ['leapp-upgrade-el7toel8'],
'deps': ['leapp-upgrade-el7toel8-deps']},
'8': {'pkgs': ['leapp-upgrade-el8toel9'],
'deps': ['leapp-upgrade-el8toel9-deps']}
},
LeappComponents.COCKPIT: {'7': {'pkgs': ['cockpit-leapp']},
'8': {'pkgs': ['cockpit-leapp']}
},
LeappComponents.TOOLS: {'7': {'pkgs': ['snactor']},
'8': {'pkgs': ['snactor']}
}
}

GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS = frozenset((LeappComponents.FRAMEWORK,
LeappComponents.REPOSITORY,
LeappComponents.TOOLS))


def get_installed_rpms():
rpm_cmd = [
'/bin/rpm',
Expand Down Expand Up @@ -114,3 +154,102 @@ def check_file_modification(config):
"""
output = _read_rpm_modifications(config)
return _parse_config_modification(output, config)


def _get_leapp_packages_of_type(major_version, component, type_='pkgs'):
"""
Private implementation of get_leapp_packages() and get_leapp_deps_packages().
:param major_version: Same as for :func:`get_leapp_packages` and
:func:`get_leapp_deps_packages`
:param component: Same as for :func:`get_leapp_packages` and :func:`get_leapp_deps_packages`
:param type_: Either "pkgs" or "deps". Determines which set of packages we're looking for.
Corresponds to the keys in the `_LEAPP_PACKAGES_MAP`.
Retrieving the set of leapp and leapp-deps packages only differs in which key is used to
retrieve the packages from _LEAPP_PACKAGES_MAP. This function abstracts that difference.
"""
res = set()

major_versions = [major_version] if isinstance(major_version, str) else major_version
if not major_versions:
# No major_version of interest specified -> treat as if only current source system version
# requested
major_versions = [get_source_major_version()]

components = [component] if isinstance(component, str) else component
if not components:
error_msg = ("At least one component must be specified when calling this"
" function, available choices are {choices}".format(
choices=sorted(_LEAPP_PACKAGES_MAP.keys()))
)
raise ValueError(error_msg)

for comp in components:
for a_major_version in major_versions:
if comp not in _LEAPP_PACKAGES_MAP:
error_msg = "The requested component {comp} is unknown, available choices are {choices}".format(
comp=component, choices=sorted(_LEAPP_PACKAGES_MAP.keys()))
raise ValueError(error_msg)

if a_major_version not in _LEAPP_PACKAGES_MAP[comp]:
error_msg = "The requested major_version {ver} is unknown, available choices are {choices}".format(
ver=a_major_version, choices=sorted(_LEAPP_PACKAGES_MAP[comp].keys()))
raise ValueError(error_msg)

# All went well otherwise, get the data
res.update(_LEAPP_PACKAGES_MAP[comp][a_major_version].get(type_, []))

return sorted(res)


def get_leapp_packages(major_version=None, component=GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS):
"""
Get list of leapp packages.
:param major_version: a list or string specifying major_versions. If not defined then current
system_version will be used.
:param component: a list or a single enum value specifying leapp components
(use enum :class: LeappComponents) If defined then only packages related to the specific
component(s) will be returned.
The default set of components is in `GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS` and
simple modifications of the default can be achieved with code like:
.. code-block:: python
get_leapp_packages(
component=GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS.difference(
[LeappComponents.TOOLS]
))
:raises ValueError: if a requested component or major_version doesn't exist.
.. note::
Call :func:`get_leapp_dep_packages` as well if you also need the deps metapackages.
Those packages determine which RPMs need to be installed for leapp to function.
They aren't just Requires on the base leapp and leapp-repository RPMs because they
need to be switched from the old system_version's to the new ones at a different
point in the upgrade than the base RPMs.
"""
return _get_leapp_packages_of_type(major_version, component, type_="pkgs")


def get_leapp_dep_packages(major_version=None, component=GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS):
"""
Get list of leapp dep metapackages.
:param major_version: a list or string specifying major_versions. If not defined then current
system_version will be used.
:param component: a list or a single enum value specifying leapp components
(use enum :class: LeappComponents) If defined then only packages related to the specific
component(s) will be returned.
The default set of components is in `GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS` and
simple modifications of the default can be achieved with code like:
.. code-block:: python
get_leapp_packages(
component=GET_LEAPP_PACKAGES_DEFAULT_COMPONENTS.difference(
[LeappComponents.TOOLS]
))
:raises ValueError: if a requested component or major_version doesn't exist.
"""
return _get_leapp_packages_of_type(major_version, component, type_="deps")
67 changes: 66 additions & 1 deletion repos/system_upgrade/common/libraries/tests/test_rpms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
from leapp.libraries.common.rpms import _parse_config_modification
import pytest

from leapp.libraries.common.rpms import _parse_config_modification, get_leapp_dep_packages, get_leapp_packages
from leapp.libraries.common.testutils import CurrentActorMocked
from leapp.libraries.stdlib import api


def test_parse_config_modification():
Expand Down Expand Up @@ -30,3 +34,64 @@ def test_parse_config_modification():
"S.5....T. c /etc/ssh/sshd_config",
]
assert _parse_config_modification(data, "/etc/ssh/sshd_config")


@pytest.mark.parametrize('major_version,component,result', [
(None, None, ['leapp', 'python3-leapp', 'leapp-upgrade-el8toel9', 'snactor']),
('7', None, ['leapp', 'python2-leapp', 'leapp-upgrade-el7toel8', 'snactor']),
(['7', '8'], None, ['leapp', 'python2-leapp', 'leapp-upgrade-el7toel8',
'python3-leapp', 'leapp-upgrade-el8toel9', 'snactor']),
('8', 'framework', ['leapp', 'python3-leapp']),
])
def test_get_leapp_packages(major_version, component, result, monkeypatch):
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.9', dst_ver='9.3'))

kwargs = {}
if major_version:
kwargs["major_version"] = major_version
if component:
kwargs["component"] = component

assert set(get_leapp_packages(** kwargs)) == set(result)


@pytest.mark.parametrize('major_version,component,result', [
('8', 'nosuchcomponent',
(ValueError,
r"component nosuchcomponent is unknown, available choices are \['cockpit', 'framework', 'repository', 'tools']")
),
('nosuchversion', "framework",
(ValueError, r"major_version nosuchversion is unknown, available choices are \['7', '8']")),
('nosuchversion', False,
(ValueError, r"At least one component must be specified when calling this function,"
r" available choices are \['cockpit', 'framework', 'repository', 'tools']")),
])
def test_get_leapp_packages_errors(major_version, component, result, monkeypatch):
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.9', dst_ver='9.3'))

kwargs = {}
if major_version:
kwargs["major_version"] = major_version
if component is not None:
kwargs["component"] = component

exc_type, exc_msg = result
with pytest.raises(exc_type, match=exc_msg):
get_leapp_packages(**kwargs)


@pytest.mark.parametrize('major_version,component,result', [
(None, None, ['leapp-deps', 'leapp-upgrade-el8toel9-deps']),
('8', 'framework', ['leapp-deps']),
('7', 'tools', []),
])
def test_get_leapp_dep_packages(major_version, component, result, monkeypatch):
monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(arch='x86_64', src_ver='8.9', dst_ver='9.3'))

kwargs = {}
if major_version:
kwargs["major_version"] = major_version
if component:
kwargs["component"] = component

assert frozenset(get_leapp_dep_packages(**kwargs)) == frozenset(result)

0 comments on commit 118133a

Please sign in to comment.