Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- "ini_file - fixes adding or replacing a whole section (https://github.com/ansible-collections/community.general/pull/10288)."
163 changes: 97 additions & 66 deletions plugins/modules/ini_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@
value: xxxxxxxxxxxxxxxxxxxx
mode: '0600'
state: present

- name: Add or replace whole section
community.general.ini_file:
path: /etc/wireguard/wg0.conf
section: Peer
value: |
AllowedIps = 10.4.0.11/32
PublicKey = xxxxxxxxxxxxxxxxxxxx
mode: '0600'
state: present
"""

import io
Expand Down Expand Up @@ -427,72 +437,84 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
# 2. edit all the remaining lines where we have a matching option
# 3. delete remaining lines where we have a matching option
# 4. insert missing option line(s) at the end of the section

if state == 'present' and option:
for index, line in enumerate(section_lines):
if match_function(option, line):
match = match_function(option, line)
if values and match.group(8) in values:
matched_value = match.group(8)
if not matched_value and allow_no_value:
if state == 'present':
if option:
for index, line in enumerate(section_lines):
if match_function(option, line):
match = match_function(option, line)
if values and match.group(8) in values:
matched_value = match.group(8)
if not matched_value and allow_no_value:
# replace existing option with no value line(s)
newline = u'%s\n' % option
option_no_value_present = True
else:
# replace existing option=value line(s)
newline = assignment_format % (option, matched_value)
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
values.remove(matched_value)
elif not values and allow_no_value:
# replace existing option with no value line(s)
newline = u'%s\n' % option
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
option_no_value_present = True
else:
# replace existing option=value line(s)
newline = assignment_format % (option, matched_value)
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
values.remove(matched_value)
elif not values and allow_no_value:
# replace existing option with no value line(s)
newline = u'%s\n' % option
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
option_no_value_present = True
break

if state == 'present' and exclusive and not allow_no_value:
# override option with no value to option with value if not allow_no_value
if len(values) > 0:
for index, line in enumerate(section_lines):
if not changed_lines[index] and match_function(option, line):
newline = assignment_format % (option, values.pop(0))
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
if len(values) == 0:
break
# remove all remaining option occurrences from the rest of the section
for index in range(len(section_lines) - 1, 0, -1):
if not changed_lines[index] and match_function(option, section_lines[index]):
del section_lines[index]
del changed_lines[index]
changed = True
msg = 'option changed'

if state == 'present':
# insert missing option line(s) at the end of the section
for index in range(len(section_lines), 0, -1):
# search backwards for previous non-blank or non-comment line
if not non_blank_non_comment_pattern.match(section_lines[index - 1]):
if option and values:
# insert option line(s)
for element in values[::-1]:
# items are added backwards, so traverse the list backwards to not confuse the user
# otherwise some of their options might appear in reverse order for whatever fancy reason ¯\_(ツ)_/¯
if element is not None:
# insert option=value line
section_lines.insert(index, assignment_format % (option, element))
msg = 'option added'
changed = True
elif element is None and allow_no_value:
# insert option with no value line
section_lines.insert(index, u'%s\n' % option)
msg = 'option added'
changed = True
elif option and not values and allow_no_value and not option_no_value_present:
# insert option with no value line(s)
section_lines.insert(index, u'%s\n' % option)
msg = 'option added'
changed = True
break
if exclusive and not allow_no_value:
# override option with no value to option with value if not allow_no_value
if len(values) > 0:
for index, line in enumerate(section_lines):
if not changed_lines[index] and match_function(option, line):
newline = assignment_format % (option, values.pop(0))
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
if len(values) == 0:
break
# remove all remaining option occurrences from the rest of the section
for index in range(len(section_lines) - 1, 0, -1):
if not changed_lines[index] and match_function(
option, section_lines[index]
):
del section_lines[index]
del changed_lines[index]
changed = True
msg = 'option changed'

# insert missing option line(s) at the end of the section
for index in range(len(section_lines), 0, -1):
# search backwards for previous non-blank or non-comment line
if not non_blank_non_comment_pattern.match(section_lines[index - 1]):
if values:
# insert option line(s)
for element in values[::-1]:
# items are added backwards, so traverse the list backwards to not confuse the user
# otherwise some of their options might appear in reverse order for whatever fancy reason ¯\_(ツ)_/¯
if element is not None:
# insert option=value line
section_lines.insert(
index, assignment_format % (option, element)
)
msg = 'option added'
changed = True
elif element is None and allow_no_value:
# insert option with no value line
section_lines.insert(index, u'%s\n' % option)
msg = 'option added'
changed = True
elif allow_no_value and not option_no_value_present :
# insert option with no value line(s)
section_lines.insert(index, u'%s\n' % option)
msg = 'option added'
changed = True
break
elif within_section and len(section_lines) > 0 and len(values) > 0:
original = ''.join(section_lines[1:])
replacement = ''.join(values)
if not replacement.endswith('\n'):
replacement += '\n'
if original != replacement:
section_lines = [section_lines[0], replacement]
msg = 'section replaced'
changed = True

if state == 'absent':
if option:
Expand Down Expand Up @@ -539,11 +561,20 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
for value in condition['values']:
if value not in values:
values.append(value)
if option and values:
for value in values:
ini_lines.append(assignment_format % (option, value))
elif option and not values and allow_no_value:
ini_lines.append(u'%s\n' % option)
if option:
if values:
for value in values:
ini_lines.append(assignment_format % (option, value))
elif not values and allow_no_value:
ini_lines.append('%s\n' % option)
else:
msg = 'only section added'
elif len(values) > 0:
replacement = ''.join(values)
if not replacement.endswith('\n'):
replacement += '\n'
ini_lines.append(replacement)
msg = 'section added'
else:
msg = 'only section added'
changed = True
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
# 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 section selection

- name: test-section 1 - Create starting ini file
copy:
content: |
[drinks]
fav = lemonade
beverage = orange juice

dest: "{{ output_file }}"

- name: test-section 1 - Add new block
ini_file:
dest: "{{ output_file }}"
section: food
value: |
fav = hamburger
beverage = None
state: present
register: result1

- name: test-section 1 - Read modified file
slurp:
src: "{{ output_file }}"
register: output_content

- name: test-section 1 - Create expected result
set_fact:
expected1: |
[drinks]
fav = lemonade
beverage = orange juice
car = volvo

[food]
fav = hamburger
beverage = None
output1: "{{ output_content.content | b64decode }}"

- name: test-section 1 - Section was added at end
assert:
that:
- result1 is changed
- result1.msg == 'section added'
- output1 == expected1

# ----------------

- name: test-section 2 - Create starting ini file
copy:
content: |
[drinks]
fav = lemonade
beverage = orange juice

[drinks]
fav = lemonade
beverage = pineapple juice

dest: "{{ output_file }}"

- name: test-section 2 - Modify starting ini file
ini_file:
dest: "{{ output_file }}"
section: drinks
section_has_values:
- option: beverage
value: pineapple juice
value: |
fav = lemonade
car = volvo
state: present
register: result1

- name: test-section 2 - Read modified file
slurp:
src: "{{ output_file }}"
register: output_content

- name: test-section 2 - Create expected result
set_fact:
expected1: |
[drinks]
fav = lemonade
beverage = orange juice

[drinks]
fav = lemonade
car = volvo
output1: "{{ output_content.content | b64decode }}"

- name: test-section 2 - Second section was replaced with value
assert:
that:
- result1 is changed
- result1.msg == 'section replaced'
- output1 == expected1

# ----------------

- name: test-section 3 - Create starting ini file
copy:
content: |
[drinks]
fav = lemonade
beverage = orange juice

[drinks]
fav = lemonade
beverage = pineapple juice

dest: "{{ output_file }}"

- name: test-section 3 - Modify starting ini file
ini_file:
dest: "{{ output_file }}"
section: drinks
section_has_values:
- option: beverage
value: pineapple juice
state: absent
register: result1

- name: test-section 3 - Read modified file
slurp:
src: "{{ output_file }}"
register: output_content

- name: test-section 3 - Create expected result
set_fact:
expected1: |
[drinks]
fav = lemonade
beverage = orange juice
output1: "{{ output_content.content | b64decode }}"

- name: test-section 3 - Section was removed for matching section
assert:
that:
- result1 is changed
- result1.msg == 'section removed'
- output1 == expected1