From 876e93f233c41aa6c1742ed874ac167f0ddc4dbb Mon Sep 17 00:00:00 2001 From: PeterMocary Date: Fri, 24 Jun 2022 15:23:30 +0200 Subject: [PATCH 15/32] Scanpkgmanager: detect proxy configuration This new information enables targetuserspacecreator actor to inform user why the package installation might have failed --- .../libraries/scanpkgmanager.py | 53 ++++++++++++++++++- .../tests/test_scanpkgmanager.py | 49 +++++++++++++++++ .../actors/targetuserspacecreator/actor.py | 4 ++ .../libraries/userspacegen.py | 24 +++++++-- .../common/models/packagemanagerinfo.py | 5 ++ .../common/models/repositoriesfacts.py | 1 + 6 files changed, 131 insertions(+), 5 deletions(-) diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py index 6f6a79d2..7c97fb1a 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/libraries/scanpkgmanager.py @@ -1,9 +1,13 @@ import os +import re from leapp.libraries.common.config.version import get_source_major_version from leapp.libraries.stdlib import api from leapp.models import PkgManagerInfo +YUM_CONFIG_PATH = '/etc/yum.conf' +DNF_CONFIG_PATH = '/etc/dnf/dnf.conf' + def _get_releasever_path(): default_manager = 'yum' if get_source_major_version() == '7' else 'dnf' @@ -28,5 +32,52 @@ def get_etc_releasever(): return releasever +def _get_config_contents(config_path): + if os.path.isfile(config_path): + with open(config_path, 'r') as config: + return config.read() + return '' + + +def _get_proxy_if_set(manager_config_path): + """ + Get proxy address from specified package manager config. + + :param manager_config_path: path to a package manager config + :returns: proxy address or None when not set + :rtype: String + """ + + config = _get_config_contents(manager_config_path) + + for line in config.split('\n'): + if re.match('^proxy[ \t]*=', line): + proxy_address = line.split('=', 1)[1] + return proxy_address.strip() + + return None + + +def get_configured_proxies(): + """ + Get a list of proxies used in dnf and yum configuration files. + + :returns: sorted list of unique proxies + :rtype: List + """ + + configured_proxies = set() + for config_path in (DNF_CONFIG_PATH, YUM_CONFIG_PATH): + proxy = _get_proxy_if_set(config_path) + if proxy: + configured_proxies.add(proxy) + + return sorted(configured_proxies) + + def process(): - api.produce(PkgManagerInfo(etc_releasever=get_etc_releasever())) + pkg_manager_info = PkgManagerInfo() + pkg_manager_info.etc_releasever = get_etc_releasever() + pkg_manager_info.configured_proxies = get_configured_proxies() + + api.produce(pkg_manager_info) diff --git a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py index 3be6fa2f..e78b532f 100644 --- a/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py +++ b/repos/system_upgrade/common/actors/scanpkgmanager/tests/test_scanpkgmanager.py @@ -9,6 +9,9 @@ from leapp.libraries.common.testutils import CurrentActorMocked, produce_mocked from leapp.libraries.stdlib import api CUR_DIR = os.path.dirname(os.path.abspath(__file__)) +PROXY_ADDRESS = 'https://192.168.121.123:3128' +YUM_CONFIG_PATH = '/etc/yum.conf' +DNF_CONFIG_PATH = '/etc/dnf/dnf.conf' def mock_releasever_exists(overrides): @@ -36,6 +39,8 @@ def test_get_etcreleasever(monkeypatch, etcrelease_exists): monkeypatch.setattr(scanpkgmanager.api, 'produce', produce_mocked()) monkeypatch.setattr(scanpkgmanager.api, 'current_actor', CurrentActorMocked()) monkeypatch.setattr(scanpkgmanager, '_get_releasever_path', mocked_get_releasever_path) + monkeypatch.setattr(scanpkgmanager, '_get_proxy_if_set', lambda x: None) + monkeypatch.setattr(pluginscanner, 'scan_enabled_package_manager_plugins', lambda: []) scanpkgmanager.process() @@ -44,3 +49,47 @@ def test_get_etcreleasever(monkeypatch, etcrelease_exists): assert api.produce.model_instances[0].etc_releasever else: assert not api.produce.model_instances[0].etc_releasever + + +@pytest.mark.parametrize('proxy_set', [True, False]) +def test_get_proxy_if_set(monkeypatch, proxy_set): + + config_path = '/path/to/config.conf' + config_contents = '[main]\n' + if proxy_set: + config_contents += 'proxy = \t{} '.format(PROXY_ADDRESS) + + def mocked_get_config_contents(path): + assert path == config_path + return config_contents + + monkeypatch.setattr(scanpkgmanager, '_get_config_contents', mocked_get_config_contents) + + proxy = scanpkgmanager._get_proxy_if_set(config_path) + + if proxy_set: + assert proxy == PROXY_ADDRESS + + assert proxy_set == bool(proxy) + + +@pytest.mark.parametrize( + ('proxy_set_in_dnf_config', 'proxy_set_in_yum_config', 'expected_output'), + [ + (True, True, [PROXY_ADDRESS]), + (True, False, [PROXY_ADDRESS]), + (False, False, []) + ] +) +def test_get_configured_proxies(monkeypatch, proxy_set_in_dnf_config, proxy_set_in_yum_config, expected_output): + + def mocked_get_proxy_if_set(path): + proxy = PROXY_ADDRESS if proxy_set_in_yum_config else None + if path == DNF_CONFIG_PATH: + proxy = PROXY_ADDRESS if proxy_set_in_dnf_config else None + return proxy + + monkeypatch.setattr(scanpkgmanager, '_get_proxy_if_set', mocked_get_proxy_if_set) + + configured_proxies = scanpkgmanager.get_configured_proxies() + assert configured_proxies == expected_output diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/actor.py b/repos/system_upgrade/common/actors/targetuserspacecreator/actor.py index 7e5c7db7..04fb2e8b 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/actor.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/actor.py @@ -5,7 +5,9 @@ from leapp.models import RequiredTargetUserspacePackages # deprecated from leapp.models import TMPTargetRepositoriesFacts # deprecated all the time from leapp.models import ( CustomTargetRepositoryFile, + PkgManagerInfo, Report, + RepositoriesFacts, RepositoriesMapping, RHSMInfo, RHUIInfo, @@ -36,12 +38,14 @@ class TargetUserspaceCreator(Actor): CustomTargetRepositoryFile, RHSMInfo, RHUIInfo, + RepositoriesFacts, RepositoriesMapping, RequiredTargetUserspacePackages, StorageInfo, TargetRepositories, TargetUserSpacePreupgradeTasks, XFSPresence, + PkgManagerInfo, ) produces = (TargetUserSpaceInfo, UsedTargetRepositories, Report, TMPTargetRepositoriesFacts,) tags = (IPUWorkflowTag, TargetTransactionFactsPhaseTag) diff --git a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py index c39af66f..00acacd9 100644 --- a/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py +++ b/repos/system_upgrade/common/actors/targetuserspacecreator/libraries/userspacegen.py @@ -12,6 +12,8 @@ from leapp.models import RequiredTargetUserspacePackages # deprecated from leapp.models import TMPTargetRepositoriesFacts # deprecated from leapp.models import ( CustomTargetRepositoryFile, + PkgManagerInfo, + RepositoriesFacts, RHSMInfo, RHUIInfo, StorageInfo, @@ -166,10 +168,24 @@ def prepare_target_userspace(context, userspace_dir, enabled_repos, packages): try: context.call(cmd, callback_raw=utils.logging_handler) except CalledProcessError as exc: - raise StopActorExecutionError( - message='Unable to install RHEL {} userspace packages.'.format(target_major_version), - details={'details': str(exc), 'stderr': exc.stderr} - ) + message = 'Unable to install RHEL {} userspace packages.'.format(target_major_version) + details = {'details': str(exc), 'stderr': exc.stderr} + + # If a proxy was set in dnf config, it should be the reason why dnf + # failed since leapp does not support updates behind proxy yet. + for manager_info in api.consume(PkgManagerInfo): + if manager_info.configured_proxies: + details['details'] = ("DNF failed to install userspace packages, likely due to the proxy " + "configuration detected in the YUM/DNF configuration file.") + + # Similarly if a proxy was set specifically for one of the repositories. + for repo_facts in api.consume(RepositoriesFacts): + for repo_file in repo_facts.repositories: + if any(repo_data.proxy and repo_data.enabled for repo_data in repo_file.data): + details['details'] = ("DNF failed to install userspace packages, likely due to the proxy " + "configuration detected in a repository configuration file.") + + raise StopActorExecutionError(message=message, details=details) def _get_all_rhui_pkgs(): diff --git a/repos/system_upgrade/common/models/packagemanagerinfo.py b/repos/system_upgrade/common/models/packagemanagerinfo.py index ba6391c3..aa450978 100644 --- a/repos/system_upgrade/common/models/packagemanagerinfo.py +++ b/repos/system_upgrade/common/models/packagemanagerinfo.py @@ -17,3 +17,8 @@ class PkgManagerInfo(Model): In case the value is empty string, it means the file exists but it is empty. In such a case the original configuration is obviously broken. """ + + configured_proxies = fields.List(fields.String(), default=[]) + """ + A sorted list of proxies present in yum and dnf configuration files. + """ diff --git a/repos/system_upgrade/common/models/repositoriesfacts.py b/repos/system_upgrade/common/models/repositoriesfacts.py index 722c579f..cd2124fc 100644 --- a/repos/system_upgrade/common/models/repositoriesfacts.py +++ b/repos/system_upgrade/common/models/repositoriesfacts.py @@ -13,6 +13,7 @@ class RepositoryData(Model): mirrorlist = fields.Nullable(fields.String()) enabled = fields.Boolean(default=True) additional_fields = fields.Nullable(fields.String()) + proxy = fields.Nullable(fields.String()) class RepositoryFile(Model): -- 2.38.1