mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Add warning when using an empty regexp in lineinfile (#42013)
* Revert "Account for empty string regexp in lineinfile (#41451)"
This reverts commit 4b5b4a760c
.
* Use context managers for interacting with files
* Store line and regexp parameters in a variable
* Add warning when regexp is an empty string
* Remove '=' from error messages
* Update warning message and add changelog
* Add tests
* Improve warning message
Offer an equivalent regexp that won't trigger the warning.
Update tests to match new warning.
* Add porting guide entry for lineinfile change
This commit is contained in:
parent
b6f2aad600
commit
fb55038d75
4 changed files with 34 additions and 15 deletions
2
changelogs/fragments/lineinfile-empty-regexp.yml
Normal file
2
changelogs/fragments/lineinfile-empty-regexp.yml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
minor_changes:
|
||||||
|
- lineinfile - add warning when using an empty regexp (https://github.com/ansible/ansible/issues/29443)
|
|
@ -70,6 +70,11 @@ Major changes in popular modules are detailed here
|
||||||
:ref:`DEFAULT_SYSLOG_FACILITY`. If you have :ref:`DEFAULT_SYSLOG_FACILITY` configured, the
|
:ref:`DEFAULT_SYSLOG_FACILITY`. If you have :ref:`DEFAULT_SYSLOG_FACILITY` configured, the
|
||||||
location of remote logs on systems which use journald may change.
|
location of remote logs on systems which use journald may change.
|
||||||
|
|
||||||
|
* The ``lineinfile`` module was changed to show a warning when using an empty string as a regexp.
|
||||||
|
Since an empty regexp matches every line in a file, it will replace the last line in a file rather
|
||||||
|
than inserting. If this is the desired behavior, use ``'^'`` which will match every line and
|
||||||
|
will not trigger the warning.
|
||||||
|
|
||||||
|
|
||||||
Modules removed
|
Modules removed
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -252,7 +252,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
if module._diff:
|
if module._diff:
|
||||||
diff['before'] = to_native(b('').join(b_lines))
|
diff['before'] = to_native(b('').join(b_lines))
|
||||||
|
|
||||||
if regexp:
|
if regexp is not None:
|
||||||
bre_m = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
|
bre_m = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
|
||||||
|
|
||||||
if insertafter not in (None, 'BOF', 'EOF'):
|
if insertafter not in (None, 'BOF', 'EOF'):
|
||||||
|
@ -268,7 +268,7 @@ def present(module, dest, regexp, line, insertafter, insertbefore, create,
|
||||||
m = None
|
m = None
|
||||||
b_line = to_bytes(line, errors='surrogate_or_strict')
|
b_line = to_bytes(line, errors='surrogate_or_strict')
|
||||||
for lineno, b_cur_line in enumerate(b_lines):
|
for lineno, b_cur_line in enumerate(b_lines):
|
||||||
if regexp:
|
if regexp is not None:
|
||||||
match_found = bre_m.search(b_cur_line)
|
match_found = bre_m.search(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'))
|
||||||
|
@ -410,14 +410,14 @@ def absent(module, dest, regexp, line, backup):
|
||||||
if module._diff:
|
if module._diff:
|
||||||
diff['before'] = to_native(b('').join(b_lines))
|
diff['before'] = to_native(b('').join(b_lines))
|
||||||
|
|
||||||
if regexp:
|
if regexp is not None:
|
||||||
bre_c = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
|
bre_c = re.compile(to_bytes(regexp, errors='surrogate_or_strict'))
|
||||||
found = []
|
found = []
|
||||||
|
|
||||||
b_line = to_bytes(line, errors='surrogate_or_strict')
|
b_line = to_bytes(line, errors='surrogate_or_strict')
|
||||||
|
|
||||||
def matcher(b_cur_line):
|
def matcher(b_cur_line):
|
||||||
if regexp:
|
if regexp is not None:
|
||||||
match_found = bre_c.search(b_cur_line)
|
match_found = bre_c.search(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'))
|
||||||
|
@ -478,18 +478,24 @@ def main():
|
||||||
path = params['path']
|
path = params['path']
|
||||||
firstmatch = params['firstmatch']
|
firstmatch = params['firstmatch']
|
||||||
regexp = params['regexp']
|
regexp = params['regexp']
|
||||||
line = params.get('line', None)
|
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.")
|
||||||
|
|
||||||
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):
|
||||||
module.fail_json(rc=256, msg='Path %s is a directory !' % path)
|
module.fail_json(rc=256, msg='Path %s is a directory !' % path)
|
||||||
|
|
||||||
if params['state'] == 'present':
|
if params['state'] == 'present':
|
||||||
if backrefs and not regexp:
|
if backrefs and regexp is None:
|
||||||
module.fail_json(msg='regexp= is required with backrefs=true')
|
module.fail_json(msg='regexp is required with backrefs=true')
|
||||||
|
|
||||||
if params.get('line', None) is None:
|
if line is None:
|
||||||
module.fail_json(msg='line= is required with state=present')
|
module.fail_json(msg='line is required with state=present')
|
||||||
|
|
||||||
# Deal with the insertafter default value manually, to avoid errors
|
# Deal with the insertafter default value manually, to avoid errors
|
||||||
# because of the mutually_exclusive mechanism.
|
# because of the mutually_exclusive mechanism.
|
||||||
|
@ -497,13 +503,11 @@ 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'
|
||||||
|
|
||||||
line = params['line']
|
|
||||||
|
|
||||||
present(module, path, regexp, line,
|
present(module, path, regexp, line,
|
||||||
ins_aft, ins_bef, create, backup, backrefs, firstmatch)
|
ins_aft, ins_bef, create, backup, backrefs, firstmatch)
|
||||||
else:
|
else:
|
||||||
if not regexp and line is None:
|
if regexp is None and line is None:
|
||||||
module.fail_json(msg='one of line= or regexp= is required with state=absent')
|
module.fail_json(msg='one of line or regexp is required with state=absent')
|
||||||
|
|
||||||
absent(module, path, regexp, line, backup)
|
absent(module, path, regexp, line, backup)
|
||||||
|
|
||||||
|
|
|
@ -720,6 +720,8 @@
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
# Issue 29443
|
# Issue 29443
|
||||||
|
# When using an empty regexp, replace the last line (since it matches every line)
|
||||||
|
# but also provide a warning.
|
||||||
|
|
||||||
- name: Deploy the test file for lineinfile
|
- name: Deploy the test file for lineinfile
|
||||||
copy:
|
copy:
|
||||||
|
@ -746,8 +748,14 @@
|
||||||
path: "{{ output_dir }}/test.txt"
|
path: "{{ output_dir }}/test.txt"
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- name: Assert that the file contents match what is expected
|
- name: Assert that the file contents match what is expected and a warning was displayed
|
||||||
assert:
|
assert:
|
||||||
that:
|
that:
|
||||||
- insert_empty_regexp is changed
|
- insert_empty_regexp is changed
|
||||||
- result.stat.checksum == '3baeade8eb2ecf4b01d70d541e9b8258b67c7f9f'
|
- warning_message in insert_empty_regexp.warnings
|
||||||
|
- result.stat.checksum == '23555a98ceaa88756b4c7c7bba49d9f86eed868f'
|
||||||
|
vars:
|
||||||
|
warning_message: >-
|
||||||
|
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.
|
||||||
|
|
Loading…
Reference in a new issue