Skip to content

Commit c6bcc3a

Browse files
committed
Support setting whole section when option is unset
Docs sound like you can set a section by not setting option, but it didn't work previously, this fixes that.
1 parent d4f2b2f commit c6bcc3a

File tree

3 files changed

+247
-67
lines changed

3 files changed

+247
-67
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
bugfixes:
2+
- "ini_file - fixes adding or replacing a whole section (https://github.com/ansible-collections/community.general/pull/10288)."

plugins/modules/ini_file.py

Lines changed: 98 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,16 @@
254254
value: xxxxxxxxxxxxxxxxxxxx
255255
mode: '0600'
256256
state: present
257+
258+
- name: Add or replace whole section
259+
community.general.ini_file:
260+
path: /etc/wireguard/wg0.conf
261+
section: Peer
262+
value: |
263+
AllowedIps = 10.4.0.11/32
264+
PublicKey = xxxxxxxxxxxxxxxxxxxx
265+
mode: '0600'
266+
state: present
257267
"""
258268

259269
import io
@@ -427,72 +437,84 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
427437
# 2. edit all the remaining lines where we have a matching option
428438
# 3. delete remaining lines where we have a matching option
429439
# 4. insert missing option line(s) at the end of the section
430-
431-
if state == 'present' and option:
432-
for index, line in enumerate(section_lines):
433-
if match_function(option, line):
434-
match = match_function(option, line)
435-
if values and match.group(8) in values:
436-
matched_value = match.group(8)
437-
if not matched_value and allow_no_value:
440+
if state == 'present':
441+
if option:
442+
for index, line in enumerate(section_lines):
443+
if match_function(option, line):
444+
match = match_function(option, line)
445+
if values and match.group(8) in values:
446+
matched_value = match.group(8)
447+
if not matched_value and allow_no_value:
448+
# replace existing option with no value line(s)
449+
newline = u'%s\n' % option
450+
option_no_value_present = True
451+
else:
452+
# replace existing option=value line(s)
453+
newline = assignment_format % (option, matched_value)
454+
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
455+
values.remove(matched_value)
456+
elif not values and allow_no_value:
438457
# replace existing option with no value line(s)
439458
newline = u'%s\n' % option
459+
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
440460
option_no_value_present = True
441-
else:
442-
# replace existing option=value line(s)
443-
newline = assignment_format % (option, matched_value)
444-
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
445-
values.remove(matched_value)
446-
elif not values and allow_no_value:
447-
# replace existing option with no value line(s)
448-
newline = u'%s\n' % option
449-
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
450-
option_no_value_present = True
451-
break
452-
453-
if state == 'present' and exclusive and not allow_no_value:
454-
# override option with no value to option with value if not allow_no_value
455-
if len(values) > 0:
456-
for index, line in enumerate(section_lines):
457-
if not changed_lines[index] and match_function(option, line):
458-
newline = assignment_format % (option, values.pop(0))
459-
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
460-
if len(values) == 0:
461461
break
462-
# remove all remaining option occurrences from the rest of the section
463-
for index in range(len(section_lines) - 1, 0, -1):
464-
if not changed_lines[index] and match_function(option, section_lines[index]):
465-
del section_lines[index]
466-
del changed_lines[index]
467-
changed = True
468-
msg = 'option changed'
469462

470-
if state == 'present':
471-
# insert missing option line(s) at the end of the section
472-
for index in range(len(section_lines), 0, -1):
473-
# search backwards for previous non-blank or non-comment line
474-
if not non_blank_non_comment_pattern.match(section_lines[index - 1]):
475-
if option and values:
476-
# insert option line(s)
477-
for element in values[::-1]:
478-
# items are added backwards, so traverse the list backwards to not confuse the user
479-
# otherwise some of their options might appear in reverse order for whatever fancy reason ¯\_(ツ)_/¯
480-
if element is not None:
481-
# insert option=value line
482-
section_lines.insert(index, assignment_format % (option, element))
483-
msg = 'option added'
484-
changed = True
485-
elif element is None and allow_no_value:
486-
# insert option with no value line
487-
section_lines.insert(index, u'%s\n' % option)
488-
msg = 'option added'
489-
changed = True
490-
elif option and not values and allow_no_value and not option_no_value_present:
491-
# insert option with no value line(s)
492-
section_lines.insert(index, u'%s\n' % option)
493-
msg = 'option added'
494-
changed = True
495-
break
463+
if exclusive and not allow_no_value:
464+
# override option with no value to option with value if not allow_no_value
465+
if len(values) > 0:
466+
for index, line in enumerate(section_lines):
467+
if not changed_lines[index] and match_function(option, line):
468+
newline = assignment_format % (option, values.pop(0))
469+
(changed, msg) = update_section_line(option, changed, section_lines, index, changed_lines, ignore_spaces, newline, msg)
470+
if len(values) == 0:
471+
break
472+
# remove all remaining option occurrences from the rest of the section
473+
for index in range(len(section_lines) - 1, 0, -1):
474+
if not changed_lines[index] and match_function(
475+
option, section_lines[index]
476+
):
477+
del section_lines[index]
478+
del changed_lines[index]
479+
changed = True
480+
msg = 'option changed'
481+
482+
# insert missing option line(s) at the end of the section
483+
for index in range(len(section_lines), 0, -1):
484+
# search backwards for previous non-blank or non-comment line
485+
if not non_blank_non_comment_pattern.match(section_lines[index - 1]):
486+
if values:
487+
# insert option line(s)
488+
for element in values[::-1]:
489+
# items are added backwards, so traverse the list backwards to not confuse the user
490+
# otherwise some of their options might appear in reverse order for whatever fancy reason ¯\_(ツ)_/¯
491+
if element is not None:
492+
# insert option=value line
493+
section_lines.insert(
494+
index, assignment_format % (option, element)
495+
)
496+
msg = 'option added'
497+
changed = True
498+
elif element is None and allow_no_value:
499+
# insert option with no value line
500+
section_lines.insert(index, u'%s\n' % option)
501+
msg = 'option added'
502+
changed = True
503+
elif allow_no_value and not option_no_value_present :
504+
# insert option with no value line(s)
505+
section_lines.insert(index, u'%s\n' % option)
506+
msg = 'option added'
507+
changed = True
508+
break
509+
elif within_section and len(section_lines) > 0 and len(values) > 0:
510+
original = ''.join(section_lines[1:])
511+
replacement = ''.join(values)
512+
if not replacement.endswith('\n'):
513+
replacement += '\n'
514+
if original != replacement:
515+
section_lines = [section_lines[0], replacement]
516+
msg = 'section replaced'
517+
changed = True
496518

497519
if state == 'absent':
498520
if option:
@@ -539,11 +561,20 @@ def do_ini(module, filename, section=None, section_has_values=None, option=None,
539561
for value in condition['values']:
540562
if value not in values:
541563
values.append(value)
542-
if option and values:
543-
for value in values:
544-
ini_lines.append(assignment_format % (option, value))
545-
elif option and not values and allow_no_value:
546-
ini_lines.append(u'%s\n' % option)
564+
if option:
565+
if values:
566+
for value in values:
567+
ini_lines.append(assignment_format % (option, value))
568+
elif not values and allow_no_value:
569+
ini_lines.append('%s\n' % option)
570+
else:
571+
msg = 'only section added'
572+
elif len(values) > 0:
573+
replacement = ''.join(values)
574+
if not replacement.endswith('\n'):
575+
replacement += '\n'
576+
ini_lines.append(replacement)
577+
msg = 'section added'
547578
else:
548579
msg = 'only section added'
549580
changed = True
@@ -659,4 +690,4 @@ def main():
659690

660691

661692
if __name__ == '__main__':
662-
main()
693+
main()
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
---
2+
# Copyright (c) Ansible Project
3+
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
4+
# SPDX-License-Identifier: GPL-3.0-or-later
5+
6+
## testing section selection
7+
8+
- name: test-section 1 - Create starting ini file
9+
copy:
10+
content: |
11+
[drinks]
12+
fav = lemonade
13+
beverage = orange juice
14+
15+
dest: "{{ output_file }}"
16+
17+
- name: test-section 1 - Add new block
18+
ini_file:
19+
dest: "{{ output_file }}"
20+
section: food
21+
value: |
22+
fav = hamburger
23+
beverage = None
24+
state: present
25+
register: result1
26+
27+
- name: test-section 1 - Read modified file
28+
slurp:
29+
src: "{{ output_file }}"
30+
register: output_content
31+
32+
- name: test-section 1 - Create expected result
33+
set_fact:
34+
expected1: |
35+
[drinks]
36+
fav = lemonade
37+
beverage = orange juice
38+
car = volvo
39+
40+
[food]
41+
fav = hamburger
42+
beverage = None
43+
output1: "{{ output_content.content | b64decode }}"
44+
45+
- name: test-section 1 - Section was added at end
46+
assert:
47+
that:
48+
- result1 is changed
49+
- result1.msg == 'section added'
50+
- output1 == expected1
51+
52+
# ----------------
53+
54+
- name: test-section 2 - Create starting ini file
55+
copy:
56+
content: |
57+
[drinks]
58+
fav = lemonade
59+
beverage = orange juice
60+
61+
[drinks]
62+
fav = lemonade
63+
beverage = pineapple juice
64+
65+
dest: "{{ output_file }}"
66+
67+
- name: test-section 2 - Modify starting ini file
68+
ini_file:
69+
dest: "{{ output_file }}"
70+
section: drinks
71+
section_has_values:
72+
- option: beverage
73+
value: pineapple juice
74+
value: |
75+
fav = lemonade
76+
car = volvo
77+
state: present
78+
register: result1
79+
80+
- name: test-section 2 - Read modified file
81+
slurp:
82+
src: "{{ output_file }}"
83+
register: output_content
84+
85+
- name: test-section 2 - Create expected result
86+
set_fact:
87+
expected1: |
88+
[drinks]
89+
fav = lemonade
90+
beverage = orange juice
91+
92+
[drinks]
93+
fav = lemonade
94+
car = volvo
95+
output1: "{{ output_content.content | b64decode }}"
96+
97+
- name: test-section 2 - Second section was replaced with value
98+
assert:
99+
that:
100+
- result1 is changed
101+
- result1.msg == 'section replaced'
102+
- output1 == expected1
103+
104+
# ----------------
105+
106+
- name: test-section 3 - Create starting ini file
107+
copy:
108+
content: |
109+
[drinks]
110+
fav = lemonade
111+
beverage = orange juice
112+
113+
[drinks]
114+
fav = lemonade
115+
beverage = pineapple juice
116+
117+
dest: "{{ output_file }}"
118+
119+
- name: test-section 3 - Modify starting ini file
120+
ini_file:
121+
dest: "{{ output_file }}"
122+
section: drinks
123+
section_has_values:
124+
- option: beverage
125+
value: pineapple juice
126+
state: absent
127+
register: result1
128+
129+
- name: test-section 3 - Read modified file
130+
slurp:
131+
src: "{{ output_file }}"
132+
register: output_content
133+
134+
- name: test-section 3 - Create expected result
135+
set_fact:
136+
expected1: |
137+
[drinks]
138+
fav = lemonade
139+
beverage = orange juice
140+
output1: "{{ output_content.content | b64decode }}"
141+
142+
- name: test-section 3 - Section was removed for matching section
143+
assert:
144+
that:
145+
- result1 is changed
146+
- result1.msg == 'section removed'
147+
- output1 == expected1

0 commit comments

Comments
 (0)