Skip to content

Commit b2a27d9

Browse files
committed
Add capability to specify state args as a dict
Also add some more type hints and change some format calls to f strings. Fix state retry tests with invalid assumptions of duration and comment.
1 parent d15a539 commit b2a27d9

File tree

11 files changed

+451
-307
lines changed

11 files changed

+451
-307
lines changed

changelog/68367.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Added capability to specify module arguments in an sls as a dict

salt/state.py

Lines changed: 246 additions & 252 deletions
Large diffs are not rendered by default.

tests/pytests/functional/modules/state/test_state.py

Lines changed: 70 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,13 @@
77

88
import pytest
99

10-
import salt.loader
1110
import salt.modules.cmdmod as cmd
1211
import salt.modules.config as config
1312
import salt.modules.grains as grains
1413
import salt.modules.saltutil as saltutil
1514
import salt.modules.state as state_mod
16-
import salt.utils.atomicfile
17-
import salt.utils.files
18-
import salt.utils.path
1915
import salt.utils.platform
2016
import salt.utils.state as state_util
21-
import salt.utils.stringutils
2217

2318
log = logging.getLogger(__name__)
2419

@@ -646,6 +641,36 @@ def test_pydsl(state, state_tree, tmp_path):
646641
assert testfile.exists()
647642

648643

