Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions salt/states/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -4097,8 +4097,8 @@ def recurse(
:ref:`backup_mode documentation <file-state-backups>` for more details.

include_pat
When copying, include only this pattern from the source. Default
is glob match; if prefixed with 'E@', then regexp match.
When copying, include only this pattern, or list of patterns, from the
source. Default is glob match; if prefixed with 'E@', then regexp match.
Example:

.. code-block:: text
Expand All @@ -4108,9 +4108,19 @@ def recurse(
- include_pat: E@hello :: regexp matches 'otherhello',
'hello01' ...

.. versionchanged:: Sodium

List patterns are now supported

.. code-block:: text

- include_pat:
- hello01
- hello02

exclude_pat
Exclude this pattern from the source when copying. If both
`include_pat` and `exclude_pat` are supplied, then it will apply
Exclude this pattern, or list of patterns, from the source when copying.
If both `include_pat` and `exclude_pat` are supplied, then it will apply
conditions cumulatively. i.e. first select based on include_pat, and
then within that result apply exclude_pat.

Expand All @@ -4125,6 +4135,16 @@ def recurse(
- exclude_pat: E@(APPDATA)|(TEMPDATA) :: regexp matches APPDATA
or TEMPDATA for exclusion

.. versionchanged:: Sodium

List patterns are now supported

.. code-block:: text

- exclude_pat:
- APPDATA.01
- APPDATA.02

maxdepth
When copying, only copy paths which are of depth `maxdepth` from the
source path.
Expand Down Expand Up @@ -4169,6 +4189,7 @@ def recurse(
True to inherit permissions from parent, otherwise False

.. versionadded:: 2017.7.7

"""
if "env" in kwargs:
# "env" is not supported; Use "saltenv".
Expand Down
25 changes: 19 additions & 6 deletions salt/utils/stringutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,19 +466,32 @@ def check_include_exclude(path_str, include_pat=None, exclude_pat=None):
- If both include_pat and exclude_pat are supplied: return 'True' if
include_pat matches AND exclude_pat does not match
"""

def _pat_check(path_str, check_pat):
if re.match("E@", check_pat):
return True if re.search(check_pat[2:], path_str) else False
else:
return True if fnmatch.fnmatch(path_str, check_pat) else False

ret = True # -- default true
# Before pattern match, check if it is regexp (E@'') or glob(default)
if include_pat:
if re.match("E@", include_pat):
retchk_include = True if re.search(include_pat[2:], path_str) else False
if isinstance(include_pat, list):
for include_line in include_pat:
retchk_include = _pat_check(path_str, include_line)
if retchk_include:
break
else:
retchk_include = True if fnmatch.fnmatch(path_str, include_pat) else False
retchk_include = _pat_check(path_str, include_pat)

if exclude_pat:
if re.match("E@", exclude_pat):
retchk_exclude = False if re.search(exclude_pat[2:], path_str) else True
if isinstance(exclude_pat, list):
for exclude_line in exclude_pat:
retchk_exclude = not _pat_check(path_str, exclude_line)
if not retchk_exclude:
break
else:
retchk_exclude = False if fnmatch.fnmatch(path_str, exclude_pat) else True
retchk_exclude = not _pat_check(path_str, exclude_pat)

# Now apply include/exclude conditions
if include_pat and not exclude_pat:
Expand Down
27 changes: 27 additions & 0 deletions tests/unit/utils/test_stringutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,3 +535,30 @@ def test_check_whitelist_blacklist(self):
"foo",
blacklist=123,
)

def test_check_include_exclude_empty(self):
self.assertTrue(salt.utils.stringutils.check_include_exclude("/some/test"))

def test_check_include_exclude_exclude(self):
self.assertFalse(
salt.utils.stringutils.check_include_exclude("/some/test", None, "*test*")
)

def test_check_include_exclude_exclude_list(self):
self.assertFalse(
salt.utils.stringutils.check_include_exclude("/some/test", None, ["*test"])
)

def test_check_include_exclude_exclude_include(self):
self.assertTrue(
salt.utils.stringutils.check_include_exclude(
"/some/test", "*test*", "/some/"
)
)

def test_check_include_exclude_regex(self):
self.assertFalse(
salt.utils.stringutils.check_include_exclude(
"/some/test", None, "E@/some/(test|other)"
)
)