/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/pytests/functional/modules/state/requisites/test_require.py
840 строк
22 KB
Daniel A. Wozniak
Merge branch '3007.x' into merge/3007.x/3008.x-26-06-23
24 июн 2026, 01:19
24 июн 2026, 01:19
611b4be
Код
Авторство
О чём код?
import datetime import time import pytest from . import normalize_ret pytestmark = [ pytest.mark.windows_whitelisted, pytest.mark.core_test, ] def test_requisites_full_sls_require(state, state_tree): """ Test the sls special command in requisites """ full_sls_contents = """ B: cmd.run: - name: echo B C: cmd.run: - name: echo C """ sls_contents = """ include: - fullsls A: cmd.run: - name: echo A - require: - sls: fullsls """ expected_result = { "cmd_|-A_|-echo A_|-run": { "__run_num__": 2, "comment": 'Command "echo A" run', "result": True, "changes": True, }, "cmd_|-B_|-echo B_|-run": { "__run_num__": 0, "comment": 'Command "echo B" run', "result": True, "changes": True, }, "cmd_|-C_|-echo C_|-run": { "__run_num__": 1, "comment": 'Command "echo C" run', "result": True, "changes": True, }, } with pytest.helpers.temp_file( "requisite.sls", sls_contents, state_tree ), pytest.helpers.temp_file("fullsls.sls", full_sls_contents, state_tree): ret = state.sls("requisite") result = normalize_ret(ret.raw) assert result == expected_result def test_requisites_require_no_state_module(state, state_tree): """ Call sls file containing several require_in and require with a missing req. Ensure an error is given. """ sls_contents = """ # Complex require/require_in graph # # Relative order of C>E is given by the definition order # # D (1) <--+ # | # B (2) ---+ <-+ <-+ <-+ # | | | # C (3) <--+ --|---|---+ # | | | # E (4) ---|---|---+ <-+ # | | | # A (5) ---+ --+ ------+ # A: cmd.run: - name: echo A fifth - require: - C B: cmd.run: - name: echo B second - require_in: - A - C C: cmd.run: - name: echo C third D: cmd.run: - name: echo D first - require_in: - B E: cmd.run: - name: echo E fourth - require: - B - require_in: - A # will fail with "Referenced state does not exist for requisite" G: cmd.run: - name: echo G - require: - Z # will fail with "Referenced state does not exist for requisite" H: cmd.run: - name: echo H - require: - Z """ errmsgs = [ ( "Referenced state does not exist for requisite [require: (id: Z)]" " in state [echo G] in SLS [requisite]" ), ( "Referenced state does not exist for requisite [require: (id: Z)]" " in state [echo H] in SLS [requisite]" ), ] with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == errmsgs def test_requisites_require_ordering_and_errors_1(state, state_tree): """ Call sls file containing several require_in and require. Ensure there are errors due to requisites. """ sls_contents = """ # Complex require/require_in graph # # Relative order of C>E is given by the definition order # # D (1) <--+ # | # B (2) ---+ <-+ <-+ <-+ # | | | # C (3) <--+ --|---|---+ # | | | # E (4) ---|---|---+ <-+ # | | | # A (5) ---+ --+ ------+ # A: cmd.run: - name: echo A fifth - require: - cmd: C B: cmd.run: - name: echo B second - require_in: - cmd: A - cmd: C C: cmd.run: - name: echo C third D: cmd.run: - name: echo D first - require_in: - cmd: B E: cmd.run: - name: echo E fourth - require: - cmd: B - require_in: - cmd: A # will fail with "Referenced state does not exist for requisite" F: cmd.run: - name: echo F - require: - foobar: A # will fail with "Referenced state does not exist for requisite" G: cmd.run: - name: echo G - require: - cmd: Z # will fail with "Referenced state does not exist for requisite" H: cmd.run: - name: echo H - require: - cmd: Z """ errmsgs = [ ( "Referenced state does not exist for requisite [require: (foobar: A)]" " in state [echo F] in SLS [requisite]" ), ( "Referenced state does not exist for requisite [require: (cmd: Z)]" " in state [echo G] in SLS [requisite]" ), ( "Referenced state does not exist for requisite [require: (cmd: Z)]" " in state [echo H] in SLS [requisite]" ), ] with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == errmsgs def test_requisites_require_ordering_and_errors_2(state, state_tree): """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. """ sls_contents = """ # will fail with "Data failed to compile:" A: cmd.run: - name: echo A - require_in: - foobar: W """ errmsg = ( "Cannot extend ID 'W' in 'base:requisite'. It is not part of the high" " state.\nThis is likely due to a missing include statement or an incorrectly" " typed ID.\nEnsure that a state with an ID of 'W' is available\nin environment" " 'base' and to SLS 'requisite'" ) with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == [errmsg] def test_requisites_require_ordering_and_errors_3(state, state_tree): """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. """ sls_contents = """ # issue #8772 # should fail with "Data failed to compile:" B: cmd.run: - name: echo B last - require_in: # state foobar does not exists in A - foobar: A A: cmd.run: - name: echo A first """ errmsg = ( "Cannot extend ID 'A' in 'base:requisite'. It is not part of the high state.\n" "This is likely due to a missing include statement or an incorrectly typed ID.\n" "Ensure that a state with an ID of 'A' is available\n" "in environment 'base' and to SLS 'requisite'" ) with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == [errmsg] def test_requisites_require_ordering_and_errors_4(state, state_tree): """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. """ sls_contents = """ A: cmd.run: - name: echo A B: cmd.run: - name: echo B # here used without "-" - require: cmd: A C: cmd.run: - name: echo C # here used without "-" - require_in: cmd: A """ # issue #8235 # FIXME: Why is require enforcing list syntax while require_in does not? # And why preventing it? # Currently this state fails, should return C/B/A errmsg = ( "The require statement in state 'B' in SLS 'requisite' needs to be formed as a" " list" ) with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == [errmsg] def test_requisites_require_ordering_and_errors_5(state, state_tree): """ Call sls file containing several require_in and require. Ensure that some of them are failing and that the order is right. """ sls_contents = """ A: cmd.run: - name: echo A - require: - cmd: B B: cmd.run: - name: echo B - require: - cmd: A """ errmsg = ( "Recursive requisites were found: " "({'SLS': 'requisite', 'ID': 'B', 'NAME': 'echo B'}, " "'require', {'SLS': 'requisite', 'ID': 'A', 'NAME': 'echo A'}), " "({'SLS': 'requisite', 'ID': 'A', 'NAME': 'echo A'}, 'require', " "{'SLS': 'requisite', 'ID': 'B', 'NAME': 'echo B'})" ) with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert ret.failed assert ret.errors == [errmsg] def test_requisites_require_with_order_first_last(state, state_tree): """ Call sls file containing a state with require_in order first and require and order last. Ensure that the order is right. """ sls_contents = """ # Complex require/require_in graph # # Relative order of A > D is given by the definition order # # B (1) <------+ # | # A (2) + # | # D (3) <--+ --| # | # E (4) | # | # C (5) ---+ # A: test.succeed_with_changes B: test.succeed_with_changes: - order: first - require_in: - D C: test.succeed_with_changes: - order: last - require: - D D: test.succeed_with_changes E: test.succeed_with_changes """ expected_result = { "test_|-B_|-B_|-succeed_with_changes": { "__run_num__": 0, "result": True, "changes": True, "comment": "Success!", }, "test_|-A_|-A_|-succeed_with_changes": { "__run_num__": 1, "result": True, "changes": True, "comment": "Success!", }, "test_|-D_|-D_|-succeed_with_changes": { "__run_num__": 2, "result": True, "changes": True, "comment": "Success!", }, "test_|-E_|-E_|-succeed_with_changes": { "__run_num__": 3, "result": True, "changes": True, "comment": "Success!", }, "test_|-C_|-C_|-succeed_with_changes": { "__run_num__": 4, "result": True, "changes": True, "comment": "Success!", }, } with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") result = normalize_ret(ret.raw) assert result == expected_result def test_requisites_require_any(state, state_tree): """ Call sls file containing require_any. Ensure that the order is right. """ sls_contents = """ # Complex require/require_in graph # # Relative order of C>E is given by the definition order # # D (1) <--+ # | # B (2) ---+ <-+ <-+ <-+ # | | | # C (3) <--+ --|---|---+ # | | | # E (4) ---|---|---+ <-+ # | | | # A (5) ---+ --+ ------+ # # A should success since B succeeds even though C fails. A: cmd.run: - name: echo A - require_any: - cmd: B - cmd: C - cmd: D B: cmd.run: - name: echo B C: cmd.run: - name: "$(which false)" D: cmd.run: - name: echo D """ expected_result = { "cmd_|-A_|-echo A_|-run": { "__run_num__": 3, "comment": 'Command "echo A" run', "result": True, "changes": True, }, "cmd_|-B_|-echo B_|-run": { "__run_num__": 0, "comment": 'Command "echo B" run', "result": True, "changes": True, }, "cmd_|-C_|-$(which false)_|-run": { "__run_num__": 1, "comment": 'Command "$(which false)" run', "result": False, "changes": True, }, "cmd_|-D_|-echo D_|-run": { "__run_num__": 2, "comment": 'Command "echo D" run', "result": True, "changes": True, }, } with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") result = normalize_ret(ret.raw) assert result == expected_result def test_requisites_require_any_fail(state, state_tree): """ Call sls file containing require_any. Ensure that the order is right. """ sls_contents = """ # D should fail since both E & F fail E: cmd.run: - name: 'false' F: cmd.run: - name: 'false' D: cmd.run: - name: echo D - require_any: - cmd: E - cmd: F """ with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert "One or more requisite failed" in ret["cmd_|-D_|-echo D_|-run"].comment def test_issue_38683_require_order_failhard_combination(state, state_tree): """ This tests the case where require, order, and failhard are all used together in a state definition. Previously, the order option, which used in tandem with require and failhard, would cause the state compiler to stacktrace. This exposed a logic error in the ``check_failhard`` function of the state compiler. With the logic error resolved, this test should now pass. See https://github.com/saltstack/salt/issues/38683 for more information. """ sls_contents = """ a: test.show_notification: - name: a - text: message - require: - test: b - order: 1 - failhard: True b: test.fail_with_changes: - name: b - failhard: True """ state_id = "test_|-b_|-b_|-fail_with_changes" with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") assert state_id in ret assert ret[state_id].result is False assert ret[state_id].comment == "Failure!" @pytest.mark.skip_on_windows def test_parallel_state_with_requires(state, state_tree): """ This is a test case for https://github.com/saltstack/salt/issues/49273 Parallel state object has any requisites """ sls_contents = """ barrier: cmd.run: - name: sleep 1 {%- for x in range(1, 10) %} blah-{{x}}: cmd.run: - name: sleep 2 - require: - barrier - barrier2 - parallel: true {% endfor %} barrier2: test.nop """ with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): start_time = time.time() ret = state.sls( "requisite", __pub_jid="1", # Because these run in parallel we need a fake JID) ) end_time = time.time() # We're running 3 states that sleep for 10 seconds each # they'll run in parallel so we should be below 30 seconds # confirm that the total runtime is below 30s assert (end_time - start_time) < 30 for item in range(1, 10): _id = f"cmd_|-blah-{item}_|-sleep 2_|-run" assert "__parallel__" in ret[_id] @pytest.mark.skip_on_windows def test_parallel_state_with_requires_on_parallel(state, state_tree): """ Parallel states requiring other parallel states should not block state execution while waiting on their requisites. Issue #59959 """ sls_contents = """ service_a: cmd.run: - name: sleep 2 - parallel: True service_b1: cmd.run: - name: sleep 5 - parallel: True - require: - service_a service_b2: cmd.run: - name: 'true' - parallel: True - require: - service_b1 service_c: cmd.run: - name: 'true' - parallel: True - require: - service_a """ with pytest.helpers.temp_file("requisite_parallel.sls", sls_contents, state_tree): ret = state.sls( "requisite_parallel", __pub_jid="1", # Because these run in parallel we need a fake JID) ) start_b1 = datetime.datetime.combine( datetime.date.today(), datetime.time.fromisoformat( ret["cmd_|-service_b1_|-sleep 5_|-run"]["start_time"] ), ) start_c = datetime.datetime.combine( datetime.date.today(), datetime.time.fromisoformat( ret["cmd_|-service_c_|-true_|-run"]["start_time"] ), ) start_diff = start_c - start_b1 # Expected order: # a > (b1, c) > b2 # When b2 blocks while waiting for b1, c has to wait for b1 as well. # c should approximately start at the same time as b1 though. assert start_diff < datetime.timedelta(seconds=5) # b1 sleeps for 5 seconds for state_ret in ret.raw.values(): assert "__parallel__" in state_ret @pytest.mark.skip_on_windows def test_regular_state_requires_parallel(state, state_tree, tmp_path): """ Regular states requiring parallel states should block until all requisites are executed. """ tmpfile = tmp_path / "foo" sls_contents = f""" service_a: cmd.run: - name: sleep 3 - parallel: True service_b: cmd.run: - name: 'touch {tmpfile}' - parallel: True - require: - service_a service_c: cmd.run: - name: 'test -f {tmpfile}' - require: - service_b """ with pytest.helpers.temp_file("requisite_parallel_2.sls", sls_contents, state_tree): ret = state.sls( "requisite_parallel_2", __pub_jid="1", # Because these run in parallel we need a fake JID) ) assert ret[f"cmd_|-service_c_|-test -f {tmpfile}_|-run"]["result"] is True def test_issue_59922_conflict_in_name_and_id_for_require_in(state, state_tree): """ Make sure that state_type is always honored while compiling down require_in to corresponding require statement. """ sls_contents = """ X: test.succeed_without_changes: - name: A A: cmd.run: - name: echo A B: cmd.run: - name: echo B - require_in: - test: A """ expected_result = { "cmd_|-A_|-echo A_|-run": { "__run_num__": 2, "comment": 'Command "echo A" run', "result": True, "changes": True, }, "test_|-X_|-A_|-succeed_without_changes": { "__run_num__": 1, "comment": "Success!", "result": True, "changes": False, }, "cmd_|-B_|-echo B_|-run": { "__run_num__": 0, "comment": 'Command "echo B" run', "result": True, "changes": True, }, } with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") result = normalize_ret(ret.raw) assert result == expected_result def test_issue_61121_extend_is_to_strict(state, state_tree): """ test that extend works as advertised with adding new service_types to a state id """ sls_contents = """ A: test.succeed_without_changes: - name: a extend: A: cmd: - run - name: echo A """ expected_result = { "test_|-A_|-a_|-succeed_without_changes": { "__run_num__": 0, "changes": False, "result": True, "comment": "Success!", }, "cmd_|-A_|-echo A_|-run": { "__run_num__": 1, "changes": True, "result": True, "comment": 'Command "echo A" run', }, } with pytest.helpers.temp_file("requisite.sls", sls_contents, state_tree): ret = state.sls("requisite") result = normalize_ret(ret.raw) assert result == expected_result def test_issue_30971_sls_empty_output_requisite_not_found(state, state_tree): """ Requiring an SLS file that produces no output (empty) should not result in a "requisites were not found" error. Issue #30971: When an SLS file has conditional logic that results in no states being generated (e.g., empty pillar data), a state that requires that SLS file should still succeed since the SLS was processed. """ empty_sls_contents = """ {% if False %} test_state: test.succeed_without_changes: - name: test {% endif %} """ requiring_sls_contents = """ include: - empty_sls test_requiring_state: test.succeed_without_changes: - name: test_requiring - require: - sls: empty_sls """ with pytest.helpers.temp_file( "empty_sls.sls", empty_sls_contents, state_tree ), pytest.helpers.temp_file("requiring.sls", requiring_sls_contents, state_tree): ret = state.sls("requiring") for state_return in ret: assert ( state_return.result is True ), f"State {state_return.name} failed: {state_return.comment}" assert "The following requisites were not found" not in state_return.comment