diff --git a/plugins/modules/packaging/os/rpm_ostree_pkg.py b/plugins/modules/packaging/os/rpm_ostree_pkg.py new file mode 100644 index 0000000000..7c430732e7 --- /dev/null +++ b/plugins/modules/packaging/os/rpm_ostree_pkg.py @@ -0,0 +1,173 @@ +#!/usr/bin/python +# Copyright: (c) 2018, Dusty Mabe +# Copyright: (c) 2018, Ansible Project +# Copyright: (c) 2021, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: rpm_ostree_pkg +short_description: Install or uninstall overlay additional packages +version_added: "2.0.0" +description: + - Install or uninstall overlay additional packages using C(rpm-ostree) command. +options: + name: + description: + - Name of overlay package to install or remove. + required: true + type: list + elements: str + aliases: [ pkg ] + state: + description: + - State of the overlay package. + - C(present) simply ensures that a desired package is installed. + - C(absent) removes the specified package. + choices: [ 'absent', 'present' ] + default: 'present' + type: str +author: +- Dusty Mabe (@dustymabe) +- Abhijeet Kasurde (@Akasurde) +notes: +- Does not support C(check_mode). +''' + +EXAMPLES = r''' +- name: Install overlay package + community.general.rpm_ostree_pkg: + name: nfs-utils + state: present + +- name: Remove overlay package + community.general.rpm_ostree_pkg: + name: nfs-utils + state: absent +''' + +RETURN = r''' +rc: + description: Return code of rpm-ostree command. + returned: always + type: int + sample: 0 +changed: + description: State changes. + returned: always + type: bool + sample: True +action: + description: Action performed. + returned: always + type: str + sample: 'install' +packages: + description: A list of packages specified. + returned: always + type: list + sample: ['nfs-utils'] +stdout: + description: Stdout of rpm-ostree command. + returned: always + type: str + sample: 'Staging deployment...done\n...' +stderr: + description: Stderr of rpm-ostree command. + returned: always + type: str + sample: '' +cmd: + description: Full command used for performed action. + returned: always + type: str + sample: 'rpm-ostree uninstall --allow-inactive --idempotent --unchanged-exit-77 nfs-utils' +''' + +from ansible.module_utils.basic import AnsibleModule + + +class RpmOstreePkg: + def __init__(self, module): + self.module = module + self.params = module.params + self.state = module.params['state'] + + def ensure(self): + results = dict( + rc=0, + changed=False, + action='', + packages=[], + stdout='', + stderr='', + cmd='', + ) + + # Ensure rpm-ostree command exists + cmd = [self.module.get_bin_path('rpm-ostree', required=True)] + + # Decide action to perform + if self.state in ('present'): + results['action'] = 'install' + cmd.append('install') + elif self.state in ('absent'): + results['action'] = 'uninstall' + cmd.append('uninstall') + + # Additional parameters + cmd.extend(['--allow-inactive', '--idempotent', '--unchanged-exit-77']) + for pkg in self.params['name']: + cmd.append(pkg) + results['packages'].append(pkg) + + rc, out, err = self.module.run_command(cmd) + + results.update(dict( + rc=rc, + cmd=' '.join(cmd), + stdout=out, + stderr=err, + )) + + # A few possible options: + # - rc=0 - succeeded in making a change + # - rc=77 - no change was needed + # - rc=? - error + if rc == 0: + results['changed'] = True + elif rc == 77: + results['changed'] = False + results['rc'] = 0 + else: + self.module.fail_json(msg='non-zero return code', **results) + + self.module.exit_json(**results) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + state=dict( + default="present", + choices=['absent', 'present'] + ), + name=dict( + aliases=["pkg"], + required=True, + type='list', + elements='str', + ), + ), + ) + + rpm_ostree_pkg = RpmOstreePkg(module) + rpm_ostree_pkg.ensure() + + +if __name__ == '__main__': + main() diff --git a/plugins/modules/rpm_ostree_pkg.py b/plugins/modules/rpm_ostree_pkg.py new file mode 120000 index 0000000000..f23edec531 --- /dev/null +++ b/plugins/modules/rpm_ostree_pkg.py @@ -0,0 +1 @@ +./packaging/os/rpm_ostree_pkg.py \ No newline at end of file diff --git a/tests/unit/plugins/modules/packaging/os/test_rpm_ostree_pkg.py b/tests/unit/plugins/modules/packaging/os/test_rpm_ostree_pkg.py new file mode 100644 index 0000000000..537a50d980 --- /dev/null +++ b/tests/unit/plugins/modules/packaging/os/test_rpm_ostree_pkg.py @@ -0,0 +1,107 @@ +# +# Copyright: (c) 2021, Abhijeet Kasurde +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.community.general.tests.unit.compat.mock import call, patch +from ansible_collections.community.general.plugins.modules.packaging.os import rpm_ostree_pkg +from ansible_collections.community.general.tests.unit.plugins.modules.utils import ( + AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args) + + +class RpmOSTreeModuleTestCase(ModuleTestCase): + module = rpm_ostree_pkg + + def setUp(self): + super(RpmOSTreeModuleTestCase, self).setUp() + ansible_module_path = "ansible_collections.community.general.plugins.modules.packaging.os.rpm_ostree_pkg.AnsibleModule" + self.mock_run_command = patch('%s.run_command' % ansible_module_path) + self.module_main_command = self.mock_run_command.start() + self.mock_get_bin_path = patch('%s.get_bin_path' % ansible_module_path) + self.get_bin_path = self.mock_get_bin_path.start() + self.get_bin_path.return_value = '/testbin/rpm-ostree' + + def tearDown(self): + self.mock_run_command.stop() + self.mock_get_bin_path.stop() + super(RpmOSTreeModuleTestCase, self).tearDown() + + def module_main(self, exit_exc): + with self.assertRaises(exit_exc) as exc: + self.module.main() + return exc.exception.args[0] + + def test_present(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_present_unchanged(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (77, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertFalse(result['changed']) + self.assertEqual(0, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_present_failed(self): + set_module_args({'name': 'nfs-utils', 'state': 'present'}) + self.module_main_command.side_effect = [ + (1, '', ''), + ] + + result = self.module_main(AnsibleFailJson) + + self.assertFalse(result['changed']) + self.assertEqual(1, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'install', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_absent(self): + set_module_args({'name': 'nfs-utils', 'state': 'absent'}) + self.module_main_command.side_effect = [ + (0, '', ''), + ] + + result = self.module_main(AnsibleExitJson) + + self.assertTrue(result['changed']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'uninstall', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ]) + + def test_absent_failed(self): + set_module_args({'name': 'nfs-utils', 'state': 'absent'}) + self.module_main_command.side_effect = [ + (1, '', ''), + ] + + result = self.module_main(AnsibleFailJson) + + self.assertFalse(result['changed']) + self.assertEqual(1, result['rc']) + self.assertEqual(['nfs-utils'], result['packages']) + self.module_main_command.assert_has_calls([ + call(['/testbin/rpm-ostree', 'uninstall', '--allow-inactive', '--idempotent', '--unchanged-exit-77', 'nfs-utils']), + ])