From 45b171a06470d5a9a900fffbd91aec7e051451d7 Mon Sep 17 00:00:00 2001 From: Cameron Nemo Date: Fri, 10 Apr 2020 11:15:02 -0700 Subject: [PATCH] Xbps various changes (#95) * fix(modules/xbps): do not upgrade in check mode Ensure that while in check mode, this module does not upgrade all packages on the system. * chore(modules/xbps): refactor update_cache Pull out the update_cache logic from the main function. * feat(modules/xbps): upgrade xbps package itself Perform an upgrade of the xbps package itself, when necessary. Otherwise, users' playbooks will fail to install or upgrade packages when the xbps version is out of date. See https://github.com/void-linux/xbps/issues/229 for more information. --- plugins/modules/packaging/os/xbps.py | 103 +++++++++++++++++++-------- 1 file changed, 72 insertions(+), 31 deletions(-) diff --git a/plugins/modules/packaging/os/xbps.py b/plugins/modules/packaging/os/xbps.py index dd390cb235..de06565497 100644 --- a/plugins/modules/packaging/os/xbps.py +++ b/plugins/modules/packaging/os/xbps.py @@ -50,21 +50,41 @@ options: - Whether or not to upgrade whole system type: bool default: 'no' + upgrade_xbps: + description: + - Whether or not to upgrade the xbps package when necessary. + Before installing new packages, + xbps requires the user to update the xbps package itself. + Thus when this option is set to C(no), + upgrades and installations will fail when xbps is not up to date. + type: bool + default: 'yes' ''' EXAMPLES = ''' -# Install package foo -- xbps: name=foo state=present -# Upgrade package foo -- xbps: name=foo state=latest update_cache=yes -# Remove packages foo and bar -- xbps: name=foo,bar state=absent -# Recursively remove package foo -- xbps: name=foo state=absent recurse=yes -# Update package cache -- xbps: update_cache=yes -# Upgrade packages -- xbps: upgrade=yes +- name: Install package foo (automatically updating the xbps package if needed) + xbps: name=foo state=present + +- name: Upgrade package foo + xbps: name=foo state=latest update_cache=yes + +- name: Remove packages foo and bar + xbps: name=foo,bar state=absent + +- name: Recursively remove package foo + xbps: name=foo state=absent recurse=yes + +- name: Update package cache + xbps: update_cache=yes + +- name: Upgrade packages + xbps: upgrade=yes + +- name: Install a package, failing if the xbps package is out of date + xbps: + name: foo + state: present + upgrade_xbps: no ''' RETURN = ''' @@ -124,6 +144,13 @@ def update_package_db(module, xbps_path): return False +def upgrade_xbps(module, xbps_path, exit_on_success=False): + cmdupgradexbps = "%s -uy xbps" % (xbps_path['install']) + rc, stdout, stderr = module.run_command(cmdupgradexbps, check_rc=False) + if rc != 0: + module.fail_json(msg='Could not upgrade xbps itself') + + def upgrade(module, xbps_path): """Returns true is full upgrade succeeds""" cmdupgrade = "%s -uy" % (xbps_path['install']) @@ -133,10 +160,17 @@ def upgrade(module, xbps_path): if rc == 0: if(len(stdout.splitlines()) == 0): module.exit_json(changed=False, msg='Nothing to upgrade') + elif module.check_mode: + module.exit_json(changed=True, msg='Would have performed upgrade') else: rc, stdout, stderr = module.run_command(cmdupgrade, check_rc=False) if rc == 0: module.exit_json(changed=True, msg='System upgraded') + elif rc == 16 and module.params['upgrade_xbps']: + upgrade_xbps(module, xbps_path) + # avoid loops by not trying self-upgrade again + module.params['upgrade_xbps'] = False + upgrade(module, xbps_path) else: module.fail_json(msg="Could not upgrade") else: @@ -188,16 +222,18 @@ def install_packages(module, xbps_path, state, packages): cmd = "%s -y %s" % (xbps_path['install'], " ".join(toInstall)) rc, stdout, stderr = module.run_command(cmd, check_rc=False) - if rc != 0 and not (state == 'latest' and rc == 17): + if rc == 16 and module.params['upgrade_xbps']: + upgrade_xbps(module, xbps_path) + # avoid loops by not trying self-update again + module.params['upgrade_xbps'] = False + install_packages(module, xbps_path, state, packages) + elif rc != 0 and not (state == 'latest' and rc == 17): module.fail_json(msg="failed to install %s" % (package)) module.exit_json(changed=True, msg="installed %s package(s)" % (len(toInstall)), packages=toInstall) - module.exit_json(changed=False, msg="package(s) already installed", - packages=[]) - def check_packages(module, xbps_path, packages, state): """Returns change status of command""" @@ -219,6 +255,22 @@ def check_packages(module, xbps_path, packages, state): packages=[]) +def update_cache(module, xbps_path, upgrade_planned): + """Update package cache""" + if module.check_mode: + if upgrade_planned: + return + module.exit_json( + changed=True, msg='Would have updated the package cache' + ) + changed = update_package_db(module, xbps_path) + if not upgrade_planned: + module.exit_json(changed=changed, msg=( + 'Updated the package master lists' if changed + else 'Package list already up to date' + )) + + def main(): """Returns, calling appropriate command""" @@ -232,7 +284,8 @@ def main(): force=dict(default=False, type='bool'), upgrade=dict(default=False, type='bool'), update_cache=dict(default=True, aliases=['update-cache'], - type='bool') + type='bool'), + upgrade_xbps=dict(default=True, type='bool') ), required_one_of=[['name', 'update_cache', 'upgrade']], supports_check_mode=True) @@ -254,20 +307,8 @@ def main(): elif p['state'] in ['absent', 'removed']: p['state'] = 'absent' - if p["update_cache"] and not module.check_mode: - changed = update_package_db(module, xbps_path) - if p['name'] is None and not p['upgrade']: - if changed: - module.exit_json(changed=True, - msg='Updated the package master lists') - else: - module.exit_json(changed=False, - msg='Package list already up to date') - - if (p['update_cache'] and module.check_mode and not - (p['name'] or p['upgrade'])): - module.exit_json(changed=True, - msg='Would have updated the package cache') + if p['update_cache']: + update_cache(module, xbps_path, (p['name'] or p['upgrade'])) if p['upgrade']: upgrade(module, xbps_path)