Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce rpms.get_leapp_packages library function #1156

Merged
merged 1 commit into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Loading