1
0
Fork 0
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:
njutn95 2023-10-25 08:48:49 +02:00 committed by GitHub
parent 714e06089e
commit 6ee1f27304
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 152 additions and 8 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- ini_file - add ``modify_inactive_option`` option (https://github.com/ansible-collections/community.general/pull/7401).

View file

@ -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)

View file

@ -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

View file

@ -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