forked from rpms/leapp-repository
75c9028095
- Enable new upgrade path for RHEL 8.10 -> RHEL 9.4 (including RHEL with SAP HANA) - Introduce generic transition of systemd services states during the IPU - Introduce possibility to upgrade with local repositories - Improve possibilities of upgrade when a proxy is configured in DNF configutation file - Fix handling of symlinks under /etc/pki when managing certificates - Fix the upgrade with custom https repositories - Default to the NO_RHSM mode when subscription-manager is not installed - Detect customized configuration of dynamic linker - Drop the invalid `tuv` target channel for the --channel option - Fix the issue of going out of bounds in the isccfg parser - Fix traceback when saving the rhsm facts results and the /etc/rhsm/facts directory doesn’t exist yet - Load all rpm repository substitutions that dnf knows about, not just "releasever" only - Simplify handling of upgrades on systems using RHUI, reducing the maintenance burden for cloud providers - Detect possible unexpected RPM GPG keys has been installed during RPM transaction - Resolves: RHEL-16729
617 lines
29 KiB
Diff
617 lines
29 KiB
Diff
From 7dabc85a0ab5595bd4c7b232c78f14d04eed40fc Mon Sep 17 00:00:00 2001
|
|
From: PeterMocary <petermocary@gmail.com>
|
|
Date: Tue, 22 Aug 2023 17:03:48 +0200
|
|
Subject: [PATCH 38/38] add detection for custom libraries registered by
|
|
ld.so.conf
|
|
|
|
The in-place upgrade process does not support custom libraries
|
|
and also does not handle customized configuration of dynamic linked.
|
|
In such a case it can happen (and it happens) that the upgrade could
|
|
break in critical phases when linked libraries dissapear or are not
|
|
compatible with the new system.
|
|
|
|
We cannot decide whether or not such a custom configuration affects
|
|
the upgrade negatively, so let's detect any customisations
|
|
or unexpected configurations related to dynamic linker and in such
|
|
a case generate a high severity report, informing user about the
|
|
possible impact on the upgrade process.
|
|
|
|
Currently it's detectect:
|
|
* modified default LD configuration: /etc/ld.so.conf
|
|
* drop int configuration files under /etc/ld.so.conf.d/ that are
|
|
not owned by any RHEL RPMs
|
|
* envars: LD_LIBRARY_PATH, LD_PRELOAD
|
|
|
|
Jira ref.: OAMG-4460 / RHEL-11958
|
|
BZ ref.: BZ 1927700
|
|
---
|
|
.../checkdynamiclinkerconfiguration/actor.py | 22 +++
|
|
.../checkdynamiclinkerconfiguration.py | 79 ++++++++
|
|
.../test_checkdynamiclinkerconfiguration.py | 65 +++++++
|
|
.../scandynamiclinkerconfiguration/actor.py | 23 +++
|
|
.../scandynamiclinkerconfiguration.py | 117 +++++++++++
|
|
.../test_scandynamiclinkerconfiguration.py | 181 ++++++++++++++++++
|
|
.../common/models/dynamiclinker.py | 41 ++++
|
|
7 files changed, 528 insertions(+)
|
|
create mode 100644 repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/actor.py
|
|
create mode 100644 repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/libraries/checkdynamiclinkerconfiguration.py
|
|
create mode 100644 repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/tests/test_checkdynamiclinkerconfiguration.py
|
|
create mode 100644 repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/actor.py
|
|
create mode 100644 repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/libraries/scandynamiclinkerconfiguration.py
|
|
create mode 100644 repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/tests/test_scandynamiclinkerconfiguration.py
|
|
create mode 100644 repos/system_upgrade/common/models/dynamiclinker.py
|
|
|
|
diff --git a/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/actor.py b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/actor.py
|
|
new file mode 100644
|
|
index 00000000..6671eef4
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/actor.py
|
|
@@ -0,0 +1,22 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor.checkdynamiclinkerconfiguration import check_dynamic_linker_configuration
|
|
+from leapp.models import DynamicLinkerConfiguration, Report
|
|
+from leapp.tags import ChecksPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class CheckDynamicLinkerConfiguration(Actor):
|
|
+ """
|
|
+ Check for customization of dynamic linker configuration.
|
|
+
|
|
+ The in-place upgrade could potentionally be impacted in a negative way due
|
|
+ to the customization of dynamic linker configuration by user. This actor creates high
|
|
+ severity report upon detecting such customization.
|
|
+ """
|
|
+
|
|
+ name = 'check_dynamic_linker_configuration'
|
|
+ consumes = (DynamicLinkerConfiguration,)
|
|
+ produces = (Report,)
|
|
+ tags = (ChecksPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ check_dynamic_linker_configuration()
|
|
diff --git a/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/libraries/checkdynamiclinkerconfiguration.py b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/libraries/checkdynamiclinkerconfiguration.py
|
|
new file mode 100644
|
|
index 00000000..9ead892e
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/libraries/checkdynamiclinkerconfiguration.py
|
|
@@ -0,0 +1,79 @@
|
|
+from leapp import reporting
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import DynamicLinkerConfiguration
|
|
+
|
|
+LD_SO_CONF_DIR = '/etc/ld.so.conf.d'
|
|
+LD_SO_CONF_MAIN = '/etc/ld.so.conf'
|
|
+LD_LIBRARY_PATH_VAR = 'LD_LIBRARY_PATH'
|
|
+LD_PRELOAD_VAR = 'LD_PRELOAD'
|
|
+FMT_LIST_SEPARATOR_1 = '\n- '
|
|
+FMT_LIST_SEPARATOR_2 = '\n - '
|
|
+
|
|
+
|
|
+def _report_custom_dynamic_linker_configuration(summary):
|
|
+ reporting.create_report([
|
|
+ reporting.Title(
|
|
+ 'Detected customized configuration for dynamic linker.'
|
|
+ ),
|
|
+ reporting.Summary(summary),
|
|
+ reporting.Remediation(hint=('Remove or revert the custom dynamic linker configurations and apply the changes '
|
|
+ 'using the ldconfig command. In case of possible active software collections we '
|
|
+ 'suggest disabling them persistently.')),
|
|
+ reporting.RelatedResource('file', '/etc/ld.so.conf'),
|
|
+ reporting.RelatedResource('directory', '/etc/ld.so.conf.d'),
|
|
+ reporting.Severity(reporting.Severity.HIGH),
|
|
+ reporting.Groups([reporting.Groups.OS_FACTS]),
|
|
+ ])
|
|
+
|
|
+
|
|
+def check_dynamic_linker_configuration():
|
|
+ configuration = next(api.consume(DynamicLinkerConfiguration), None)
|
|
+ if not configuration:
|
|
+ return
|
|
+
|
|
+ custom_configurations = ''
|
|
+ if configuration.main_config.modified:
|
|
+ custom_configurations += (
|
|
+ '{}The {} file has unexpected contents:{}{}'
|
|
+ .format(FMT_LIST_SEPARATOR_1, LD_SO_CONF_MAIN,
|
|
+ FMT_LIST_SEPARATOR_2, FMT_LIST_SEPARATOR_2.join(configuration.main_config.modified_lines))
|
|
+ )
|
|
+
|
|
+ custom_configs = []
|
|
+ for config in configuration.included_configs:
|
|
+ if config.modified:
|
|
+ custom_configs.append(config.path)
|
|
+
|
|
+ if custom_configs:
|
|
+ custom_configurations += (
|
|
+ '{}The following drop in config files were marked as custom:{}{}'
|
|
+ .format(FMT_LIST_SEPARATOR_1, FMT_LIST_SEPARATOR_2, FMT_LIST_SEPARATOR_2.join(custom_configs))
|
|
+ )
|
|
+
|
|
+ if configuration.used_variables:
|
|
+ custom_configurations += (
|
|
+ '{}The following variables contain unexpected dynamic linker configuration:{}{}'
|
|
+ .format(FMT_LIST_SEPARATOR_1, FMT_LIST_SEPARATOR_2,
|
|
+ FMT_LIST_SEPARATOR_2.join(configuration.used_variables))
|
|
+ )
|
|
+
|
|
+ if custom_configurations:
|
|
+ summary = (
|
|
+ 'Custom configurations to the dynamic linker could potentially impact '
|
|
+ 'the upgrade in a negative way. The custom configuration includes '
|
|
+ 'modifications to {main_conf}, custom or modified drop in config '
|
|
+ 'files in the {conf_dir} directory and additional entries in the '
|
|
+ '{ldlib_envar} or {ldpre_envar} variables. These modifications '
|
|
+ 'configure the dynamic linker to use different libraries that might '
|
|
+ 'not be provided by Red Hat products or might not be present during '
|
|
+ 'the whole upgrade process. The following custom configurations '
|
|
+ 'were detected by leapp:{cust_configs}'
|
|
+ .format(
|
|
+ main_conf=LD_SO_CONF_MAIN,
|
|
+ conf_dir=LD_SO_CONF_DIR,
|
|
+ ldlib_envar=LD_LIBRARY_PATH_VAR,
|
|
+ ldpre_envar=LD_PRELOAD_VAR,
|
|
+ cust_configs=custom_configurations
|
|
+ )
|
|
+ )
|
|
+ _report_custom_dynamic_linker_configuration(summary)
|
|
diff --git a/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/tests/test_checkdynamiclinkerconfiguration.py b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/tests/test_checkdynamiclinkerconfiguration.py
|
|
new file mode 100644
|
|
index 00000000..d640f0c5
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/checkdynamiclinkerconfiguration/tests/test_checkdynamiclinkerconfiguration.py
|
|
@@ -0,0 +1,65 @@
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor.checkdynamiclinkerconfiguration import (
|
|
+ check_dynamic_linker_configuration,
|
|
+ LD_LIBRARY_PATH_VAR,
|
|
+ LD_PRELOAD_VAR
|
|
+)
|
|
+from leapp.libraries.common.testutils import create_report_mocked, CurrentActorMocked
|
|
+from leapp.libraries.stdlib import api
|
|
+from leapp.models import DynamicLinkerConfiguration, LDConfigFile, MainLDConfigFile
|
|
+
|
|
+INCLUDED_CONFIG_PATHS = ['/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/mariadb-x86_64.conf',
|
|
+ '/custom/path/custom1.conf']
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('included_configs_modifications', 'used_variables', 'modified_lines'),
|
|
+ [
|
|
+ ([False, False, False], [], []),
|
|
+ ([True, True, True], [], []),
|
|
+ ([False, False, False], [LD_LIBRARY_PATH_VAR], []),
|
|
+ ([False, False, False], [], ['modified line 1', 'midified line 2']),
|
|
+ ([True, False, True], [LD_LIBRARY_PATH_VAR, LD_PRELOAD_VAR], ['modified line']),
|
|
+ ])
|
|
+def test_check_ld_so_configuration(monkeypatch, included_configs_modifications, used_variables, modified_lines):
|
|
+ assert len(INCLUDED_CONFIG_PATHS) == len(included_configs_modifications)
|
|
+
|
|
+ main_config = MainLDConfigFile(path="/etc/ld.so.conf", modified=any(modified_lines), modified_lines=modified_lines)
|
|
+ included_configs = []
|
|
+ for path, modified in zip(INCLUDED_CONFIG_PATHS, included_configs_modifications):
|
|
+ included_configs.append(LDConfigFile(path=path, modified=modified))
|
|
+
|
|
+ configuration = DynamicLinkerConfiguration(main_config=main_config,
|
|
+ included_configs=included_configs,
|
|
+ used_variables=used_variables)
|
|
+
|
|
+ monkeypatch.setattr(api, 'current_actor', CurrentActorMocked(msgs=[configuration]))
|
|
+ monkeypatch.setattr(reporting, 'create_report', create_report_mocked())
|
|
+
|
|
+ check_dynamic_linker_configuration()
|
|
+
|
|
+ report_expected = any(included_configs_modifications) or modified_lines or used_variables
|
|
+ if not report_expected:
|
|
+ assert reporting.create_report.called == 0
|
|
+ return
|
|
+
|
|
+ assert reporting.create_report.called == 1
|
|
+ assert 'configuration for dynamic linker' in reporting.create_report.reports[0]['title']
|
|
+ summary = reporting.create_report.reports[0]['summary']
|
|
+
|
|
+ if any(included_configs_modifications):
|
|
+ assert 'The following drop in config files were marked as custom:' in summary
|
|
+ for config, modified in zip(INCLUDED_CONFIG_PATHS, included_configs_modifications):
|
|
+ assert modified == (config in summary)
|
|
+
|
|
+ if modified_lines:
|
|
+ assert 'The /etc/ld.so.conf file has unexpected contents' in summary
|
|
+ for line in modified_lines:
|
|
+ assert line in summary
|
|
+
|
|
+ if used_variables:
|
|
+ assert 'The following variables contain unexpected dynamic linker configuration:' in summary
|
|
+ for var in used_variables:
|
|
+ assert '- {}'.format(var) in summary
|
|
diff --git a/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/actor.py b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/actor.py
|
|
new file mode 100644
|
|
index 00000000..11283cd0
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/actor.py
|
|
@@ -0,0 +1,23 @@
|
|
+from leapp.actors import Actor
|
|
+from leapp.libraries.actor.scandynamiclinkerconfiguration import scan_dynamic_linker_configuration
|
|
+from leapp.models import DynamicLinkerConfiguration, InstalledRedHatSignedRPM
|
|
+from leapp.tags import FactsPhaseTag, IPUWorkflowTag
|
|
+
|
|
+
|
|
+class ScanDynamicLinkerConfiguration(Actor):
|
|
+ """
|
|
+ Scan the dynamic linker configuration and find modifications.
|
|
+
|
|
+ The dynamic linker configuration files can be used to replace standard libraries
|
|
+ with different custom libraries. The in-place upgrade does not support customization
|
|
+ of this configuration by user. This actor produces information about detected
|
|
+ modifications.
|
|
+ """
|
|
+
|
|
+ name = 'scan_dynamic_linker_configuration'
|
|
+ consumes = (InstalledRedHatSignedRPM,)
|
|
+ produces = (DynamicLinkerConfiguration,)
|
|
+ tags = (FactsPhaseTag, IPUWorkflowTag)
|
|
+
|
|
+ def process(self):
|
|
+ scan_dynamic_linker_configuration()
|
|
diff --git a/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/libraries/scandynamiclinkerconfiguration.py b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/libraries/scandynamiclinkerconfiguration.py
|
|
new file mode 100644
|
|
index 00000000..1a6ab6a2
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/libraries/scandynamiclinkerconfiguration.py
|
|
@@ -0,0 +1,117 @@
|
|
+import glob
|
|
+import os
|
|
+
|
|
+from leapp.libraries.common.rpms import has_package
|
|
+from leapp.libraries.stdlib import api, CalledProcessError, run
|
|
+from leapp.models import DynamicLinkerConfiguration, InstalledRedHatSignedRPM, LDConfigFile, MainLDConfigFile
|
|
+
|
|
+LD_SO_CONF_DIR = '/etc/ld.so.conf.d'
|
|
+LD_SO_CONF_MAIN = '/etc/ld.so.conf'
|
|
+LD_SO_CONF_DEFAULT_INCLUDE = 'ld.so.conf.d/*.conf'
|
|
+LD_SO_CONF_COMMENT_PREFIX = '#'
|
|
+LD_LIBRARY_PATH_VAR = 'LD_LIBRARY_PATH'
|
|
+LD_PRELOAD_VAR = 'LD_PRELOAD'
|
|
+
|
|
+
|
|
+def _read_file(file_path):
|
|
+ with open(file_path, 'r') as fd:
|
|
+ return fd.readlines()
|
|
+
|
|
+
|
|
+def _is_modified(config_path):
|
|
+ """ Decide if the configuration file was modified based on the package it belongs to. """
|
|
+ result = run(['rpm', '-Vf', config_path], checked=False)
|
|
+ if not result['exit_code']:
|
|
+ return False
|
|
+ modification_flags = result['stdout'].split(' ', 1)[0]
|
|
+ # The file is considered modified only when the checksum does not match
|
|
+ return '5' in modification_flags
|
|
+
|
|
+
|
|
+def _is_included_config_custom(config_path):
|
|
+ if not os.path.isfile(config_path):
|
|
+ return False
|
|
+
|
|
+ # Check if the config file has any lines that have an effect on dynamic linker configuration
|
|
+ has_effective_line = False
|
|
+ for line in _read_file(config_path):
|
|
+ line = line.strip()
|
|
+ if line and not line.startswith(LD_SO_CONF_COMMENT_PREFIX):
|
|
+ has_effective_line = True
|
|
+ break
|
|
+
|
|
+ if not has_effective_line:
|
|
+ return False
|
|
+
|
|
+ is_custom = False
|
|
+ try:
|
|
+ package_name = run(['rpm', '-qf', '--queryformat', '%{NAME}', config_path])['stdout']
|
|
+ is_custom = not has_package(InstalledRedHatSignedRPM, package_name) or _is_modified(config_path)
|
|
+ except CalledProcessError:
|
|
+ is_custom = True
|
|
+
|
|
+ return is_custom
|
|
+
|
|
+
|
|
+def _parse_main_config():
|
|
+ """
|
|
+ Extracts included configs from the main dynamic linker configuration file (/etc/ld.so.conf)
|
|
+ along with lines that are likely custom. The lines considered custom are simply those that are
|
|
+ not includes.
|
|
+
|
|
+ :returns: tuple containing all the included files and lines considered custom
|
|
+ :rtype: tuple(list, list)
|
|
+ """
|
|
+ config = _read_file(LD_SO_CONF_MAIN)
|
|
+
|
|
+ included_configs = []
|
|
+ other_lines = []
|
|
+ for line in config:
|
|
+ line = line.strip()
|
|
+ if line.startswith('include'):
|
|
+ cfg_glob = line.split(' ', 1)[1].strip()
|
|
+ cfg_glob = os.path.join('/etc', cfg_glob) if not os.path.isabs(cfg_glob) else cfg_glob
|
|
+ included_configs.append(cfg_glob)
|
|
+ elif line and not line.startswith(LD_SO_CONF_COMMENT_PREFIX):
|
|
+ other_lines.append(line)
|
|
+
|
|
+ return included_configs, other_lines
|
|
+
|
|
+
|
|
+def scan_dynamic_linker_configuration():
|
|
+ included_configs, other_lines = _parse_main_config()
|
|
+
|
|
+ is_default_include_present = '/etc/' + LD_SO_CONF_DEFAULT_INCLUDE in included_configs
|
|
+ if not is_default_include_present:
|
|
+ api.current_logger().debug('The default include "{}" is not present in '
|
|
+ 'the {} file.'.format(LD_SO_CONF_DEFAULT_INCLUDE, LD_SO_CONF_MAIN))
|
|
+
|
|
+ if is_default_include_present and len(included_configs) != 1:
|
|
+ # The additional included configs will most likely be created manually by the user
|
|
+ # and therefore will get flagged as custom in the next part of this function
|
|
+ api.current_logger().debug('The default include "{}" is not the only include in '
|
|
+ 'the {} file.'.format(LD_SO_CONF_DEFAULT_INCLUDE, LD_SO_CONF_MAIN))
|
|
+
|
|
+ main_config_file = MainLDConfigFile(path=LD_SO_CONF_MAIN, modified=any(other_lines), modified_lines=other_lines)
|
|
+
|
|
+ # Expand the config paths from globs and ensure uniqueness of resulting paths
|
|
+ config_paths = set()
|
|
+ for cfg_glob in included_configs:
|
|
+ for cfg in glob.glob(cfg_glob):
|
|
+ config_paths.add(cfg)
|
|
+
|
|
+ included_config_files = []
|
|
+ for config_path in config_paths:
|
|
+ config_file = LDConfigFile(path=config_path, modified=_is_included_config_custom(config_path))
|
|
+ included_config_files.append(config_file)
|
|
+
|
|
+ # Check if dynamic linker variables used for specifying custom libraries are set
|
|
+ variables = [LD_LIBRARY_PATH_VAR, LD_PRELOAD_VAR]
|
|
+ used_variables = [var for var in variables if os.getenv(var, None)]
|
|
+
|
|
+ configuration = DynamicLinkerConfiguration(main_config=main_config_file,
|
|
+ included_configs=included_config_files,
|
|
+ used_variables=used_variables)
|
|
+
|
|
+ if other_lines or any([config.modified for config in included_config_files]) or used_variables:
|
|
+ api.produce(configuration)
|
|
diff --git a/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/tests/test_scandynamiclinkerconfiguration.py b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/tests/test_scandynamiclinkerconfiguration.py
|
|
new file mode 100644
|
|
index 00000000..21144951
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/actors/scandynamiclinkerconfiguration/tests/test_scandynamiclinkerconfiguration.py
|
|
@@ -0,0 +1,181 @@
|
|
+import glob
|
|
+import os
|
|
+
|
|
+import pytest
|
|
+
|
|
+from leapp import reporting
|
|
+from leapp.libraries.actor import scandynamiclinkerconfiguration
|
|
+from leapp.libraries.common.testutils import produce_mocked
|
|
+from leapp.libraries.stdlib import api, CalledProcessError
|
|
+from leapp.models import InstalledRedHatSignedRPM
|
|
+
|
|
+INCLUDED_CONFIGS_GLOB_DICT_1 = {'/etc/ld.so.conf.d/*.conf': ['/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/mariadb-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/bind-export-x86_64.conf']}
|
|
+
|
|
+INCLUDED_CONFIGS_GLOB_DICT_2 = {'/etc/ld.so.conf.d/*.conf': ['/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/mariadb-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/bind-export-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/custom1.conf',
|
|
+ '/etc/ld.so.conf.d/custom2.conf']}
|
|
+
|
|
+INCLUDED_CONFIGS_GLOB_DICT_3 = {'/etc/ld.so.conf.d/*.conf': ['/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/custom1.conf',
|
|
+ '/etc/ld.so.conf.d/mariadb-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/bind-export-x86_64.conf',
|
|
+ '/etc/ld.so.conf.d/custom2.conf'],
|
|
+ '/custom/path/*.conf': ['/custom/path/custom1.conf',
|
|
+ '/custom/path/custom2.conf']}
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('included_configs_glob_dict', 'other_lines', 'custom_configs', 'used_variables'),
|
|
+ [
|
|
+ (INCLUDED_CONFIGS_GLOB_DICT_1, [], [], []),
|
|
+ (INCLUDED_CONFIGS_GLOB_DICT_1, ['/custom/path.lib'], [], []),
|
|
+ (INCLUDED_CONFIGS_GLOB_DICT_1, [], [], ['LD_LIBRARY_PATH']),
|
|
+ (INCLUDED_CONFIGS_GLOB_DICT_2, [], ['/etc/ld.so.conf.d/custom1.conf',
|
|
+ '/etc/ld.so.conf.d/custom2.conf'], []),
|
|
+ (INCLUDED_CONFIGS_GLOB_DICT_3, ['/custom/path.lib'], ['/etc/ld.so.conf.d/custom1.conf',
|
|
+ '/etc/ld.so.conf.d/custom2.conf'
|
|
+ '/custom/path/custom1.conf',
|
|
+ '/custom/path/custom2.conf'], []),
|
|
+ ])
|
|
+def test_scan_dynamic_linker_configuration(monkeypatch, included_configs_glob_dict, other_lines,
|
|
+ custom_configs, used_variables):
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, '_parse_main_config',
|
|
+ lambda: (included_configs_glob_dict.keys(), other_lines))
|
|
+ monkeypatch.setattr(glob, 'glob', lambda glob: included_configs_glob_dict[glob])
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, '_is_included_config_custom',
|
|
+ lambda config: config in custom_configs)
|
|
+ monkeypatch.setattr(api, 'produce', produce_mocked())
|
|
+
|
|
+ for var in used_variables:
|
|
+ monkeypatch.setenv(var, '/some/path')
|
|
+
|
|
+ scandynamiclinkerconfiguration.scan_dynamic_linker_configuration()
|
|
+
|
|
+ produce_expected = custom_configs or other_lines or used_variables
|
|
+ if not produce_expected:
|
|
+ assert not api.produce.called
|
|
+ return
|
|
+
|
|
+ assert api.produce.called == 1
|
|
+
|
|
+ configuration = api.produce.model_instances[0]
|
|
+
|
|
+ all_configs = []
|
|
+ for configs in included_configs_glob_dict.values():
|
|
+ all_configs += configs
|
|
+
|
|
+ assert len(all_configs) == len(configuration.included_configs)
|
|
+ for config in configuration.included_configs:
|
|
+ if config.path in custom_configs:
|
|
+ assert config.modified
|
|
+
|
|
+ assert configuration.main_config.path == scandynamiclinkerconfiguration.LD_SO_CONF_MAIN
|
|
+ if other_lines:
|
|
+ assert configuration.main_config.modified
|
|
+ assert configuration.main_config.modified_lines == other_lines
|
|
+
|
|
+ if used_variables:
|
|
+ assert configuration.used_variables == used_variables
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('config_contents', 'included_config_paths', 'other_lines'),
|
|
+ [
|
|
+ (['include ld.so.conf.d/*.conf\n'],
|
|
+ ['/etc/ld.so.conf.d/*.conf'], []),
|
|
+ (['include ld.so.conf.d/*.conf\n', '\n', '/custom/path.lib\n', '#comment'],
|
|
+ ['/etc/ld.so.conf.d/*.conf'], ['/custom/path.lib']),
|
|
+ (['include ld.so.conf.d/*.conf\n', 'include /custom/path.conf\n'],
|
|
+ ['/etc/ld.so.conf.d/*.conf', '/custom/path.conf'], []),
|
|
+ (['include ld.so.conf.d/*.conf\n', '#include /custom/path.conf\n', '#/custom/path.conf\n'],
|
|
+ ['/etc/ld.so.conf.d/*.conf'], []),
|
|
+ ([' \n'],
|
|
+ [], [])
|
|
+ ])
|
|
+def test_parse_main_config(monkeypatch, config_contents, included_config_paths, other_lines):
|
|
+ def mocked_read_file(path):
|
|
+ assert path == scandynamiclinkerconfiguration.LD_SO_CONF_MAIN
|
|
+ return config_contents
|
|
+
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, '_read_file', mocked_read_file)
|
|
+
|
|
+ _included_config_paths, _other_lines = scandynamiclinkerconfiguration._parse_main_config()
|
|
+
|
|
+ assert _included_config_paths == included_config_paths
|
|
+ assert _other_lines == other_lines
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('config_path', 'run_result', 'is_modified'),
|
|
+ [
|
|
+ ('/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ '.......T. c /etc/ld.so.conf.d/dyninst-x86_64.conf', False),
|
|
+ ('/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ 'S.5....T. c /etc/ld.so.conf.d/dyninst-x86_64.conf', True),
|
|
+ ('/etc/ld.so.conf.d/kernel-3.10.0-1160.el7.x86_64.conf',
|
|
+ '', False)
|
|
+ ])
|
|
+def test_is_modified(monkeypatch, config_path, run_result, is_modified):
|
|
+ def mocked_run(command, checked):
|
|
+ assert config_path in command
|
|
+ assert checked is False
|
|
+ exit_code = 1 if run_result else 0
|
|
+ return {'stdout': run_result, 'exit_code': exit_code}
|
|
+
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, 'run', mocked_run)
|
|
+
|
|
+ _is_modified = scandynamiclinkerconfiguration._is_modified(config_path)
|
|
+ assert _is_modified == is_modified
|
|
+
|
|
+
|
|
+@pytest.mark.parametrize(('config_path',
|
|
+ 'config_contents', 'run_result',
|
|
+ 'is_installed_rh_signed_package', 'is_modified', 'has_effective_lines'),
|
|
+ [
|
|
+ ('/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ ['/usr/lib64/dyninst\n'], 'dyninst',
|
|
+ True, False, True), # RH sighend package without modification - Not custom
|
|
+ ('/etc/ld.so.conf.d/dyninst-x86_64.conf',
|
|
+ ['/usr/lib64/my_dyninst\n'], 'dyninst',
|
|
+ True, True, True), # Was modified by user - Custom
|
|
+ ('/etc/custom/custom.conf',
|
|
+ ['/usr/lib64/custom'], 'custom',
|
|
+ False, None, True), # Third-party package - Custom
|
|
+ ('/etc/custom/custom.conf',
|
|
+ ['#/usr/lib64/custom\n'], 'custom',
|
|
+ False, None, False), # Third-party package without effective lines - Not custom
|
|
+ ('/etc/ld.so.conf.d/somelib.conf',
|
|
+ ['/usr/lib64/somelib\n'], CalledProcessError,
|
|
+ None, None, True), # User created configuration file - Custom
|
|
+ ('/etc/ld.so.conf.d/somelib.conf',
|
|
+ ['#/usr/lib64/somelib\n'], CalledProcessError,
|
|
+ None, None, False) # User created configuration file without effective lines - Not custom
|
|
+ ])
|
|
+def test_is_included_config_custom(monkeypatch, config_path, config_contents, run_result,
|
|
+ is_installed_rh_signed_package, is_modified, has_effective_lines):
|
|
+ def mocked_run(command):
|
|
+ assert config_path in command
|
|
+ if run_result and not isinstance(run_result, str):
|
|
+ raise CalledProcessError("message", command, "result")
|
|
+ return {'stdout': run_result}
|
|
+
|
|
+ def mocked_has_package(model, package_name):
|
|
+ assert model is InstalledRedHatSignedRPM
|
|
+ assert package_name == run_result
|
|
+ return is_installed_rh_signed_package
|
|
+
|
|
+ def mocked_read_file(path):
|
|
+ assert path == config_path
|
|
+ return config_contents
|
|
+
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, 'run', mocked_run)
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, 'has_package', mocked_has_package)
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, '_read_file', mocked_read_file)
|
|
+ monkeypatch.setattr(scandynamiclinkerconfiguration, '_is_modified', lambda *_: is_modified)
|
|
+ monkeypatch.setattr(os.path, 'isfile', lambda _: True)
|
|
+
|
|
+ result = scandynamiclinkerconfiguration._is_included_config_custom(config_path)
|
|
+ is_custom = not isinstance(run_result, str) or not is_installed_rh_signed_package or is_modified
|
|
+ is_custom &= has_effective_lines
|
|
+ assert result == is_custom
|
|
diff --git a/repos/system_upgrade/common/models/dynamiclinker.py b/repos/system_upgrade/common/models/dynamiclinker.py
|
|
new file mode 100644
|
|
index 00000000..4dc107f4
|
|
--- /dev/null
|
|
+++ b/repos/system_upgrade/common/models/dynamiclinker.py
|
|
@@ -0,0 +1,41 @@
|
|
+from leapp.models import fields, Model
|
|
+from leapp.topics import SystemFactsTopic
|
|
+
|
|
+
|
|
+class LDConfigFile(Model):
|
|
+ """
|
|
+ Represents a config file related to dynamic linker configuration
|
|
+ """
|
|
+ topic = SystemFactsTopic
|
|
+
|
|
+ path = fields.String()
|
|
+ """ Absolute path to the configuration file """
|
|
+
|
|
+ modified = fields.Boolean()
|
|
+ """ If True the file is considered custom and will trigger a report """
|
|
+
|
|
+
|
|
+class MainLDConfigFile(LDConfigFile):
|
|
+ """
|
|
+ Represents the main configuration file of the dynamic linker /etc/ld.so.conf
|
|
+ """
|
|
+ topic = SystemFactsTopic
|
|
+
|
|
+ modified_lines = fields.List(fields.String(), default=[])
|
|
+ """ Lines that are considered custom, generally those that are not includes of other configs """
|
|
+
|
|
+
|
|
+class DynamicLinkerConfiguration(Model):
|
|
+ """
|
|
+ Facts about configuration of dynamic linker
|
|
+ """
|
|
+ topic = SystemFactsTopic
|
|
+
|
|
+ main_config = fields.Model(MainLDConfigFile)
|
|
+ """ The main configuration file of dynamic linker (/etc/ld.so.conf) """
|
|
+
|
|
+ included_configs = fields.List(fields.Model(LDConfigFile))
|
|
+ """ All the configs that are included by the main configuration file """
|
|
+
|
|
+ used_variables = fields.List(fields.String(), default=[])
|
|
+ """ Environment variables that are currently used to modify dynamic linker configuration """
|
|
--
|
|
2.41.0
|
|
|