mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add 'state' parameter for alternatives (#4557)
* Add 'activate' parameter for alternatives Allow alternatives to be installed without being set as the current selection. * add changelog fragment * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> * rename 'activate' -> 'selected' * rework 'selected' parameter -> 'state' * handle unsetting of currently selected alternative * add integration tests for 'state' parameter * fix linting issues * fix for Python 2.7 compatibility * Remove alternatives file. Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
d7e5e85f3e
commit
29c49febd9
5 changed files with 149 additions and 9 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- alternatives - add ``state`` parameter, which provides control over whether the alternative should be set as the active selection for its alternatives group (https://github.com/ansible-collections/community.general/issues/4543, https://github.com/ansible-collections/community.general/pull/4557).
|
|
@ -41,6 +41,16 @@ options:
|
||||||
- The priority of the alternative.
|
- The priority of the alternative.
|
||||||
type: int
|
type: int
|
||||||
default: 50
|
default: 50
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- C(present) - install the alternative (if not already installed), but do
|
||||||
|
not set it as the currently selected alternative for the group.
|
||||||
|
- C(selected) - install the alternative (if not already installed), and
|
||||||
|
set it as the currently selected alternative for the group.
|
||||||
|
choices: [ present, selected ]
|
||||||
|
default: selected
|
||||||
|
type: str
|
||||||
|
version_added: 4.8.0
|
||||||
requirements: [ update-alternatives ]
|
requirements: [ update-alternatives ]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@ -61,6 +71,13 @@ EXAMPLES = r'''
|
||||||
name: java
|
name: java
|
||||||
path: /usr/lib/jvm/java-7-openjdk-i386/jre/bin/java
|
path: /usr/lib/jvm/java-7-openjdk-i386/jre/bin/java
|
||||||
priority: -10
|
priority: -10
|
||||||
|
|
||||||
|
- name: Install Python 3.5 but do not select it
|
||||||
|
community.general.alternatives:
|
||||||
|
name: python
|
||||||
|
path: /usr/bin/python3.5
|
||||||
|
link: /usr/bin/python
|
||||||
|
state: present
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
@ -70,6 +87,15 @@ import subprocess
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
|
class AlternativeState:
|
||||||
|
PRESENT = "present"
|
||||||
|
SELECTED = "selected"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def to_list(cls):
|
||||||
|
return [cls.PRESENT, cls.SELECTED]
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
||||||
module = AnsibleModule(
|
module = AnsibleModule(
|
||||||
|
@ -78,6 +104,11 @@ def main():
|
||||||
path=dict(type='path', required=True),
|
path=dict(type='path', required=True),
|
||||||
link=dict(type='path'),
|
link=dict(type='path'),
|
||||||
priority=dict(type='int', default=50),
|
priority=dict(type='int', default=50),
|
||||||
|
state=dict(
|
||||||
|
type='str',
|
||||||
|
choices=AlternativeState.to_list(),
|
||||||
|
default=AlternativeState.SELECTED,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
@ -87,6 +118,7 @@ def main():
|
||||||
path = params['path']
|
path = params['path']
|
||||||
link = params['link']
|
link = params['link']
|
||||||
priority = params['priority']
|
priority = params['priority']
|
||||||
|
state = params['state']
|
||||||
|
|
||||||
UPDATE_ALTERNATIVES = module.get_bin_path('update-alternatives', True)
|
UPDATE_ALTERNATIVES = module.get_bin_path('update-alternatives', True)
|
||||||
|
|
||||||
|
@ -126,9 +158,20 @@ def main():
|
||||||
link = line.split()[1]
|
link = line.split()[1]
|
||||||
break
|
break
|
||||||
|
|
||||||
|
changed = False
|
||||||
if current_path != path:
|
if current_path != path:
|
||||||
|
|
||||||
|
# Check mode: expect a change if this alternative is not already
|
||||||
|
# installed, or if it is to be set as the current selection.
|
||||||
if module.check_mode:
|
if module.check_mode:
|
||||||
module.exit_json(changed=True, current_path=current_path)
|
module.exit_json(
|
||||||
|
changed=(
|
||||||
|
path not in all_alternatives or
|
||||||
|
state == AlternativeState.SELECTED
|
||||||
|
),
|
||||||
|
current_path=current_path,
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# install the requested path if necessary
|
# install the requested path if necessary
|
||||||
if path not in all_alternatives:
|
if path not in all_alternatives:
|
||||||
|
@ -141,18 +184,34 @@ def main():
|
||||||
[UPDATE_ALTERNATIVES, '--install', link, name, path, str(priority)],
|
[UPDATE_ALTERNATIVES, '--install', link, name, path, str(priority)],
|
||||||
check_rc=True
|
check_rc=True
|
||||||
)
|
)
|
||||||
|
changed = True
|
||||||
|
|
||||||
# select the requested path
|
# set the current selection to this path (if requested)
|
||||||
module.run_command(
|
if state == AlternativeState.SELECTED:
|
||||||
[UPDATE_ALTERNATIVES, '--set', name, path],
|
module.run_command(
|
||||||
check_rc=True
|
[UPDATE_ALTERNATIVES, '--set', name, path],
|
||||||
)
|
check_rc=True
|
||||||
|
)
|
||||||
|
changed = True
|
||||||
|
|
||||||
module.exit_json(changed=True)
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
module.fail_json(msg=str(dir(cpe)))
|
module.fail_json(msg=str(dir(cpe)))
|
||||||
else:
|
elif current_path == path and state == AlternativeState.PRESENT:
|
||||||
module.exit_json(changed=False)
|
# Case where alternative is currently selected, but state is set
|
||||||
|
# to 'present'. In this case, we set to auto mode.
|
||||||
|
if module.check_mode:
|
||||||
|
module.exit_json(changed=True, current_path=current_path)
|
||||||
|
|
||||||
|
changed = True
|
||||||
|
try:
|
||||||
|
module.run_command(
|
||||||
|
[UPDATE_ALTERNATIVES, '--auto', name],
|
||||||
|
check_rc=True,
|
||||||
|
)
|
||||||
|
except subprocess.CalledProcessError as cpe:
|
||||||
|
module.fail_json(msg=str(dir(cpe)))
|
||||||
|
|
||||||
|
module.exit_json(changed=changed)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -49,6 +49,12 @@
|
||||||
# Test that path is checked: alternatives must fail when path is nonexistent
|
# Test that path is checked: alternatives must fail when path is nonexistent
|
||||||
- import_tasks: path_is_checked.yml
|
- import_tasks: path_is_checked.yml
|
||||||
|
|
||||||
|
# Test operation of the 'state' parameter
|
||||||
|
- block:
|
||||||
|
- include_tasks: remove_links.yml
|
||||||
|
- include_tasks: tests_state.yml
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
always:
|
always:
|
||||||
- include_tasks: remove_links.yml
|
- include_tasks: remove_links.yml
|
||||||
|
|
||||||
|
@ -62,6 +68,7 @@
|
||||||
path: '/usr/bin/dummy{{ item }}'
|
path: '/usr/bin/dummy{{ item }}'
|
||||||
state: absent
|
state: absent
|
||||||
with_sequence: start=1 end=4
|
with_sequence: start=1 end=4
|
||||||
|
|
||||||
# *Disable tests on Fedora 24*
|
# *Disable tests on Fedora 24*
|
||||||
# Shippable Fedora 24 image provides chkconfig-1.7-2.fc24.x86_64 but not the
|
# Shippable Fedora 24 image provides chkconfig-1.7-2.fc24.x86_64 but not the
|
||||||
# latest available version (chkconfig-1.8-1.fc24.x86_64). update-alternatives
|
# latest available version (chkconfig-1.8-1.fc24.x86_64). update-alternatives
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
path: '{{ item }}'
|
path: '{{ item }}'
|
||||||
state: absent
|
state: absent
|
||||||
with_items:
|
with_items:
|
||||||
|
- "{{ alternatives_dir }}/dummy"
|
||||||
- /etc/alternatives/dummy
|
- /etc/alternatives/dummy
|
||||||
- /usr/bin/dummy
|
- /usr/bin/dummy
|
||||||
|
|
71
tests/integration/targets/alternatives/tasks/tests_state.yml
Normal file
71
tests/integration/targets/alternatives/tasks/tests_state.yml
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# Add a few dummy alternatives with state = present and make sure that the
|
||||||
|
# group is in 'auto' mode and the highest priority alternative is selected.
|
||||||
|
- name: Add some dummy alternatives with state = present
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: "/usr/bin/dummy{{ item.n }}"
|
||||||
|
link: /usr/bin/dummy
|
||||||
|
priority: "{{ item.priority }}"
|
||||||
|
state: present
|
||||||
|
loop:
|
||||||
|
- { n: 1, priority: 50 }
|
||||||
|
- { n: 2, priority: 70 }
|
||||||
|
- { n: 3, priority: 25 }
|
||||||
|
|
||||||
|
- name: Ensure that the link group is in auto mode
|
||||||
|
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
|
||||||
|
|
||||||
|
# Execute current selected 'dummy' and ensure it's the alternative we expect
|
||||||
|
- name: Execute the current dummy command
|
||||||
|
shell: dummy
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy2"
|
||||||
|
|
||||||
|
# Add another alternative with state = 'selected' and make sure that
|
||||||
|
# this change results in the group being set to manual mode, and the
|
||||||
|
# new alternative being the selected one.
|
||||||
|
- name: Add another dummy alternative with state = selected
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: /usr/bin/dummy4
|
||||||
|
link: /usr/bin/dummy
|
||||||
|
priority: 10
|
||||||
|
state: selected
|
||||||
|
|
||||||
|
- name: Ensure that the link group is in manual mode
|
||||||
|
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
|
||||||
|
|
||||||
|
- name: Execute the current dummy command
|
||||||
|
shell: dummy
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy4"
|
||||||
|
|
||||||
|
# Set the currently selected alternative to state = 'present' (was previously
|
||||||
|
# selected), and ensure that this results in the group being set to 'auto'
|
||||||
|
# mode, and the highest priority alternative is selected.
|
||||||
|
- name: Set current selected dummy to state = present
|
||||||
|
alternatives:
|
||||||
|
name: dummy
|
||||||
|
path: /usr/bin/dummy4
|
||||||
|
link: /usr/bin/dummy
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure that the link group is in auto mode
|
||||||
|
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
|
||||||
|
|
||||||
|
- name: Execute the current dummy command
|
||||||
|
shell: dummy
|
||||||
|
register: cmd
|
||||||
|
|
||||||
|
- name: Ensure that the expected command was executed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- cmd.stdout == "dummy2"
|
Loading…
Reference in a new issue