644+
def test_sls_with_state_args_dict(state, state_tree):
645+
"""
646+
Call sls file with state argument as a dict.
647+
"""
648+
sls_contents = """
649+
A:
650+
test.succeed_without_changes:
651+
name: echo foo
652+
"""
653+
with pytest.helpers.temp_file("testing.sls", sls_contents, state_tree):
654+
ret = state.sls("testing")
655+
for staterun in ret:
656+
assert staterun.result is True
657+
658+
659+
def test_sls_with_state_args_list(state, state_tree):
660+
"""
661+
Call sls file with state argument as a dict instead of a list.
662+
"""
663+
sls_contents = """
664+
A:
665+
test.succeed_without_changes:
666+
- name: echo foo
667+
"""
668+
with pytest.helpers.temp_file("testing.sls", sls_contents, state_tree):
669+
ret = state.sls("testing")
670+
for staterun in ret:
671+
assert staterun.result is True
672+
673+
649674
def test_issues_7905_and_8174_sls_syntax_error(state, state_tree):
650675
"""
651676
Call sls file with yaml syntax error.
@@ -680,12 +705,15 @@ def test_issues_7905_and_8174_sls_syntax_error(state, state_tree):
680705
"badlist1.sls", badlist_1_sls_contents, state_tree
681706
), pytest.helpers.temp_file("badlist2.sls", badlist_2_sls_contents, state_tree):
682707
ret = state.sls("badlist1")
683-
assert ret.failed
684-
assert ret.errors == ["State 'A' in SLS 'badlist1' is not formed as a list"]
708+
staterun = ret["cmd_|-A_|-A_|-run"]
709+
assert staterun.result is False
710+
assert staterun.changes["stderr"] == "/bin/sh: line 1: A: command not found"
685711

686712
ret = state.sls("badlist2")
687713
assert ret.failed
688-
assert ret.errors == ["State 'C' in SLS 'badlist2' is not formed as a list"]
714+
assert ret.errors == [
715+
"State 'C' in SLS 'badlist2' is not formed as a list or dict"
716+
]
689717

690718

691719
def test_retry_option(state, state_tree):
@@ -737,7 +765,7 @@ def test_retry_option_is_true(state, state_tree):
737765
for state_return in ret:
738766
assert state_return.result is False
739767
assert expected_comment in state_return.comment
740-
assert state_return.full_return["duration"] >= 3
768+
assert state_return.full_return["duration"] >= 30
741769

742770

743771
@pytest.mark.skip_initial_gh_actions_failure(skip=_check_skip)
@@ -769,7 +797,7 @@ def test_retry_option_success(state, state_tree, tmp_path):
769797
assert state_return.result is True
770798
assert state_return.full_return["duration"] < duration
771799
# It should not take 2 attempts
772-
assert "Attempt 2" not in state_return.comment
800+
assert "Attempt 1" not in state_return.comment
773801

774802

775803
@pytest.mark.skip_on_windows(
@@ -820,41 +848,44 @@ def test_retry_option_eventual_success(state, state_tree, tmp_path):
820848
testfile1 = tmp_path / "testfile-1"
821849
testfile2 = tmp_path / "testfile-2"
822850

851+
interval = 2
852+
823853
def create_testfile(testfile1, testfile2):
824854
while True:
825855
if testfile1.exists():
826856
break
827-
time.sleep(2)
857+
time.sleep(interval)
828858
testfile2.touch()
829859

830860
thread = threading.Thread(target=create_testfile, args=(testfile1, testfile2))
831-
sls_contents = """
861+
sls_contents = f"""
832862
file_test_a:
833863
file.managed:
834-
- name: {}
864+
- name: {testfile1}
835865
- content: 'a'
836866
837867
file_test:
838868
file.exists:
839-
- name: {}
869+
- name: {testfile2}
840870
- retry:
841871
until: True
842872
attempts: 5
843-
interval: 2
873+
interval: {interval}
844874
splay: 0
845875
- require:
846876
- file_test_a
847-
""".format(
848-
testfile1, testfile2
849-
)
877+
"""
850878
with pytest.helpers.temp_file("retry.sls", sls_contents, state_tree):
851879
thread.start()
852880
ret = state.sls("retry")
853-
for state_return in ret:
881+
for num, state_return in enumerate(ret):
854882
assert state_return.result is True
855-
assert state_return.full_return["duration"] > 4
856-
# It should not take 5 attempts
857-
assert "Attempt 5" not in state_return.comment
883+
assert state_return.full_return["duration"] > interval
884+
if num == 1:
885+
# It should retry at least 1 time
886+
assert "Attempt 1" in state_return.comment
887+
# It should not take 5 attempts
888+
assert "Attempt 5" not in state_return.comment
858889

859890

860891
@pytest.mark.skip_on_windows(
@@ -867,45 +898,48 @@ def test_retry_option_eventual_success_parallel(state, state_tree, tmp_path):
867898
testfile1 = tmp_path / "testfile-1"
868899
testfile2 = tmp_path / "testfile-2"
869900

901+
interval = 2
902+
870903
def create_testfile(testfile1, testfile2):
871904
while True:
872905
if testfile1.exists():
873906
break
874-
time.sleep(2)
907+
time.sleep(interval)
875908
testfile2.touch()
876909

877910
thread = threading.Thread(target=create_testfile, args=(testfile1, testfile2))
878-
sls_contents = """
911+
sls_contents = f"""
879912
file_test_a:
880913
file.managed:
881-
- name: {}
914+
- name: {testfile1}
882915
- content: 'a'
883916
884917
file_test:
885918
file.exists:
886-
- name: {}
919+
- name: {testfile2}
887920
- retry:
888921
until: True
889922
attempts: 5
890-
interval: 2
923+
interval: {interval}
891924
splay: 0
892925
- parallel: True
893926
- require:
894927
- file_test_a
895-
""".format(
896-
testfile1, testfile2
897-
)
928+
"""
898929
with pytest.helpers.temp_file("retry.sls", sls_contents, state_tree):
899930
thread.start()
900931
ret = state.sls(
901932
"retry", __pub_jid="1"
902933
) # Because these run in parallel we need a fake JID
903-
for state_return in ret:
934+
for num, state_return in enumerate(ret):
904935
log.debug("=== state_return %s ===", state_return)
905936
assert state_return.result is True
906-
assert state_return.full_return["duration"] > 4
907-
# It should not take 5 attempts
908-
assert "Attempt 5" not in state_return.comment
937+
assert state_return.full_return["duration"] > interval
938+
if num == 1:
939+
# It should retry at least 1 time
940+
assert "Attempt 1" in state_return.comment
941+
# It should not take 5 attempts
942+
assert "Attempt 5" not in state_return.comment
909943

910944

911945
def test_state_non_base_environment(state, state_tree_prod, tmp_path):
@@ -1131,8 +1165,8 @@ def test_it(name):
11311165
"""
11321166
This should not fail on spawning platforms:
11331167
requires_env.test_it:
1134-
- name: foo
1135-
- parallel: true
1168+
name: foo
1169+
parallel: true
11361170
"""
11371171
)
11381172
with pytest.helpers.temp_file(

tests/pytests/integration/cli/test_salt_call.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,28 @@ def test_local_sls_call(salt_master, salt_call_cli):
8181
assert state_run_dict["changes"]["ret"] == "hello"
8282

8383

84+
def test_local_sls_call_with_dict_args(salt_master, salt_call_cli):
85+
sls_contents = """
86+
regular-module:
87+
module.run:
88+
name: test.echo
89+
text: hello
90+
"""
91+
with salt_master.state_tree.base.temp_file("saltcalllocal.sls", sls_contents):
92+
ret = salt_call_cli.run(
93+
"--local",
94+
"--file-root",
95+
str(salt_master.state_tree.base.paths[0]),
96+
"state.sls",
97+
"saltcalllocal",
98+
)
99+
assert ret.returncode == 0
100+
state_run_dict = next(iter(ret.data.values()))
101+
assert state_run_dict["name"] == "test.echo"
102+
assert state_run_dict["result"] is True
103+
assert state_run_dict["changes"]["ret"] == "hello"
104+
105+
84106
def test_local_salt_call(salt_call_cli):
85107
"""
86108
This tests to make sure that salt-call does not execute the

