Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[master] Account for included states that only include other states used as requisites #65326

Merged
1 change: 1 addition & 0 deletions changelog/65080.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Keep track when an included file only includes sls files but is a requisite.
26 changes: 24 additions & 2 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"__env__",
"__sls__",
"__id__",
"__sls_included_from__",
"__orchestration_jid__",
"__pub_user",
"__pub_arg",
Expand Down Expand Up @@ -668,6 +669,8 @@ def compile_high_data(self, high):
chunk["__sls__"] = body["__sls__"]
if "__env__" in body:
chunk["__env__"] = body["__env__"]
if "__sls_included_from__" in body:
chunk["__sls_included_from__"] = body["__sls_included_from__"]
chunk["__id__"] = name
for arg in run:
if isinstance(arg, str):
Expand Down Expand Up @@ -1702,6 +1705,8 @@ def compile_high_data(self, high, orchestration_jid=None):
chunk["__sls__"] = body["__sls__"]
if "__env__" in body:
chunk["__env__"] = body["__env__"]
if "__sls_included_from__" in body:
chunk["__sls_included_from__"] = body["__sls_included_from__"]
chunk["__id__"] = name
for arg in run:
if isinstance(arg, str):
Expand Down Expand Up @@ -2911,7 +2916,9 @@ def check_requisite(self, low, running, chunks, pre=False):
for chunk in chunks:
if req_key == "sls":
# Allow requisite tracking of entire sls files
if fnmatch.fnmatch(chunk["__sls__"], req_val):
if fnmatch.fnmatch(
chunk["__sls__"], req_val
) or req_val in chunk.get("__sls_included_from__", []):
found = True
reqs[r_state].append(chunk)
continue
Expand Down Expand Up @@ -3126,7 +3133,9 @@ def call_chunk(self, low, running, chunks, depth=0):
continue
if req_key == "sls":
# Allow requisite tracking of entire sls files
if fnmatch.fnmatch(chunk["__sls__"], req_val):
if fnmatch.fnmatch(
chunk["__sls__"], req_val
) or req_val in chunk.get("__sls_included_from__", []):
if requisite == "prereq":
chunk["__prereq__"] = True
reqs.append(chunk)
Expand Down Expand Up @@ -4324,6 +4333,9 @@ def render_state(self, sls, saltenv, mods, matches, local=False, context=None):
else:
include = state.pop("include")

# Keep track if the state only includes includes
empty_state_dict = len(state) == 0

dmurphy18 marked this conversation as resolved.
Show resolved Hide resolved
self._handle_extend(state, sls, saltenv, errors)
self._handle_exclude(state, sls, saltenv, errors)
self._handle_state_decls(state, sls, saltenv, errors)
Expand Down Expand Up @@ -4424,6 +4436,16 @@ def render_state(self, sls, saltenv, mods, matches, local=False, context=None):
context=context,
)
if nstate:
for item in nstate:
# Skip existing state keywords
if item.startswith("__"):
continue
if "__sls_included_from__" not in nstate[item]:
nstate[item]["__sls_included_from__"] = []
nstate[item]["__sls_included_from__"].append(
sls
)

self.merge_included_states(state, nstate, errors)
state.update(nstate)
if err:
Expand Down
154 changes: 154 additions & 0 deletions tests/pytests/integration/states/test_include.py
dmurphy18 marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,157 @@ def test_issue_64111(salt_master, salt_minion, salt_call_cli):
with tf("common/file1.sls", file1_sls):
ret = salt_call_cli.run("state.apply", "common")
assert ret.returncode == 0


@pytest.mark.slow_test
def test_issue_65080(salt_master, salt_minion, salt_call_cli):
"""
Test scenario when a state includes another state that only includes a third state
"""

only_include_sls = """
include:
- includetest.another-include
"""

another_include_sls = """
/tmp/from-test-file.txt:
file.managed:
- contents: Hello from test-file.sls
"""

init_sls = """
include:
- includetest.only-include

/tmp/from-init.txt:
file.managed:
- contents: Hello from init.sls
- require:
- sls: includetest.only-include
"""

tf = salt_master.state_tree.base.temp_file

with tf("includetest/init.sls", init_sls):
with tf("includetest/another-include.sls", another_include_sls):
with tf("includetest/only-include.sls", only_include_sls):
ret = salt_call_cli.run("state.apply", "includetest")
assert ret.returncode == 0

ret = salt_call_cli.run("state.show_low_sls", "includetest")
assert "__sls_included_from__" in ret.data[0]
assert (
"includetest.only-include" in ret.data[0]["__sls_included_from__"]
)


@pytest.mark.slow_test
def test_issue_65080_multiple_includes(salt_master, salt_minion, salt_call_cli):
"""
Test scenario when a state includes another state that only includes a third state
"""

only_include_one_sls = """
include:
- includetest.include-one
"""

only_include_two_sls = """
include:
- includetest.include-two
"""

include_one_sls = """
/tmp/from-test-file1.txt:
file.managed:
- contents: Hello from test-file.sls
"""

include_two_sls = """
/tmp/from-test-file2.txt:
file.managed:
- contents: Hello from test-file.sls
"""

init_sls = """
include:
- includetest.only-include-one
- includetest.only-include-two

/tmp/from-init.txt:
file.managed:
- contents: Hello from init.sls
- require:
- sls: includetest.only-include-one
- sls: includetest.only-include-two
"""

tf = salt_master.state_tree.base.temp_file

with tf("includetest/init.sls", init_sls):
with tf("includetest/include-one.sls", include_one_sls), tf(
"includetest/include-two.sls", include_two_sls
):
with tf("includetest/only-include-one.sls", only_include_one_sls), tf(
"includetest/only-include-two.sls", only_include_two_sls
):
ret = salt_call_cli.run("state.apply", "includetest")
assert ret.returncode == 0

ret = salt_call_cli.run("state.show_low_sls", "includetest")
assert "__sls_included_from__" in ret.data[0]
assert (
"includetest.only-include-one"
in ret.data[0]["__sls_included_from__"]
)

assert "__sls_included_from__" in ret.data[1]
assert (
"includetest.only-include-two"
in ret.data[1]["__sls_included_from__"]
)


@pytest.mark.slow_test
def test_issue_65080_saltenv(salt_master, salt_minion, salt_call_cli):
"""
Test scenario when a state includes another state that only includes a third state
"""

only_include_sls = """
include:
- includetest.another-include
"""

another_include_sls = """
/tmp/from-test-file.txt:
file.managed:
- contents: Hello from test-file.sls
"""

init_sls = """
include:
- prod: includetest.only-include

/tmp/from-init.txt:
file.managed:
- contents: Hello from init.sls
- require:
- sls: includetest.only-include
"""

base_tf = salt_master.state_tree.base.temp_file
prod_tf = salt_master.state_tree.prod.temp_file

with base_tf("includetest/init.sls", init_sls):
with prod_tf("includetest/only-include.sls", only_include_sls):
with prod_tf("includetest/another-include.sls", another_include_sls):
ret = salt_call_cli.run("state.apply", "includetest")
assert ret.returncode == 0

ret = salt_call_cli.run("state.show_low_sls", "includetest")
assert "__sls_included_from__" in ret.data[0]
assert (
"includetest.only-include" in ret.data[0]["__sls_included_from__"]
)
1 change: 1 addition & 0 deletions tests/pytests/unit/state/test_state_highstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@ def test_dont_extend_in_excluded_sls_file(highstate, state_tree_dir):
),
("__sls__", "test2"),
("__env__", "base"),
("__sls_included_from__", ["test1"]),
]
),
),
Expand Down