Add zypper skip post errors feature (#9973)

* Add zypper skip post errors feature

* Add feature to handle zypper return code 107 with skip_post_errors (default: false).
* Add integration test to verify the skip_post_errors flag.
* Add changelog fragment

Issue: #9972
Issue-Ref: https://github.com/ansible-collections/community.general/issues/9972

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update plugins/modules/zypper.py

* Update changelogs/fragments/9972-zypper-skip-post-errors.yml

Co-authored-by: Felix Fontein <felix@fontein.de>

* Update tests/integration/targets/zypper/tasks/zypper.yml

Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com>
This commit is contained in:
mk2km 2025-04-19 10:00:00 +03:00 committed by GitHub
parent 1243846c3a
commit 80252b29f8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 127 additions and 3 deletions

View file

@ -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).

View file

@ -158,6 +158,13 @@ options:
description: description:
- Adds C(--quiet) option to I(zypper) install/update command. - Adds C(--quiet) option to I(zypper) install/update command.
version_added: '10.2.0' 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: notes:
- When used with a C(loop:) each package is processed individually, it is much more efficient to pass the list directly - 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. to the O(name) option.
@ -248,6 +255,12 @@ EXAMPLES = r"""
state: present state: present
environment: environment:
ZYPP_LOCK_TIMEOUT: 20 ZYPP_LOCK_TIMEOUT: 20
- name: Install the package with post-install error without failing
community.general.zypper:
name: <package_with_post_install_error>
state: present
skip_post_errors: true
""" """
import os.path 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) m.fail_json(msg=errmsg, rc=rc, stdout=stdout, stderr=stderr, cmd=cmd)
else: else:
return {}, rc, stdout, stderr return {}, rc, stdout, stderr
elif rc in [0, 102, 103, 106]: elif rc in [0, 102, 103, 106, 107]:
# zypper exit codes # zypper exit codes
# 0: success # 0: success
# 106: signature verification failed # 106: signature verification failed
# 102: ZYPPER_EXIT_INF_REBOOT_NEEDED - Returned after a successful installation of a patch which requires reboot of computer. # 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 # 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: if packages is None:
firstrun = True firstrun = True
packages = {} 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 # if this was the first run and it failed with 103
# run zypper again with the same command to complete update # run zypper again with the same command to complete update
return parse_zypper_xml(m, cmd, fail_not_found=fail_not_found, packages=packages) 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']: if m.params['simple_errors']:
stdout = get_simple_errors(dom) or stdout stdout = get_simple_errors(dom) or stdout
return packages, rc, stdout, stderr 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']: if m.params['simple_errors']:
stdout = get_simple_errors(dom) or stdout stdout = get_simple_errors(dom) or stdout
@ -602,6 +620,7 @@ def main():
clean_deps=dict(required=False, default=False, type='bool'), clean_deps=dict(required=False, default=False, type='bool'),
simple_errors=dict(required=False, default=False, type='bool'), simple_errors=dict(required=False, default=False, type='bool'),
quiet=dict(required=False, default=True, type='bool'), quiet=dict(required=False, default=True, type='bool'),
skip_post_errors=dict(required=False, default=False, type='bool'),
), ),
supports_check_mode=True supports_check_mode=True
) )

View file

@ -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

View file

@ -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

View file

@ -255,6 +255,91 @@
that: that:
- "stat_result.stat.exists == true" - "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 # test simultaneous remove and install using +- prefixes