-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce bootc functionality (#8606)
* introduce bootc functionality Signed-off-by: Ryan Cook <[email protected]> Co-authored-by: Alexei Znamensky <[email protected]> * fix of test Signed-off-by: Ryan Cook <[email protected]> * switch stdout var Signed-off-by: Ryan Cook <[email protected]> * Feedback on NOTE format Co-authored-by: Felix Fontein <[email protected]> * addition of trailing comma Co-authored-by: Felix Fontein <[email protected]> * addition of trailing comma Co-authored-by: Felix Fontein <[email protected]> * incorporating feedback from russoz Signed-off-by: Ryan Cook <[email protected]> * error in stdout Signed-off-by: Ryan Cook <[email protected]> * proper rc checking and status Signed-off-by: Ryan Cook <[email protected]> * linting Signed-off-by: Ryan Cook <[email protected]> * Update version Co-authored-by: Felix Fontein <[email protected]> --------- Signed-off-by: Ryan Cook <[email protected]> Co-authored-by: Alexei Znamensky <[email protected]> Co-authored-by: Felix Fontein <[email protected]>
- Loading branch information
1 parent
52126b8
commit 1a8f172
Showing
3 changed files
with
169 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
#!/usr/bin/python | ||
|
||
# Copyright (c) 2024, Ryan Cook <[email protected]> | ||
# 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 | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
DOCUMENTATION = ''' | ||
--- | ||
module: bootc_manage | ||
version_added: 9.3.0 | ||
author: | ||
- Ryan Cook (@cooktheryan) | ||
short_description: Bootc Switch and Upgrade | ||
description: | ||
- This module manages the switching and upgrading of C(bootc). | ||
options: | ||
state: | ||
description: | ||
- 'Control to apply the latest image or switch the image.' | ||
- 'B(Note:) This will not reboot the system.' | ||
- 'Please use M(ansible.builtin.reboot) to reboot the system.' | ||
required: true | ||
type: str | ||
choices: ['switch', 'latest'] | ||
image: | ||
description: | ||
- 'The image to switch to.' | ||
- 'This is required when O(state=switch).' | ||
required: false | ||
type: str | ||
''' | ||
|
||
EXAMPLES = ''' | ||
# Switch to a different image | ||
- name: Provide image to switch to a different image and retain the current running image | ||
community.general.bootc_manage: | ||
state: switch | ||
image: "example.com/image:latest" | ||
# Apply updates of the current running image | ||
- name: Apply updates of the current running image | ||
community.general.bootc_manage: | ||
state: latest | ||
''' | ||
|
||
RETURN = ''' | ||
''' | ||
|
||
|
||
from ansible.module_utils.basic import AnsibleModule | ||
from ansible.module_utils.common.locale import get_best_parsable_locale | ||
|
||
|
||
def main(): | ||
argument_spec = dict( | ||
state=dict(type='str', required=True, choices=['switch', 'latest']), | ||
image=dict(type='str', required=False), | ||
) | ||
module = AnsibleModule( | ||
argument_spec=argument_spec, | ||
required_if=[ | ||
('state', 'switch', ['image']), | ||
], | ||
) | ||
|
||
state = module.params['state'] | ||
image = module.params['image'] | ||
|
||
if state == 'switch': | ||
command = ['bootc', 'switch', image, '--retain'] | ||
elif state == 'latest': | ||
command = ['bootc', 'upgrade'] | ||
|
||
locale = get_best_parsable_locale(module) | ||
module.run_command_environ_update = dict(LANG=locale, LC_ALL=locale, LC_MESSAGES=locale, LC_CTYPE=locale, LANGUAGE=locale) | ||
rc, stdout, err = module.run_command(command, check_rc=True) | ||
|
||
if 'Queued for next boot: ' in stdout: | ||
result = {'changed': True, 'stdout': stdout} | ||
module.exit_json(**result) | ||
elif 'No changes in ' in stdout or 'Image specification is unchanged.' in stdout: | ||
result = {'changed': False, 'stdout': stdout} | ||
module.exit_json(**result) | ||
else: | ||
result = {'changed': False, 'stderr': err} | ||
module.fail_json(msg='ERROR: Command execution failed.', **result) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# Copyright (c) Ansible project | ||
# 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 | ||
|
||
from __future__ import (absolute_import, division, print_function) | ||
__metaclass__ = type | ||
|
||
from ansible_collections.community.general.tests.unit.compat.mock import patch | ||
from ansible_collections.community.general.plugins.modules import bootc_manage | ||
from ansible_collections.community.general.tests.unit.plugins.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args | ||
|
||
|
||
class TestBootcManageModule(ModuleTestCase): | ||
|
||
def setUp(self): | ||
super(TestBootcManageModule, self).setUp() | ||
self.module = bootc_manage | ||
|
||
def tearDown(self): | ||
super(TestBootcManageModule, self).tearDown() | ||
|
||
def test_switch_without_image(self): | ||
"""Failure if state is 'switch' but no image provided""" | ||
set_module_args({'state': 'switch'}) | ||
with self.assertRaises(AnsibleFailJson) as result: | ||
self.module.main() | ||
self.assertEqual(result.exception.args[0]['msg'], "state is switch but all of the following are missing: image") | ||
|
||
def test_switch_with_image(self): | ||
"""Test successful switch with image provided""" | ||
set_module_args({'state': 'switch', 'image': 'example.com/image:latest'}) | ||
with patch('ansible.module_utils.basic.AnsibleModule.run_command') as run_command_mock: | ||
run_command_mock.return_value = (0, 'Queued for next boot: ', '') | ||
with self.assertRaises(AnsibleExitJson) as result: | ||
self.module.main() | ||
self.assertTrue(result.exception.args[0]['changed']) | ||
|
||
def test_latest_state(self): | ||
"""Test successful upgrade to the latest state""" | ||
set_module_args({'state': 'latest'}) | ||
with patch('ansible.module_utils.basic.AnsibleModule.run_command') as run_command_mock: | ||
run_command_mock.return_value = (0, 'Queued for next boot: ', '') | ||
with self.assertRaises(AnsibleExitJson) as result: | ||
self.module.main() | ||
self.assertTrue(result.exception.args[0]['changed']) | ||
|
||
def test_latest_state_no_change(self): | ||
"""Test no change for latest state""" | ||
set_module_args({'state': 'latest'}) | ||
with patch('ansible.module_utils.basic.AnsibleModule.run_command') as run_command_mock: | ||
run_command_mock.return_value = (0, 'No changes in ', '') | ||
with self.assertRaises(AnsibleExitJson) as result: | ||
self.module.main() | ||
self.assertFalse(result.exception.args[0]['changed']) | ||
|
||
def test_switch_image_failure(self): | ||
"""Test failure during image switch""" | ||
set_module_args({'state': 'switch', 'image': 'example.com/image:latest'}) | ||
with patch('ansible.module_utils.basic.AnsibleModule.run_command') as run_command_mock: | ||
run_command_mock.return_value = (1, '', 'ERROR') | ||
with self.assertRaises(AnsibleFailJson) as result: | ||
self.module.main() | ||
self.assertEqual(result.exception.args[0]['msg'], 'ERROR: Command execution failed.') | ||
|
||
def test_latest_state_failure(self): | ||
"""Test failure during upgrade""" | ||
set_module_args({'state': 'latest'}) | ||
with patch('ansible.module_utils.basic.AnsibleModule.run_command') as run_command_mock: | ||
run_command_mock.return_value = (1, '', 'ERROR') | ||
with self.assertRaises(AnsibleFailJson) as result: | ||
self.module.main() | ||
self.assertEqual(result.exception.args[0]['msg'], 'ERROR: Command execution failed.') |