tests/pytests/integration/modules/test_cmdmod.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,16 @@ def test_avoid_injecting_shell_code_as_root(
5050
cmd = "echo $(id -u)"
5151

5252
ret = salt_call_cli.run("cmd.run_stdout", cmd, python_shell=True)
53-
root_id = ret.json
53+
root_id = ret.data
5454
ret = salt_call_cli.run(
5555
"cmd.run_stdout", cmd, runas=running_username, python_shell=True
5656
)
57-
runas_root_id = ret.json
57+
runas_root_id = ret.data
5858

5959
ret = salt_call_cli.run(
6060
"cmd.run_stdout", cmd, runas=non_root_account.username, python_shell=True
6161
)
62-
user_id = ret.json
62+
user_id = ret.data
6363

6464
assert user_id != root_id
6565
assert user_id != runas_root_id

tests/pytests/integration/modules/test_state.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@ def test_logging_and_state_output_order(salt_master, salt_minion, salt_cli, tmp_
1515
"""
1616
target_path = tmp_path / "file-target.txt"
1717
sls_name = "file-target"
18-
sls_contents = """
18+
sls_contents = f"""
1919
add_contents_pillar_sls:
2020
file.managed:
21-
- name: {}
21+
- name: {target_path}
2222
- contents: foo
23-
""".format(
24-
target_path
25-
)
23+
"""
2624
sls_tempfile = salt_master.state_tree.base.temp_file(
2725
f"{sls_name}.sls", sls_contents
2826
)

tests/pytests/integration/states/test_file.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,8 +1323,8 @@ def test_directory_recurse(salt_master, salt_call_cli, tmp_path, grains):
13231323
with sls_tempfile:
13241324
ret = salt_call_cli.run("state.sls", sls_name)
13251325
key = f"file_|-{target_dir}_|-{target_dir}_|-directory"
1326-
assert key in ret.json
1327-
result = ret.json[key]
1326+
assert key in ret.data
1327+
result = ret.data[key]
13281328
assert "changes" in result and result["changes"]
13291329

13301330
# Permissions of file should not have changed.

tests/pytests/pkg/integration/test_salt_call.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,30 @@ def state_name(salt_master):
4646
yield name
4747

4848

49-
def test_sls(salt_call_cli, salt_master, state_name):
49+
@pytest.fixture
50+
def state_name_dict_arg(salt_master):
51+
name = "some-test-state"
52+
sls_contents = """
53+
test_foo:
54+
test.succeed_with_changes:
55+
name: foo
56+
"""
57+
with salt_master.state_tree.base.temp_file(f"{name}.sls", sls_contents):
58+
if not platform.is_windows() and not platform.is_darwin():
59+
subprocess.run(
60+
[
61+
"chown",
62+
"-R",
63+
"salt:salt",
64+
str(salt_master.state_tree.base.write_path),
65+
],
66+
check=False,
67+
)
68+
yield name
69+
70+
71+
@pytest.mark.parametrize("fixture_name", ["state_name", "state_name_dict_arg"])
72+
def test_sls(salt_call_cli, salt_master, fixture_name):
5073
"""
5174
Test calling a sls file
5275
"""

tests/pytests/unit/state/test_reactor_compiler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ def test_compiler_pad_funcs_short_sls(minion_opts, tmp_path):
205205
}
206206
},
207207
[
208-
"State 'master_pub' in SLS '/srv/reactor/start.sls' is not formed as a list"
208+
"State 'master_pub' in SLS '/srv/reactor/start.sls' is not formed as a list or dict"
209209
],
210210
),
211211
(

0 commit comments

Comments
 (0)