forked from rpms/leapp-repository
1815 lines
76 KiB
Diff
1815 lines
76 KiB
Diff
|
From 9ed71946b763e1b1e3049ebd55a0d61eba42015e Mon Sep 17 00:00:00 2001
|
||
|
From: Jakub Jelen <jjelen@redhat.com>
|
||
|
Date: Wed, 15 Jun 2022 21:49:22 +0200
|
||
|
Subject: [PATCH 37/37] Enable gpgcheck during IPU (+ add --nogpgcheck CLI
|
||
|
option)
|
||
|
|
||
|
Previously the gpgcheck=0 has been enforced during the IPU as we have
|
||
|
not have any reasonable solution doing upgrade with allowed gpgcheck.
|
||
|
Previously such upgrades automatically imported any gpgkeys without
|
||
|
any possible check whether the actual keys are valid or not, which
|
||
|
could lead to the automatical import of compromised/spurious gpg keys
|
||
|
and user would not know about that. So using the original solution,
|
||
|
user was asked for the import of new keys when installing additional
|
||
|
content from new repositories (if keys have been different from those
|
||
|
used in the original system).
|
||
|
|
||
|
To do the upgrade in the original way (without gpgcheck), execute
|
||
|
leapp with the `--nogpgcheck` option, or specify `LEAPP_NOGPGCHECK=1`
|
||
|
(envar). In such a case, all actions described below are skipped.
|
||
|
|
||
|
The current solution enables the GPG check by default but also could
|
||
|
require additional actions from user to be able to upgrade. The goal
|
||
|
is to ensure that no additional GPG keys are imported by DNF
|
||
|
automatically during any action (partially resolved, read bellow).
|
||
|
To be able to achive this, we are importing gpg keys automatically
|
||
|
from a *trusted directory* before any dnf transaction is executed, so:
|
||
|
a) into the rpmdb of the target userspace container, before the
|
||
|
actual installation of the target userspace container
|
||
|
b) into overlayed rpmdb when calculating/testing the upgrade
|
||
|
transaction
|
||
|
c) into the system rpmdb right before the execution of the DNF
|
||
|
upgrade transaction
|
||
|
|
||
|
The case a) is handled directly in the target_userspace_creator actor.
|
||
|
The other cases are handled via DNFWorkaround msg, using the
|
||
|
`importrpmgpgkeys` tool script.
|
||
|
|
||
|
The *trusted directory* is in this case located under
|
||
|
`files/rpm-gpg/`, where the directory name is major release of the
|
||
|
target system in case of production releases, in other cases it has
|
||
|
the *beta* suffix. So e.g.:
|
||
|
files/rpm-gpg/8
|
||
|
files/rpm-gpg/8beta
|
||
|
That's because production and beta repositories have different gpg
|
||
|
keys and it is not wanted to mix production and beta keys. Beta
|
||
|
repositories are used only when the target production type is beta:
|
||
|
LEAPP_DEVEL_TARGET_PRODUCT_TYPE=beta
|
||
|
|
||
|
Introducing the missinggpgkeysinhibitor actor that checks gpg keys
|
||
|
based on `gpgkey` specified in repofiles per each used target
|
||
|
repository which does not explicitly specify `gpgcheck=0`.
|
||
|
Such a key is compared with the currently installed gpg keys in the
|
||
|
host rpmdb and keys inside the *trusted directory*. If the key
|
||
|
is not stored in any of those places, the upgrade is inhibited with
|
||
|
the corresponding report. User can resolve the problem installing the
|
||
|
missing gpg keys or storing them to the trusted directory.
|
||
|
|
||
|
Currently supported protocols for the gpg keys are
|
||
|
file:///
|
||
|
http://
|
||
|
https://
|
||
|
If a key cannot be obtained (including use of an unsupported protocol,
|
||
|
e.g. ftp://) the actor prompt a log, but does not generate a report
|
||
|
about that (so the upgrade can continue, which could later lead into
|
||
|
a failure during the download of packages - one of TODOs).
|
||
|
|
||
|
This is not the final commit for this feature and additional work
|
||
|
is expected before the new release is introduced. Regarding that,
|
||
|
see the code for new TODO / FIXME notes that are put into the code.
|
||
|
|
||
|
Summary of some TODOs planned to address in followup PR:
|
||
|
- add checks that DNF does not import additional GPG keys during
|
||
|
any action
|
||
|
- report GPG keys that could not be checked, informing user about
|
||
|
possible consequences - the report should not inhibit the upgrade
|
||
|
- possibly introduce fallback for getting file:///... gpg keys
|
||
|
as currently they are obtained from the target userspace container
|
||
|
but if not present, the host system should be possibly checked:
|
||
|
- Note that if the file has been created manually (custom repo file)
|
||
|
most likely the gpgkey will be stored only on the host system
|
||
|
- and in such a case the file would need to be copied from the
|
||
|
host system into the container.
|
||
|
|
||
|
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
|
||
|
---
|
||
|
commands/preupgrade/__init__.py | 1 +
|
||
|
commands/rerun/__init__.py | 1 +
|
||
|
commands/upgrade/__init__.py | 1 +
|
||
|
commands/upgrade/util.py | 3 +
|
||
|
.../actors/missinggpgkeysinhibitor/actor.py | 40 ++
|
||
|
.../libraries/missinggpgkey.py | 368 ++++++++++++
|
||
|
.../tests/component_test_missinggpgkey.py | 522 ++++++++++++++++++
|
||
|
.../tests/unit_test_missinggpgkey.py | 209 +++++++
|
||
|
.../libraries/userspacegen.py | 62 ++-
|
||
|
.../rpm-gpg/8/RPM-GPG-KEY-redhat-release | 89 +++
|
||
|
.../rpm-gpg/8beta/RPM-GPG-KEY-redhat-beta | 29 +
|
||
|
.../rpm-gpg/9/RPM-GPG-KEY-redhat-release | 66 +++
|
||
|
.../rpm-gpg/9beta/RPM-GPG-KEY-redhat-beta | 29 +
|
||
|
.../common/libraries/dnfplugin.py | 13 +-
|
||
|
.../common/libraries/tests/test_dnfplugin.py | 21 +-
|
||
|
.../common/models/targetrepositories.py | 34 ++
|
||
|
16 files changed, 1455 insertions(+), 33 deletions(-)
|
||
|
create mode 100644 repos/system_upgrade/common/actors/missinggpgkeysinhibitor/actor.py
|
||
|
create mode 100644 repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
||
|
create mode 100644 repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/component_test_missinggpgkey.py
|
||
|
create mode 100644 repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/unit_test_missinggpgkey.py
|
||
|
create mode 100644 repos/system_upgrade/common/files/rpm-gpg/8/RPM-GPG-KEY-redhat-release
|
||
|
create mode 100644 repos/system_upgrade/common/files/rpm-gpg/8beta/RPM-GPG-KEY-redhat-beta
|
||
|
create mode 100644 repos/system_upgrade/common/files/rpm-gpg/9/RPM-GPG-KEY-redhat-release
|
||
|
create mode 100644 repos/system_upgrade/common/files/rpm-gpg/9beta/RPM-GPG-KEY-redhat-beta
|
||
|
|
||
|
diff --git a/commands/preupgrade/__init__.py b/commands/preupgrade/__init__.py
|
||
|
index d612fbb1..a1577a63 100644
|
||
|
--- a/commands/preupgrade/__init__.py
|
||
|
+++ b/commands/preupgrade/__init__.py
|
||
|
@@ -30,6 +30,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i
|
||
|
command_utils.get_upgrade_flavour()))
|
||
|
@command_opt('report-schema', help='Specify report schema version for leapp-report.json',
|
||
|
choices=['1.0.0', '1.1.0', '1.2.0'], default=get_config().get('report', 'schema'))
|
||
|
+@command_opt('nogpgcheck', is_flag=True, help='Disable RPM GPG checks. Same as yum/dnf --nogpgcheck option.')
|
||
|
@breadcrumbs.produces_breadcrumbs
|
||
|
def preupgrade(args, breadcrumbs):
|
||
|
util.disable_database_sync()
|
||
|
diff --git a/commands/rerun/__init__.py b/commands/rerun/__init__.py
|
||
|
index 57149571..a06dd266 100644
|
||
|
--- a/commands/rerun/__init__.py
|
||
|
+++ b/commands/rerun/__init__.py
|
||
|
@@ -68,6 +68,7 @@ def rerun(args):
|
||
|
verbose=args.verbose,
|
||
|
reboot=False,
|
||
|
no_rhsm=False,
|
||
|
+ nogpgcheck=False,
|
||
|
channel=None,
|
||
|
report_schema='1.1.0',
|
||
|
whitelist_experimental=[],
|
||
|
diff --git a/commands/upgrade/__init__.py b/commands/upgrade/__init__.py
|
||
|
index 005538ed..8b257fa9 100644
|
||
|
--- a/commands/upgrade/__init__.py
|
||
|
+++ b/commands/upgrade/__init__.py
|
||
|
@@ -36,6 +36,7 @@ from leapp.utils.output import beautify_actor_exception, report_errors, report_i
|
||
|
command_utils.get_upgrade_flavour()))
|
||
|
@command_opt('report-schema', help='Specify report schema version for leapp-report.json',
|
||
|
choices=['1.0.0', '1.1.0', '1.2.0'], default=get_config().get('report', 'schema'))
|
||
|
+@command_opt('nogpgcheck', is_flag=True, help='Disable RPM GPG checks. Same as yum/dnf --nogpgcheck option.')
|
||
|
@breadcrumbs.produces_breadcrumbs
|
||
|
def upgrade(args, breadcrumbs):
|
||
|
skip_phases_until = None
|
||
|
diff --git a/commands/upgrade/util.py b/commands/upgrade/util.py
|
||
|
index aa433786..6055c65b 100644
|
||
|
--- a/commands/upgrade/util.py
|
||
|
+++ b/commands/upgrade/util.py
|
||
|
@@ -206,6 +206,9 @@ def prepare_configuration(args):
|
||
|
# Make sure we convert rel paths into abs ones while we know what CWD is
|
||
|
os.environ['LEAPP_TARGET_ISO'] = os.path.abspath(target_iso_path)
|
||
|
|
||
|
+ if args.nogpgcheck:
|
||
|
+ os.environ['LEAPP_NOGPGCHECK'] = '1'
|
||
|
+
|
||
|
# Check upgrade path and fail early if it's unsupported
|
||
|
target_version, flavor = command_utils.vet_upgrade_path(args)
|
||
|
os.environ['LEAPP_UPGRADE_PATH_TARGET_RELEASE'] = target_version
|
||
|
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/actor.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/actor.py
|
||
|
new file mode 100644
|
||
|
index 00000000..6f836a5b
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/actor.py
|
||
|
@@ -0,0 +1,40 @@
|
||
|
+from leapp.actors import Actor
|
||
|
+from leapp.libraries.actor import missinggpgkey
|
||
|
+from leapp.models import (
|
||
|
+ DNFWorkaround,
|
||
|
+ InstalledRPM,
|
||
|
+ TargetUserSpaceInfo,
|
||
|
+ TMPTargetRepositoriesFacts,
|
||
|
+ UsedTargetRepositories
|
||
|
+)
|
||
|
+from leapp.reporting import Report
|
||
|
+from leapp.tags import IPUWorkflowTag, TargetTransactionChecksPhaseTag
|
||
|
+
|
||
|
+
|
||
|
+class MissingGpgKeysInhibitor(Actor):
|
||
|
+ """
|
||
|
+ Check if all used target repositories have signing gpg keys
|
||
|
+ imported in the existing RPM DB or they are planned to be imported
|
||
|
+
|
||
|
+ Right now, we can not check the package signatures yet, but we can do some
|
||
|
+ best effort estimation based on the gpgkey option in the repofile
|
||
|
+ and content of the existing rpm db.
|
||
|
+
|
||
|
+ Also register the DNFWorkaround to import trusted gpg keys - files provided
|
||
|
+ inside the GPG_CERTS_FOLDER directory.
|
||
|
+
|
||
|
+ In case that leapp is executed with --nogpgcheck, all actions are skipped.
|
||
|
+ """
|
||
|
+
|
||
|
+ name = 'missing_gpg_keys_inhibitor'
|
||
|
+ consumes = (
|
||
|
+ InstalledRPM,
|
||
|
+ TMPTargetRepositoriesFacts,
|
||
|
+ TargetUserSpaceInfo,
|
||
|
+ UsedTargetRepositories,
|
||
|
+ )
|
||
|
+ produces = (DNFWorkaround, Report,)
|
||
|
+ tags = (IPUWorkflowTag, TargetTransactionChecksPhaseTag,)
|
||
|
+
|
||
|
+ def process(self):
|
||
|
+ missinggpgkey.process()
|
||
|
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
||
|
new file mode 100644
|
||
|
index 00000000..b8b28df2
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/libraries/missinggpgkey.py
|
||
|
@@ -0,0 +1,368 @@
|
||
|
+import json
|
||
|
+import os
|
||
|
+import re
|
||
|
+import shutil
|
||
|
+import tempfile
|
||
|
+
|
||
|
+from six.moves import urllib
|
||
|
+
|
||
|
+from leapp import reporting
|
||
|
+from leapp.exceptions import StopActorExecutionError
|
||
|
+from leapp.libraries.common import config
|
||
|
+from leapp.libraries.common.config.version import get_source_major_version, get_target_major_version
|
||
|
+from leapp.libraries.stdlib import api, run
|
||
|
+from leapp.models import (
|
||
|
+ DNFWorkaround,
|
||
|
+ InstalledRPM,
|
||
|
+ TargetUserSpaceInfo,
|
||
|
+ TMPTargetRepositoriesFacts,
|
||
|
+ UsedTargetRepositories
|
||
|
+)
|
||
|
+from leapp.utils.deprecation import suppress_deprecation
|
||
|
+
|
||
|
+GPG_CERTS_FOLDER = 'rpm-gpg'
|
||
|
+
|
||
|
+
|
||
|
+def _gpg_show_keys(key_path):
|
||
|
+ """
|
||
|
+ Show keys in given file in version-agnostic manner
|
||
|
+
|
||
|
+ This runs gpg --show-keys (EL8) or gpg --with-fingerprints (EL7)
|
||
|
+ to verify the given file exists, is readable and contains valid
|
||
|
+ OpenPGP key data, which is printed in parsable format (--with-colons).
|
||
|
+ """
|
||
|
+ try:
|
||
|
+ cmd = ['gpg2']
|
||
|
+ # RHEL7 gnupg requires different switches to get the same output
|
||
|
+ if get_source_major_version() == '7':
|
||
|
+ cmd.append('--with-fingerprint')
|
||
|
+ else:
|
||
|
+ cmd.append('--show-keys')
|
||
|
+ cmd += ['--with-colons', key_path]
|
||
|
+ # TODO: discussed, most likely the checked=False will be dropped
|
||
|
+ # and error will be handled in other functions
|
||
|
+ return run(cmd, split=True, checked=False)
|
||
|
+ except OSError as err:
|
||
|
+ # NOTE: this is hypothetic; gnupg2 has to be installed on RHEL 7+
|
||
|
+ error = 'Failed to read fingerprint from GPG key {}: {}'.format(key_path, str(err))
|
||
|
+ api.current_logger().error(error)
|
||
|
+ return {}
|
||
|
+
|
||
|
+
|
||
|
+def _parse_fp_from_gpg(output):
|
||
|
+ """
|
||
|
+ Parse the output of gpg --show-keys --with-colons.
|
||
|
+
|
||
|
+ Return list of 8 characters fingerprints per each gpgkey for the given
|
||
|
+ output from stdlib.run() or None if some error occurred. Either the
|
||
|
+ command return non-zero exit code, the file does not exists, its not
|
||
|
+ readable or does not contain any openpgp data.
|
||
|
+ """
|
||
|
+ if not output or output['exit_code']:
|
||
|
+ return []
|
||
|
+
|
||
|
+ # we are interested in the lines of the output starting with "pub:"
|
||
|
+ # the colons are used for separating the fields in output like this
|
||
|
+ # pub:-:4096:1:999F7CBF38AB71F4:1612983048:::-:::escESC::::::23::0:
|
||
|
+ # ^--------------^ this is the fingerprint we need
|
||
|
+ # ^------^ but RPM version is just the last 8 chars lowercase
|
||
|
+ # Also multiple gpg keys can be stored in the file, so go through all "pub"
|
||
|
+ # lines
|
||
|
+ gpg_fps = []
|
||
|
+ for line in output['stdout']:
|
||
|
+ if not line or not line.startswith('pub:'):
|
||
|
+ continue
|
||
|
+ parts = line.split(':')
|
||
|
+ if len(parts) >= 4 and len(parts[4]) == 16:
|
||
|
+ gpg_fps.append(parts[4][8:].lower())
|
||
|
+ else:
|
||
|
+ api.current_logger().warning(
|
||
|
+ 'Cannot parse the gpg2 output. Line: "{}"'
|
||
|
+ .format(line)
|
||
|
+ )
|
||
|
+
|
||
|
+ return gpg_fps
|
||
|
+
|
||
|
+
|
||
|
+def _read_gpg_fp_from_file(key_path):
|
||
|
+ """
|
||
|
+ Returns the list of public key fingerprints from the given file
|
||
|
+
|
||
|
+ Logs warning in case no OpenPGP data found in the given file or it is not
|
||
|
+ readable for some reason.
|
||
|
+ """
|
||
|
+ res = _gpg_show_keys(key_path)
|
||
|
+ fp = _parse_fp_from_gpg(res)
|
||
|
+ if not fp:
|
||
|
+ error = 'Unable to read OpenPGP keys from {}: {}'.format(key_path, res['stderr'])
|
||
|
+ api.current_logger().error(error)
|
||
|
+ return fp
|
||
|
+
|
||
|
+
|
||
|
+def _get_path_to_gpg_certs():
|
||
|
+ """
|
||
|
+ Get path to the directory with trusted target gpg keys in leapp tree
|
||
|
+ """
|
||
|
+ # XXX This is copy&paste from TargetUserspaceCreator actor.
|
||
|
+ # Potential changes need to happen in both places to keep them in sync.
|
||
|
+ target_major_version = get_target_major_version()
|
||
|
+ target_product_type = config.get_product_type('target')
|
||
|
+ certs_dir = target_major_version
|
||
|
+ # only beta is special in regards to the GPG signing keys
|
||
|
+ if target_product_type == 'beta':
|
||
|
+ certs_dir = '{}beta'.format(target_major_version)
|
||
|
+ return os.path.join(api.get_common_folder_path(GPG_CERTS_FOLDER), certs_dir)
|
||
|
+
|
||
|
+
|
||
|
+def _expand_vars(path):
|
||
|
+ """
|
||
|
+ Expand variables like $releasever and $basearch to the target system version
|
||
|
+ """
|
||
|
+ r = path.replace('$releasever', get_target_major_version())
|
||
|
+ r = r.replace('$basearch', api.current_actor().configuration.architecture)
|
||
|
+ return r
|
||
|
+
|
||
|
+
|
||
|
+def _get_abs_file_path(target_userspace, file_url):
|
||
|
+ """
|
||
|
+ Return the absolute path for file_url if starts with file:///
|
||
|
+
|
||
|
+ If the file_url starts with 'file:///', return its absolute path to
|
||
|
+ the target userspace container, as such a file is supposed to be located
|
||
|
+ on the target system.
|
||
|
+
|
||
|
+ For all other cases, return the originally obtained value.
|
||
|
+ """
|
||
|
+ # TODO(pstodulk): @Jakuje: are we sure the file will be inside the
|
||
|
+ # target userspace container? What if it's a file locally stored by user
|
||
|
+ # and the repository is defined like that as well? Possibly it's just
|
||
|
+ # a corner corner case. I guess it does not have a high prio tbh, but want
|
||
|
+ # to be sure.
|
||
|
+ if not isinstance(target_userspace, TargetUserSpaceInfo):
|
||
|
+ # not need to cover this by tests, it's seatbelt
|
||
|
+ raise ValueError('target_userspace must by TargetUserSpaceInfo object')
|
||
|
+
|
||
|
+ prefix = 'file:///'
|
||
|
+ if not file_url.startswith(prefix):
|
||
|
+ return file_url
|
||
|
+ return os.path.join(target_userspace.path, file_url[len(prefix):])
|
||
|
+
|
||
|
+
|
||
|
+def _pubkeys_from_rpms(installed_rpms):
|
||
|
+ """
|
||
|
+ Return the list of fingerprints of GPG keys in RPM DB
|
||
|
+
|
||
|
+ This function returns short 8 characters fingerprints of trusted GPG keys
|
||
|
+ "installed" in the source OS RPM database. These look like normal packages
|
||
|
+ named "gpg-pubkey" and the fingerprint is present in the version field.
|
||
|
+ """
|
||
|
+ return [pkg.version for pkg in installed_rpms.items if pkg.name == 'gpg-pubkey']
|
||
|
+
|
||
|
+
|
||
|
+def _get_pubkeys(installed_rpms):
|
||
|
+ """
|
||
|
+ Get pubkeys from installed rpms and the trusted directory
|
||
|
+ """
|
||
|
+ pubkeys = _pubkeys_from_rpms(installed_rpms)
|
||
|
+ certs_path = _get_path_to_gpg_certs()
|
||
|
+ for certname in os.listdir(certs_path):
|
||
|
+ key_file = os.path.join(certs_path, certname)
|
||
|
+ fps = _read_gpg_fp_from_file(key_file)
|
||
|
+ if fps:
|
||
|
+ pubkeys += fps
|
||
|
+ # TODO: what about else: ?
|
||
|
+ # The warning is now logged in _read_gpg_fp_from_file. We can raise
|
||
|
+ # the priority of the message or convert it to report though.
|
||
|
+ return pubkeys
|
||
|
+
|
||
|
+
|
||
|
+def _the_nogpgcheck_option_used():
|
||
|
+ return config.get_env('LEAPP_NOGPGCHECK', False) == '1'
|
||
|
+
|
||
|
+
|
||
|
+def _consume_data():
|
||
|
+ try:
|
||
|
+ used_target_repos = next(api.consume(UsedTargetRepositories)).repos
|
||
|
+ except StopIteration:
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ 'Could not check for valid GPG keys', details={'details': 'No UsedTargetRepositories facts'}
|
||
|
+ )
|
||
|
+
|
||
|
+ try:
|
||
|
+ target_repos = next(api.consume(TMPTargetRepositoriesFacts)).repositories
|
||
|
+ except StopIteration:
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ 'Could not check for valid GPG keys', details={'details': 'No TMPTargetRepositoriesFacts facts'}
|
||
|
+ )
|
||
|
+ try:
|
||
|
+ installed_rpms = next(api.consume(InstalledRPM))
|
||
|
+ except StopIteration:
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ 'Could not check for valid GPG keys', details={'details': 'No InstalledRPM facts'}
|
||
|
+ )
|
||
|
+ try:
|
||
|
+ target_userspace = next(api.consume(TargetUserSpaceInfo))
|
||
|
+ except StopIteration:
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ 'Could not check for valid GPG keys', details={'details': 'No TargetUserSpaceInfo facts'}
|
||
|
+ )
|
||
|
+
|
||
|
+ return used_target_repos, target_repos, installed_rpms, target_userspace
|
||
|
+
|
||
|
+
|
||
|
+def _get_repo_gpgkey_urls(repo):
|
||
|
+ """
|
||
|
+ Return the list or repository gpgkeys that should be checked
|
||
|
+
|
||
|
+ If the gpgcheck is disabled for the repo or gpgkey is not specified,
|
||
|
+ return an empty list.
|
||
|
+
|
||
|
+ Returned gpgkeys are URLs with already expanded variables
|
||
|
+ (e.g. $releasever) as gpgkey can contain list of URLs separated by comma
|
||
|
+ or whitespaces.
|
||
|
+ """
|
||
|
+
|
||
|
+ repo_additional = json.loads(repo.additional_fields)
|
||
|
+
|
||
|
+ # TODO does the case matter here?
|
||
|
+ if 'gpgcheck' in repo_additional and repo_additional['gpgcheck'] in ('0', 'False', 'no'):
|
||
|
+ # NOTE: https://dnf.readthedocs.io/en/latest/conf_ref.html#boolean-label
|
||
|
+ # nothing to do with repos with enforced gpgcheck=0
|
||
|
+ return []
|
||
|
+
|
||
|
+ if 'gpgkey' not in repo_additional:
|
||
|
+ # This means rpm will bail out at some time if the key is not present
|
||
|
+ # but we will not know if the needed key is present or not before we will have
|
||
|
+ # the packages at least downloaded
|
||
|
+ # TODO(pstodulk): possibly we should return None if gpgcheck is disabled
|
||
|
+ # and empty list when gpgkey is missing? So we could evaluate that better
|
||
|
+ # outside.
|
||
|
+ api.current_logger().warning(
|
||
|
+ 'The gpgcheck for the {} repository is enabled'
|
||
|
+ ' but gpgkey is not specified. Cannot be checked.'
|
||
|
+ .format(repo.repoid)
|
||
|
+ )
|
||
|
+ return []
|
||
|
+
|
||
|
+ return re.findall(r'[^,\s]+', _expand_vars(repo_additional['gpgkey']))
|
||
|
+
|
||
|
+
|
||
|
+def _report_missing_keys(missing_keys):
|
||
|
+ # TODO(pstodulk): polish the report, use FMT_LIST_SEPARATOR
|
||
|
+ # the list of keys should be mentioned in the summary
|
||
|
+ summary = (
|
||
|
+ "Some of the target repositories require GPG keys that are missing from the current"
|
||
|
+ " RPM DB. Leapp will not be able to verify packages from these repositories during the upgrade process."
|
||
|
+ )
|
||
|
+ hint = (
|
||
|
+ "Please, review the following list and import the GPG keys before "
|
||
|
+ "continuing the upgrade:\n * {}".format('\n * '.join(missing_keys))
|
||
|
+ )
|
||
|
+ reporting.create_report(
|
||
|
+ [
|
||
|
+ reporting.Title("Missing GPG key from target system repository"),
|
||
|
+ reporting.Summary(summary),
|
||
|
+ reporting.Severity(reporting.Severity.HIGH),
|
||
|
+ reporting.Groups([reporting.Groups.REPOSITORY, reporting.Groups.INHIBITOR]),
|
||
|
+ reporting.Remediation(hint=hint),
|
||
|
+ # TODO(pstodulk): @Jakuje: let's sync about it
|
||
|
+ # TODO update external documentation ?
|
||
|
+ # reporting.ExternalLink(
|
||
|
+ # title=(
|
||
|
+ # "Customizing your Red Hat Enterprise Linux "
|
||
|
+ # "in-place upgrade"
|
||
|
+ # ),
|
||
|
+ # url=(
|
||
|
+ # "https://access.redhat.com/articles/4977891/"
|
||
|
+ # "#repos-known-issues"
|
||
|
+ # ),
|
||
|
+ # ),
|
||
|
+ ]
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def register_dnfworkaround():
|
||
|
+ api.produce(DNFWorkaround(
|
||
|
+ display_name='import trusted gpg keys to RPM DB',
|
||
|
+ script_path=api.current_actor().get_common_tool_path('importrpmgpgkeys'),
|
||
|
+ script_args=[_get_path_to_gpg_certs()],
|
||
|
+ ))
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def process():
|
||
|
+ """
|
||
|
+ Process the repositories and find missing signing keys
|
||
|
+
|
||
|
+ UsedTargetRepositories doesn't contain baseurl attribute. So gathering
|
||
|
+ them from model TMPTargetRepositoriesFacts.
|
||
|
+ """
|
||
|
+ # when the user decided to ignore gpg signatures on the packages, we can ignore these checks altogether
|
||
|
+ if _the_nogpgcheck_option_used():
|
||
|
+ api.current_logger().warning('The --nogpgcheck option is used: skipping all related checks.')
|
||
|
+ return
|
||
|
+
|
||
|
+ used_target_repos, target_repos, installed_rpms, target_userspace = _consume_data()
|
||
|
+
|
||
|
+ target_repo_id_to_repositories_facts_map = {
|
||
|
+ repo.repoid: repo
|
||
|
+ for repofile in target_repos
|
||
|
+ for repo in repofile.data
|
||
|
+ }
|
||
|
+
|
||
|
+ # These are used only for getting the installed gpg-pubkey "packages"
|
||
|
+ pubkeys = _get_pubkeys(installed_rpms)
|
||
|
+ missing_keys = list()
|
||
|
+ processed_gpgkey_urls = set()
|
||
|
+ tmpdir = None
|
||
|
+ for repoid in used_target_repos:
|
||
|
+ if repoid.repoid not in target_repo_id_to_repositories_facts_map:
|
||
|
+ api.current_logger().warning('The target repository {} metadata not available'.format(repoid.repoid))
|
||
|
+ continue
|
||
|
+
|
||
|
+ repo = target_repo_id_to_repositories_facts_map[repoid.repoid]
|
||
|
+ for gpgkey_url in _get_repo_gpgkey_urls(repo):
|
||
|
+ if gpgkey_url in processed_gpgkey_urls:
|
||
|
+ continue
|
||
|
+ processed_gpgkey_urls.add(gpgkey_url)
|
||
|
+
|
||
|
+ if gpgkey_url.startswith('file:///'):
|
||
|
+ key_file = _get_abs_file_path(target_userspace, gpgkey_url)
|
||
|
+ elif gpgkey_url.startswith('http://') or gpgkey_url.startswith('https://'):
|
||
|
+ # delay creating temporary directory until we need it
|
||
|
+ tmpdir = tempfile.mkdtemp() if tmpdir is None else tmpdir
|
||
|
+ # FIXME: what to do with dummy? it's fd, that should be closed also
|
||
|
+ dummy, tmp_file = tempfile.mkstemp(dir=tmpdir)
|
||
|
+ try:
|
||
|
+ urllib.request.urlretrieve(gpgkey_url, tmp_file)
|
||
|
+ key_file = tmp_file
|
||
|
+ except urllib.error.URLError as err:
|
||
|
+ # TODO(pstodulk): create report for the repoids which cannot be checked?
|
||
|
+ # (no inhibitor)
|
||
|
+ api.current_logger().warning(
|
||
|
+ 'Failed to download the gpgkey {}: {}'.format(gpgkey_url, str(err)))
|
||
|
+ continue
|
||
|
+ else:
|
||
|
+ # TODO: report?
|
||
|
+ api.current_logger().error(
|
||
|
+ 'Skipping unknown protocol for gpgkey {}'.format(gpgkey_url))
|
||
|
+ continue
|
||
|
+ fps = _read_gpg_fp_from_file(key_file)
|
||
|
+ if not fps:
|
||
|
+ # TODO: for now. I think it should be treated better
|
||
|
+ api.current_logger().warning(
|
||
|
+ "Cannot get any gpg key from the file: {}".format(gpgkey_url)
|
||
|
+ )
|
||
|
+ continue
|
||
|
+ for fp in fps:
|
||
|
+ if fp not in pubkeys and gpgkey_url not in missing_keys:
|
||
|
+ missing_keys.append(_get_abs_file_path(target_userspace, gpgkey_url))
|
||
|
+
|
||
|
+ if tmpdir:
|
||
|
+ # clean up temporary directory with downloaded gpg keys
|
||
|
+ shutil.rmtree(tmpdir)
|
||
|
+
|
||
|
+ if missing_keys:
|
||
|
+ _report_missing_keys(missing_keys)
|
||
|
+
|
||
|
+ register_dnfworkaround()
|
||
|
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/component_test_missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/component_test_missinggpgkey.py
|
||
|
new file mode 100644
|
||
|
index 00000000..5af5f026
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/component_test_missinggpgkey.py
|
||
|
@@ -0,0 +1,522 @@
|
||
|
+import pytest
|
||
|
+from six.moves.urllib.error import URLError
|
||
|
+
|
||
|
+from leapp import reporting
|
||
|
+from leapp.exceptions import StopActorExecutionError
|
||
|
+from leapp.libraries.actor.missinggpgkey import _pubkeys_from_rpms, process
|
||
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked, logger_mocked, produce_mocked
|
||
|
+from leapp.libraries.stdlib import api
|
||
|
+from leapp.models import (
|
||
|
+ InstalledRPM,
|
||
|
+ Report,
|
||
|
+ RepositoriesFacts,
|
||
|
+ RepositoryData,
|
||
|
+ RepositoryFile,
|
||
|
+ RPM,
|
||
|
+ TargetUserSpaceInfo,
|
||
|
+ TMPTargetRepositoriesFacts,
|
||
|
+ UsedTargetRepositories,
|
||
|
+ UsedTargetRepository
|
||
|
+)
|
||
|
+from leapp.utils.deprecation import suppress_deprecation
|
||
|
+
|
||
|
+# Note, that this is not a real component test as described in the documentation,
|
||
|
+# but basically unit test calling the "main" function process() to simulate the
|
||
|
+# whole process as I was initially advised not to use these component tests.
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_installedrpm_no_my_key():
|
||
|
+ return [
|
||
|
+ RPM(
|
||
|
+ name='rpm',
|
||
|
+ version='4.16.1.3',
|
||
|
+ release='17.el9',
|
||
|
+ epoch='0',
|
||
|
+ packager='Red Hat, Inc. <http://bugzilla.redhat.com/bugzilla>',
|
||
|
+ arch='x86_64',
|
||
|
+ pgpsig='RSA/SHA256, Mon 08 Aug 2022 09:10:15 AM UTC, Key ID 199e2f91fd431d51',
|
||
|
+ repository='BaseOS',
|
||
|
+ ),
|
||
|
+ RPM(
|
||
|
+ name='gpg-pubkey',
|
||
|
+ version='fd431d51',
|
||
|
+ release='4ae0493b',
|
||
|
+ epoch='0',
|
||
|
+ packager='Red Hat, Inc. (release key 2) <security@redhat.com>',
|
||
|
+ arch='noarch',
|
||
|
+ pgpsig=''
|
||
|
+ ),
|
||
|
+ RPM(
|
||
|
+ name='gpg-pubkey',
|
||
|
+ version='5a6340b3',
|
||
|
+ release='6229229e',
|
||
|
+ epoch='0',
|
||
|
+ packager='Red Hat, Inc. (auxiliary key 3) <security@redhat.com>',
|
||
|
+ arch='noarch',
|
||
|
+ pgpsig=''
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_installedrpm():
|
||
|
+ return InstalledRPM(
|
||
|
+ items=[
|
||
|
+ RPM(
|
||
|
+ name='gpg-pubkey',
|
||
|
+ version='3228467c',
|
||
|
+ release='613798eb',
|
||
|
+ epoch='0',
|
||
|
+ packager='edora (epel9) <epel@fedoraproject.org>',
|
||
|
+ arch='noarch',
|
||
|
+ pgpsig=''
|
||
|
+ ),
|
||
|
+ ] + _get_test_installedrpm_no_my_key(),
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_targuserspaceinfo(path='nopath'):
|
||
|
+ return TargetUserSpaceInfo(
|
||
|
+ path=path,
|
||
|
+ scratch='',
|
||
|
+ mounts='',
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_usedtargetrepositories_list():
|
||
|
+ return [
|
||
|
+ UsedTargetRepository(
|
||
|
+ repoid='BaseOS',
|
||
|
+ ),
|
||
|
+ UsedTargetRepository(
|
||
|
+ repoid='AppStream',
|
||
|
+ ),
|
||
|
+ UsedTargetRepository(
|
||
|
+ repoid='MyAnotherRepo',
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_usedtargetrepositories():
|
||
|
+ return UsedTargetRepositories(
|
||
|
+ repos=_get_test_usedtargetrepositories_list()
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_target_repofile():
|
||
|
+ return RepositoryFile(
|
||
|
+ file='/etc/yum.repos.d/target_rhel.repo',
|
||
|
+ data=[
|
||
|
+ RepositoryData(
|
||
|
+ repoid='BaseOS',
|
||
|
+ name="RHEL BaseOS repository",
|
||
|
+ baseurl="/whatever/",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}'
|
||
|
+ ),
|
||
|
+ RepositoryData(
|
||
|
+ repoid='AppStream',
|
||
|
+ name="RHEL AppStream repository",
|
||
|
+ baseurl="/whatever/",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"}'
|
||
|
+ ),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_target_repofile_additional():
|
||
|
+ return RepositoryFile(
|
||
|
+ file='/etc/yum.repos.d/my_target_rhel.repo',
|
||
|
+ data=[
|
||
|
+ RepositoryData(
|
||
|
+ repoid='MyRepo',
|
||
|
+ name="My repository",
|
||
|
+ baseurl="/whatever/",
|
||
|
+ enabled=False,
|
||
|
+ ),
|
||
|
+ RepositoryData(
|
||
|
+ repoid='MyAnotherRepo',
|
||
|
+ name="My another repository",
|
||
|
+ baseurl="/whatever/",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"file:///etc/pki/rpm-gpg/RPM-GPG-KEY-my-release"}'
|
||
|
+ ),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def _get_test_tmptargetrepositoriesfacts():
|
||
|
+ return TMPTargetRepositoriesFacts(
|
||
|
+ repositories=[
|
||
|
+ _get_test_target_repofile(),
|
||
|
+ _get_test_target_repofile_additional(),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_nogpgcheck(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with the --nogpgcheck commandline switch
|
||
|
+
|
||
|
+ This test should skip any checks and just log a message that no checks were executed
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ envars={'LEAPP_NOGPGCHECK': '1'},
|
||
|
+ msgs=[
|
||
|
+ _get_test_installedrpm(),
|
||
|
+ _get_test_usedtargetrepositories(),
|
||
|
+ _get_test_tmptargetrepositoriesfacts(),
|
||
|
+ ],
|
||
|
+ ))
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+
|
||
|
+ process()
|
||
|
+
|
||
|
+ assert api.produce.called == 0
|
||
|
+ assert len(api.current_logger.warnmsg) == 1
|
||
|
+ assert '--nogpgcheck option is used' in api.current_logger.warnmsg[0]
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.parametrize('msgs', [
|
||
|
+ [],
|
||
|
+ [_get_test_installedrpm],
|
||
|
+ [_get_test_usedtargetrepositories],
|
||
|
+ [_get_test_tmptargetrepositoriesfacts],
|
||
|
+ # These are just incomplete lists of required facts
|
||
|
+ [_get_test_installedrpm(), _get_test_usedtargetrepositories()],
|
||
|
+ [_get_test_usedtargetrepositories(), _get_test_tmptargetrepositoriesfacts()],
|
||
|
+ [_get_test_installedrpm(), _get_test_tmptargetrepositoriesfacts()],
|
||
|
+])
|
||
|
+def test_perform_missing_facts(monkeypatch, msgs):
|
||
|
+ """
|
||
|
+ Executes the "main" function with missing required facts
|
||
|
+
|
||
|
+ The missing facts (either RPM information, Target Repositories or their facts) cause
|
||
|
+ the StopActorExecutionError excepction. But this should be rare as the required facts
|
||
|
+ are clearly defined in the actor interface.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=msgs))
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ # TODO: the gpg call should be mocked
|
||
|
+
|
||
|
+ with pytest.raises(StopActorExecutionError):
|
||
|
+ process()
|
||
|
+ # nothing produced
|
||
|
+ assert api.produce.called == 0
|
||
|
+ # not skipped by --nogpgcheck
|
||
|
+ assert not api.current_logger.warnmsg
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def _get_test_tmptargetrepositoriesfacts_partial():
|
||
|
+ return [
|
||
|
+ _get_test_installedrpm(),
|
||
|
+ _get_test_usedtargetrepositories(),
|
||
|
+ TMPTargetRepositoriesFacts(
|
||
|
+ repositories=[
|
||
|
+ _get_test_target_repofile(),
|
||
|
+ # missing MyAnotherRepo
|
||
|
+ ]
|
||
|
+ )
|
||
|
+ ]
|
||
|
+
|
||
|
+
|
||
|
+def _gpg_show_keys_mocked(key_path):
|
||
|
+ """
|
||
|
+ Get faked output from gpg reading keys.
|
||
|
+
|
||
|
+ This is needed to get away from dependency on the filesystem
|
||
|
+ """
|
||
|
+ if key_path == '/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release':
|
||
|
+ return {
|
||
|
+ 'stdout': [
|
||
|
+ 'pub:-:4096:1:199E2F91FD431D51:1256212795:::-:::scSC::::::23::0:',
|
||
|
+ 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:',
|
||
|
+ ('uid:-::::1256212795::DC1CAEC7997B3575101BB0FCAAC6191792660D8F::'
|
||
|
+ 'Red Hat, Inc. (release key 2) <security@redhat.com>::::::::::0:'),
|
||
|
+ 'pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:::scSC::::::23::0:',
|
||
|
+ 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:',
|
||
|
+ ('uid:-::::1646863006::DA7F68E3872D6E7BDCE05225E7EB5F3ACDD9699F::'
|
||
|
+ 'Red Hat, Inc. (auxiliary key 3) <security@redhat.com>::::::::::0:'),
|
||
|
+ ],
|
||
|
+ 'stderr': (),
|
||
|
+ 'exit_code': 0,
|
||
|
+ }
|
||
|
+ if key_path == '/etc/pki/rpm-gpg/RPM-GPG-KEY-my-release': # actually epel9 key
|
||
|
+ return {
|
||
|
+ 'stdout': [
|
||
|
+ 'pub:-:4096:1:8A3872BF3228467C:1631033579:::-:::escESC::::::23::0:',
|
||
|
+ 'fpr:::::::::FF8AD1344597106ECE813B918A3872BF3228467C:',
|
||
|
+ ('uid:-::::1631033579::3EED52B2BDE50880047DB883C87B0FCAE458D111::'
|
||
|
+ 'Fedora (epel9) <epel@fedoraproject.org>::::::::::0:'),
|
||
|
+ ],
|
||
|
+ 'stderr': (),
|
||
|
+ 'exit_code': 0,
|
||
|
+ }
|
||
|
+
|
||
|
+ return {
|
||
|
+ 'stdout': [
|
||
|
+ 'pub:-:4096:1:F55AD3FB5323552A:1628617948:::-:::escESC::::::23::0:',
|
||
|
+ 'fpr:::::::::ACB5EE4E831C74BB7C168D27F55AD3FB5323552A:',
|
||
|
+ ('uid:-::::1628617948::4830BB019772421B89ABD0BBE245B89C73BF053F::'
|
||
|
+ 'Fedora (37) <fedora-37-primary@fedoraproject.org>::::::::::0:'),
|
||
|
+ ],
|
||
|
+ 'stderr': (),
|
||
|
+ 'exit_code': 0,
|
||
|
+ }
|
||
|
+
|
||
|
+
|
||
|
+def _get_pubkeys_mocked(installed_rpms):
|
||
|
+ """
|
||
|
+ This skips getting fps from files in container for simplification
|
||
|
+ """
|
||
|
+ return _pubkeys_from_rpms(installed_rpms)
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_missing_some_repo_facts(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with missing repositories facts
|
||
|
+
|
||
|
+ This is misalignment in the provided facts UsedTargetRepositories and TMPTargetRepositoriesFacts,
|
||
|
+ where we miss some metadata that are required by the first message.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=_get_test_tmptargetrepositoriesfacts_partial())
|
||
|
+ )
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+
|
||
|
+ with pytest.raises(StopActorExecutionError):
|
||
|
+ process()
|
||
|
+ assert api.produce.called == 0
|
||
|
+ assert reporting.create_report.called == 0
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def _get_test_tmptargetrepositoriesfacts_https_unused():
|
||
|
+ return [
|
||
|
+ _get_test_targuserspaceinfo(),
|
||
|
+ _get_test_installedrpm(),
|
||
|
+ _get_test_usedtargetrepositories(),
|
||
|
+ TMPTargetRepositoriesFacts(
|
||
|
+ repositories=[
|
||
|
+ _get_test_target_repofile(),
|
||
|
+ _get_test_target_repofile_additional(),
|
||
|
+ RepositoryFile(
|
||
|
+ file='/etc/yum.repos.d/internet.repo',
|
||
|
+ data=[
|
||
|
+ RepositoryData(
|
||
|
+ repoid='ExternalRepo',
|
||
|
+ name="External repository",
|
||
|
+ baseurl="/whatever/path",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"https://example.com/rpm-gpg/key.gpg"}',
|
||
|
+ ),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+ ],
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_https_gpgkey_unused(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with repositories providing keys over internet
|
||
|
+
|
||
|
+ The external repository is not listed in UsedTargetRepositories so the repository
|
||
|
+ is not checked and we should not get any error here.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=_get_test_tmptargetrepositoriesfacts_https_unused()
|
||
|
+ ))
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._get_pubkeys', _get_pubkeys_mocked)
|
||
|
+
|
||
|
+ process()
|
||
|
+ assert not api.current_logger.warnmsg
|
||
|
+ # This is the DNFWorkaround
|
||
|
+ assert api.produce.called == 1
|
||
|
+ assert reporting.create_report.called == 1
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def get_test_tmptargetrepositoriesfacts_https():
|
||
|
+ return (
|
||
|
+ _get_test_targuserspaceinfo(),
|
||
|
+ _get_test_installedrpm(),
|
||
|
+ UsedTargetRepositories(
|
||
|
+ repos=_get_test_usedtargetrepositories_list() + [
|
||
|
+ UsedTargetRepository(
|
||
|
+ repoid='ExternalRepo',
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+ ),
|
||
|
+ TMPTargetRepositoriesFacts(
|
||
|
+ repositories=[
|
||
|
+ _get_test_target_repofile(),
|
||
|
+ _get_test_target_repofile_additional(),
|
||
|
+ RepositoryFile(
|
||
|
+ file='/etc/yum.repos.d/internet.repo',
|
||
|
+ data=[
|
||
|
+ RepositoryData(
|
||
|
+ repoid='ExternalRepo',
|
||
|
+ name="External repository",
|
||
|
+ baseurl="/whatever/path",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"https://example.com/rpm-gpg/key.gpg"}',
|
||
|
+ ),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+ ],
|
||
|
+ ),
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def get_test_tmptargetrepositoriesfacts_ftp():
|
||
|
+ return (
|
||
|
+ _get_test_targuserspaceinfo(),
|
||
|
+ _get_test_installedrpm(),
|
||
|
+ UsedTargetRepositories(
|
||
|
+ repos=_get_test_usedtargetrepositories_list() + [
|
||
|
+ UsedTargetRepository(
|
||
|
+ repoid='ExternalRepo',
|
||
|
+ ),
|
||
|
+ ]
|
||
|
+ ),
|
||
|
+ TMPTargetRepositoriesFacts(
|
||
|
+ repositories=[
|
||
|
+ _get_test_target_repofile(),
|
||
|
+ _get_test_target_repofile_additional(),
|
||
|
+ RepositoryFile(
|
||
|
+ file='/etc/yum.repos.d/internet.repo',
|
||
|
+ data=[
|
||
|
+ RepositoryData(
|
||
|
+ repoid='ExternalRepo',
|
||
|
+ name="External repository",
|
||
|
+ baseurl="/whatever/path",
|
||
|
+ enabled=True,
|
||
|
+ additional_fields='{"gpgkey":"ftp://example.com/rpm-gpg/key.gpg"}',
|
||
|
+ ),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+ ],
|
||
|
+ ),
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def _urlretrive_mocked(url, filename=None, reporthook=None, data=None):
|
||
|
+ return filename
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_https_gpgkey(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with repositories providing keys over internet
|
||
|
+
|
||
|
+ This produces an report.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=get_test_tmptargetrepositoriesfacts_https())
|
||
|
+ )
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._get_pubkeys', _get_pubkeys_mocked)
|
||
|
+ monkeypatch.setattr('six.moves.urllib.request.urlretrieve', _urlretrive_mocked)
|
||
|
+
|
||
|
+ process()
|
||
|
+ # This is the DNFWorkaround
|
||
|
+ assert api.produce.called == 1
|
||
|
+ assert reporting.create_report.called == 1
|
||
|
+
|
||
|
+
|
||
|
+def _urlretrive_mocked_urlerror(url, filename=None, reporthook=None, data=None):
|
||
|
+ raise URLError('error')
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_https_gpgkey_urlerror(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with repositories providing keys over internet
|
||
|
+
|
||
|
+ This results in warning message printed. Other than that, no report is still produced.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=get_test_tmptargetrepositoriesfacts_https())
|
||
|
+ )
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._get_pubkeys', _get_pubkeys_mocked)
|
||
|
+ monkeypatch.setattr('six.moves.urllib.request.urlretrieve', _urlretrive_mocked_urlerror)
|
||
|
+
|
||
|
+ process()
|
||
|
+ assert len(api.current_logger.warnmsg) == 1
|
||
|
+ assert 'Failed to download the gpgkey https://example.com/rpm-gpg/key.gpg:' in api.current_logger.warnmsg[0]
|
||
|
+ # This is the DNFWorkaround
|
||
|
+ assert api.produce.called == 1
|
||
|
+ assert reporting.create_report.called == 1
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_ftp_gpgkey(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with repositories providing keys over internet
|
||
|
+
|
||
|
+ This results in error message printed. Other than that, no report is still produced.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=get_test_tmptargetrepositoriesfacts_ftp())
|
||
|
+ )
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._get_pubkeys', _get_pubkeys_mocked)
|
||
|
+
|
||
|
+ process()
|
||
|
+ assert len(api.current_logger.errmsg) == 1
|
||
|
+ assert 'Skipping unknown protocol for gpgkey ftp://example.com/rpm-gpg/key.gpg' in api.current_logger.errmsg[0]
|
||
|
+ # This is the DNFWorkaround
|
||
|
+ assert api.produce.called == 1
|
||
|
+ assert reporting.create_report.called == 1
|
||
|
+
|
||
|
+
|
||
|
+@suppress_deprecation(TMPTargetRepositoriesFacts)
|
||
|
+def get_test_data_missing_key():
|
||
|
+ return [
|
||
|
+ _get_test_targuserspaceinfo(),
|
||
|
+ InstalledRPM(items=_get_test_installedrpm_no_my_key()),
|
||
|
+ _get_test_usedtargetrepositories(),
|
||
|
+ _get_test_tmptargetrepositoriesfacts(),
|
||
|
+ ]
|
||
|
+
|
||
|
+
|
||
|
+def test_perform_report(monkeypatch):
|
||
|
+ """
|
||
|
+ Executes the "main" function with missing keys
|
||
|
+
|
||
|
+ This should result in report outlining what key mentioned in target repositories is missing.
|
||
|
+ """
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(
|
||
|
+ msgs=get_test_data_missing_key())
|
||
|
+ )
|
||
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
||
|
+ monkeypatch.setattr(api, 'current_logger', logger_mocked())
|
||
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._gpg_show_keys', _gpg_show_keys_mocked)
|
||
|
+ monkeypatch.setattr('leapp.libraries.actor.missinggpgkey._get_pubkeys', _get_pubkeys_mocked)
|
||
|
+
|
||
|
+ process()
|
||
|
+ assert not api.current_logger.warnmsg
|
||
|
+ # This is the DNFWorkaround
|
||
|
+ assert api.produce.called == 1
|
||
|
+ assert reporting.create_report.called == 1
|
||
|
diff --git a/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/unit_test_missinggpgkey.py b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/unit_test_missinggpgkey.py
|
||
|
new file mode 100644
|
||
|
index 00000000..8a46f97b
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/actors/missinggpgkeysinhibitor/tests/unit_test_missinggpgkey.py
|
||
|
@@ -0,0 +1,209 @@
|
||
|
+import os
|
||
|
+import shutil
|
||
|
+import sys
|
||
|
+import tempfile
|
||
|
+
|
||
|
+import distro
|
||
|
+import pytest
|
||
|
+
|
||
|
+from leapp.libraries.actor.missinggpgkey import (
|
||
|
+ _expand_vars,
|
||
|
+ _get_path_to_gpg_certs,
|
||
|
+ _get_pubkeys,
|
||
|
+ _get_repo_gpgkey_urls,
|
||
|
+ _gpg_show_keys,
|
||
|
+ _parse_fp_from_gpg,
|
||
|
+ _pubkeys_from_rpms
|
||
|
+)
|
||
|
+from leapp.libraries.common.testutils import CurrentActorMocked
|
||
|
+from leapp.libraries.stdlib import api
|
||
|
+from leapp.models import InstalledRPM, RepositoryData, RPM
|
||
|
+
|
||
|
+
|
||
|
+def is_rhel7():
|
||
|
+ return int(distro.major_version()) < 8
|
||
|
+
|
||
|
+
|
||
|
+def test_gpg_show_keys(current_actor_context, monkeypatch):
|
||
|
+ src = '7.9' if is_rhel7() else '8.6'
|
||
|
+ current_actor = CurrentActorMocked(src_ver=src)
|
||
|
+ monkeypatch.setattr(api, 'current_actor', current_actor)
|
||
|
+
|
||
|
+ # python2 compatibility :/
|
||
|
+ dirpath = tempfile.mkdtemp()
|
||
|
+
|
||
|
+ # using GNUPGHOME env should avoid gnupg modifying the system
|
||
|
+ os.environ['GNUPGHOME'] = dirpath
|
||
|
+
|
||
|
+ try:
|
||
|
+ # non-existing file
|
||
|
+ non_existent_path = os.path.join(dirpath, 'nonexistent')
|
||
|
+ res = _gpg_show_keys(non_existent_path)
|
||
|
+ if is_rhel7():
|
||
|
+ err_msg = "gpg: can't open `{}'".format(non_existent_path)
|
||
|
+ else:
|
||
|
+ err_msg = "gpg: can't open '{}': No such file or directory\n".format(non_existent_path)
|
||
|
+ assert not res['stdout']
|
||
|
+ assert err_msg in res['stderr']
|
||
|
+ assert res['exit_code'] == 2
|
||
|
+
|
||
|
+ fp = _parse_fp_from_gpg(res)
|
||
|
+ assert fp == []
|
||
|
+
|
||
|
+ # no gpg data found
|
||
|
+ no_key_path = os.path.join(dirpath, "no_key")
|
||
|
+ with open(no_key_path, "w") as f:
|
||
|
+ f.write('test')
|
||
|
+
|
||
|
+ res = _gpg_show_keys(no_key_path)
|
||
|
+ if is_rhel7():
|
||
|
+ err_msg = ('gpg: no valid OpenPGP data found.\n'
|
||
|
+ 'gpg: processing message failed: Unknown system error\n')
|
||
|
+ else:
|
||
|
+ err_msg = 'gpg: no valid OpenPGP data found.\n'
|
||
|
+ assert not res['stdout']
|
||
|
+ assert res['stderr'] == err_msg
|
||
|
+ assert res['exit_code'] == 2
|
||
|
+
|
||
|
+ fp = _parse_fp_from_gpg(res)
|
||
|
+ assert fp == []
|
||
|
+
|
||
|
+ # with some test data now -- rhel9 release key
|
||
|
+ # rhel9_key_path = os.path.join(api.get_common_folder_path('rpm-gpg'), '9')
|
||
|
+ cur_dir = os.path.dirname(os.path.abspath(__file__))
|
||
|
+ rhel9_key_path = os.path.join(cur_dir, '..', '..', '..', 'files', 'rpm-gpg', '9',
|
||
|
+ 'RPM-GPG-KEY-redhat-release')
|
||
|
+ res = _gpg_show_keys(rhel9_key_path)
|
||
|
+ finally:
|
||
|
+ shutil.rmtree(dirpath)
|
||
|
+
|
||
|
+ if is_rhel7():
|
||
|
+ assert len(res['stdout']) == 4
|
||
|
+ assert res['stdout'][0] == ('pub:-:4096:1:199E2F91FD431D51:1256212795:::-:'
|
||
|
+ 'Red Hat, Inc. (release key 2) <security@redhat.com>:')
|
||
|
+ assert res['stdout'][1] == 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:'
|
||
|
+ assert res['stdout'][2] == ('pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:'
|
||
|
+ 'Red Hat, Inc. (auxiliary key 3) <security@redhat.com>:')
|
||
|
+ assert res['stdout'][3] == 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:'
|
||
|
+ else:
|
||
|
+ assert len(res['stdout']) == 6
|
||
|
+ assert res['stdout'][0] == 'pub:-:4096:1:199E2F91FD431D51:1256212795:::-:::scSC::::::23::0:'
|
||
|
+ assert res['stdout'][1] == 'fpr:::::::::567E347AD0044ADE55BA8A5F199E2F91FD431D51:'
|
||
|
+ assert res['stdout'][2] == ('uid:-::::1256212795::DC1CAEC7997B3575101BB0FCAAC6191792660D8F::'
|
||
|
+ 'Red Hat, Inc. (release key 2) <security@redhat.com>::::::::::0:')
|
||
|
+ assert res['stdout'][3] == 'pub:-:4096:1:5054E4A45A6340B3:1646863006:::-:::scSC::::::23::0:'
|
||
|
+ assert res['stdout'][4] == 'fpr:::::::::7E4624258C406535D56D6F135054E4A45A6340B3:'
|
||
|
+ assert res['stdout'][5] == ('uid:-::::1646863006::DA7F68E3872D6E7BDCE05225E7EB5F3ACDD9699F::'
|
||
|
+ 'Red Hat, Inc. (auxiliary key 3) <security@redhat.com>::::::::::0:')
|
||
|
+
|
||
|
+ err = '{}/trustdb.gpg: trustdb created'.format(dirpath)
|
||
|
+ assert err in res['stderr']
|
||
|
+ assert res['exit_code'] == 0
|
||
|
+
|
||
|
+ # now, parse the output too
|
||
|
+ fp = _parse_fp_from_gpg(res)
|
||
|
+ assert fp == ['fd431d51', '5a6340b3']
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.parametrize('res, exp', [
|
||
|
+ ({'exit_code': 2, 'stdout': '', 'stderr': ''}, []),
|
||
|
+ ({'exit_code': 2, 'stdout': '', 'stderr': 'bash: gpg2: command not found...'}, []),
|
||
|
+ ({'exit_code': 0, 'stdout': 'Some other output', 'stderr': ''}, []),
|
||
|
+ ({'exit_code': 0, 'stdout': ['Some other output', 'other line'], 'stderr': ''}, []),
|
||
|
+ ({'exit_code': 0, 'stdout': ['pub:-:4096:1:199E2F91FD431D:'], 'stderr': ''}, []),
|
||
|
+ ({'exit_code': 0, 'stdout': ['pub:-:4096:1:5054E4A45A6340B3:1..'], 'stderr': ''}, ['5a6340b3']),
|
||
|
+])
|
||
|
+def test_parse_fp_from_gpg(res, exp):
|
||
|
+ fp = _parse_fp_from_gpg(res)
|
||
|
+ assert fp == exp
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.parametrize('target, product_type, exp', [
|
||
|
+ ('8.6', 'beta', '../../files/rpm-gpg/8beta'),
|
||
|
+ ('8.8', 'htb', '../../files/rpm-gpg/8'),
|
||
|
+ ('9.0', 'beta', '../../files/rpm-gpg/9beta'),
|
||
|
+ ('9.2', 'ga', '../../files/rpm-gpg/9'),
|
||
|
+])
|
||
|
+def test_get_path_to_gpg_certs(current_actor_context, monkeypatch, target, product_type, exp):
|
||
|
+ current_actor = CurrentActorMocked(dst_ver=target,
|
||
|
+ envars={'LEAPP_DEVEL_TARGET_PRODUCT_TYPE': product_type})
|
||
|
+ monkeypatch.setattr(api, 'current_actor', current_actor)
|
||
|
+
|
||
|
+ p = _get_path_to_gpg_certs()
|
||
|
+ assert p == exp
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.parametrize('data, exp', [
|
||
|
+ ('bare string', 'bare string'),
|
||
|
+ ('with dollar$$$', 'with dollar$$$'),
|
||
|
+ ('path/with/$basearch/something', 'path/with/x86_64/something'),
|
||
|
+ ('path/with/$releasever/something', 'path/with/9/something'),
|
||
|
+ ('path/with/$releasever/$basearch', 'path/with/9/x86_64'),
|
||
|
+ ('path/with/$releasever/$basearch', 'path/with/9/x86_64'),
|
||
|
+])
|
||
|
+def test_expand_vars(monkeypatch, data, exp):
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(dst_ver='9.1')) # x86_64 arch is default
|
||
|
+ res = _expand_vars(data)
|
||
|
+ assert res == exp
|
||
|
+
|
||
|
+
|
||
|
+def _get_test_installed_rmps():
|
||
|
+ return InstalledRPM(
|
||
|
+ items=[
|
||
|
+ RPM(name='gpg-pubkey',
|
||
|
+ version='9570ff31',
|
||
|
+ release='5e3006fb',
|
||
|
+ epoch='0',
|
||
|
+ packager='Fedora (33) <fedora-33-primary@fedoraproject.org>',
|
||
|
+ arch='noarch',
|
||
|
+ pgpsig=''),
|
||
|
+ RPM(name='rpm',
|
||
|
+ version='4.17.1',
|
||
|
+ release='3.fc35',
|
||
|
+ epoch='0',
|
||
|
+ packager='Fedora Project',
|
||
|
+ arch='x86_64',
|
||
|
+ pgpsig='RSA/SHA256, Tue 02 Aug 2022 03:12:43 PM CEST, Key ID db4639719867c58f'),
|
||
|
+ ],
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
+def test_pubkeys_from_rpms():
|
||
|
+ installed_rpm = _get_test_installed_rmps()
|
||
|
+ assert _pubkeys_from_rpms(installed_rpm) == ['9570ff31']
|
||
|
+
|
||
|
+
|
||
|
+# @pytest.mark.parametrize('target, product_type, exp', [
|
||
|
+# ('8.6', 'beta', ['F21541EB']),
|
||
|
+# ('8.8', 'htb', ['FD431D51', 'D4082792']), # ga
|
||
|
+# ('9.0', 'beta', ['F21541EB']),
|
||
|
+# ('9.2', 'ga', ['FD431D51', '5A6340B3']),
|
||
|
+# ])
|
||
|
+# Def test_get_pubkeys(current_actor_context, monkeypatch, target, product_type, exp):
|
||
|
+# current_actor = CurrentActorMocked(dst_ver=target,
|
||
|
+# envars={'LEAPP_DEVEL_TARGET_PRODUCT_TYPE': product_type})
|
||
|
+# monkeypatch.setattr(api, 'current_actor', current_actor)
|
||
|
+# installed_rpm = _get_test_installed_rmps()
|
||
|
+#
|
||
|
+# p = _get_pubkeys(installed_rpm)
|
||
|
+# assert '9570ff31' in p
|
||
|
+# for x in exp:
|
||
|
+# assert x in p
|
||
|
+
|
||
|
+
|
||
|
+@pytest.mark.parametrize('repo, exp', [
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgcheck":0}'), []),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgcheck":"no"}'), []),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgcheck":"False"}'), []),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgkey":"dummy"}'), ["dummy"]),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgkey":"dummy, another"}'),
|
||
|
+ ["dummy", "another"]),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgkey":"dummy\\nanother"}'),
|
||
|
+ ["dummy", "another"]),
|
||
|
+ (RepositoryData(repoid='dummy', name='name', additional_fields='{"gpgkey":"$releasever"}'),
|
||
|
+ ["9"]),
|
||
|
+])
|
||
|
+def test_get_repo_gpgkey_urls(monkeypatch, repo, exp):
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(dst_ver='9.1'))
|
||
|
+ keys = _get_repo_gpgkey_urls(repo)
|
||
|
+ assert keys == exp
|
||
|
diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
|
||
|
index 0415f0fe..f2391ee8 100644
|
||
|
--- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
|
||
|
+++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py
|
||
|
@@ -52,6 +52,7 @@ from leapp.utils.deprecation import suppress_deprecation
|
||
|
# Issue: #486
|
||
|
|
||
|
PROD_CERTS_FOLDER = 'prod-certs'
|
||
|
+GPG_CERTS_FOLDER = 'rpm-gpg'
|
||
|
PERSISTENT_PACKAGE_CACHE_DIR = '/var/lib/leapp/persistent_package_cache'
|
||
|
|
||
|
|
||
|
@@ -136,32 +137,65 @@ def _backup_to_persistent_package_cache(userspace_dir):
|
||
|
target_context.copytree_from('/var/cache/dnf', PERSISTENT_PACKAGE_CACHE_DIR)
|
||
|
|
||
|
|
||
|
+def _the_nogpgcheck_option_used():
|
||
|
+ return get_env('LEAPP_NOGPGCHECK', False) == '1'
|
||
|
+
|
||
|
+
|
||
|
+def _get_path_to_gpg_certs(target_major_version):
|
||
|
+ target_product_type = get_product_type('target')
|
||
|
+ certs_dir = target_major_version
|
||
|
+ # only beta is special in regards to the GPG signing keys
|
||
|
+ if target_product_type == 'beta':
|
||
|
+ certs_dir = '{}beta'.format(target_major_version)
|
||
|
+ return os.path.join(api.get_common_folder_path(GPG_CERTS_FOLDER), certs_dir)
|
||
|
+
|
||
|
+
|
||
|
+def _import_gpg_keys(context, install_root_dir, target_major_version):
|
||
|
+ certs_path = _get_path_to_gpg_certs(target_major_version)
|
||
|
+ # Import the RHEL X+1 GPG key to be able to verify the installation of initial packages
|
||
|
+ try:
|
||
|
+ # Import also any other keys provided by the customer in the same directory
|
||
|
+ for certname in os.listdir(certs_path):
|
||
|
+ cmd = ['rpm', '--root', install_root_dir, '--import', os.path.join(certs_path, certname)]
|
||
|
+ context.call(cmd, callback_raw=utils.logging_handler)
|
||
|
+ except CalledProcessError as exc:
|
||
|
+ raise StopActorExecutionError(
|
||
|
+ message=(
|
||
|
+ 'Unable to import GPG certificates to install RHEL {} userspace packages.'
|
||
|
+ .format(target_major_version)
|
||
|
+ ),
|
||
|
+ details={'details': str(exc), 'stderr': exc.stderr}
|
||
|
+ )
|
||
|
+
|
||
|
+
|
||
|
def prepare_target_userspace(context, userspace_dir, enabled_repos, packages):
|
||
|
"""
|
||
|
Implement the creation of the target userspace.
|
||
|
"""
|
||
|
_backup_to_persistent_package_cache(userspace_dir)
|
||
|
|
||
|
- target_major_version = get_target_major_version()
|
||
|
run(['rm', '-rf', userspace_dir])
|
||
|
_create_target_userspace_directories(userspace_dir)
|
||
|
- with mounting.BindMount(
|
||
|
- source=userspace_dir, target=os.path.join(context.base_dir, 'el{}target'.format(target_major_version))
|
||
|
- ):
|
||
|
+
|
||
|
+ target_major_version = get_target_major_version()
|
||
|
+ install_root_dir = '/el{}target'.format(target_major_version)
|
||
|
+ with mounting.BindMount(source=userspace_dir, target=os.path.join(context.base_dir, install_root_dir.lstrip('/'))):
|
||
|
_restore_persistent_package_cache(userspace_dir)
|
||
|
+ if not _the_nogpgcheck_option_used():
|
||
|
+ _import_gpg_keys(context, install_root_dir, target_major_version)
|
||
|
|
||
|
repos_opt = [['--enablerepo', repo] for repo in enabled_repos]
|
||
|
repos_opt = list(itertools.chain(*repos_opt))
|
||
|
- cmd = ['dnf',
|
||
|
- 'install',
|
||
|
- '-y',
|
||
|
- '--nogpgcheck',
|
||
|
- '--setopt=module_platform_id=platform:el{}'.format(target_major_version),
|
||
|
- '--setopt=keepcache=1',
|
||
|
- '--releasever', api.current_actor().configuration.version.target,
|
||
|
- '--installroot', '/el{}target'.format(target_major_version),
|
||
|
- '--disablerepo', '*'
|
||
|
- ] + repos_opt + packages
|
||
|
+ cmd = ['dnf', 'install', '-y']
|
||
|
+ if _the_nogpgcheck_option_used():
|
||
|
+ cmd.append('--nogpgcheck')
|
||
|
+ cmd += [
|
||
|
+ '--setopt=module_platform_id=platform:el{}'.format(target_major_version),
|
||
|
+ '--setopt=keepcache=1',
|
||
|
+ '--releasever', api.current_actor().configuration.version.target,
|
||
|
+ '--installroot', install_root_dir,
|
||
|
+ '--disablerepo', '*'
|
||
|
+ ] + repos_opt + packages
|
||
|
if config.is_verbose():
|
||
|
cmd.append('-v')
|
||
|
if rhsm.skip_rhsm():
|
||
|
diff --git a/repos/system_upgrade/common/files/rpm-gpg/8/RPM-GPG-KEY-redhat-release b/repos/system_upgrade/common/files/rpm-gpg/8/RPM-GPG-KEY-redhat-release
|
||
|
new file mode 100644
|
||
|
index 00000000..6744de9e
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/files/rpm-gpg/8/RPM-GPG-KEY-redhat-release
|
||
|
@@ -0,0 +1,89 @@
|
||
|
+The following public key can be used to verify RPM packages built and
|
||
|
+signed by Red Hat, Inc. This key is used for packages in Red Hat
|
||
|
+products shipped after November 2009, and for all updates to those
|
||
|
+products.
|
||
|
+
|
||
|
+Questions about this key should be sent to security@redhat.com.
|
||
|
+
|
||
|
+pub 4096R/FD431D51 2009-10-22 Red Hat, Inc. (release key 2) <security@redhat.com>
|
||
|
+
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+Version: GnuPG v1.2.6 (GNU/Linux)
|
||
|
+
|
||
|
+mQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF
|
||
|
+0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF
|
||
|
+0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c
|
||
|
+u7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh
|
||
|
+XGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H
|
||
|
+5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW
|
||
|
+9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj
|
||
|
+/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1
|
||
|
+PcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY
|
||
|
+HVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF
|
||
|
+buhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB
|
||
|
+tDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0
|
||
|
+LmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK
|
||
|
+CRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC
|
||
|
+2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf
|
||
|
+C/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5
|
||
|
+un3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E
|
||
|
+0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE
|
||
|
+IGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh
|
||
|
+8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL
|
||
|
+Ght5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki
|
||
|
+JUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25
|
||
|
+OFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq
|
||
|
+dzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==
|
||
|
+=zbHE
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+
|
||
|
+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
|
||
|
+vt61kpkXoRGxjTlc6h+69aidSg==
|
||
|
+=ls8J
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
diff --git a/repos/system_upgrade/common/files/rpm-gpg/8beta/RPM-GPG-KEY-redhat-beta b/repos/system_upgrade/common/files/rpm-gpg/8beta/RPM-GPG-KEY-redhat-beta
|
||
|
new file mode 100644
|
||
|
index 00000000..1efd1509
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/files/rpm-gpg/8beta/RPM-GPG-KEY-redhat-beta
|
||
|
@@ -0,0 +1,29 @@
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+Version: GnuPG v1.2.6 (GNU/Linux)
|
||
|
+
|
||
|
+mQINBEmkAzABEAC2/c7bP1lHQ3XScxbIk0LQWe1YOiibQBRLwf8Si5PktgtuPibT
|
||
|
+kKpZjw8p4D+fM7jD1WUzUE0X7tXg2l/eUlMM4dw6XJAQ1AmEOtlwSg7rrMtTvM0A
|
||
|
+BEtI7Km6fC6sU6RtBMdcqD1cH/6dbsfh8muznVA7UlX+PRBHVzdWzj6y8h84dBjo
|
||
|
+gzcbYu9Hezqgj/lLzicqsSZPz9UdXiRTRAIhp8V30BD8uRaaa0KDDnD6IzJv3D9P
|
||
|
+xQWbFM4Z12GN9LyeZqmD7bpKzZmXG/3drvfXVisXaXp3M07t3NlBa3Dt8NFIKZ0D
|
||
|
+FRXBz5bvzxRVmdH6DtkDWXDPOt+Wdm1rZrCOrySFpBZQRpHw12eo1M1lirANIov7
|
||
|
+Z+V1Qh/aBxj5EUu32u9ZpjAPPNtQF6F/KjaoHHHmEQAuj4DLex4LY646Hv1rcv2i
|
||
|
+QFuCdvLKQGSiFBrfZH0j/IX3/0JXQlZzb3MuMFPxLXGAoAV9UP/Sw/WTmAuTzFVm
|
||
|
+G13UYFeMwrToOiqcX2VcK0aC1FCcTP2z4JW3PsWvU8rUDRUYfoXovc7eg4Vn5wHt
|
||
|
+0NBYsNhYiAAf320AUIHzQZYi38JgVwuJfFu43tJZE4Vig++RQq6tsEx9Ftz3EwRR
|
||
|
+fJ9z9mEvEiieZm+vbOvMvIuimFVPSCmLH+bI649K8eZlVRWsx3EXCVb0nQARAQAB
|
||
|
+tDBSZWQgSGF0LCBJbmMuIChiZXRhIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0LmNv
|
||
|
+bT6JAjYEEwECACAFAkpSM+cCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRCT
|
||
|
+ioDK8hVB6/9tEAC0+KmzeKceXQ/GTUoU6jy9vtkFCFrmv+c7ol4XpdTt0QhqBOwy
|
||
|
+6m2mKWwmm8KfYfy0cADQ4y/EcoXl7FtFBwYmkCuEQGXhTDn9DvVjhooIq59LEMBQ
|
||
|
+OW879RwwzRIZ8ebbjMUjDPF5MfPQqP2LBu9N4KvXlZp4voykwuuaJ+cbsKZR6pZ6
|
||
|
+0RQKPHKP+NgUFC0fff7XY9cuOZZWFAeKRhLN2K7bnRHKxp+kELWb6R9ZfrYwZjWc
|
||
|
+MIPbTd1khE53L4NTfpWfAnJRtkPSDOKEGVlVLtLq4HEAxQt07kbslqISRWyXER3u
|
||
|
+QOJj64D1ZiIMz6t6uZ424VE4ry9rBR0Jz55cMMx5O/ni9x3xzFUgH8Su2yM0r3jE
|
||
|
+Rf24+tbOaPf7tebyx4OKe+JW95hNVstWUDyGbs6K9qGfI/pICuO1nMMFTo6GqzQ6
|
||
|
+DwLZvJ9QdXo7ujEtySZnfu42aycaQ9ZLC2DOCQCUBY350Hx6FLW3O546TAvpTfk0
|
||
|
+B6x+DV7mJQH7MGmRXQsE7TLBJKjq28Cn4tVp04PmybQyTxZdGA/8zY6pPl6xyVMH
|
||
|
+V68hSBKEVT/rlouOHuxfdmZva1DhVvUC6Xj7+iTMTVJUAq/4Uyn31P1OJmA2a0PT
|
||
|
+CAqWkbJSgKFccsjPoTbLyxhuMSNkEZFHvlZrSK9vnPzmfiRH0Orx3wYpMQ==
|
||
|
+=21pb
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
diff --git a/repos/system_upgrade/common/files/rpm-gpg/9/RPM-GPG-KEY-redhat-release b/repos/system_upgrade/common/files/rpm-gpg/9/RPM-GPG-KEY-redhat-release
|
||
|
new file mode 100644
|
||
|
index 00000000..afd9e05a
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/files/rpm-gpg/9/RPM-GPG-KEY-redhat-release
|
||
|
@@ -0,0 +1,66 @@
|
||
|
+The following public key can be used to verify RPM packages built and
|
||
|
+signed by Red Hat, Inc. This key is used for packages in Red Hat
|
||
|
+products shipped after November 2009, and for all updates to those
|
||
|
+products.
|
||
|
+
|
||
|
+Questions about this key should be sent to security@redhat.com.
|
||
|
+
|
||
|
+pub 4096R/FD431D51 2009-10-22 Red Hat, Inc. (release key 2) <security@redhat.com>
|
||
|
+
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+
|
||
|
+mQINBErgSTsBEACh2A4b0O9t+vzC9VrVtL1AKvUWi9OPCjkvR7Xd8DtJxeeMZ5eF
|
||
|
+0HtzIG58qDRybwUe89FZprB1ffuUKzdE+HcL3FbNWSSOXVjZIersdXyH3NvnLLLF
|
||
|
+0DNRB2ix3bXG9Rh/RXpFsNxDp2CEMdUvbYCzE79K1EnUTVh1L0Of023FtPSZXX0c
|
||
|
+u7Pb5DI5lX5YeoXO6RoodrIGYJsVBQWnrWw4xNTconUfNPk0EGZtEnzvH2zyPoJh
|
||
|
+XGF+Ncu9XwbalnYde10OCvSWAZ5zTCpoLMTvQjWpbCdWXJzCm6G+/hx9upke546H
|
||
|
+5IjtYm4dTIVTnc3wvDiODgBKRzOl9rEOCIgOuGtDxRxcQkjrC+xvg5Vkqn7vBUyW
|
||
|
+9pHedOU+PoF3DGOM+dqv+eNKBvh9YF9ugFAQBkcG7viZgvGEMGGUpzNgN7XnS1gj
|
||
|
+/DPo9mZESOYnKceve2tIC87p2hqjrxOHuI7fkZYeNIcAoa83rBltFXaBDYhWAKS1
|
||
|
+PcXS1/7JzP0ky7d0L6Xbu/If5kqWQpKwUInXtySRkuraVfuK3Bpa+X1XecWi24JY
|
||
|
+HVtlNX025xx1ewVzGNCTlWn1skQN2OOoQTV4C8/qFpTW6DTWYurd4+fE0OJFJZQF
|
||
|
+buhfXYwmRlVOgN5i77NTIJZJQfYFj38c/Iv5vZBPokO6mffrOTv3MHWVgQARAQAB
|
||
|
+tDNSZWQgSGF0LCBJbmMuIChyZWxlYXNlIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0
|
||
|
+LmNvbT6JAjYEEwECACAFAkrgSTsCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAK
|
||
|
+CRAZni+R/UMdUWzpD/9s5SFR/ZF3yjY5VLUFLMXIKUztNN3oc45fyLdTI3+UClKC
|
||
|
+2tEruzYjqNHhqAEXa2sN1fMrsuKec61Ll2NfvJjkLKDvgVIh7kM7aslNYVOP6BTf
|
||
|
+C/JJ7/ufz3UZmyViH/WDl+AYdgk3JqCIO5w5ryrC9IyBzYv2m0HqYbWfphY3uHw5
|
||
|
+un3ndLJcu8+BGP5F+ONQEGl+DRH58Il9Jp3HwbRa7dvkPgEhfFR+1hI+Btta2C7E
|
||
|
+0/2NKzCxZw7Lx3PBRcU92YKyaEihfy/aQKZCAuyfKiMvsmzs+4poIX7I9NQCJpyE
|
||
|
+IGfINoZ7VxqHwRn/d5mw2MZTJjbzSf+Um9YJyA0iEEyD6qjriWQRbuxpQXmlAJbh
|
||
|
+8okZ4gbVFv1F8MzK+4R8VvWJ0XxgtikSo72fHjwha7MAjqFnOq6eo6fEC/75g3NL
|
||
|
+Ght5VdpGuHk0vbdENHMC8wS99e5qXGNDued3hlTavDMlEAHl34q2H9nakTGRF5Ki
|
||
|
+JUfNh3DVRGhg8cMIti21njiRh7gyFI2OccATY7bBSr79JhuNwelHuxLrCFpY7V25
|
||
|
+OFktl15jZJaMxuQBqYdBgSay2G0U6D1+7VsWufpzd/Abx1/c3oi9ZaJvW22kAggq
|
||
|
+dzdA27UUYjWvx42w9menJwh/0jeQcTecIUd0d0rFcw/c1pvgMMl/Q73yzKgKYw==
|
||
|
+=zbHE
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+
|
||
|
+mQINBGIpIp4BEAC/o5e1WzLIsS6/JOQCs4XYATYTcf6B6ALzcP05G0W3uRpUQSrL
|
||
|
+FRKNrU8ZCelm/B+XSh2ljJNeklp2WLxYENDOsftDXGoyLr2hEkI5OyK267IHhFNJ
|
||
|
+g+BN+T5Cjh4ZiiWij6o9F7x2ZpxISE9M4iI80rwSv1KOnGSw5j2zD2EwoMjTVyVE
|
||
|
+/t3s5XJxnDclB7ZqL+cgjv0mWUY/4+b/OoRTkhq7b8QILuZp75Y64pkrndgakm1T
|
||
|
+8mAGXV02mEzpNj9DyAJdUqa11PIhMJMxxHOGHJ8CcHZ2NJL2e7yJf4orTj+cMhP5
|
||
|
+LzJcVlaXnQYu8Zkqa0V6J1Qdj8ZXL72QsmyicRYXAtK9Jm5pvBHuYU2m6Ja7dBEB
|
||
|
+Vkhe7lTKhAjkZC5ErPmANNS9kPdtXCOpwN1lOnmD2m04hks3kpH9OTX7RkTFUSws
|
||
|
+eARAfRID6RLfi59B9lmAbekecnsMIFMx7qR7ZKyQb3GOuZwNYOaYFevuxusSwCHv
|
||
|
+4FtLDIhk+Fge+EbPdEva+VLJeMOb02gC4V/cX/oFoPkxM1A5LHjkuAM+aFLAiIRd
|
||
|
+Np/tAPWk1k6yc+FqkcDqOttbP4ciiXb9JPtmzTCbJD8lgH0rGp8ufyMXC9x7/dqX
|
||
|
+TjsiGzyvlMnrkKB4GL4DqRFl8LAR02A3846DD8CAcaxoXggL2bJCU2rgUQARAQAB
|
||
|
+tDVSZWQgSGF0LCBJbmMuIChhdXhpbGlhcnkga2V5IDMpIDxzZWN1cml0eUByZWRo
|
||
|
+YXQuY29tPokCUgQTAQgAPBYhBH5GJCWMQGU11W1vE1BU5KRaY0CzBQJiKSKeAhsD
|
||
|
+BQsJCAcCAyICAQYVCgkICwIEFgIDAQIeBwIXgAAKCRBQVOSkWmNAsyBfEACuTN/X
|
||
|
+YR+QyzeRw0pXcTvMqzNE4DKKr97hSQEwZH1/v1PEPs5O3psuVUm2iam7bqYwG+ry
|
||
|
+EskAgMHi8AJmY0lioQD5/LTSLTrM8UyQnU3g17DHau1NHIFTGyaW4a7xviU4C2+k
|
||
|
+c6X0u1CPHI1U4Q8prpNcfLsldaNYlsVZtUtYSHKPAUcswXWliW7QYjZ5tMSbu8jR
|
||
|
+OMOc3mZuf0fcVFNu8+XSpN7qLhRNcPv+FCNmk/wkaQfH4Pv+jVsOgHqkV3aLqJeN
|
||
|
+kNUnpyEKYkNqo7mNfNVWOcl+Z1KKKwSkIi3vg8maC7rODsy6IX+Y96M93sqYDQom
|
||
|
+aaWue2gvw6thEoH4SaCrCL78mj2YFpeg1Oew4QwVcBnt68KOPfL9YyoOicNs4Vuu
|
||
|
+fb/vjU2ONPZAeepIKA8QxCETiryCcP43daqThvIgdbUIiWne3gae6eSj0EuUPoYe
|
||
|
+H5g2Lw0qdwbHIOxqp2kvN96Ii7s1DK3VyhMt/GSPCxRnDRJ8oQKJ2W/I1IT5VtiU
|
||
|
+zMjjq5JcYzRPzHDxfVzT9CLeU/0XQ+2OOUAiZKZ0dzSyyVn8xbpviT7iadvjlQX3
|
||
|
+CINaPB+d2Kxa6uFWh+ZYOLLAgZ9B8NKutUHpXN66YSfe79xFBSFWKkJ8cSIMk13/
|
||
|
+Ifs7ApKlKCCRDpwoDqx/sjIaj1cpOfLHYjnefg==
|
||
|
+=UZd/
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
diff --git a/repos/system_upgrade/common/files/rpm-gpg/9beta/RPM-GPG-KEY-redhat-beta b/repos/system_upgrade/common/files/rpm-gpg/9beta/RPM-GPG-KEY-redhat-beta
|
||
|
new file mode 100644
|
||
|
index 00000000..1efd1509
|
||
|
--- /dev/null
|
||
|
+++ b/repos/system_upgrade/common/files/rpm-gpg/9beta/RPM-GPG-KEY-redhat-beta
|
||
|
@@ -0,0 +1,29 @@
|
||
|
+-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
+Version: GnuPG v1.2.6 (GNU/Linux)
|
||
|
+
|
||
|
+mQINBEmkAzABEAC2/c7bP1lHQ3XScxbIk0LQWe1YOiibQBRLwf8Si5PktgtuPibT
|
||
|
+kKpZjw8p4D+fM7jD1WUzUE0X7tXg2l/eUlMM4dw6XJAQ1AmEOtlwSg7rrMtTvM0A
|
||
|
+BEtI7Km6fC6sU6RtBMdcqD1cH/6dbsfh8muznVA7UlX+PRBHVzdWzj6y8h84dBjo
|
||
|
+gzcbYu9Hezqgj/lLzicqsSZPz9UdXiRTRAIhp8V30BD8uRaaa0KDDnD6IzJv3D9P
|
||
|
+xQWbFM4Z12GN9LyeZqmD7bpKzZmXG/3drvfXVisXaXp3M07t3NlBa3Dt8NFIKZ0D
|
||
|
+FRXBz5bvzxRVmdH6DtkDWXDPOt+Wdm1rZrCOrySFpBZQRpHw12eo1M1lirANIov7
|
||
|
+Z+V1Qh/aBxj5EUu32u9ZpjAPPNtQF6F/KjaoHHHmEQAuj4DLex4LY646Hv1rcv2i
|
||
|
+QFuCdvLKQGSiFBrfZH0j/IX3/0JXQlZzb3MuMFPxLXGAoAV9UP/Sw/WTmAuTzFVm
|
||
|
+G13UYFeMwrToOiqcX2VcK0aC1FCcTP2z4JW3PsWvU8rUDRUYfoXovc7eg4Vn5wHt
|
||
|
+0NBYsNhYiAAf320AUIHzQZYi38JgVwuJfFu43tJZE4Vig++RQq6tsEx9Ftz3EwRR
|
||
|
+fJ9z9mEvEiieZm+vbOvMvIuimFVPSCmLH+bI649K8eZlVRWsx3EXCVb0nQARAQAB
|
||
|
+tDBSZWQgSGF0LCBJbmMuIChiZXRhIGtleSAyKSA8c2VjdXJpdHlAcmVkaGF0LmNv
|
||
|
+bT6JAjYEEwECACAFAkpSM+cCGwMGCwkIBwMCBBUCCAMEFgIDAQIeAQIXgAAKCRCT
|
||
|
+ioDK8hVB6/9tEAC0+KmzeKceXQ/GTUoU6jy9vtkFCFrmv+c7ol4XpdTt0QhqBOwy
|
||
|
+6m2mKWwmm8KfYfy0cADQ4y/EcoXl7FtFBwYmkCuEQGXhTDn9DvVjhooIq59LEMBQ
|
||
|
+OW879RwwzRIZ8ebbjMUjDPF5MfPQqP2LBu9N4KvXlZp4voykwuuaJ+cbsKZR6pZ6
|
||
|
+0RQKPHKP+NgUFC0fff7XY9cuOZZWFAeKRhLN2K7bnRHKxp+kELWb6R9ZfrYwZjWc
|
||
|
+MIPbTd1khE53L4NTfpWfAnJRtkPSDOKEGVlVLtLq4HEAxQt07kbslqISRWyXER3u
|
||
|
+QOJj64D1ZiIMz6t6uZ424VE4ry9rBR0Jz55cMMx5O/ni9x3xzFUgH8Su2yM0r3jE
|
||
|
+Rf24+tbOaPf7tebyx4OKe+JW95hNVstWUDyGbs6K9qGfI/pICuO1nMMFTo6GqzQ6
|
||
|
+DwLZvJ9QdXo7ujEtySZnfu42aycaQ9ZLC2DOCQCUBY350Hx6FLW3O546TAvpTfk0
|
||
|
+B6x+DV7mJQH7MGmRXQsE7TLBJKjq28Cn4tVp04PmybQyTxZdGA/8zY6pPl6xyVMH
|
||
|
+V68hSBKEVT/rlouOHuxfdmZva1DhVvUC6Xj7+iTMTVJUAq/4Uyn31P1OJmA2a0PT
|
||
|
+CAqWkbJSgKFccsjPoTbLyxhuMSNkEZFHvlZrSK9vnPzmfiRH0Orx3wYpMQ==
|
||
|
+=21pb
|
||
|
+-----END PGP PUBLIC KEY BLOCK-----
|
||
|
diff --git a/repos/system_upgrade/common/libraries/dnfplugin.py b/repos/system_upgrade/common/libraries/dnfplugin.py
|
||
|
index 0ef9ea9b..7a15abc4 100644
|
||
|
--- a/repos/system_upgrade/common/libraries/dnfplugin.py
|
||
|
+++ b/repos/system_upgrade/common/libraries/dnfplugin.py
|
||
|
@@ -6,6 +6,7 @@ import shutil
|
||
|
|
||
|
from leapp.exceptions import StopActorExecutionError
|
||
|
from leapp.libraries.common import dnfconfig, guards, mounting, overlaygen, rhsm, utils
|
||
|
+from leapp.libraries.common.config import get_env
|
||
|
from leapp.libraries.common.config.version import get_target_major_version, get_target_version
|
||
|
from leapp.libraries.stdlib import api, CalledProcessError, config
|
||
|
from leapp.models import DNFWorkaround
|
||
|
@@ -74,6 +75,10 @@ def _rebuild_rpm_db(context, root=None):
|
||
|
context.call(cmd)
|
||
|
|
||
|
|
||
|
+def _the_nogpgcheck_option_used():
|
||
|
+ return get_env('LEAPP_NOGPGCHECK', '0') == '1'
|
||
|
+
|
||
|
+
|
||
|
def build_plugin_data(target_repoids, debug, test, tasks, on_aws):
|
||
|
"""
|
||
|
Generates a dictionary with the DNF plugin data.
|
||
|
@@ -93,7 +98,7 @@ def build_plugin_data(target_repoids, debug, test, tasks, on_aws):
|
||
|
'debugsolver': debug,
|
||
|
'disable_repos': True,
|
||
|
'enable_repos': target_repoids,
|
||
|
- 'gpgcheck': False,
|
||
|
+ 'gpgcheck': not _the_nogpgcheck_option_used(),
|
||
|
'platform_id': 'platform:el{}'.format(get_target_major_version()),
|
||
|
'releasever': get_target_version(),
|
||
|
'installroot': '/installroot',
|
||
|
@@ -269,8 +274,10 @@ def install_initramdisk_requirements(packages, target_userspace_info, used_repos
|
||
|
cmd = [
|
||
|
'dnf',
|
||
|
'install',
|
||
|
- '-y',
|
||
|
- '--nogpgcheck',
|
||
|
+ '-y']
|
||
|
+ if _the_nogpgcheck_option_used():
|
||
|
+ cmd.append('--nogpgcheck')
|
||
|
+ cmd += [
|
||
|
'--setopt=module_platform_id=platform:el{}'.format(get_target_major_version()),
|
||
|
'--setopt=keepcache=1',
|
||
|
'--releasever', api.current_actor().configuration.version.target,
|
||
|
diff --git a/repos/system_upgrade/common/libraries/tests/test_dnfplugin.py b/repos/system_upgrade/common/libraries/tests/test_dnfplugin.py
|
||
|
index 3d0b908f..1ca95945 100644
|
||
|
--- a/repos/system_upgrade/common/libraries/tests/test_dnfplugin.py
|
||
|
+++ b/repos/system_upgrade/common/libraries/tests/test_dnfplugin.py
|
||
|
@@ -5,6 +5,8 @@ import pytest
|
||
|
import leapp.models
|
||
|
from leapp.libraries.common import dnfplugin
|
||
|
from leapp.libraries.common.config.version import get_major_version
|
||
|
+from leapp.libraries.common.testutils import CurrentActorMocked
|
||
|
+from leapp.libraries.stdlib import api
|
||
|
from leapp.models.fields import Boolean
|
||
|
from leapp.topics import Topic
|
||
|
|
||
|
@@ -61,7 +63,7 @@ class DATADnfPluginDataDnfConf(leapp.models.Model):
|
||
|
debugsolver = fields.Boolean()
|
||
|
disable_repos = BooleanEnum(choices=[True])
|
||
|
enable_repos = fields.List(fields.StringEnum(choices=TEST_ENABLE_REPOS_CHOICES))
|
||
|
- gpgcheck = BooleanEnum(choices=[False])
|
||
|
+ gpgcheck = fields.Boolean()
|
||
|
platform_id = fields.StringEnum(choices=['platform:el8', 'platform:el9'])
|
||
|
releasever = fields.String()
|
||
|
installroot = fields.StringEnum(choices=['/installroot'])
|
||
|
@@ -94,16 +96,6 @@ del leapp.models.DATADnfPluginDataRHUIAWS
|
||
|
del leapp.models.DATADnfPluginData
|
||
|
|
||
|
|
||
|
-def _mocked_get_target_major_version(version):
|
||
|
- def impl():
|
||
|
- return version
|
||
|
- return impl
|
||
|
-
|
||
|
-
|
||
|
-def _mocked_api_get_file_path(name):
|
||
|
- return 'some/random/file/path/{}'.format(name)
|
||
|
-
|
||
|
-
|
||
|
_CONFIG_BUILD_TEST_DEFINITION = (
|
||
|
# Parameter, Input Data, Expected Fields with data
|
||
|
('debug', False, ('dnf_conf', 'debugsolver'), False),
|
||
|
@@ -131,9 +123,7 @@ def test_build_plugin_data_variations(
|
||
|
expected_value,
|
||
|
):
|
||
|
used_target_major_version = get_major_version(used_target_version)
|
||
|
- monkeypatch.setattr(dnfplugin, 'get_target_version', _mocked_get_target_major_version(used_target_version))
|
||
|
- monkeypatch.setattr(dnfplugin, 'get_target_major_version',
|
||
|
- _mocked_get_target_major_version(used_target_major_version))
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(dst_ver=used_target_version))
|
||
|
inputs = {
|
||
|
'target_repoids': ['BASEOS', 'APPSTREAM'],
|
||
|
'debug': True,
|
||
|
@@ -161,8 +151,7 @@ def test_build_plugin_data_variations(
|
||
|
|
||
|
|
||
|
def test_build_plugin_data(monkeypatch):
|
||
|
- monkeypatch.setattr(dnfplugin, 'get_target_version', _mocked_get_target_major_version('8.4'))
|
||
|
- monkeypatch.setattr(dnfplugin, 'get_target_major_version', _mocked_get_target_major_version('8'))
|
||
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(dst_ver='8.4'))
|
||
|
# Use leapp to validate format and data
|
||
|
created = DATADnfPluginData.create(
|
||
|
dnfplugin.build_plugin_data(
|
||
|
diff --git a/repos/system_upgrade/common/models/targetrepositories.py b/repos/system_upgrade/common/models/targetrepositories.py
|
||
|
index a5a245f1..02c6c5e5 100644
|
||
|
--- a/repos/system_upgrade/common/models/targetrepositories.py
|
||
|
+++ b/repos/system_upgrade/common/models/targetrepositories.py
|
||
|
@@ -22,14 +22,48 @@ class CustomTargetRepository(TargetRepositoryBase):
|
||
|
|
||
|
|
||
|
class TargetRepositories(Model):
|
||
|
+ """
|
||
|
+ Repositories supposed to be used during the IPU process
|
||
|
+
|
||
|
+ The list of the actually used repositories could be just subset
|
||
|
+ of these repositoies. In case of `custom_repositories`, all such repositories
|
||
|
+ must be available otherwise the upgrade is inhibited. But in case of
|
||
|
+ `rhel_repos`, only BaseOS and Appstream repos are required now. If others
|
||
|
+ are missing, upgrade can still continue.
|
||
|
+ """
|
||
|
topic = TransactionTopic
|
||
|
rhel_repos = fields.List(fields.Model(RHELTargetRepository))
|
||
|
+ """
|
||
|
+ Expected target YUM RHEL repositories provided via RHSM
|
||
|
+
|
||
|
+ These repositories are stored inside /etc/yum.repos.d/redhat.repo and
|
||
|
+ are expected to be used based on the provided repositories mapping.
|
||
|
+ """
|
||
|
+
|
||
|
custom_repos = fields.List(fields.Model(CustomTargetRepository), default=[])
|
||
|
+ """
|
||
|
+ Custom YUM repositories required to be used for the IPU
|
||
|
+
|
||
|
+ Usually contains third-party or custom repositories specified by user
|
||
|
+ to be used for the IPU. But can contain also RHEL repositories. Difference
|
||
|
+ is that these repositories are not mapped automatically but are explicitly
|
||
|
+ required by user or by an additional product via actors.
|
||
|
+ """
|
||
|
|
||
|
|
||
|
class UsedTargetRepositories(Model):
|
||
|
+ """
|
||
|
+ Repositories that are used for the IPU process
|
||
|
+
|
||
|
+ This is the source of truth about the repositories used during the upgrade.
|
||
|
+ Once specified, it is used for all actions related to the upgrade rpm
|
||
|
+ transaction itself.
|
||
|
+ """
|
||
|
topic = TransactionTopic
|
||
|
repos = fields.List(fields.Model(UsedTargetRepository))
|
||
|
+ """
|
||
|
+ The list of the used target repositories.
|
||
|
+ """
|
||
|
|
||
|
|
||
|
class CustomTargetRepositoryFile(Model):
|
||
|
--
|
||
|
2.38.1
|
||
|
|