From de03e4a0c689565aa03303c1804c0c52f270aeaa Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Fri, 27 May 2022 11:28:33 +0000 Subject: [PATCH 01/14] Ensure ca-certificates package is in the latest version --- .../src/command/dnf_config_manager.py | 7 ++----- .../download-requirements/src/mode/debian_family_mode.py | 5 ++++- .../download-requirements/src/mode/red_hat_family_mode.py | 5 ++--- .../roles/repository/tasks/RedHat/install-packages.yml | 1 + docs/changelogs/CHANGELOG-2.0.md | 4 ++++ 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_config_manager.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_config_manager.py index e9ce7af3ea..2d75b12db8 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_config_manager.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_config_manager.py @@ -28,9 +28,6 @@ def get_variable(self, name: str) -> str: chunks = var.split('=', maxsplit=1) if name == chunks[0].strip(): value = chunks[1].strip() - break + return value - if not value: - raise DnfVariableNotfound(f'Variable not found: {name}') - - return value + raise DnfVariableNotfound(f'Variable not found: {name}') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py index e01b66a60c..405be76ddd 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py @@ -39,9 +39,12 @@ def _install_base_packages(self): # install prerequisites which might be missing installed_packages = self._tools.apt.list_installed_packages() + # Ensure ca-certificates package is in the latest version + self._tools.apt.install('ca-certificates') + for package in ['wget', 'gpg', 'curl', 'tar']: if package not in installed_packages: - self._tools.apt.install(package, assume_yes=True) + self._tools.apt.install(package) self.__installed_packages.append(package) logging.info(f'- {package}') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 438f059d8f..7a8d5dd100 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -67,9 +67,8 @@ def _install_base_packages(self): self.__remove_dnf_cache_for_custom_repos() self._tools.dnf.makecache(True) - # tar does not come by default from image. We install it, but don't want to remove it - if not self._tools.rpm.is_package_installed('tar'): - self._tools.dnf.install('tar') + # Ensure ca-certificates package is in the latest version + self._tools.dnf.install('ca-certificates') for package in self.__base_packages: if not self._tools.rpm.is_package_installed(package): diff --git a/ansible/playbooks/roles/repository/tasks/RedHat/install-packages.yml b/ansible/playbooks/roles/repository/tasks/RedHat/install-packages.yml index ad59e47391..c56e0b70fc 100644 --- a/ansible/playbooks/roles/repository/tasks/RedHat/install-packages.yml +++ b/ansible/playbooks/roles/repository/tasks/RedHat/install-packages.yml @@ -21,6 +21,7 @@ - python36 - python3-pyyaml - rsync # for Ansible (synchronize module) + - tar state: present register: result retries: 3 diff --git a/docs/changelogs/CHANGELOG-2.0.md b/docs/changelogs/CHANGELOG-2.0.md index e731e79ec5..4ae12d45fa 100644 --- a/docs/changelogs/CHANGELOG-2.0.md +++ b/docs/changelogs/CHANGELOG-2.0.md @@ -11,6 +11,10 @@ - [#3164](https://github.com/epiphany-platform/epiphany/issues/3164) - Specify version and allow containerd.io package downgrade in haproxy_runc role +### Fixed + +- [#3165](https://github.com/epiphany-platform/epiphany/issues/3165) - download-requirements.py may fail due to expired certificate + ### Updated - [#3080](https://github.com/epiphany-platform/epiphany/issues/3080) - update Filebeat to the latest compatible version with OpenSearch From aeb9019954d4a4f5db7f60f9278f5e1a3bb983bb Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Thu, 2 Jun 2022 09:07:32 +0000 Subject: [PATCH 02/14] Add tar to base packages for RHEL mode --- .../files/download-requirements/src/mode/red_hat_family_mode.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 7a8d5dd100..838260066f 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -19,7 +19,7 @@ def __init__(self, config: Config): super().__init__(config) self.__all_queried_packages: Set[str] = set() self.__archs: List[str] = [config.os_arch.value, 'noarch'] - self.__base_packages: List[str] = ['curl', 'python3-dnf-plugins-core', 'wget'] + self.__base_packages: List[str] = ['curl', 'python3-dnf-plugins-core', 'wget', 'tar'] self.__installed_packages: List[str] = [] self.__dnf_cache_path: Path = Path('/var/cache/dnf') From f2198c9a417194eef07b6d5106ab1c7a7d2c25db Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Thu, 2 Jun 2022 10:57:59 +0000 Subject: [PATCH 03/14] Ensure tar is not uninstalled too early --- .../src/mode/base_mode.py | 17 +++++-- .../src/mode/debian_family_mode.py | 12 +++-- .../src/mode/red_hat_family_mode.py | 45 ++++++++++++------- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/base_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/base_mode.py index 3ae4ed2fba..980841243b 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/base_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/base_mode.py @@ -209,9 +209,15 @@ def _cleanup(self): """ pass - def _clean_up_repository_files(self): + def _cleanup_packages(self): """ - Additional routines before unpacking backup to remove repository files under the /etc directory. + Remove installed packages. + """ + pass + + def _remove_repository_files(self): + """ + Additional routines before unpacking backup to remove all repository files under the /etc directory. """ pass @@ -221,7 +227,7 @@ def __restore_repositories(self): """ if self._cfg.repos_backup_file.exists() and self._cfg.repos_backup_file.stat().st_size: logging.info('Restoring repository files...') - self._clean_up_repository_files() + self._remove_repository_files() self._tools.tar.unpack(filename=self._cfg.repos_backup_file, directory=Path('/'), absolute_names=True, @@ -291,4 +297,9 @@ def run(self): self._cleanup() logging.info('Done running cleanup.') + # requires tar but has to be run after cleanup self.__restore_repositories() + + logging.info('Cleaning up installed packages...') + self._cleanup_packages() + logging.info('Done cleaning up installed packages.') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py index 405be76ddd..7ee8af4290 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py @@ -16,6 +16,7 @@ def __init__(self, config: Config): super().__init__(config) self.__create_repo_paths() self.__installed_packages: List[str] = [] + self.__repos_config_dir: Path = Path('/etc/apt/sources.list.d') def __create_repo_paths(self): for repo in self._repositories.keys(): @@ -126,9 +127,12 @@ def _download_grafana_dashboard(self, dashboard: str, output_file: Path): def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest) - def _clean_up_repository_files(self): - for repofile in Path('/etc/apt/sources.list.d').iterdir(): - repofile.unlink() + def _remove_repository_files(self): + logging.debug(f'Removing files from {self.__repos_config_dir}...') + for repo_file in self.__repos_config_dir.iterdir(): + logging.debug(f'- {repo_file.name}') + repo_file.unlink() + logging.debug('Done removing files.') def _cleanup(self): # cleaning up 3rd party repositories @@ -136,6 +140,6 @@ def _cleanup(self): if data['path'].exists(): data['path'].unlink() - # remove installed packages + def _cleanup_packages(self): for package in self.__installed_packages: self._tools.apt.remove(package) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 838260066f..f30ed775bb 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -20,8 +20,9 @@ def __init__(self, config: Config): self.__all_queried_packages: Set[str] = set() self.__archs: List[str] = [config.os_arch.value, 'noarch'] self.__base_packages: List[str] = ['curl', 'python3-dnf-plugins-core', 'wget', 'tar'] - self.__installed_packages: List[str] = [] self.__dnf_cache_path: Path = Path('/var/cache/dnf') + self.__installed_packages: List[str] = [] + self.__repos_config_dir: Path = Path('/etc/yum.repos.d') try: dnf_config = configparser.ConfigParser() @@ -114,22 +115,33 @@ def _add_third_party_repositories(self): def __remove_dnf_cache_for_custom_repos(self): # clean metadata for upgrades (when the same package can be downloaded from changed repo) - repocaches: List[str] = list(self.__dnf_cache_path.iterdir()) + cache_paths: List[str] = list(self.__dnf_cache_path.iterdir()) id_names = [ '2ndquadrant', 'docker-ce', 'epel', - ] + [self._repositories[key]['id'] for key in self._repositories.keys()] + ] + [repo['id'] for _, repo in self._repositories.items()] + + matched_cache_paths: List[str] = [] + + for path in cache_paths: + for repo_name in id_names: + if path.name.startswith(repo_name): + matched_cache_paths.append(path) + break + + if matched_cache_paths: + matched_cache_paths.sort() + logging.debug(f'Removing DNF cache files from {self.__dnf_cache_path}...') - for repocache in repocaches: - matched_ids = [repocache.name.startswith(repo_name) for repo_name in id_names] - if any(matched_ids): + for path in matched_cache_paths: + logging.debug(f'- {path.name}') try: - if repocache.is_dir(): - shutil.rmtree(str(repocache)) + if path.is_dir(): + shutil.rmtree(str(path)) else: - repocache.unlink() + path.unlink() except FileNotFoundError: logging.debug('__remove_dnf_cache_for_custom_repos: cache directory already removed') @@ -215,14 +227,17 @@ def _download_grafana_dashboard(self, dashboard: str, output_file: Path): def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest, additional_params=False) - def _clean_up_repository_files(self): - for repofile in Path('/etc/yum.repos.d').iterdir(): - repofile.unlink() + def _remove_repository_files(self): + logging.debug(f'Removing files from {self.__repos_config_dir}...') + for repo_file in self.__repos_config_dir.iterdir(): + logging.debug(f'- {repo_file.name}') + repo_file.unlink() + logging.debug('Done removing files.') def _cleanup(self): - # remove installed packages + self.__remove_dnf_cache_for_custom_repos() + + def _cleanup_packages(self): for package in self.__installed_packages: if self._tools.rpm.is_package_installed(package): self._tools.dnf.remove(package) - - self.__remove_dnf_cache_for_custom_repos() From 442df6c5ce9896259a23d88aee7bb39d53653cc5 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Fri, 3 Jun 2022 13:22:55 +0000 Subject: [PATCH 04/14] Use constants instead of string literals --- .../src/mode/debian_family_mode.py | 12 ++++++------ .../src/mode/red_hat_family_mode.py | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py index 7ee8af4290..207149e715 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py @@ -11,23 +11,23 @@ class DebianFamilyMode(BaseMode): """ Used by distros based of Debian GNU/Linux """ + APT_REPOS_DIR = Path('/etc/apt/sources.list.d') def __init__(self, config: Config): super().__init__(config) self.__create_repo_paths() self.__installed_packages: List[str] = [] - self.__repos_config_dir: Path = Path('/etc/apt/sources.list.d') def __create_repo_paths(self): - for repo in self._repositories.keys(): - self._repositories[repo]['path'] = Path('/etc/apt/sources.list.d') / f'{repo}.list' + for repo_id, repo_item in self._repositories.items(): + repo_item['path'] = self.APT_REPOS_DIR / f'{repo_id}.list' def _create_backup_repositories(self): if not self._cfg.repos_backup_file.exists(): logging.debug('Creating backup for system repositories...') self._tools.tar.pack(self._cfg.repos_backup_file, targets=[Path('/etc/apt/sources.list'), - Path('/etc/apt/sources.list.d')], + self.APT_REPOS_DIR], verbose=True, preserve=True, absolute_names=True, @@ -128,8 +128,8 @@ def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest) def _remove_repository_files(self): - logging.debug(f'Removing files from {self.__repos_config_dir}...') - for repo_file in self.__repos_config_dir.iterdir(): + logging.debug(f'Removing files from {self.APT_REPOS_DIR}...') + for repo_file in self.APT_REPOS_DIR.iterdir(): logging.debug(f'- {repo_file.name}') repo_file.unlink() logging.debug('Done removing files.') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index f30ed775bb..f2e11b041a 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -14,22 +14,22 @@ class RedHatFamilyMode(BaseMode): """ Used by distros based of RedHat GNU/Linux """ + DNF_REPOS_DIR = Path('/etc/yum.repos.d') def __init__(self, config: Config): super().__init__(config) self.__all_queried_packages: Set[str] = set() self.__archs: List[str] = [config.os_arch.value, 'noarch'] self.__base_packages: List[str] = ['curl', 'python3-dnf-plugins-core', 'wget', 'tar'] - self.__dnf_cache_path: Path = Path('/var/cache/dnf') + self.__dnf_cache_dir: Path = Path('/var/cache/dnf') self.__installed_packages: List[str] = [] - self.__repos_config_dir: Path = Path('/etc/yum.repos.d') try: dnf_config = configparser.ConfigParser() with Path('/etc/dnf/dnf.conf').open() as dnf_config_file: dnf_config.read(dnf_config_file) - self.__dnf_cache_path = Path(dnf_config['main']['cachedir']) + self.__dnf_cache_dir = Path(dnf_config['main']['cachedir']) except FileNotFoundError: logging.debug('RedHatFamilyMode.__init__(): dnf config file not found') except configparser.Error as e: @@ -115,7 +115,7 @@ def _add_third_party_repositories(self): def __remove_dnf_cache_for_custom_repos(self): # clean metadata for upgrades (when the same package can be downloaded from changed repo) - cache_paths: List[str] = list(self.__dnf_cache_path.iterdir()) + cache_paths: List[str] = list(self.__dnf_cache_dir.iterdir()) id_names = [ '2ndquadrant', @@ -133,7 +133,7 @@ def __remove_dnf_cache_for_custom_repos(self): if matched_cache_paths: matched_cache_paths.sort() - logging.debug(f'Removing DNF cache files from {self.__dnf_cache_path}...') + logging.debug(f'Removing DNF cache files from {self.__dnf_cache_dir}...') for path in matched_cache_paths: logging.debug(f'- {path.name}') @@ -228,8 +228,8 @@ def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest, additional_params=False) def _remove_repository_files(self): - logging.debug(f'Removing files from {self.__repos_config_dir}...') - for repo_file in self.__repos_config_dir.iterdir(): + logging.debug(f'Removing files from {self.DNF_REPOS_DIR}...') + for repo_file in self.DNF_REPOS_DIR.iterdir(): logging.debug(f'- {repo_file.name}') repo_file.unlink() logging.debug('Done removing files.') From 881723f6e84a3e4c5d6e057c59f32b8d4561fe0e Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Fri, 3 Jun 2022 16:29:35 +0000 Subject: [PATCH 05/14] Ignore non-critical DNF error --- .../files/download-requirements/src/command/dnf.py | 8 ++++++-- .../download-requirements/src/command/dnf_download.py | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index 5fbd0e8fe3..b313204d54 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -1,9 +1,13 @@ from typing import Dict, List +import re from src.command.command import Command from src.error import CriticalError +def filter_non_critical_errors(stderr: str): + re.sub('^Failed to set locale, defaulting to .+\n', '', stderr) + class Dnf(Command): """ Interface for `dnf` @@ -49,7 +53,7 @@ def update(self, package: str = None, if 'error' in proc.stdout: raise CriticalError( f'Found an error. dnf update failed for package `{package}`, reason: `{proc.stdout}`') - if proc.stderr: + if filter_non_critical_errors(proc.stderr): raise CriticalError( f'dnf update failed for packages `{package}`, reason: `{proc.stderr}`') @@ -72,7 +76,7 @@ def install(self, package: str, if 'error' in proc.stdout: raise CriticalError( f'Found an error. dnf install failed for package `{package}`, reason: `{proc.stdout}`') - if proc.stderr: + if filter_non_critical_errors(proc.stderr): raise CriticalError( f'dnf install failed for package `{package}`, reason: `{proc.stderr}`') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py index 24f59df960..df2e017aa0 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py @@ -2,6 +2,7 @@ from typing import List from src.command.command import Command +from src.command.dnf import filter_non_critical_errors from src.error import CriticalError @@ -38,6 +39,6 @@ def download_packages(self, packages: List[str], if 'error' in process.stdout: raise CriticalError( f'Found an error. dnf download failed for packages `{packages}`, reason: `{process.stdout}`') - if process.stderr: + if filter_non_critical_errors(process.stderr): raise CriticalError( f'dnf download failed for packages `{packages}`, reason: `{process.stderr}`') From 2365c1f0a542107df1cba6d243348b1fe6c5e743 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Fri, 3 Jun 2022 16:36:37 +0000 Subject: [PATCH 06/14] Ensure dnf config-manager command --- .../src/mode/red_hat_family_mode.py | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index f2e11b041a..814d2156e4 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -51,22 +51,25 @@ def _create_backup_repositories(self): logging.debug('Done.') def _install_base_packages(self): + # Ensure dnf config-manager command + DNF_CONFIG_MANAGER_PACKAGE = 'dnf-plugins-core' + if not self._tools.rpm.is_package_installed(DNF_CONFIG_MANAGER_PACKAGE): + self._tools.dnf.install(DNF_CONFIG_MANAGER_PACKAGE) + self.__installed_packages.append(DNF_CONFIG_MANAGER_PACKAGE) # Bug in RHEL 8.4 https://bugzilla.redhat.com/show_bug.cgi?id=2004853 releasever = '8' if self._tools.dnf_config_manager.get_variable('releasever') == '8.4' else None self._tools.dnf.update(package='libmodulemd', releasever=releasever) - # some packages are from EPEL repo - # make sure that we reinstall it before proceeding - if self._tools.rpm.is_package_installed('epel-release'): - if not self._tools.dnf.is_repo_enabled('epel') or not self._tools.dnf.is_repo_enabled('epel-modular'): - self._tools.dnf.remove('epel-release') - - self._tools.dnf.install('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') - self.__installed_packages.append('epel-release') + # some packages are from EPEL repo, ensure the latest version + if not self._tools.rpm.is_package_installed('epel-release'): + self._tools.dnf.install('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') + self.__installed_packages.append('epel-release') + else: + self._tools.dnf.update('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') self.__remove_dnf_cache_for_custom_repos() - self._tools.dnf.makecache(True) + self._tools.dnf.makecache(timer=True) # Ensure ca-certificates package is in the latest version self._tools.dnf.install('ca-certificates') From e5b076ad16ef7f05f297da06c8d0da938a31884d Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Sat, 4 Jun 2022 15:42:17 +0000 Subject: [PATCH 07/14] Do not use constants for better readability --- .../src/mode/debian_family_mode.py | 9 ++++----- .../src/mode/red_hat_family_mode.py | 16 +++++++--------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py index 207149e715..9d21371d7c 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/debian_family_mode.py @@ -11,7 +11,6 @@ class DebianFamilyMode(BaseMode): """ Used by distros based of Debian GNU/Linux """ - APT_REPOS_DIR = Path('/etc/apt/sources.list.d') def __init__(self, config: Config): super().__init__(config) @@ -20,14 +19,14 @@ def __init__(self, config: Config): def __create_repo_paths(self): for repo_id, repo_item in self._repositories.items(): - repo_item['path'] = self.APT_REPOS_DIR / f'{repo_id}.list' + repo_item['path'] = Path('/etc/apt/sources.list.d') / f'{repo_id}.list' def _create_backup_repositories(self): if not self._cfg.repos_backup_file.exists(): logging.debug('Creating backup for system repositories...') self._tools.tar.pack(self._cfg.repos_backup_file, targets=[Path('/etc/apt/sources.list'), - self.APT_REPOS_DIR], + Path('/etc/apt/sources.list.d')], verbose=True, preserve=True, absolute_names=True, @@ -128,8 +127,8 @@ def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest) def _remove_repository_files(self): - logging.debug(f'Removing files from {self.APT_REPOS_DIR}...') - for repo_file in self.APT_REPOS_DIR.iterdir(): + logging.debug('Removing files from /etc/apt/sources.list.d...') + for repo_file in Path('/etc/apt/sources.list.d').iterdir(): logging.debug(f'- {repo_file.name}') repo_file.unlink() logging.debug('Done removing files.') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 814d2156e4..aa5ff08f4d 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -14,7 +14,6 @@ class RedHatFamilyMode(BaseMode): """ Used by distros based of RedHat GNU/Linux """ - DNF_REPOS_DIR = Path('/etc/yum.repos.d') def __init__(self, config: Config): super().__init__(config) @@ -26,7 +25,7 @@ def __init__(self, config: Config): try: dnf_config = configparser.ConfigParser() - with Path('/etc/dnf/dnf.conf').open() as dnf_config_file: + with Path('/etc/dnf/dnf.conf').open(encoding='utf-8') as dnf_config_file: dnf_config.read(dnf_config_file) self.__dnf_cache_dir = Path(dnf_config['main']['cachedir']) @@ -51,11 +50,10 @@ def _create_backup_repositories(self): logging.debug('Done.') def _install_base_packages(self): - # Ensure dnf config-manager command - DNF_CONFIG_MANAGER_PACKAGE = 'dnf-plugins-core' - if not self._tools.rpm.is_package_installed(DNF_CONFIG_MANAGER_PACKAGE): - self._tools.dnf.install(DNF_CONFIG_MANAGER_PACKAGE) - self.__installed_packages.append(DNF_CONFIG_MANAGER_PACKAGE) + # Ensure `dnf config-manager` command + if not self._tools.rpm.is_package_installed('dnf-plugins-core'): + self._tools.dnf.install('dnf-plugins-core') + self.__installed_packages.append('dnf-plugins-core') # Bug in RHEL 8.4 https://bugzilla.redhat.com/show_bug.cgi?id=2004853 releasever = '8' if self._tools.dnf_config_manager.get_variable('releasever') == '8.4' else None @@ -231,8 +229,8 @@ def _download_crane_binary(self, url: str, dest: Path): self._tools.wget.download(url, dest, additional_params=False) def _remove_repository_files(self): - logging.debug(f'Removing files from {self.DNF_REPOS_DIR}...') - for repo_file in self.DNF_REPOS_DIR.iterdir(): + logging.debug('Removing files from /etc/yum.repos.d...') + for repo_file in Path('/etc/yum.repos.d').iterdir(): logging.debug(f'- {repo_file.name}') repo_file.unlink() logging.debug('Done removing files.') From d77634f75f4ee4d22e7da4c96318b3858fa4bf81 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Tue, 7 Jun 2022 08:44:43 +0000 Subject: [PATCH 08/14] Ensure epel repo is enabled --- .../download-requirements/src/mode/red_hat_family_mode.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index aa5ff08f4d..9c3a6bc71f 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -59,6 +59,14 @@ def _install_base_packages(self): releasever = '8' if self._tools.dnf_config_manager.get_variable('releasever') == '8.4' else None self._tools.dnf.update(package='libmodulemd', releasever=releasever) + # epel-release package is re-installed if repo it provides is not enabled + if ( + self._tools.rpm.is_package_installed('epel-release') and + (not self._tools.dnf.is_repo_enabled('epel') + or not self._tools.dnf.is_repo_enabled('epel-modular')) + ): + self._tools.dnf.remove('epel-release') + # some packages are from EPEL repo, ensure the latest version if not self._tools.rpm.is_package_installed('epel-release'): self._tools.dnf.install('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') From db85df34eb31b2ab40dcd175ce2605ba8b6256b1 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:17:37 +0000 Subject: [PATCH 09/14] Fix is_repo_enabled method --- .../download-requirements/src/command/dnf.py | 38 +++++++++---------- .../tests/command/test_dnf.py | 10 +---- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index b313204d54..96faf7c1cc 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -1,3 +1,4 @@ +from cgitb import enable from typing import Dict, List import re @@ -91,29 +92,28 @@ def remove(self, package: str, no_ask: str = '-y' if assume_yes else '' self.run(['remove', no_ask, package]) - def is_repo_enabled(self, repo: str) -> bool: - output = self.run(['repolist', - '--enabled', - '--quiet', - '-y']).stdout - if repo in output: - return True + def __get_repo_ids(self, repoinfo_extra_args: List[str] = None) -> List[str]: + repoinfo_args: List[str] = ['--quiet', '-y'] - return False + if repoinfo_extra_args: + repoinfo_args.extend(repoinfo_extra_args) - def find_rhel_repo_id(self, patterns: List[str]) -> List[str]: - output = self.run(['repolist', - '--all', - '--quiet', - '-y']).stdout + output = self.run(['repoinfo'] + repoinfo_args).stdout + repo_ids: List[str] = [] - repos: List[str] = [] - for line in output.split('\n'): - for pattern in patterns: - if pattern in line: - repos.append(pattern) + for line in output.splitlines(): + if 'Repo-id' in line: # e.g. `Repo-id : epel` + repo_ids.append(line.split(':')[1].strip()) - return repos + return repo_ids + + def is_repo_enabled(self, repo: str) -> bool: + enabled_repos = self.__get_repo_ids() + + if repo in enabled_repos: + return True + + return False def accept_keys(self): # to accept import of repo's GPG key (for repo_gpgcheck=1) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py index 983789d0a7..883b8ff927 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py @@ -25,12 +25,6 @@ def test_interface_remove(mocker): def test_interface_is_repo_enabled(mocker): - ''' Check argument construction for `dnf repolist enabled` ''' + ''' Check argument construction for `dnf repoinfo enabled` ''' with CommandRunMock(mocker, Dnf(1).is_repo_enabled, {'repo': 'some_repo'}) as call_args: - assert call_args == ['dnf' , 'repolist', '--enabled', '--quiet', '-y'] - - -def test_interface_find_rhel_repo_id(mocker): - ''' Check argument construction for `dnf repolist all` ''' - with CommandRunMock(mocker, Dnf(1).find_rhel_repo_id, {'patterns': ['pat1', 'pat2']}) as call_args: - assert call_args == ['dnf' , 'repolist', '--all', '--quiet', '-y'] + assert call_args == ['dnf' , 'repoinfo', '--quiet', '-y'] From 0fd848ae30d5a55536341776e8ce7655d4dd439e Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:48:16 +0000 Subject: [PATCH 10/14] Preserve epel-release package --- .../src/mode/red_hat_family_mode.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 9c3a6bc71f..422e7568bc 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -59,9 +59,11 @@ def _install_base_packages(self): releasever = '8' if self._tools.dnf_config_manager.get_variable('releasever') == '8.4' else None self._tools.dnf.update(package='libmodulemd', releasever=releasever) - # epel-release package is re-installed if repo it provides is not enabled + # epel-release package is re-installed when repo it provides is not enabled + epel_package_initially_present: bool = self._tools.rpm.is_package_installed('epel-release') + if ( - self._tools.rpm.is_package_installed('epel-release') and + epel_package_initially_present and (not self._tools.dnf.is_repo_enabled('epel') or not self._tools.dnf.is_repo_enabled('epel-modular')) ): @@ -70,7 +72,9 @@ def _install_base_packages(self): # some packages are from EPEL repo, ensure the latest version if not self._tools.rpm.is_package_installed('epel-release'): self._tools.dnf.install('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') - self.__installed_packages.append('epel-release') + + if not epel_package_initially_present: + self.__installed_packages.append('epel-release') else: self._tools.dnf.update('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') From 4754933d3eaeeb82f6e080f161f7bcac5f6c461e Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Tue, 7 Jun 2022 10:57:17 +0000 Subject: [PATCH 11/14] Remove accidental import --- .../repository/files/download-requirements/src/command/dnf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index 96faf7c1cc..7c36ddfa0c 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -1,4 +1,3 @@ -from cgitb import enable from typing import Dict, List import re From e4d446ae5e648d5f25c2f899590c4130dd027106 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Tue, 7 Jun 2022 17:28:29 +0000 Subject: [PATCH 12/14] Apply suggestions from code review --- .../download-requirements/src/command/dnf.py | 29 ++++++++++++++----- .../src/command/dnf_download.py | 10 ++----- .../src/mode/red_hat_family_mode.py | 26 +++++++---------- .../tests/command/test_dnf.py | 4 +-- .../tests/command/test_dnf_base.py | 12 ++++++++ 5 files changed, 49 insertions(+), 32 deletions(-) create mode 100644 ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf_base.py diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index 7c36ddfa0c..731f0d940f 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -1,21 +1,29 @@ from typing import Dict, List -import re from src.command.command import Command from src.error import CriticalError -def filter_non_critical_errors(stderr: str): - re.sub('^Failed to set locale, defaulting to .+\n', '', stderr) - -class Dnf(Command): +class DnfBase(Command): """ - Interface for `dnf` + Base class for `dnf` interfaces """ def __init__(self, retries: int): super().__init__('dnf', retries) + def _filter_non_critical_errors(self, stderr: str) -> str: # pylint: disable=no-self-use + output_lines = [line for line in stderr.split( + '\n') if not line.startswith('Failed to set locale, defaulting to')] + + return '\n'.join(output_lines) + + +class Dnf(DnfBase): + """ + Interface for `dnf` + """ + def update(self, package: str = None, disablerepo: str = None, enablerepo: str = None, @@ -53,7 +61,7 @@ def update(self, package: str = None, if 'error' in proc.stdout: raise CriticalError( f'Found an error. dnf update failed for package `{package}`, reason: `{proc.stdout}`') - if filter_non_critical_errors(proc.stderr): + if self._filter_non_critical_errors(proc.stderr): raise CriticalError( f'dnf update failed for packages `{package}`, reason: `{proc.stderr}`') @@ -76,7 +84,7 @@ def install(self, package: str, if 'error' in proc.stdout: raise CriticalError( f'Found an error. dnf install failed for package `{package}`, reason: `{proc.stdout}`') - if filter_non_critical_errors(proc.stderr): + if self._filter_non_critical_errors(proc.stderr): raise CriticalError( f'dnf install failed for package `{package}`, reason: `{proc.stderr}`') @@ -114,6 +122,11 @@ def is_repo_enabled(self, repo: str) -> bool: return False + def are_all_repos_enabled(self, repos: List[str]) -> bool: + enabled_repos = self.__get_repo_ids() + + return all(repo in enabled_repos for repo in repos) + def accept_keys(self): # to accept import of repo's GPG key (for repo_gpgcheck=1) self.run(['repolist', '-y']) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py index df2e017aa0..5b46f00e19 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf_download.py @@ -1,19 +1,15 @@ from pathlib import Path from typing import List -from src.command.command import Command -from src.command.dnf import filter_non_critical_errors +from src.command.dnf import DnfBase from src.error import CriticalError -class DnfDownload(Command): +class DnfDownload(DnfBase): """ Interface for `dnf download` """ - def __init__(self, retries: int): - super().__init__('dnf', retries) - def download_packages(self, packages: List[str], archlist: List[str], destdir: Path, @@ -39,6 +35,6 @@ def download_packages(self, packages: List[str], if 'error' in process.stdout: raise CriticalError( f'Found an error. dnf download failed for packages `{packages}`, reason: `{process.stdout}`') - if filter_non_critical_errors(process.stderr): + if self._filter_non_critical_errors(process.stderr): raise CriticalError( f'dnf download failed for packages `{packages}`, reason: `{process.stderr}`') diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 422e7568bc..ddafa1f455 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -62,12 +62,8 @@ def _install_base_packages(self): # epel-release package is re-installed when repo it provides is not enabled epel_package_initially_present: bool = self._tools.rpm.is_package_installed('epel-release') - if ( - epel_package_initially_present and - (not self._tools.dnf.is_repo_enabled('epel') - or not self._tools.dnf.is_repo_enabled('epel-modular')) - ): - self._tools.dnf.remove('epel-release') + if epel_package_initially_present and not self._tools.dnf.are_all_repos_enabled(['epel', 'epel-modular']): + self._tools.dnf.remove('epel-release') # some packages are from EPEL repo, ensure the latest version if not self._tools.rpm.is_package_installed('epel-release'): @@ -128,21 +124,21 @@ def _add_third_party_repositories(self): def __remove_dnf_cache_for_custom_repos(self): # clean metadata for upgrades (when the same package can be downloaded from changed repo) - cache_paths: List[str] = list(self.__dnf_cache_dir.iterdir()) + cache_paths: List[Path] = list(self.__dnf_cache_dir.iterdir()) - id_names = [ + def get_matched_paths(repo_id: str, paths: List[Path]) -> List[Path]: + return [path for path in paths if path.name.startswith(repo_id)] + + repo_ids = [ '2ndquadrant', 'docker-ce', 'epel', - ] + [repo['id'] for _, repo in self._repositories.items()] + ] + [repo['id'] for repo in self._repositories.values()] - matched_cache_paths: List[str] = [] + matched_cache_paths: List[Path] = [] - for path in cache_paths: - for repo_name in id_names: - if path.name.startswith(repo_name): - matched_cache_paths.append(path) - break + for repo_id in repo_ids: + matched_cache_paths.extend(get_matched_paths(repo_id, cache_paths)) if matched_cache_paths: matched_cache_paths.sort() diff --git a/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py index 883b8ff927..fc94fc92e8 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf.py @@ -1,7 +1,7 @@ -from tests.mocks.command_run_mock import CommandRunMock - from src.command.dnf import Dnf +from tests.mocks.command_run_mock import CommandRunMock + def test_interface_update(mocker): ''' Check argument construction for `dnf update` ''' diff --git a/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf_base.py b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf_base.py new file mode 100644 index 0000000000..0f2c377c1a --- /dev/null +++ b/ansible/playbooks/roles/repository/files/download-requirements/tests/command/test_dnf_base.py @@ -0,0 +1,12 @@ +from src.command.dnf import DnfBase + + +def test_filter_non_critical_errors(): + STDERR = '\n'.join([ + '1st line', + 'Failed to set locale, defaulting to C.UTF-8', + '3rd line']) + + base = DnfBase(1) + output = base._filter_non_critical_errors(STDERR) + assert output == "1st line\n3rd line" From d775dadb6a6df69985be7aee166e9ec24cf6fac9 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Wed, 8 Jun 2022 09:36:08 +0000 Subject: [PATCH 13/14] Apply suggestions from 2nd review --- .../files/download-requirements/src/command/dnf.py | 11 +++++------ .../src/mode/red_hat_family_mode.py | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index 731f0d940f..eab607cebb 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -12,9 +12,9 @@ class DnfBase(Command): def __init__(self, retries: int): super().__init__('dnf', retries) - def _filter_non_critical_errors(self, stderr: str) -> str: # pylint: disable=no-self-use - output_lines = [line for line in stderr.split( - '\n') if not line.startswith('Failed to set locale, defaulting to')] + def _filter_non_critical_errors(self, stderr: str) -> str: + output_lines = [line for line in stderr.split('\n') + if not line.startswith('Failed to set locale, defaulting to')] return '\n'.join(output_lines) @@ -122,9 +122,8 @@ def is_repo_enabled(self, repo: str) -> bool: return False - def are_all_repos_enabled(self, repos: List[str]) -> bool: - enabled_repos = self.__get_repo_ids() - + def are_repos_enabled(self, repos: List[str]) -> bool: + enabled_repos: List[str] = self.__get_repo_ids() return all(repo in enabled_repos for repo in repos) def accept_keys(self): diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index ddafa1f455..99ec3fc692 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -62,8 +62,8 @@ def _install_base_packages(self): # epel-release package is re-installed when repo it provides is not enabled epel_package_initially_present: bool = self._tools.rpm.is_package_installed('epel-release') - if epel_package_initially_present and not self._tools.dnf.are_all_repos_enabled(['epel', 'epel-modular']): - self._tools.dnf.remove('epel-release') + if epel_package_initially_present and not self._tools.dnf.are_repos_enabled(['epel', 'epel-modular']): + self._tools.dnf.remove('epel-release') # some packages are from EPEL repo, ensure the latest version if not self._tools.rpm.is_package_installed('epel-release'): From 8a0307bc5bdc99667fa07a98e69f9cdbb39d9569 Mon Sep 17 00:00:00 2001 From: to-bar <46519524+to-bar@users.noreply.github.com> Date: Wed, 8 Jun 2022 16:20:23 +0000 Subject: [PATCH 14/14] Fix `The same or higher version of epel-release is already installed` error --- .../download-requirements/src/command/dnf.py | 15 ++++++++++++--- .../src/mode/red_hat_family_mode.py | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py index eab607cebb..1196237b74 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/command/dnf.py @@ -27,15 +27,17 @@ class Dnf(DnfBase): def update(self, package: str = None, disablerepo: str = None, enablerepo: str = None, + ignore_already_installed_error: bool = False, releasever: str = None, assume_yes: bool = True): - """ Interface for `dnf update` :param package: :param disablerepo: :param enablerepo: + :param ignore_already_installed_error: if set to True, + `The same or higher version of {package} is already installed` error is ignored :param releasever: :param assume_yes: if set to True, -y flag will be used """ @@ -61,11 +63,18 @@ def update(self, package: str = None, if 'error' in proc.stdout: raise CriticalError( f'Found an error. dnf update failed for package `{package}`, reason: `{proc.stdout}`') - if self._filter_non_critical_errors(proc.stderr): + + filtered_stderr: str = self._filter_non_critical_errors(proc.stderr) + + if filtered_stderr: + if (ignore_already_installed_error + and all(string in filtered_stderr for string in + ('The same or higher version', 'is already installed, cannot update it.'))): + return + raise CriticalError( f'dnf update failed for packages `{package}`, reason: `{proc.stderr}`') - def install(self, package: str, assume_yes: bool = True): """ diff --git a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py index 99ec3fc692..34d0df11b3 100644 --- a/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py +++ b/ansible/playbooks/roles/repository/files/download-requirements/src/mode/red_hat_family_mode.py @@ -72,7 +72,8 @@ def _install_base_packages(self): if not epel_package_initially_present: self.__installed_packages.append('epel-release') else: - self._tools.dnf.update('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm') + self._tools.dnf.update('https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm', + ignore_already_installed_error=True) self.__remove_dnf_cache_for_custom_repos() self._tools.dnf.makecache(timer=True)