Skip to content

Commit

Permalink
Fix Zypper Package Manager Return Code 8 (#122)
Browse files Browse the repository at this point in the history
Fixes Zypper package manager return code 8. This error occurs when a package fails to install due to a conflict with another package.
  • Loading branch information
benank authored Mar 17, 2022
1 parent abbe3e8 commit 6776987
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
21 changes: 21 additions & 0 deletions src/core/src/package_managers/ZypperPackageManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def __init__(self, env_layer, execution_config, composite_logger, telemetry_writ
self.zypper_exitcode_zypp_lib_exit_err = 4
self.zypper_exitcode_no_repos = 6
self.zypper_exitcode_zypp_locked = 7
self.zypper_exitcode_zypp_exit_err_commit = 8
self.zypper_exitcode_reboot_required = 102
self.zypper_exitcode_zypper_updated = 103
self.zypper_exitcode_repos_skipped = 106
Expand Down Expand Up @@ -128,6 +129,15 @@ def invoke_package_manager(self, command):
self.__refresh_repo_services()
continue

if code == self.zypper_exitcode_zypp_exit_err_commit:
# Run command again with --replacefiles to fix file conflicts
self.composite_logger.log_warning("Warning: package conflict detected on command: {0}".format(str(command)))
modified_command = self.modify_upgrade_or_patch_command_to_replacefiles(command)
if modified_command is not None:
command = modified_command
self.composite_logger.log_debug("Retrying with modified command to replace files: {0}".format(str(command)))
continue

self.log_errors_on_invoke(command, out, code)
error_msg = 'Unexpected return code (' + str(code) + ') from package manager on command: ' + command
self.status_handler.add_error_to_status(error_msg, Constants.PatchOperationErrorCodes.PACKAGE_MANAGER_FAILURE)
Expand Down Expand Up @@ -156,6 +166,17 @@ def invoke_package_manager(self, command):
self.force_reboot = True
return out

def modify_upgrade_or_patch_command_to_replacefiles(self, command):
""" Modifies a command to invoke_package_manager for update or patch to include a --replacefiles flag.
If it is a dry run or already has the flag, it returns None. Otherwise, returns the new command. """
if "--dry-run" in command or "--replacefiles" in command:
return None

if self.single_package_upgrade_cmd in command:
return command.replace(self.single_package_upgrade_cmd, self.single_package_upgrade_cmd + '--replacefiles ')
elif self.zypper_install_security_patches in command:
return command.replace(self.zypper_install_security_patches, self.zypper_install_security_patches + ' --replacefiles')

def log_errors_on_invoke(self, command, out, code):
"""Logs verbose error messages if there is an error on invoke_package_manager"""
self.composite_logger.log('[ERROR] Package manager was invoked using: ' + command)
Expand Down
44 changes: 44 additions & 0 deletions src/core/tests/Test_ZypperPackageManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,50 @@ def mock_run_command_output(cmd, no_output=False, chk_err=False):

package_manager.env_layer.run_command_output = backup_mocked_method

def test_package_manager_exit_err_commit(self):
package_manager = self.container.get('package_manager')
self.runtime.status_handler.set_current_operation(Constants.INSTALLATION)

# Test command modifications with --replacefiles
cmd_to_run = 'sudo zypper --non-interactive update samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1'
replacefiles_cmd_to_run = 'sudo zypper --non-interactive update --replacefiles samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1'
self.assertEqual(replacefiles_cmd_to_run, package_manager.modify_upgrade_or_patch_command_to_replacefiles(cmd_to_run))
cmd_to_run += " --dry-run"
self.assertEqual(None, package_manager.modify_upgrade_or_patch_command_to_replacefiles(cmd_to_run))
self.assertEqual(None, package_manager.modify_upgrade_or_patch_command_to_replacefiles(replacefiles_cmd_to_run))
cmd_to_run = 'sudo zypper --non-interactive patch --category security'
replacefiles_cmd_to_run = 'sudo zypper --non-interactive patch --category security --replacefiles'
self.assertEqual(replacefiles_cmd_to_run, package_manager.modify_upgrade_or_patch_command_to_replacefiles(cmd_to_run))

# Wrap count in a mutable container to modify in mocked method to keep track of calls
counter = [0]
replacefiles_counter = [0]
backup_mocked_method = package_manager.env_layer.run_command_output

def mock_run_command_output(cmd, no_output=False, chk_err=False):
# Only check for refresh services cmd
if cmd == 'sudo zypper --non-interactive update --replacefiles samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1':
# After refreshing, allow it to succeed
replacefiles_counter[0] += 1
self.runtime.set_legacy_test_type('HappyPath')
elif cmd == 'sudo zypper --non-interactive update samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1':
counter[0] += 1
return backup_mocked_method(cmd, no_output, chk_err)

package_manager.env_layer.run_command_output = mock_run_command_output

# AnotherSadPath uses return code 8
self.runtime.set_legacy_test_type('AnotherSadPath')

cmd_to_run = 'sudo zypper --non-interactive update samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1'
package_manager.invoke_package_manager(cmd_to_run)
self.assertEqual(counter[0], 1)
self.assertEqual(replacefiles_counter[0], 1)
self.assertFalse(self.is_string_in_status_file('Unexpected return code (8) from package manager on command'))

package_manager.env_layer.run_command_output = backup_mocked_method


if __name__ == '__main__':
unittest.main()

Expand Down
27 changes: 27 additions & 0 deletions src/core/tests/library/LegacyEnvLayerExtensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,30 @@ def run_command_output(self, cmd, no_output=False, chk_err=True):
output = "Retrieving repository 'SLE-Module-Basesystem15-SP3-Pool' metadata ................................................................[done]\n" + \
"Building repository 'SLE-Module-Basesystem15-SP3-Pool' cache .....................................................................[done]\n" + \
"All repositories have been refreshed."
elif cmd.find('sudo zypper --non-interactive update --replacefiles'):
code = 0
output = "Refreshing service 'Advanced_Systems_Management_Module_x86_64'.\n" + \
"Loading repository data...\n" + \
"Reading installed packages...\n" + \
"Resolving package dependencies...\n" + \
"The following 5 items are locked and will not be changed by any action:\n" + \
" Installed:\n" + \
" auoms azsec-clamav azsec-monitor azure-security qualys-command-line-agent\n" + \
"The following 3 packages are going to be upgraded:\n" + \
" samba-client-libs samba-libs samba-libs-python3\n" + \
"3 packages to upgrade.\n" + \
"Overall download size: 6.0 MiB. Already cached: 0 B. No additional space will be used or freed after the operation.\n" + \
"Continue? [y/n/...? shows all options] (y): y\n" + \
"Retrieving package samba-client-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 (1/3), 5.2 MiB ( 21.0 MiB unpacked)\n" + \
"Retrieving: samba-client-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64.rpm .................................................[done]\n" + \
"Retrieving package samba-libs-python3-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 (2/3), 367.5 KiB (290.3 KiB unpacked)\n" + \
"Retrieving: samba-libs-python3-4.15.4+git.331.61fc89677dd-3.60.1.x86_64.rpm ................................................[done]\n" + \
"Retrieving package samba-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 (3/3), 441.7 KiB (544.9 KiB unpacked)\n" + \
"Retrieving: samba-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64.rpm ........................................................[done]\n" + \
"Checking for file conflicts: ...............................................................................................[done]\n" + \
"(1/3) Installing: samba-client-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 ...............................................[done]\n" + \
"(2/3) Installing: samba-libs-python3-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 ..............................................[done]\n" + \
"(3/3) Installing: samba-libs-4.15.4+git.331.61fc89677dd-3.60.1.x86_64 ......................................................[done]"
elif self.legacy_package_manager_name is Constants.YUM:
if cmd.find("--security check-update") > -1:
code = 100
Expand Down Expand Up @@ -579,6 +603,9 @@ def run_command_output(self, cmd, no_output=False, chk_err=True):
if cmd.find('sudo zypper refresh') > -1:
code = 6
output = 'Warning: There are no enabled repositories defined. | Use \'zypper addrepo\' or \'zypper modifyrepo\' commands to add or enable repositories.'
elif cmd.find('sudo zypper --non-interactive update samba-libs=4.15.4+git.327.37e0a40d45f-3.57.1') > -1:
code = 8
output = ''
elif self.legacy_test_type == 'ExceptionPath':
code = -1
output = ''
Expand Down

0 comments on commit 6776987

Please sign in to comment.