mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add modify_inactive_option
option to ini_file
module to ignore matching commented key:value pairs (#7401)
* Add `modify_inactive_option` option to `ini_file` module to ignore matching commented key:value pairs * Add changelog fragment * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> * Minor comment fix * Apply suggestions from code review Co-authored-by: Felix Fontein <felix@fontein.de> --------- Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
parent
714e06089e
commit
6ee1f27304
4 changed files with 152 additions and 8 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- ini_file - add ``modify_inactive_option`` option (https://github.com/ansible-collections/community.general/pull/7401).
|
|
@ -116,6 +116,14 @@ options:
|
||||||
- Allow option without value and without '=' symbol.
|
- Allow option without value and without '=' symbol.
|
||||||
type: bool
|
type: bool
|
||||||
default: false
|
default: false
|
||||||
|
modify_inactive_option:
|
||||||
|
description:
|
||||||
|
- By default the module replaces a commented line that matches the given option.
|
||||||
|
- Set this option to V(false) to avoid this. This is useful when you want to keep commented example
|
||||||
|
C(key=value) pairs for documentation purposes.
|
||||||
|
type: bool
|
||||||
|
default: true
|
||||||
|
version_added: 8.0.0
|
||||||
follow:
|
follow:
|
||||||
description:
|
description:
|
||||||
- This flag indicates that filesystem links, if they exist, should be followed.
|
- This flag indicates that filesystem links, if they exist, should be followed.
|
||||||
|
@ -190,7 +198,7 @@ def match_opt(option, line):
|
||||||
|
|
||||||
def match_active_opt(option, line):
|
def match_active_opt(option, line):
|
||||||
option = re.escape(option)
|
option = re.escape(option)
|
||||||
return re.match('( |\t)*(%s)( |\t)*(=|$)( |\t)*(.*)' % option, line)
|
return re.match('()( |\t)*(%s)( |\t)*(=|$)( |\t)*(.*)' % option, line)
|
||||||
|
|
||||||
|
|
||||||
def update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg):
|
def update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg):
|
||||||
|
@ -213,7 +221,7 @@ def update_section_line(option, changed, section_lines, index, changed_lines, ig
|
||||||
|
|
||||||
def do_ini(module, filename, section=None, option=None, values=None,
|
def do_ini(module, filename, section=None, option=None, values=None,
|
||||||
state='present', exclusive=True, backup=False, no_extra_spaces=False,
|
state='present', exclusive=True, backup=False, no_extra_spaces=False,
|
||||||
ignore_spaces=False, create=True, allow_no_value=False, follow=False):
|
ignore_spaces=False, create=True, allow_no_value=False, modify_inactive_option=True, follow=False):
|
||||||
|
|
||||||
if section is not None:
|
if section is not None:
|
||||||
section = to_text(section)
|
section = to_text(section)
|
||||||
|
@ -310,6 +318,12 @@ def do_ini(module, filename, section=None, option=None, values=None,
|
||||||
# Keep track of changed section_lines
|
# Keep track of changed section_lines
|
||||||
changed_lines = [0] * len(section_lines)
|
changed_lines = [0] * len(section_lines)
|
||||||
|
|
||||||
|
# Determine whether to consider using commented out/inactive options or only active ones
|
||||||
|
if modify_inactive_option:
|
||||||
|
match_function = match_opt
|
||||||
|
else:
|
||||||
|
match_function = match_active_opt
|
||||||
|
|
||||||
# handling multiple instances of option=value when state is 'present' with/without exclusive is a bit complex
|
# handling multiple instances of option=value when state is 'present' with/without exclusive is a bit complex
|
||||||
#
|
#
|
||||||
# 1. edit all lines where we have a option=value pair with a matching value in values[]
|
# 1. edit all lines where we have a option=value pair with a matching value in values[]
|
||||||
|
@ -319,8 +333,8 @@ def do_ini(module, filename, section=None, option=None, values=None,
|
||||||
|
|
||||||
if state == 'present' and option:
|
if state == 'present' and option:
|
||||||
for index, line in enumerate(section_lines):
|
for index, line in enumerate(section_lines):
|
||||||
if match_opt(option, line):
|
if match_function(option, line):
|
||||||
match = match_opt(option, line)
|
match = match_function(option, line)
|
||||||
if values and match.group(7) in values:
|
if values and match.group(7) in values:
|
||||||
matched_value = match.group(7)
|
matched_value = match.group(7)
|
||||||
if not matched_value and allow_no_value:
|
if not matched_value and allow_no_value:
|
||||||
|
@ -343,14 +357,14 @@ def do_ini(module, filename, section=None, option=None, values=None,
|
||||||
# override option with no value to option with value if not allow_no_value
|
# override option with no value to option with value if not allow_no_value
|
||||||
if len(values) > 0:
|
if len(values) > 0:
|
||||||
for index, line in enumerate(section_lines):
|
for index, line in enumerate(section_lines):
|
||||||
if not changed_lines[index] and match_opt(option, line):
|
if not changed_lines[index] and match_function(option, line):
|
||||||
newline = assignment_format % (option, values.pop(0))
|
newline = assignment_format % (option, values.pop(0))
|
||||||
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
|
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
|
||||||
if len(values) == 0:
|
if len(values) == 0:
|
||||||
break
|
break
|
||||||
# remove all remaining option occurrences from the rest of the section
|
# remove all remaining option occurrences from the rest of the section
|
||||||
for index in range(len(section_lines) - 1, 0, -1):
|
for index in range(len(section_lines) - 1, 0, -1):
|
||||||
if not changed_lines[index] and match_opt(option, section_lines[index]):
|
if not changed_lines[index] and match_function(option, section_lines[index]):
|
||||||
del section_lines[index]
|
del section_lines[index]
|
||||||
del changed_lines[index]
|
del changed_lines[index]
|
||||||
changed = True
|
changed = True
|
||||||
|
@ -394,7 +408,7 @@ def do_ini(module, filename, section=None, option=None, values=None,
|
||||||
section_lines = new_section_lines
|
section_lines = new_section_lines
|
||||||
elif not exclusive and len(values) > 0:
|
elif not exclusive and len(values) > 0:
|
||||||
# delete specified option=value line(s)
|
# delete specified option=value line(s)
|
||||||
new_section_lines = [i for i in section_lines if not (match_active_opt(option, i) and match_active_opt(option, i).group(6) in values)]
|
new_section_lines = [i for i in section_lines if not (match_active_opt(option, i) and match_active_opt(option, i).group(7) in values)]
|
||||||
if section_lines != new_section_lines:
|
if section_lines != new_section_lines:
|
||||||
changed = True
|
changed = True
|
||||||
msg = 'option changed'
|
msg = 'option changed'
|
||||||
|
@ -466,6 +480,7 @@ def main():
|
||||||
no_extra_spaces=dict(type='bool', default=False),
|
no_extra_spaces=dict(type='bool', default=False),
|
||||||
ignore_spaces=dict(type='bool', default=False),
|
ignore_spaces=dict(type='bool', default=False),
|
||||||
allow_no_value=dict(type='bool', default=False),
|
allow_no_value=dict(type='bool', default=False),
|
||||||
|
modify_inactive_option=dict(type='bool', default=True),
|
||||||
create=dict(type='bool', default=True),
|
create=dict(type='bool', default=True),
|
||||||
follow=dict(type='bool', default=False)
|
follow=dict(type='bool', default=False)
|
||||||
),
|
),
|
||||||
|
@ -487,6 +502,7 @@ def main():
|
||||||
no_extra_spaces = module.params['no_extra_spaces']
|
no_extra_spaces = module.params['no_extra_spaces']
|
||||||
ignore_spaces = module.params['ignore_spaces']
|
ignore_spaces = module.params['ignore_spaces']
|
||||||
allow_no_value = module.params['allow_no_value']
|
allow_no_value = module.params['allow_no_value']
|
||||||
|
modify_inactive_option = module.params['modify_inactive_option']
|
||||||
create = module.params['create']
|
create = module.params['create']
|
||||||
follow = module.params['follow']
|
follow = module.params['follow']
|
||||||
|
|
||||||
|
@ -500,7 +516,7 @@ def main():
|
||||||
|
|
||||||
(changed, backup_file, diff, msg) = do_ini(
|
(changed, backup_file, diff, msg) = do_ini(
|
||||||
module, path, section, option, values, state, exclusive, backup,
|
module, path, section, option, values, state, exclusive, backup,
|
||||||
no_extra_spaces, ignore_spaces, create, allow_no_value, follow)
|
no_extra_spaces, ignore_spaces, create, allow_no_value, modify_inactive_option, follow)
|
||||||
|
|
||||||
if not module.check_mode and os.path.exists(path):
|
if not module.check_mode and os.path.exists(path):
|
||||||
file_args = module.load_file_common_arguments(module.params)
|
file_args = module.load_file_common_arguments(module.params)
|
||||||
|
|
|
@ -44,3 +44,6 @@
|
||||||
|
|
||||||
- name: include tasks to test ignore_spaces
|
- name: include tasks to test ignore_spaces
|
||||||
include_tasks: tests/05-ignore_spaces.yml
|
include_tasks: tests/05-ignore_spaces.yml
|
||||||
|
|
||||||
|
- name: include tasks to test modify_inactive_option
|
||||||
|
include_tasks: tests/06-modify_inactive_option.yml
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
---
|
||||||
|
# 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
|
||||||
|
|
||||||
|
## testing modify_inactive_option option
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 1 - create test file
|
||||||
|
copy:
|
||||||
|
content: |
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
# Uncomment the line below to enable foo
|
||||||
|
# foo = bar
|
||||||
|
|
||||||
|
dest: "{{ output_file }}"
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 1 - set value for foo with modify_inactive_option set to true
|
||||||
|
ini_file:
|
||||||
|
path: "{{ output_file }}"
|
||||||
|
section: section1
|
||||||
|
option: foo
|
||||||
|
value: bar
|
||||||
|
modify_inactive_option: true
|
||||||
|
register: result1
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 1 - read content from output file
|
||||||
|
slurp:
|
||||||
|
src: "{{ output_file }}"
|
||||||
|
register: output_content
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 1 - set expected content and get current ini file content
|
||||||
|
set_fact:
|
||||||
|
expected1: |
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
# Uncomment the line below to enable foo
|
||||||
|
foo = bar
|
||||||
|
|
||||||
|
content1: "{{ output_content.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 1 - assert 'changed' is true, content is OK and option changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result1 is changed
|
||||||
|
- result1.msg == 'option changed'
|
||||||
|
- content1 == expected1
|
||||||
|
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 2 - create test file
|
||||||
|
copy:
|
||||||
|
content: |
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
# Uncomment the line below to enable foo
|
||||||
|
# foo = bar
|
||||||
|
|
||||||
|
dest: "{{ output_file }}"
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 2 - set value for foo with modify_inactive_option set to false
|
||||||
|
ini_file:
|
||||||
|
path: "{{ output_file }}"
|
||||||
|
section: section1
|
||||||
|
option: foo
|
||||||
|
value: bar
|
||||||
|
modify_inactive_option: false
|
||||||
|
register: result2
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 2 - read content from output file
|
||||||
|
slurp:
|
||||||
|
src: "{{ output_file }}"
|
||||||
|
register: output_content
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 2 - set expected content and get current ini file content
|
||||||
|
set_fact:
|
||||||
|
expected2: |
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
foo = bar
|
||||||
|
# Uncomment the line below to enable foo
|
||||||
|
# foo = bar
|
||||||
|
|
||||||
|
content2: "{{ output_content.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 2 - assert 'changed' is true and content is OK and option added
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result2 is changed
|
||||||
|
- result2.msg == 'option added'
|
||||||
|
- content2 == expected2
|
||||||
|
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 3 - remove foo=bar with modify_inactive_option set to true to ensure it doesn't have effect for removal
|
||||||
|
ini_file:
|
||||||
|
path: "{{ output_file }}"
|
||||||
|
section: section1
|
||||||
|
option: foo
|
||||||
|
value: bar
|
||||||
|
modify_inactive_option: true
|
||||||
|
state: absent
|
||||||
|
register: result3
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 3 - read content from output file
|
||||||
|
slurp:
|
||||||
|
src: "{{ output_file }}"
|
||||||
|
register: output_content
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 3 - set expected content and get current ini file content
|
||||||
|
set_fact:
|
||||||
|
expected3: |
|
||||||
|
|
||||||
|
[section1]
|
||||||
|
# Uncomment the line below to enable foo
|
||||||
|
# foo = bar
|
||||||
|
|
||||||
|
content3: "{{ output_content.content | b64decode }}"
|
||||||
|
|
||||||
|
- name: test-modify_inactive_option 3 - assert 'changed' is true and content is OK and active option removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result3 is changed
|
||||||
|
- result3.msg == 'option changed'
|
||||||
|
- content3 == expected3
|
Loading…
Reference in a new issue