From 69631da889e21a5513916b62d72c115064b7660b Mon Sep 17 00:00:00 2001 From: Jose Angel Munoz <5019972+imjoseangel@users.noreply.github.com> Date: Tue, 2 Feb 2021 21:37:06 +0100 Subject: [PATCH] lineinfile - add search_string parameter for non-regexp searching (#70647) * Add tests for search_string * Improve examples * Add changelog --- ...ng-parameter-for-non-regexp-searching.yaml | 2 + lib/ansible/modules/lineinfile.py | 83 ++++++-- .../targets/lineinfile/files/teststring.conf | 5 + .../targets/lineinfile/files/teststring.txt | 5 + .../lineinfile/files/teststring_58923.txt | 4 + .../targets/lineinfile/tasks/main.yml | 191 +++++++++++++++++- .../lineinfile/tasks/test_string01.yml | 142 +++++++++++++ .../lineinfile/tasks/test_string02.yml | 166 +++++++++++++++ 8 files changed, 579 insertions(+), 19 deletions(-) create mode 100644 changelogs/fragments/lineinfile-add-search_string-parameter-for-non-regexp-searching.yaml create mode 100644 test/integration/targets/lineinfile/files/teststring.conf create mode 100644 test/integration/targets/lineinfile/files/teststring.txt create mode 100644 test/integration/targets/lineinfile/files/teststring_58923.txt create mode 100644 test/integration/targets/lineinfile/tasks/test_string01.yml create mode 100644 test/integration/targets/lineinfile/tasks/test_string02.yml diff --git a/changelogs/fragments/lineinfile-add-search_string-parameter-for-non-regexp-searching.yaml b/changelogs/fragments/lineinfile-add-search_string-parameter-for-non-regexp-searching.yaml new file mode 100644 index 00000000000..7ed51e166f7 --- /dev/null +++ b/changelogs/fragments/lineinfile-add-search_string-parameter-for-non-regexp-searching.yaml @@ -0,0 +1,2 @@ +minor_changes: + - lineinfile - add search_string parameter for non-regexp searching (https://github.com/ansible/ansible/issues/70470) diff --git a/lib/ansible/modules/lineinfile.py b/lib/ansible/modules/lineinfile.py index 7b3644991ac..1ba5348463f 100644 --- a/lib/ansible/modules/lineinfile.py +++ b/lib/ansible/modules/lineinfile.py @@ -44,6 +44,17 @@ options: type: str aliases: [ regex ] 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: description: - 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. - If the C(regexp) does match, the last matching line will be replaced by the expanded line parameter. + - Mutually exclusive with C(search_string). type: bool default: no version_added: "1.1" @@ -136,6 +148,7 @@ seealso: author: - Daniel Hokka Zakrissoni (@dhozac) - Ahti Kitsik (@ahtik) + - Jose Angel Munoz (@imjoseangel) ''' EXAMPLES = r''' @@ -161,6 +174,15 @@ EXAMPLES = r''' group: root 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 ansible.builtin.lineinfile: path: /etc/httpd/conf/httpd.conf @@ -168,6 +190,13 @@ EXAMPLES = r''' insertafter: '^#Listen ' line: Listen 8080 +- name: Ensure php extension matches new pattern + lineinfile: + path: /etc/httpd/conf/httpd.conf + search_string: '' + insertafter: '^\t\n' + line: ' ' + - name: Ensure we have our own comment added to /etc/services ansible.builtin.lineinfile: path: /etc/services @@ -253,7 +282,7 @@ def check_file_attrs(module, changed, message, diff): 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): diff = {'before': '', @@ -301,8 +330,8 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create, # "If regular expressions are passed to both regexp and # insertafter, insertafter is only honored if no match for regexp is found." # Therefore: - # 1. regexp was found -> ignore insertafter, replace the founded line - # 2. regexp was not found -> insert the line after 'insertafter' or 'insertbefore' line + # 1. regexp or search_string was found -> ignore insertafter, replace the founded line + # 2. regexp or search_string was not found -> insert the line after 'insertafter' or 'insertbefore' line # Given the above: # 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: 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: if not match: 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): 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 - 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 if insertafter and insertafter != 'EOF': @@ -428,7 +467,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create, msg = 'line added' changed = True - # insert matched, but not the regexp + # insert matched, but not the regexp or search_string else: b_lines.insert(index[1], b_line + b_linesep) 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) -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') if not os.path.exists(b_dest): @@ -483,6 +522,8 @@ def absent(module, dest, regexp, line, backup): def matcher(b_cur_line): if regexp is not None: 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: match_found = b_line == b_cur_line.rstrip(b'\r\n') if match_found: @@ -521,6 +562,7 @@ def main(): path=dict(type='path', required=True, aliases=['dest', 'destfile', 'name']), state=dict(type='str', default='present', choices=['absent', 'present']), regexp=dict(type='str', aliases=['regex']), + search_string=dict(type='str'), line=dict(type='str', aliases=['value']), insertafter=dict(type='str'), insertbefore=dict(type='str'), @@ -530,7 +572,8 @@ def main(): firstmatch=dict(type='bool', default=False), validate=dict(type='str'), ), - mutually_exclusive=[['insertbefore', 'insertafter']], + mutually_exclusive=[ + ['insertbefore', 'insertafter'], ['regexp', 'search_string'], ['backrefs', 'search_string']], add_file_common_args=True, supports_check_mode=True, ) @@ -542,13 +585,17 @@ def main(): path = params['path'] firstmatch = params['firstmatch'] regexp = params['regexp'] + search_string = params['search_string'] line = params['line'] - if regexp == '': - module.warn( - "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. " - "If this is desired, use '^' to match every line in the file and avoid this warning.") + if '' in [regexp, search_string]: + msg = ("The %s 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.") + param_name = 'search string' + 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') if os.path.isdir(b_path): @@ -567,13 +614,13 @@ def main(): if ins_bef is None and ins_aft is None: ins_aft = 'EOF' - present(module, path, regexp, line, + present(module, path, regexp, search_string, line, ins_aft, ins_bef, create, backup, backrefs, firstmatch) else: - if regexp is None and line is None: - module.fail_json(msg='one of line or regexp is required with state=absent') + if (regexp, search_string, line) == (None, None, None): + 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__': diff --git a/test/integration/targets/lineinfile/files/teststring.conf b/test/integration/targets/lineinfile/files/teststring.conf new file mode 100644 index 00000000000..15404cd692d --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring.conf @@ -0,0 +1,5 @@ +[section_one] + +[section_two] + +[section_three] diff --git a/test/integration/targets/lineinfile/files/teststring.txt b/test/integration/targets/lineinfile/files/teststring.txt new file mode 100644 index 00000000000..b204494509b --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring.txt @@ -0,0 +1,5 @@ +This is line 1 +This is line 2 +(\\w)(\\s+)([\\.,]) +This is line 4 + diff --git a/test/integration/targets/lineinfile/files/teststring_58923.txt b/test/integration/targets/lineinfile/files/teststring_58923.txt new file mode 100644 index 00000000000..34579fde33a --- /dev/null +++ b/test/integration/targets/lineinfile/files/teststring_58923.txt @@ -0,0 +1,4 @@ +#!/bin/sh + +case "`uname`" in + Darwin*) if [ -z "$JAVA_HOME" ] ; then diff --git a/test/integration/targets/lineinfile/tasks/main.yml b/test/integration/targets/lineinfile/tasks/main.yml index 840051cf02f..cad926b364d 100644 --- a/test/integration/targets/lineinfile/tasks/main.yml +++ b/test/integration/targets/lineinfile/tasks/main.yml @@ -253,6 +253,8 @@ that: - "result.stat.checksum == 'ab56c210ea82839a54487464800fed4878cb2608'" +- import_tasks: test_string01.yml + - name: use create=yes lineinfile: dest: "{{ output_dir }}/new_test.txt" @@ -806,7 +808,8 @@ - oneline_insbefore_test2 is not changed - oneline_insbefore_file.stat.checksum == '4dca56d05a21f0d018cd311f43e134e4501cf6d9' -################################################################### +- import_tasks: test_string02.yml + # Issue 29443 # When using an empty regexp, replace the last line (since it matches every line) # 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. 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 ## Using firstmatch with insertafter and ensure multiple lines are not inserted @@ -1126,6 +1172,120 @@ - insertbefore_test5 is not changed - 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 # https://github.com/ansible/ansible/issues/63684 @@ -1155,3 +1315,32 @@ - testend1 is changed - testend2 is changed - 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' diff --git a/test/integration/targets/lineinfile/tasks/test_string01.yml b/test/integration/targets/lineinfile/tasks/test_string01.yml new file mode 100644 index 00000000000..6e0c12c3afa --- /dev/null +++ b/test/integration/targets/lineinfile/tasks/test_string01.yml @@ -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: + 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 +################################################################### diff --git a/test/integration/targets/lineinfile/tasks/test_string02.yml b/test/integration/targets/lineinfile/tasks/test_string02.yml new file mode 100644 index 00000000000..886b290ddcf --- /dev/null +++ b/test/integration/targets/lineinfile/tasks/test_string02.yml @@ -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 +###################################################################