lineinfile - add search_string parameter for non-regexp searching (#70647)
* Add tests for search_string * Improve examples * Add changelog
This commit is contained in:
parent
9a9272305a
commit
69631da889
8 changed files with 579 additions and 19 deletions
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- lineinfile - add search_string parameter for non-regexp searching (https://github.com/ansible/ansible/issues/70470)
|
|
@ -44,6 +44,17 @@ options:
|
||||||
type: str
|
type: str
|
||||||
aliases: [ regex ]
|
aliases: [ regex ]
|
||||||
version_added: '1.7'
|
version_added: '1.7'
|
||||||
|
search_string:
|
||||||
|
description:
|
||||||
|
- The literal string to look for in every line of the file. This does not have to match the entire line.
|
||||||
|
- For C(state=present), the line to replace if the string is found in the file. Only the last line found will be replaced.
|
||||||
|
- For C(state=absent), the line(s) to remove if the string is in the line.
|
||||||
|
- If the literal expression is not matched, the line will be
|
||||||
|
added to the file in keeping with C(insertbefore) or C(insertafter)
|
||||||
|
settings.
|
||||||
|
- Mutually exclusive with C(backrefs) and C(regexp).
|
||||||
|
type: str
|
||||||
|
version_added: '2.11'
|
||||||
state:
|
state:
|
||||||
description:
|
description:
|
||||||
- Whether the line should be there or not.
|
- Whether the line should be there or not.
|
||||||
|
@ -68,6 +79,7 @@ options:
|
||||||
does not match anywhere in the file, the file will be left unchanged.
|
does not match anywhere in the file, the file will be left unchanged.
|
||||||
- If the C(regexp) does match, the last matching line will be replaced by
|
- If the C(regexp) does match, the last matching line will be replaced by
|
||||||
the expanded line parameter.
|
the expanded line parameter.
|
||||||
|
- Mutually exclusive with C(search_string).
|
||||||
type: bool
|
type: bool
|
||||||
default: no
|
default: no
|
||||||
version_added: "1.1"
|
version_added: "1.1"
|
||||||
|
@ -136,6 +148,7 @@ seealso:
|
||||||
author:
|
author:
|
||||||
- Daniel Hokka Zakrissoni (@dhozac)
|
- Daniel Hokka Zakrissoni (@dhozac)
|
||||||
- Ahti Kitsik (@ahtik)
|
- Ahti Kitsik (@ahtik)
|
||||||
|
- Jose Angel Munoz (@imjoseangel)
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = r'''
|
EXAMPLES = r'''
|
||||||
|
@ -161,6 +174,15 @@ EXAMPLES = r'''
|
||||||
group: root
|
group: root
|
||||||
mode: '0644'
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Replace a localhost entry searching for a literal string to avoid escaping
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/hosts
|
||||||
|
search_string: '127.0.0.1'
|
||||||
|
line: 127.0.0.1 localhost
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
- name: Ensure the default Apache port is 8080
|
- name: Ensure the default Apache port is 8080
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /etc/httpd/conf/httpd.conf
|
path: /etc/httpd/conf/httpd.conf
|
||||||
|
@ -168,6 +190,13 @@ EXAMPLES = r'''
|
||||||
insertafter: '^#Listen '
|
insertafter: '^#Listen '
|
||||||
line: Listen 8080
|
line: Listen 8080
|
||||||
|
|
||||||
|
- name: Ensure php extension matches new pattern
|
||||||
|
lineinfile:
|
||||||
|
path: /etc/httpd/conf/httpd.conf
|
||||||
|
search_string: '<FilesMatch ".php[45]?$">'
|
||||||
|
insertafter: '^\t<Location \/>\n'
|
||||||
|
line: ' <FilesMatch ".php[34]?$">'
|
||||||
|
|
||||||
- name: Ensure we have our own comment added to /etc/services
|
- name: Ensure we have our own comment added to /etc/services
|
||||||
ansible.builtin.lineinfile:
|
ansible.builtin.lineinfile:
|
||||||
path: /etc/services
|
path: /etc/services
|
||||||
|
@ -253,7 +282,7 @@ def check_file_attrs(module, changed, message, diff):
|
||||||
return message, changed
|
return message, changed
|
||||||
|
|
||||||
|
|
||||||
def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
def present(module, dest, regexp, search_string, line, insertafter, insertbefore, create,
|
||||||
backup, backrefs, firstmatch):
|
backup, backrefs, firstmatch):
|
||||||
|
|
||||||
diff = {'before': '',
|
diff = {'before': '',
|
||||||
|
@ -301,8 +330,8 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
# "If regular expressions are passed to both regexp and
|
# "If regular expressions are passed to both regexp and
|
||||||
# insertafter, insertafter is only honored if no match for regexp is found."
|
# insertafter, insertafter is only honored if no match for regexp is found."
|
||||||
# Therefore:
|
# Therefore:
|
||||||
# 1. regexp was found -> ignore insertafter, replace the founded line
|
# 1. regexp or search_string was found -> ignore insertafter, replace the founded line
|
||||||
# 2. regexp was not found -> insert the line after 'insertafter' or 'insertbefore' line
|
# 2. regexp or search_string was not found -> insert the line after 'insertafter' or 'insertbefore' line
|
||||||
|
|
||||||
# Given the above:
|
# Given the above:
|
||||||
# 1. First check that there is no match for regexp:
|
# 1. First check that there is no match for regexp:
|
||||||
|
@ -315,7 +344,17 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
if firstmatch:
|
if firstmatch:
|
||||||
break
|
break
|
||||||
|
|
||||||
# 2. When no match found on the previous step,
|
# 2. Second check that there is no match for search_string:
|
||||||
|
if search_string is not None:
|
||||||
|
for lineno, b_cur_line in enumerate(b_lines):
|
||||||
|
match_found = to_bytes(search_string, errors='surrogate_or_strict') in b_cur_line
|
||||||
|
if match_found:
|
||||||
|
index[0] = lineno
|
||||||
|
match = match_found
|
||||||
|
if firstmatch:
|
||||||
|
break
|
||||||
|
|
||||||
|
# 3. When no match found on the previous step,
|
||||||
# parse for searching insertafter/insertbefore:
|
# parse for searching insertafter/insertbefore:
|
||||||
if not match:
|
if not match:
|
||||||
for lineno, b_cur_line in enumerate(b_lines):
|
for lineno, b_cur_line in enumerate(b_lines):
|
||||||
|
@ -350,9 +389,9 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
if not b_new_line.endswith(b_linesep):
|
if not b_new_line.endswith(b_linesep):
|
||||||
b_new_line += b_linesep
|
b_new_line += b_linesep
|
||||||
|
|
||||||
# If no regexp was given and no line match is found anywhere in the file,
|
# If no regexp or search_string was given and no line match is found anywhere in the file,
|
||||||
# insert the line appropriately if using insertbefore or insertafter
|
# insert the line appropriately if using insertbefore or insertafter
|
||||||
if regexp is None and match is None and not exact_line_match:
|
if (regexp, search_string, match) == (None, None, None) and not exact_line_match:
|
||||||
|
|
||||||
# Insert lines
|
# Insert lines
|
||||||
if insertafter and insertafter != 'EOF':
|
if insertafter and insertafter != 'EOF':
|
||||||
|
@ -428,7 +467,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
msg = 'line added'
|
msg = 'line added'
|
||||||
changed = True
|
changed = True
|
||||||
|
|
||||||
# insert matched, but not the regexp
|
# insert matched, but not the regexp or search_string
|
||||||
else:
|
else:
|
||||||
b_lines.insert(index[1], b_line + b_linesep)
|
b_lines.insert(index[1], b_line + b_linesep)
|
||||||
msg = 'line added'
|
msg = 'line added'
|
||||||
|
@ -456,7 +495,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=difflist)
|
module.exit_json(changed=changed, msg=msg, backup=backupdest, diff=difflist)
|
||||||
|
|
||||||
|
|
||||||
def absent(module, dest, regexp, line, backup):
|
def absent(module, dest, regexp, search_string, line, backup):
|
||||||
|
|
||||||
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
b_dest = to_bytes(dest, errors='surrogate_or_strict')
|
||||||
if not os.path.exists(b_dest):
|
if not os.path.exists(b_dest):
|
||||||
|
@ -483,6 +522,8 @@ def absent(module, dest, regexp, line, backup):
|
||||||
def matcher(b_cur_line):
|
def matcher(b_cur_line):
|
||||||
if regexp is not None:
|
if regexp is not None:
|
||||||
match_found = bre_c.search(b_cur_line)
|
match_found = bre_c.search(b_cur_line)
|
||||||
|
elif search_string is not None:
|
||||||
|
match_found = to_bytes(search_string, errors='surrogate_or_strict') in b_cur_line
|
||||||
else:
|
else:
|
||||||
match_found = b_line == b_cur_line.rstrip(b'\r\n')
|
match_found = b_line == b_cur_line.rstrip(b'\r\n')
|
||||||
if match_found:
|
if match_found:
|
||||||
|
@ -521,6 +562,7 @@ def main():
|
||||||
path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
|
path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']),
|
||||||
state=dict(type='str', default='present', choices=['absent', 'present']),
|
state=dict(type='str', default='present', choices=['absent', 'present']),
|
||||||
regexp=dict(type='str', aliases=['regex']),
|
regexp=dict(type='str', aliases=['regex']),
|
||||||
|
search_string=dict(type='str'),
|
||||||
line=dict(type='str', aliases=['value']),
|
line=dict(type='str', aliases=['value']),
|
||||||
insertafter=dict(type='str'),
|
insertafter=dict(type='str'),
|
||||||
insertbefore=dict(type='str'),
|
insertbefore=dict(type='str'),
|
||||||
|
@ -530,7 +572,8 @@ def main():
|
||||||
firstmatch=dict(type='bool', default=False),
|
firstmatch=dict(type='bool', default=False),
|
||||||
validate=dict(type='str'),
|
validate=dict(type='str'),
|
||||||
),
|
),
|
||||||
mutually_exclusive=[['insertbefore', 'insertafter']],
|
mutually_exclusive=[
|
||||||
|
['insertbefore', 'insertafter'], ['regexp', 'search_string'], ['backrefs', 'search_string']],
|
||||||
add_file_common_args=True,
|
add_file_common_args=True,
|
||||||
supports_check_mode=True,
|
supports_check_mode=True,
|
||||||
)
|
)
|
||||||
|
@ -542,13 +585,17 @@ def main():
|
||||||
path = params['path']
|
path = params['path']
|
||||||
firstmatch = params['firstmatch']
|
firstmatch = params['firstmatch']
|
||||||
regexp = params['regexp']
|
regexp = params['regexp']
|
||||||
|
search_string = params['search_string']
|
||||||
line = params['line']
|
line = params['line']
|
||||||
|
|
||||||
if regexp == '':
|
if '' in [regexp, search_string]:
|
||||||
module.warn(
|
msg = ("The %s is an empty string, which will match every line in the file. "
|
||||||
"The regular expression is an empty string, which will match every line in the file. "
|
"This may have unintended consequences, such as replacing the last line in the file rather than appending.")
|
||||||
"This may have unintended consequences, such as replacing the last line in the file rather than appending. "
|
param_name = 'search string'
|
||||||
"If this is desired, use '^' to match every line in the file and avoid this warning.")
|
if regexp == '':
|
||||||
|
param_name = 'regular expression'
|
||||||
|
msg += " If this is desired, use '^' to match every line in the file and avoid this warning."
|
||||||
|
module.warn(msg % param_name)
|
||||||
|
|
||||||
b_path = to_bytes(path, errors='surrogate_or_strict')
|
b_path = to_bytes(path, errors='surrogate_or_strict')
|
||||||
if os.path.isdir(b_path):
|
if os.path.isdir(b_path):
|
||||||
|
@ -567,13 +614,13 @@ def main():
|
||||||
if ins_bef is None and ins_aft is None:
|
if ins_bef is None and ins_aft is None:
|
||||||
ins_aft = 'EOF'
|
ins_aft = 'EOF'
|
||||||
|
|
||||||
present(module, path, regexp, line,
|
present(module, path, regexp, search_string, line,
|
||||||
ins_aft, ins_bef, create, backup, backrefs, firstmatch)
|
ins_aft, ins_bef, create, backup, backrefs, firstmatch)
|
||||||
else:
|
else:
|
||||||
if regexp is None and line is None:
|
if (regexp, search_string, line) == (None, None, None):
|
||||||
module.fail_json(msg='one of line or regexp is required with state=absent')
|
module.fail_json(msg='one of line, search_string, or regexp is required with state=absent')
|
||||||
|
|
||||||
absent(module, path, regexp, line, backup)
|
absent(module, path, regexp, search_string, line, backup)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
[section_one]
|
||||||
|
|
||||||
|
[section_two]
|
||||||
|
|
||||||
|
[section_three]
|
5
test/integration/targets/lineinfile/files/teststring.txt
Normal file
5
test/integration/targets/lineinfile/files/teststring.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
This is line 1
|
||||||
|
This is line 2
|
||||||
|
(\\w)(\\s+)([\\.,])
|
||||||
|
This is line 4
|
||||||
|
<FilesMatch ".py[45]?$">
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
case "`uname`" in
|
||||||
|
Darwin*) if [ -z "$JAVA_HOME" ] ; then
|
|
@ -253,6 +253,8 @@
|
||||||
that:
|
that:
|
||||||
- "result.stat.checksum == 'ab56c210ea82839a54487464800fed4878cb2608'"
|
- "result.stat.checksum == 'ab56c210ea82839a54487464800fed4878cb2608'"
|
||||||
|
|
||||||
|
- import_tasks: test_string01.yml
|
||||||
|
|
||||||
- name: use create=yes
|
- name: use create=yes
|
||||||
lineinfile:
|
lineinfile:
|
||||||
dest: "{{ output_dir }}/new_test.txt"
|
dest: "{{ output_dir }}/new_test.txt"
|
||||||
|
@ -806,7 +808,8 @@
|
||||||
- oneline_insbefore_test2 is not changed
|
- oneline_insbefore_test2 is not changed
|
||||||
- oneline_insbefore_file.stat.checksum == '4dca56d05a21f0d018cd311f43e134e4501cf6d9'
|
- oneline_insbefore_file.stat.checksum == '4dca56d05a21f0d018cd311f43e134e4501cf6d9'
|
||||||
|
|
||||||
###################################################################
|
- import_tasks: test_string02.yml
|
||||||
|
|
||||||
# Issue 29443
|
# Issue 29443
|
||||||
# When using an empty regexp, replace the last line (since it matches every line)
|
# When using an empty regexp, replace the last line (since it matches every line)
|
||||||
# but also provide a warning.
|
# but also provide a warning.
|
||||||
|
@ -848,6 +851,49 @@
|
||||||
This may have unintended consequences, such as replacing the last line in the file rather than appending.
|
This may have unintended consequences, such as replacing the last line in the file rather than appending.
|
||||||
If this is desired, use '^' to match every line in the file and avoid this warning.
|
If this is desired, use '^' to match every line in the file and avoid this warning.
|
||||||
|
|
||||||
|
###################################################################
|
||||||
|
# When using an empty search string, replace the last line (since it matches every line)
|
||||||
|
# but also provide a warning.
|
||||||
|
|
||||||
|
- name: Deploy the test file for lineinfile
|
||||||
|
copy:
|
||||||
|
src: teststring.txt
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the test file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.checksum == '481c2b73fe062390afdd294063a4f8285d69ac85'
|
||||||
|
- result.state == 'file'
|
||||||
|
|
||||||
|
- name: Insert a line in the file using an empty string as a search string
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
search_string: ''
|
||||||
|
line: This is line 6
|
||||||
|
register: insert_empty_literal
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file contents match what is expected and a warning was displayed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- insert_empty_literal is changed
|
||||||
|
- warning_message in insert_empty_literal.warnings
|
||||||
|
- result.stat.checksum == 'eaa79f878557d4bd8d96787a850526a0facab342'
|
||||||
|
vars:
|
||||||
|
warning_message: >-
|
||||||
|
The search string is an empty string, which will match every line in the file.
|
||||||
|
This may have unintended consequences, such as replacing the last line in the file rather than appending.
|
||||||
|
|
||||||
|
- name: meta
|
||||||
|
meta: end_play
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
## Issue #58923
|
## Issue #58923
|
||||||
## Using firstmatch with insertafter and ensure multiple lines are not inserted
|
## Using firstmatch with insertafter and ensure multiple lines are not inserted
|
||||||
|
@ -1126,6 +1172,120 @@
|
||||||
- insertbefore_test5 is not changed
|
- insertbefore_test5 is not changed
|
||||||
- insertbefore_test5_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7'
|
- insertbefore_test5_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7'
|
||||||
|
|
||||||
|
########################################################################################
|
||||||
|
# Same tests for literal
|
||||||
|
|
||||||
|
# Test insertafter with literal
|
||||||
|
- name: Deploy the test file
|
||||||
|
copy:
|
||||||
|
src: teststring_58923.txt
|
||||||
|
dest: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: initial_file
|
||||||
|
|
||||||
|
- name: Assert that the test file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- initial_file is changed
|
||||||
|
- initial_file.checksum == 'b6379ba43261c451a62102acb2c7f438a177c66e'
|
||||||
|
- initial_file.state == 'file'
|
||||||
|
|
||||||
|
# Regarding the documentation:
|
||||||
|
# If the search string is passed to both search_string and
|
||||||
|
# insertafter, insertafter is only honored if no match for search_string is found.
|
||||||
|
# Therefore,
|
||||||
|
# when search_string expressions are passed to both search_string and insertafter, then:
|
||||||
|
# 1. search_string was found -> ignore insertafter, replace the founded line
|
||||||
|
# 2. search_string was not found -> insert the line after 'insertafter' line
|
||||||
|
|
||||||
|
# literal is not present in the file, so the line must be inserted after ^#!/bin/sh
|
||||||
|
- name: Add the line using firstmatch, regexp, and insertafter
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
insertafter: '^#!/bin/sh'
|
||||||
|
search_string: export FISHEYE_OPTS
|
||||||
|
firstmatch: true
|
||||||
|
line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m"
|
||||||
|
register: insertafter_test1
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: insertafter_test1_file
|
||||||
|
|
||||||
|
- name: Add the line using firstmatch, literal, and insertafter again
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
insertafter: '^#!/bin/sh'
|
||||||
|
search_string: export FISHEYE_OPTS
|
||||||
|
firstmatch: true
|
||||||
|
line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m"
|
||||||
|
register: insertafter_test2
|
||||||
|
|
||||||
|
# Check of the prev step.
|
||||||
|
# We tried to add the same line with the same playbook,
|
||||||
|
# so nothing has been added:
|
||||||
|
- name: Stat the file again
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: insertafter_test2_file
|
||||||
|
|
||||||
|
- name: Assert insertafter tests gave the expected results
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- insertafter_test1 is changed
|
||||||
|
- insertafter_test1_file.stat.checksum == '9232aed6fe88714964d9e29d13e42cd782070b08'
|
||||||
|
- insertafter_test2 is not changed
|
||||||
|
- insertafter_test2_file.stat.checksum == '9232aed6fe88714964d9e29d13e42cd782070b08'
|
||||||
|
|
||||||
|
# Test insertbefore with literal
|
||||||
|
- name: Deploy the test file
|
||||||
|
copy:
|
||||||
|
src: teststring_58923.txt
|
||||||
|
dest: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: initial_file
|
||||||
|
|
||||||
|
- name: Assert that the test file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- initial_file is changed
|
||||||
|
- initial_file.checksum == 'b6379ba43261c451a62102acb2c7f438a177c66e'
|
||||||
|
- initial_file.state == 'file'
|
||||||
|
|
||||||
|
- name: Add the line using literal, firstmatch, and insertbefore
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
insertbefore: '^#!/bin/sh'
|
||||||
|
search_string: export FISHEYE_OPTS
|
||||||
|
firstmatch: true
|
||||||
|
line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m"
|
||||||
|
register: insertbefore_test1
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: insertbefore_test1_file
|
||||||
|
|
||||||
|
- name: Add the line using literal, firstmatch, and insertbefore again
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
insertbefore: '^#!/bin/sh'
|
||||||
|
search_string: export FISHEYE_OPTS
|
||||||
|
firstmatch: true
|
||||||
|
line: export FISHEYE_OPTS="-Xmx4096m -Xms2048m"
|
||||||
|
register: insertbefore_test2
|
||||||
|
|
||||||
|
- name: Stat the file again
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring_58923.txt"
|
||||||
|
register: insertbefore_test2_file
|
||||||
|
|
||||||
|
- name: Assert insertbefore with literal tests gave the expected results
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- insertbefore_test1 is changed
|
||||||
|
- insertbefore_test1_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7'
|
||||||
|
- insertbefore_test2 is not changed
|
||||||
|
- insertbefore_test2_file.stat.checksum == '3c6630b9d44f561ea9ad999be56a7504cadc12f7'
|
||||||
|
|
||||||
# Test inserting a line at the end of the file using regexp with insertafter
|
# Test inserting a line at the end of the file using regexp with insertafter
|
||||||
# https://github.com/ansible/ansible/issues/63684
|
# https://github.com/ansible/ansible/issues/63684
|
||||||
|
@ -1155,3 +1315,32 @@
|
||||||
- testend1 is changed
|
- testend1 is changed
|
||||||
- testend2 is changed
|
- testend2 is changed
|
||||||
- testend_file.stat.checksum == 'ef36116966836ce04f6b249fd1837706acae4e19'
|
- testend_file.stat.checksum == 'ef36116966836ce04f6b249fd1837706acae4e19'
|
||||||
|
|
||||||
|
# Test inserting a line at the end of the file using search_string with insertafter
|
||||||
|
|
||||||
|
- name: Create a file by inserting a line
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/testendliteral.txt"
|
||||||
|
create: yes
|
||||||
|
line: testline
|
||||||
|
register: testend1
|
||||||
|
|
||||||
|
- name: Insert a line at the end of the file
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/testendliteral.txt"
|
||||||
|
insertafter: testline
|
||||||
|
search_string: line at the end
|
||||||
|
line: line at the end
|
||||||
|
register: testend2
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/testendliteral.txt"
|
||||||
|
register: testend_file
|
||||||
|
|
||||||
|
- name: Assert inserting at the end gave the expected results.
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- testend1 is changed
|
||||||
|
- testend2 is changed
|
||||||
|
- testend_file.stat.checksum == 'ef36116966836ce04f6b249fd1837706acae4e19'
|
||||||
|
|
142
test/integration/targets/lineinfile/tasks/test_string01.yml
Normal file
142
test/integration/targets/lineinfile/tasks/test_string01.yml
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
---
|
||||||
|
###################################################################
|
||||||
|
# 1st search_string tests
|
||||||
|
|
||||||
|
- name: deploy the test file for lineinfile string
|
||||||
|
copy:
|
||||||
|
src: teststring.txt
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert that the test file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- "result.checksum == '481c2b73fe062390afdd294063a4f8285d69ac85'"
|
||||||
|
- "result.state == 'file'"
|
||||||
|
|
||||||
|
- name: insert a line at the beginning of the file, and back it up
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: present
|
||||||
|
line: "New line at the beginning"
|
||||||
|
insertbefore: "BOF"
|
||||||
|
backup: yes
|
||||||
|
register: result1
|
||||||
|
|
||||||
|
- name: insert a line at the beginning of the file again
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: present
|
||||||
|
line: "New line at the beginning"
|
||||||
|
insertbefore: "BOF"
|
||||||
|
register: result2
|
||||||
|
|
||||||
|
- name: Replace a line using string
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: present
|
||||||
|
line: "Thi$ i^ [ine 3"
|
||||||
|
search_string: (\\w)(\\s+)([\\.,])
|
||||||
|
register: backrefs_result1
|
||||||
|
|
||||||
|
- name: Replace a line again using string
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: present
|
||||||
|
line: "Thi$ i^ [ine 3"
|
||||||
|
search_string: (\\w)(\\s+)([\\.,])
|
||||||
|
register: backrefs_result2
|
||||||
|
|
||||||
|
- command: cat {{ output_dir }}/teststring.txt
|
||||||
|
|
||||||
|
- name: assert that the line with backrefs was changed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- backrefs_result1 is changed
|
||||||
|
- backrefs_result2 is not changed
|
||||||
|
- "backrefs_result1.msg == 'line replaced'"
|
||||||
|
|
||||||
|
- name: stat the test after the backref line was replaced
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert test checksum matches after backref line was replaced
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.stat.checksum == '8084519b53e268920a46592a112297715951f167'"
|
||||||
|
|
||||||
|
- name: remove the middle line using string
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: absent
|
||||||
|
search_string: "Thi$ i^ [ine 3"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert that the line was removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- "result.msg == '1 line(s) removed'"
|
||||||
|
|
||||||
|
- name: stat the test after the middle line was removed
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert test checksum matches after the middle line was removed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.stat.checksum == '89919ef2ef91e48ad02e0ca2bcb76dfc2a86d516'"
|
||||||
|
|
||||||
|
- name: run a validation script that succeeds using string
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: absent
|
||||||
|
search_string: <FilesMatch ".py[45]?$">
|
||||||
|
validate: "true %s"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert that the file validated after removing a line
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- "result.msg == '1 line(s) removed'"
|
||||||
|
|
||||||
|
- name: stat the test after the validation succeeded
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert test checksum matches after the validation succeeded
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.stat.checksum == 'ba9600b34febbc88bfb3ca99cd6b57f1010c19a4'"
|
||||||
|
|
||||||
|
- name: run a validation script that fails using string
|
||||||
|
lineinfile:
|
||||||
|
dest: "{{ output_dir }}/teststring.txt"
|
||||||
|
state: absent
|
||||||
|
search_string: "This is line 1"
|
||||||
|
validate: "/bin/false %s"
|
||||||
|
register: result
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
|
- name: assert that the validate failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.failed == true"
|
||||||
|
|
||||||
|
- name: stat the test after the validation failed
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.txt"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: assert test checksum matches the previous after the validation failed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- "result.stat.checksum == 'ba9600b34febbc88bfb3ca99cd6b57f1010c19a4'"
|
||||||
|
|
||||||
|
# End of string tests
|
||||||
|
###################################################################
|
166
test/integration/targets/lineinfile/tasks/test_string02.yml
Normal file
166
test/integration/targets/lineinfile/tasks/test_string02.yml
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
---
|
||||||
|
###################################################################
|
||||||
|
# 2nd search_string tests
|
||||||
|
|
||||||
|
- name: Deploy the teststring.conf file
|
||||||
|
copy:
|
||||||
|
src: teststring.conf
|
||||||
|
dest: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the teststring.conf file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.checksum == '6037f13e419b132eb3fd20a89e60c6c87a6add38'
|
||||||
|
- result.state == 'file'
|
||||||
|
|
||||||
|
# Test instertafter
|
||||||
|
- name: Insert lines after with string
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
search_string: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertafter: "{{ item.after }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_5
|
||||||
|
|
||||||
|
- name: Do the same thing again and check for changes
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
search_string: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertafter: "{{ item.after }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_6
|
||||||
|
|
||||||
|
- name: Assert that the file was changed the first time but not the second time
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- item.0 is changed
|
||||||
|
- item.1 is not changed
|
||||||
|
with_together:
|
||||||
|
- "{{ _multitest_5.results }}"
|
||||||
|
- "{{ _multitest_6.results }}"
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file contents match what is expected
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.stat.checksum == '06e2c456e5028dd7bcd0b117b5927a1139458c82'
|
||||||
|
|
||||||
|
- name: Do the same thing a third time without string and check for changes
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertafter: "{{ item.after }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_7
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file was changed when no string was provided
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- item is not changed
|
||||||
|
with_items: "{{ _multitest_7.results }}"
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file contents match what is expected
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.stat.checksum == '06e2c456e5028dd7bcd0b117b5927a1139458c82'
|
||||||
|
|
||||||
|
# Test insertbefore
|
||||||
|
- name: Deploy the test.conf file
|
||||||
|
copy:
|
||||||
|
src: teststring.conf
|
||||||
|
dest: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the teststring.conf file was deployed
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result is changed
|
||||||
|
- result.checksum == '6037f13e419b132eb3fd20a89e60c6c87a6add38'
|
||||||
|
- result.state == 'file'
|
||||||
|
|
||||||
|
- name: Insert lines before with string
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
search_string: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertbefore: "{{ item.before }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_8
|
||||||
|
|
||||||
|
- name: Do the same thing again and check for changes
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
search_string: "{{ item.regexp }}"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertbefore: "{{ item.before }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_9
|
||||||
|
|
||||||
|
- name: Assert that the file was changed the first time but not the second time
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- item.0 is changed
|
||||||
|
- item.1 is not changed
|
||||||
|
with_together:
|
||||||
|
- "{{ _multitest_8.results }}"
|
||||||
|
- "{{ _multitest_9.results }}"
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file contents match what is expected
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.stat.checksum == 'c3be9438a07c44d4c256cebfcdbca15a15b1db91'
|
||||||
|
|
||||||
|
- name: Do the same thing a third time without string and check for changes
|
||||||
|
lineinfile:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
line: "{{ item.line }}"
|
||||||
|
insertbefore: "{{ item.before }}"
|
||||||
|
with_items: "{{ test_befaf_regexp }}"
|
||||||
|
register: _multitest_10
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file was changed when no string was provided
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- item is not changed
|
||||||
|
with_items: "{{ _multitest_10.results }}"
|
||||||
|
|
||||||
|
- name: Stat the file
|
||||||
|
stat:
|
||||||
|
path: "{{ output_dir }}/teststring.conf"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- name: Assert that the file contents match what is expected
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- result.stat.checksum == 'c3be9438a07c44d4c256cebfcdbca15a15b1db91'
|
||||||
|
|
||||||
|
# End of string tests
|
||||||
|
###################################################################
|
Loading…
Reference in a new issue