Port PR #48989 to master (#56691)

* port PR #48989 to master

* fix pylint literal-comparison

* fix test for windows due to check_perms differences

Co-authored-by: Daniel Wozniak <dwozniak@saltstack.com>
This commit is contained in:
Nicholas Hughes 2020-07-28 18:24:44 -04:00 committed by GitHub
parent c10a6f0c4a
commit 1f70709318
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 21 deletions

View file

@ -2743,6 +2743,8 @@ def blockreplace(
dry_run=False, dry_run=False,
show_changes=True, show_changes=True,
append_newline=False, append_newline=False,
insert_before_match=None,
insert_after_match=None,
): ):
""" """
.. versionadded:: 2014.1.0 .. versionadded:: 2014.1.0
@ -2785,6 +2787,17 @@ def blockreplace(
If markers are not found and set to ``True`` then, the markers and If markers are not found and set to ``True`` then, the markers and
content will be prepended to the file. content will be prepended to the file.
insert_before_match
If markers are not found, this parameter can be set to a regex which will
insert the block before the first found occurrence in the file.
.. versionadded:: Sodium
insert_after_match
If markers are not found, this parameter can be set to a regex which will
insert the block after the first found occurrence in the file.
.. versionadded:: Sodium
backup backup
The file extension to use for a backup of the file if any edit is made. The file extension to use for a backup of the file if any edit is made.
@ -2823,9 +2836,16 @@ def blockreplace(
'#-- end managed zone foobar --' $'10.0.1.1 foo.foobar\\n10.0.1.2 bar.foobar' True '#-- end managed zone foobar --' $'10.0.1.1 foo.foobar\\n10.0.1.2 bar.foobar' True
""" """
if append_if_not_found and prepend_if_not_found: exclusive_params = [
append_if_not_found,
prepend_if_not_found,
bool(insert_before_match),
bool(insert_after_match),
]
if sum(exclusive_params) > 1:
raise SaltInvocationError( raise SaltInvocationError(
"Only one of append and prepend_if_not_found is permitted" "Only one of append_if_not_found, prepend_if_not_found,"
" insert_before_match, and insert_after_match is permitted"
) )
path = os.path.expanduser(path) path = os.path.expanduser(path)
@ -2844,6 +2864,18 @@ def blockreplace(
"Cannot perform string replacements on a binary file: {0}".format(path) "Cannot perform string replacements on a binary file: {0}".format(path)
) )
if insert_before_match or insert_after_match:
if insert_before_match:
if not isinstance(insert_before_match, six.string_types):
raise CommandExecutionError(
"RegEx expected in insert_before_match parameter."
)
elif insert_after_match:
if not isinstance(insert_after_match, six.string_types):
raise CommandExecutionError(
"RegEx expected in insert_after_match parameter."
)
if append_newline is None and not content.endswith((os.linesep, "\n")): if append_newline is None and not content.endswith((os.linesep, "\n")):
append_newline = True append_newline = True
@ -2966,12 +2998,26 @@ def blockreplace(
block_found = True block_found = True
elif append_if_not_found: elif append_if_not_found:
# Make sure we have a newline at the end of the file # Make sure we have a newline at the end of the file
if 0 != len(new_file): if new_file:
if not new_file[-1].endswith(linesep): if not new_file[-1].endswith(linesep):
new_file[-1] += linesep new_file[-1] += linesep
# add the markers and content at the end of file # add the markers and content at the end of file
_add_content(linesep, lines=new_file) _add_content(linesep, lines=new_file)
block_found = True block_found = True
elif insert_before_match or insert_after_match:
match_regex = insert_before_match or insert_after_match
match_idx = [
i for i, item in enumerate(orig_file) if re.search(match_regex, item)
]
if match_idx:
match_idx = match_idx[0]
for line in _add_content(linesep):
if insert_after_match:
match_idx += 1
new_file.insert(match_idx, line)
if insert_before_match:
match_idx += 1
block_found = True
else: else:
raise CommandExecutionError( raise CommandExecutionError(
"Cannot edit marked block. Markers were not found in file." "Cannot edit marked block. Markers were not found in file."
@ -3008,34 +3054,57 @@ def blockreplace(
mode=perms["mode"], mode=perms["mode"],
) )
# write new content in the file while avoiding partial reads if not block_found:
try: raise CommandExecutionError(
fh_ = salt.utils.atomicfile.atomic_open(path, "wb") "Cannot edit marked block. Markers were not found in file."
for line in new_file: )
fh_.write(
salt.utils.stringutils.to_bytes(line, encoding=file_encoding)
)
finally:
fh_.close()
# this may have overwritten file attrs diff = __utils__["stringutils.get_diff"](orig_file, new_file)
has_changes = diff != ""
if has_changes and not dry_run:
# changes detected
# backup file attrs
perms = {}
perms["user"] = get_user(path)
perms["group"] = get_group(path)
perms["mode"] = salt.utils.files.normalize_mode(get_mode(path))
# backup old content
if backup is not False:
backup_path = "{0}{1}".format(path, backup)
shutil.copy2(path, backup_path)
# copy2 does not preserve ownership
if salt.utils.platform.is_windows(): if salt.utils.platform.is_windows():
# This function resides in win_file.py and will be available # This function resides in win_file.py and will be available
# on Windows. The local function will be overridden # on Windows. The local function will be overridden
# pylint: disable=E1120,E1123 # pylint: disable=E1120,E1123
check_perms(path=path, ret=None, owner=perms["user"]) check_perms(path=backup_path, ret=None, owner=perms["user"])
# pylint: enable=E1120,E1123 # pylint: enable=E1120,E1123
else: else:
check_perms( check_perms(
path, backup_path, None, perms["user"], perms["group"], perms["mode"]
ret=None,
user=perms["user"],
group=perms["group"],
mode=perms["mode"],
) )
if show_changes: # write new content in the file while avoiding partial reads
return diff try:
fh_ = salt.utils.atomicfile.atomic_open(path, "wb")
for line in new_file:
fh_.write(salt.utils.stringutils.to_bytes(line, encoding=file_encoding))
finally:
fh_.close()
# this may have overwritten file attrs
if salt.utils.platform.is_windows():
# This function resides in win_file.py and will be available
# on Windows. The local function will be overridden
# pylint: disable=E1120,E1123
check_perms(path=path, ret=None, owner=perms["user"])
# pylint: enable=E1120,E1123
else:
check_perms(path, None, perms["user"], perms["group"], perms["mode"])
if show_changes:
return diff
return has_changes return has_changes

View file

@ -5574,6 +5574,8 @@ def blockreplace(
backup=".bak", backup=".bak",
show_changes=True, show_changes=True,
append_newline=None, append_newline=None,
insert_before_match=None,
insert_after_match=None,
): ):
""" """
Maintain an edit in a file in a zone delimited by two line markers Maintain an edit in a file in a zone delimited by two line markers
@ -5700,6 +5702,18 @@ def blockreplace(
If markers are not found and this option is set to ``True``, the If markers are not found and this option is set to ``True``, the
content block will be prepended to the file. content block will be prepended to the file.
insert_before_match
If markers are not found, this parameter can be set to a regex which will
insert the block before the first found occurrence in the file.
.. versionadded:: Sodium
insert_after_match
If markers are not found, this parameter can be set to a regex which will
insert the block after the first found occurrence in the file.
.. versionadded:: Sodium
backup backup
The file extension to use for a backup of the file if any edit is made. The file extension to use for a backup of the file if any edit is made.
Set this to ``False`` to skip making a backup. Set this to ``False`` to skip making a backup.
@ -5830,6 +5844,8 @@ def blockreplace(
content=content, content=content,
append_if_not_found=append_if_not_found, append_if_not_found=append_if_not_found,
prepend_if_not_found=prepend_if_not_found, prepend_if_not_found=prepend_if_not_found,
insert_before_match=insert_before_match,
insert_after_match=insert_after_match,
backup=backup, backup=backup,
dry_run=__opts__["test"], dry_run=__opts__["test"],
show_changes=show_changes, show_changes=show_changes,

View file

@ -486,6 +486,49 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin):
fp.read(), fp.read(),
) )
def test_replace_insert_after(self):
new_content = "Well, I didn't vote for you."
self.assertRaises(
CommandExecutionError,
filemod.blockreplace,
self.tfile.name,
marker_start="#-- START BLOCK 2",
marker_end="#-- END BLOCK 2",
content=new_content,
insert_after_match="not in the text",
backup=False,
)
with salt.utils.files.fopen(self.tfile.name, "r") as fp:
self.assertNotIn(
"#-- START BLOCK 2" + "\n" + new_content + "#-- END BLOCK 2",
salt.utils.stringutils.to_unicode(fp.read()),
)
if salt.utils.platform.is_windows():
check_perms_patch = win_file.check_perms
else:
check_perms_patch = filemod.check_perms
with patch.object(filemod, "check_perms", check_perms_patch):
filemod.blockreplace(
self.tfile.name,
marker_start="#-- START BLOCK 2",
marker_end="#-- END BLOCK 2",
content=new_content,
backup=False,
insert_after_match="malesuada",
)
with salt.utils.files.fopen(self.tfile.name, "rb") as fp:
self.assertIn(
salt.utils.stringutils.to_bytes(
os.linesep.join(
["#-- START BLOCK 2", "{0}#-- END BLOCK 2".format(new_content)]
)
),
fp.read(),
)
def test_replace_append_newline_at_eof(self): def test_replace_append_newline_at_eof(self):
""" """
Check that file.blockreplace works consistently on files with and Check that file.blockreplace works consistently on files with and
@ -592,6 +635,49 @@ class FileBlockReplaceTestCase(TestCase, LoaderModuleMockMixin):
) )
) )
def test_replace_insert_before(self):
new_content = "Well, I didn't vote for you."
self.assertRaises(
CommandExecutionError,
filemod.blockreplace,
self.tfile.name,
marker_start="#-- START BLOCK 2",
marker_end="#-- END BLOCK 2",
content=new_content,
insert_before_match="not in the text",
backup=False,
)
with salt.utils.files.fopen(self.tfile.name, "r") as fp:
self.assertNotIn(
"#-- START BLOCK 2" + "\n" + new_content + "#-- END BLOCK 2",
salt.utils.stringutils.to_unicode(fp.read()),
)
if salt.utils.platform.is_windows():
check_perms_patch = win_file.check_perms
else:
check_perms_patch = filemod.check_perms
with patch.object(filemod, "check_perms", check_perms_patch):
filemod.blockreplace(
self.tfile.name,
marker_start="#-- START BLOCK 2",
marker_end="#-- END BLOCK 2",
content=new_content,
backup=False,
insert_before_match="malesuada",
)
with salt.utils.files.fopen(self.tfile.name, "rb") as fp:
self.assertIn(
salt.utils.stringutils.to_bytes(
os.linesep.join(
["#-- START BLOCK 2", "{0}#-- END BLOCK 2".format(new_content)]
)
),
fp.read(),
)
def test_replace_partial_marked_lines(self): def test_replace_partial_marked_lines(self):
if salt.utils.platform.is_windows(): if salt.utils.platform.is_windows():
check_perms_patch = win_file.check_perms check_perms_patch = win_file.check_perms