diff --git a/changelog/57470.added b/changelog/57470.added new file mode 100644 index 00000000000..54f736915b7 --- /dev/null +++ b/changelog/57470.added @@ -0,0 +1 @@ +Add "get_return" key for onlyif and unless requisites to parse deep module results diff --git a/doc/ref/states/requisites.rst b/doc/ref/states/requisites.rst index 20977a9e269..35144c0a0b7 100644 --- a/doc/ref/states/requisites.rst +++ b/doc/ref/states/requisites.rst @@ -919,6 +919,21 @@ In the above case, ``some_check`` will be run prior to _each_ name -- once for args: - mysql-server-5.7 + .. versionchanged:: sodium + For modules which return a deeper data structure, the ``get_return`` key can + be used to access results. + + .. code-block:: yaml + + test: + test.nop: + - name: foo + - unless: + - fun: consul.get + consul_url: http://127.0.0.1:8500 + key: not-existing + get_return: res + .. _onlyif-requisite: onlyif @@ -992,6 +1007,22 @@ if the gluster commands return a 0 ret value. - /etc/crontab - 'entry1' +.. versionchanged:: sodium + For modules which return a deeper data structure, the ``get_return`` key can + be used to access results. + + .. code-block:: yaml + + test: + test.nop: + - name: foo + - onlyif: + - fun: consul.get + consul_url: http://127.0.0.1:8500 + key: does-exist + get_return: res + + .. _creates-requisite: Creates diff --git a/doc/topics/releases/3001.rst b/doc/topics/releases/3001.rst index dbec4209f9b..8172b7ec130 100644 --- a/doc/topics/releases/3001.rst +++ b/doc/topics/releases/3001.rst @@ -32,7 +32,22 @@ The ``creates`` state requisite has been migrated from the states to become a global option. This acts similar to an equivalent ``unless: test -f filename`` but can also accept a list of filenames. This allows all states to take advantage of the enhanced functionality released in Neon, of allowing -salt execution modules for requisite checks. +salt execution modules for requisite checks. + +When using salt functions ``onlyif`` or ``unless`` requisites, a ``get_return`` key can +now be used to specify a key to evaluate for truthiness. This can be used for execution modules +which return status in a nested key. + + .. code-block:: yaml + + test: + test.nop: + - name: foo + - onlyif: + - fun: consul.get + consul_url: http://127.0.0.1:8500 + key: not-existing + get_return: res State Execution Module ====================== diff --git a/salt/state.py b/salt/state.py index 10b5d47b98c..2754be152c8 100644 --- a/salt/state.py +++ b/salt/state.py @@ -960,7 +960,10 @@ class State(object): log.warning(ret["comment"]) return ret + get_return = entry.pop("get_return", None) result = self._run_check_function(entry) + if get_return: + result = salt.utils.data.traverse_dict_and_list(result, get_return) if self.state_con.get("retcode", 0): _check_cmd(self.state_con["retcode"]) elif not result: @@ -1023,7 +1026,10 @@ class State(object): log.warning(ret["comment"]) return ret + get_return = entry.pop("get_return", None) result = self._run_check_function(entry) + if get_return: + result = salt.utils.data.traverse_dict_and_list(result, get_return) if self.state_con.get("retcode", 0): _check_cmd(self.state_con["retcode"]) elif result: diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index 5233c13a16f..12e768a561a 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -116,6 +116,31 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): return_result = state_obj._run_check_onlyif(low_data, "") self.assertEqual(expected_result, return_result) + def test_verify_onlyif_parse_deep_return(self): + low_data = { + "state": "test", + "name": "foo", + "__sls__": "consol", + "__env__": "base", + "__id__": "test", + "onlyif": [ + { + "fun": "test.arg", + "get_return": "kwargs:deep:return", + "deep": {"return": "true"}, + } + ], + "order": 10000, + "fun": "nop", + } + expected_result = {"comment": "onlyif condition is true", "result": False} + + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") + state_obj = salt.state.State(minion_opts) + return_result = state_obj._run_check_onlyif(low_data, "") + self.assertEqual(expected_result, return_result) + def test_verify_onlyif_cmd_error(self): """ Simulates a failure in cmd.retcode from onlyif @@ -206,6 +231,31 @@ class StateCompilerTestCase(TestCase, AdaptedConfigurationTestCaseMixin): return_result = state_obj._run_check_unless(low_data, "") self.assertEqual(expected_result, return_result) + def test_verify_unless_parse_deep_return(self): + low_data = { + "state": "test", + "name": "foo", + "__sls__": "consol", + "__env__": "base", + "__id__": "test", + "unless": [ + { + "fun": "test.arg", + "get_return": "kwargs:deep:return", + "deep": {"return": False}, + } + ], + "order": 10000, + "fun": "nop", + } + expected_result = {"comment": "unless condition is false", "result": False} + + with patch("salt.state.State._gather_pillar") as state_patch: + minion_opts = self.get_temp_config("minion") + state_obj = salt.state.State(minion_opts) + return_result = state_obj._run_check_unless(low_data, "") + self.assertEqual(expected_result, return_result) + def test_verify_creates(self): low_data = { "state": "cmd",