From 4b5b4a760c5217bdfd1d45efd224e2762e9898b0 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Mon, 25 Jun 2018 17:57:05 -0400 Subject: [PATCH] Account for empty string regexp in lineinfile (#41451) * Use context managers for interacting with files * Account for empty string as regexp Rather than explicitly testing for None, also test for an empty string which will evaluate to False. An empty string regexp matches every line, which ends up replacing the incorrect line. * Store line parameter in a variable * Add tests --- lib/ansible/modules/files/lineinfile.py | 33 +++++++++--------- .../targets/lineinfile/tasks/main.yml | 34 +++++++++++++++++++ 2 files changed, 50 insertions(+), 17 deletions(-) diff --git a/lib/ansible/modules/files/lineinfile.py b/lib/ansible/modules/files/lineinfile.py index 33d9d21af5..3b8c14c1cb 100644 --- a/lib/ansible/modules/files/lineinfile.py +++ b/lib/ansible/modules/files/lineinfile.py @@ -193,9 +193,8 @@ from ansible.module_utils._text import to_bytes, to_native def write_changes(module, b_lines, dest): tmpfd, tmpfile = tempfile.mkstemp() - f = os.fdopen(tmpfd, 'wb') - f.writelines(b_lines) - f.close() + with open(tmpfile, 'wb') as f: + f.writelines(b_lines) validate = module.params.get('validate', None) valid = not validate @@ -247,14 +246,13 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create, b_lines = [] else: - f = open(b_dest, 'rb') - b_lines = f.readlines() - f.close() + with open(b_dest, 'rb') as f: + b_lines = f.readlines() if module._diff: diff['before'] = to_native(b('').join(b_lines)) - if regexp is not None: + if regexp: bre_m = re.compile(to_bytes(regexp, errors='surrogate_or_strict')) if insertafter not in (None, 'BOF', 'EOF'): @@ -270,7 +268,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create, m = None b_line = to_bytes(line, errors='surrogate_or_strict') for lineno, b_cur_line in enumerate(b_lines): - if regexp is not None: + if regexp: match_found = bre_m.search(b_cur_line) else: match_found = b_line == b_cur_line.rstrip(b('\r\n')) @@ -406,21 +404,20 @@ def absent(module, dest, regexp, line, backup): 'before_header': '%s (content)' % dest, 'after_header': '%s (content)' % dest} - f = open(b_dest, 'rb') - b_lines = f.readlines() - f.close() + with open(b_dest, 'rb') as f: + b_lines = f.readlines() if module._diff: diff['before'] = to_native(b('').join(b_lines)) - if regexp is not None: + if regexp: bre_c = re.compile(to_bytes(regexp, errors='surrogate_or_strict')) found = [] b_line = to_bytes(line, errors='surrogate_or_strict') def matcher(b_cur_line): - if regexp is not None: + if regexp: match_found = bre_c.search(b_cur_line) else: match_found = b_line == b_cur_line.rstrip(b('\r\n')) @@ -480,13 +477,15 @@ def main(): backrefs = params['backrefs'] path = params['path'] firstmatch = params['firstmatch'] + regexp = params['regexp'] + line = params.get('line', None) b_path = to_bytes(path, errors='surrogate_or_strict') if os.path.isdir(b_path): module.fail_json(rc=256, msg='Path %s is a directory !' % path) if params['state'] == 'present': - if backrefs and params['regexp'] is None: + if backrefs and not regexp: module.fail_json(msg='regexp= is required with backrefs=true') if params.get('line', None) is None: @@ -500,13 +499,13 @@ def main(): line = params['line'] - present(module, path, params['regexp'], line, + present(module, path, regexp, line, ins_aft, ins_bef, create, backup, backrefs, firstmatch) else: - if params['regexp'] is None and params.get('line', None) is None: + if not regexp and line is None: module.fail_json(msg='one of line= or regexp= is required with state=absent') - absent(module, path, params['regexp'], params.get('line', None), backup) + absent(module, path, regexp, line, backup) if __name__ == '__main__': diff --git a/test/integration/targets/lineinfile/tasks/main.yml b/test/integration/targets/lineinfile/tasks/main.yml index d18aa999fd..4838040ca9 100644 --- a/test/integration/targets/lineinfile/tasks/main.yml +++ b/test/integration/targets/lineinfile/tasks/main.yml @@ -717,3 +717,37 @@ assert: that: - result.stat.checksum == 'eca8d8ea089d4ea57a3b87d4091599ca8b60dfd2' + +################################################################### +# Issue 29443 + +- name: Deploy the test file for lineinfile + copy: + src: test.txt + dest: "{{ output_dir }}/test.txt" + register: result + +- name: Assert that the test file was deployed + assert: + that: + - result is changed + - result.checksum == '5feac65e442c91f557fc90069ce6efc4d346ab51' + - result.state == 'file' + +- name: Insert a line in the file using an empty string as a regular expression + lineinfile: + path: "{{ output_dir }}/test.txt" + regexp: '' + line: This is line 6 + register: insert_empty_regexp + +- name: Stat the file + stat: + path: "{{ output_dir }}/test.txt" + register: result + +- name: Assert that the file contents match what is expected + assert: + that: + - insert_empty_regexp is changed + - result.stat.checksum == '3baeade8eb2ecf4b01d70d541e9b8258b67c7f9f' \ No newline at end of file