diff --git a/changelogs/fragments/9972-zypper-skip-post-errors.yml b/changelogs/fragments/9972-zypper-skip-post-errors.yml new file mode 100644 index 0000000000..097ec67df0 --- /dev/null +++ b/changelogs/fragments/9972-zypper-skip-post-errors.yml @@ -0,0 +1,2 @@ +minor_changes: + - zypper - adds ``skip_post_errors`` that allows to skip RPM post-install errors (Zypper return code 107) (https://github.com/ansible-collections/community.general/issues/9972). diff --git a/plugins/modules/zypper.py b/plugins/modules/zypper.py index fb3be3a5d0..8ed359d32e 100644 --- a/plugins/modules/zypper.py +++ b/plugins/modules/zypper.py @@ -158,6 +158,13 @@ options: description: - Adds C(--quiet) option to I(zypper) install/update command. version_added: '10.2.0' + skip_post_errors: + type: bool + required: false + default: false + description: + - When set to V(true), ignore I(zypper) return code 107 (post install script errors). + version_added: '10.6.0' notes: - When used with a C(loop:) each package is processed individually, it is much more efficient to pass the list directly to the O(name) option. @@ -248,6 +255,12 @@ EXAMPLES = r""" state: present environment: ZYPP_LOCK_TIMEOUT: 20 + +- name: Install the package with post-install error without failing + community.general.zypper: + name: + state: present + skip_post_errors: true """ import os.path @@ -344,12 +357,13 @@ def parse_zypper_xml(m, cmd, fail_not_found=True, packages=None): m.fail_json(msg=errmsg, rc=rc, stdout=stdout, stderr=stderr, cmd=cmd) else: return {}, rc, stdout, stderr - elif rc in [0, 102, 103, 106]: + elif rc in [0, 102, 103, 106, 107]: # zypper exit codes # 0: success # 106: signature verification failed # 102: ZYPPER_EXIT_INF_REBOOT_NEEDED - Returned after a successful installation of a patch which requires reboot of computer. # 103: zypper was upgraded, run same command again + # 107: ZYPPER_EXIT_INF_RPM_SCRIPT_FAILED - Some of the packages %post install scripts returned an error, but package is installed. if packages is None: firstrun = True packages = {} @@ -368,14 +382,18 @@ def parse_zypper_xml(m, cmd, fail_not_found=True, packages=None): # if this was the first run and it failed with 103 # run zypper again with the same command to complete update return parse_zypper_xml(m, cmd, fail_not_found=fail_not_found, packages=packages) + if rc == 107 and m.params['skip_post_errors'] and firstrun: + # if this was the first run and it failed with 107 with skip_post_errors flag + # run zypper again with the same command to complete update + return parse_zypper_xml(m, cmd, fail_not_found=fail_not_found, packages=packages) - # apply simple_errors logic to rc 0,102,103,106 + # apply simple_errors logic to rc 0,102,103,106,107 if m.params['simple_errors']: stdout = get_simple_errors(dom) or stdout return packages, rc, stdout, stderr - # apply simple_errors logic to rc other than 0,102,103,106 + # apply simple_errors logic to rc other than 0,102,103,106,107 if m.params['simple_errors']: stdout = get_simple_errors(dom) or stdout @@ -602,6 +620,7 @@ def main(): clean_deps=dict(required=False, default=False, type='bool'), simple_errors=dict(required=False, default=False, type='bool'), quiet=dict(required=False, default=True, type='bool'), + skip_post_errors=dict(required=False, default=False, type='bool'), ), supports_check_mode=True ) diff --git a/tests/integration/targets/zypper/files/post_error.spec b/tests/integration/targets/zypper/files/post_error.spec new file mode 100644 index 0000000000..af5bc03108 --- /dev/null +++ b/tests/integration/targets/zypper/files/post_error.spec @@ -0,0 +1,15 @@ +Summary: Post error RPM +Name: post_error +Version: 1 +Release: 0 +License: GPLv3 +Group: Applications/System +BuildArch: noarch + +%description +Post error RPM + +%files + +%post +exit 1 diff --git a/tests/integration/targets/zypper/files/post_error.spec.license b/tests/integration/targets/zypper/files/post_error.spec.license new file mode 100644 index 0000000000..edff8c7685 --- /dev/null +++ b/tests/integration/targets/zypper/files/post_error.spec.license @@ -0,0 +1,3 @@ +GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +SPDX-License-Identifier: GPL-3.0-or-later +SPDX-FileCopyrightText: Ansible Project diff --git a/tests/integration/targets/zypper/tasks/zypper.yml b/tests/integration/targets/zypper/tasks/zypper.yml index 3eefddbdfc..818bdd9f42 100644 --- a/tests/integration/targets/zypper/tasks/zypper.yml +++ b/tests/integration/targets/zypper/tasks/zypper.yml @@ -255,6 +255,91 @@ that: - "stat_result.stat.exists == true" +# Build and install an empty rpm with error in post script +- name: uninstall post_error + zypper: + name: post_error + state: removed + +- name: install rpmbuild + zypper: + name: rpmbuild + state: present + +- name: clean zypper RPM cache + file: + name: /var/cache/zypper/RPMS + state: absent + +- name: create directory + file: + path: "{{ remote_tmp_dir | expanduser }}/zypper2" + state: directory + +- name: copy spec file + copy: + src: post_error.spec + dest: "{{ remote_tmp_dir | expanduser }}/zypper2/post_error.spec" + +- name: build rpm + command: | + rpmbuild -bb \ + --define "_topdir {{remote_tmp_dir | expanduser }}/zypper2/rpm-build" + --define "_builddir %{_topdir}" \ + --define "_rpmdir %{_topdir}" \ + --define "_srcrpmdir %{_topdir}" \ + --define "_specdir {{remote_tmp_dir | expanduser}}/zypper2" \ + --define "_sourcedir %{_topdir}" \ + {{ remote_tmp_dir }}/zypper2/post_error.spec + register: rpm_build_result + +- name: install post_error rpm with skip_post_errors + zypper: + name: "{{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/post_error-1-0.noarch.rpm" + disable_gpg_check: true + skip_post_errors: true + register: zypper_result + +- name: check post_error rpm + shell: rpm -q post_error + failed_when: false + register: rpm_result + +- name: verify installation of post_error + assert: + that: + - "zypper_result.rc == 0" + - "zypper_result.changed" + - "rpm_result.rc == 0" + +- name: uninstall post_error + zypper: + name: post_error + state: removed + +- name: install post_error rpm without skip_post_errors + zypper: + name: "{{ remote_tmp_dir | expanduser }}/zypper2/rpm-build/noarch/post_error-1-0.noarch.rpm" + disable_gpg_check: true + register: zypper_result + ignore_errors: true + +- name: check post_error rpm + shell: rpm -q post_error + failed_when: false + register: rpm_result + +- name: verify installation of post_error + assert: + that: + - "zypper_result.rc == 107" + - "not zypper_result.changed" + - "rpm_result.rc == 0" + +- name: uninstall post_error + zypper: + name: post_error + state: removed # test simultaneous remove and install using +- prefixes