From 1e6840d8dbb7b8f7a9112e9770c0b404625f0a7b Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Mon, 8 Oct 2018 09:57:43 -0700 Subject: [PATCH 1/5] Initial work to be able to disable requisites. --- salt/state.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/salt/state.py b/salt/state.py index 627bf0db68f1..d2b489c8eca0 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1683,6 +1683,7 @@ def requisite_in(self, high): ) extend = {} errors = [] + disabled = self.opts.get("disabled_requisites", []) for id_, body in six.iteritems(high): if not isinstance(body, dict): continue @@ -1701,6 +1702,10 @@ def requisite_in(self, high): key = next(iter(arg)) if key not in req_in: continue + log.debug("=== self %s ===", self.opts) + if key in disabled: + log.info("=== %s disabled ===", key) + continue rkey = key.split("_")[0] items = arg[key] if isinstance(items, dict): From 7221d50450576d8c744c0f55096907ef6e03975c Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Tue, 9 Oct 2018 13:10:28 -0700 Subject: [PATCH 2/5] Finalizing ability to disable requisites during State runs. Adding to configuration and documentation. --- conf/minion | 9 +++++++++ doc/topics/releases/sodium.rst | 6 ++++++ salt/config/__init__.py | 2 ++ salt/state.py | 17 +++++++++++++---- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/conf/minion b/conf/minion index 3950877d3e25..ed951cd7a0fb 100644 --- a/conf/minion +++ b/conf/minion @@ -565,6 +565,15 @@ # #state_aggregate: False +# Disable requisites during state runs by specifying a single requisite +# or a list of requisites to disable. +# +# disabled_requisites: require_in +# +# disabled_requisites: +# - require +# - require_in + ##### File Directory Settings ##### ########################################## # The Salt Minion can redirect all file server operations to a local directory, diff --git a/doc/topics/releases/sodium.rst b/doc/topics/releases/sodium.rst index 4bb34f8dd255..f60ad848e7da 100644 --- a/doc/topics/releases/sodium.rst +++ b/doc/topics/releases/sodium.rst @@ -77,3 +77,9 @@ You can set this setting in your roster file like so: user: root passwd: P@ssword set_path: '$PATH:/usr/local/bin/' + + +State Changes +============= +- Adding a new option for the State compiler, ``disabled_requisites`` will allow + requisites to be disabled during State runs. diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 92508ef78f62..4d315385fd18 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -925,6 +925,7 @@ def _gather_buffer_space(): # Allow raw_shell option when using the ssh # client via the Salt API "netapi_allow_raw_shell": bool, + "disabled_requisites": [], } ) @@ -1216,6 +1217,7 @@ def _gather_buffer_space(): "discovery": False, "schedule": {}, "ssh_merge_pillar": True, + "disabled_requisites": [], } ) diff --git a/salt/state.py b/salt/state.py index d2b489c8eca0..fa096f0b2459 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1683,7 +1683,9 @@ def requisite_in(self, high): ) extend = {} errors = [] - disabled = self.opts.get("disabled_requisites", []) + disabled_reqs = self.opts.get("disabled_requisites", []) + if not isinstance(disabled_reqs, list): + disabled_reqs = [disabled_reqs] for id_, body in six.iteritems(high): if not isinstance(body, dict): continue @@ -1702,9 +1704,10 @@ def requisite_in(self, high): key = next(iter(arg)) if key not in req_in: continue - log.debug("=== self %s ===", self.opts) - if key in disabled: - log.info("=== %s disabled ===", key) + if key in disabled_reqs: + log.warning( + "The %s requisite has been disabled, Ignoring.", key + ) continue rkey = key.split("_")[0] items = arg[key] @@ -2547,6 +2550,9 @@ def check_requisite(self, low, running, chunks, pre=False): Look into the running data to check the status of all requisite states """ + disabled_reqs = self.opts.get("disabled_requisites", []) + if not isinstance(disabled_reqs, list): + disabled_reqs = [disabled_reqs] present = False # If mod_watch is not available make it a require if "watch" in low: @@ -2598,6 +2604,9 @@ def check_requisite(self, low, running, chunks, pre=False): if pre: reqs["prerequired"] = [] for r_state in reqs: + if r_state in disabled_reqs: + log.warning("The %s requisite has been disabled, Ignoring.", r_state) + continue if r_state in low and low[r_state] is not None: for req in low[r_state]: if isinstance(req, six.string_types): From a3637a8296b9c6e89a15f870f8c90c113731b43d Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Tue, 9 Oct 2018 16:10:11 -0700 Subject: [PATCH 3/5] Move this down to ensure the req in the low data before we continue --- salt/state.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/salt/state.py b/salt/state.py index fa096f0b2459..3ef21ac5010c 100644 --- a/salt/state.py +++ b/salt/state.py @@ -2604,10 +2604,12 @@ def check_requisite(self, low, running, chunks, pre=False): if pre: reqs["prerequired"] = [] for r_state in reqs: - if r_state in disabled_reqs: - log.warning("The %s requisite has been disabled, Ignoring.", r_state) - continue if r_state in low and low[r_state] is not None: + if r_state in disabled_reqs: + log.warning( + "The %s requisite has been disabled, Ignoring.", r_state + ) + continue for req in low[r_state]: if isinstance(req, six.string_types): req = {"id": req} From cda90f6a51b03b0a1483d2d1939c167c349f9ba7 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Wed, 10 Oct 2018 12:53:19 -0700 Subject: [PATCH 4/5] Adding a couple tests for testing disabled_requisites --- tests/unit/test_state.py | 83 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/tests/unit/test_state.py b/tests/unit/test_state.py index 1d751dd82e97..80a122f41dfc 100644 --- a/tests/unit/test_state.py +++ b/tests/unit/test_state.py @@ -274,6 +274,89 @@ def test_verify_retry_parsing(self): with patch.object(state_obj, "_run_check", return_value=mock): self.assertDictContainsSubset(expected_result, state_obj.call(low_data)) + def test_render_requisite_require_disabled(self): + """ + Test that the state compiler correctly deliver a rendering + exception when a requisite cannot be resolved + """ + with patch("salt.state.State._gather_pillar") as state_patch: + high_data = { + "step_one": OrderedDict( + [ + ( + "test", + [ + OrderedDict( + [("require", [OrderedDict([("test", "step_two")])])] + ), + "succeed_with_changes", + {"order": 10000}, + ], + ), + ("__sls__", "test.disable_require"), + ("__env__", "base"), + ] + ), + "step_two": { + "test": ["succeed_with_changes", {"order": 10001}], + "__env__": "base", + "__sls__": "test.disable_require", + }, + } + + minion_opts = self.get_temp_config("minion") + minion_opts["disabled_requisites"] = ["require"] + state_obj = salt.state.State(minion_opts) + ret = state_obj.call_high(high_data) + run_num = ret["test_|-step_one_|-step_one_|-succeed_with_changes"][ + "__run_num__" + ] + self.assertEqual(run_num, 0) + + def test_render_requisite_require_in_disabled(self): + """ + Test that the state compiler correctly deliver a rendering + exception when a requisite cannot be resolved + """ + with patch("salt.state.State._gather_pillar") as state_patch: + high_data = { + "step_one": { + "test": ["succeed_with_changes", {"order": 10000}], + "__env__": "base", + "__sls__": "test.disable_require_in", + }, + "step_two": OrderedDict( + [ + ( + "test", + [ + OrderedDict( + [ + ( + "require_in", + [OrderedDict([("test", "step_one")])], + ) + ] + ), + "succeed_with_changes", + {"order": 10001}, + ], + ), + ("__sls__", "test.disable_require_in"), + ("__env__", "base"), + ] + ), + } + + minion_opts = self.get_temp_config("minion") + minion_opts["disabled_requisites"] = ["require_in"] + state_obj = salt.state.State(minion_opts) + ret = state_obj.call_high(high_data) + run_num = ret["test_|-step_one_|-step_one_|-succeed_with_changes"][ + "__run_num__" + ] + self.assertEqual(run_num, 0) + class HighStateTestCase(TestCase, AdaptedConfigurationTestCaseMixin): def setUp(self): From 12c15436c03acf1cd68bafe6118113474feaa8f4 Mon Sep 17 00:00:00 2001 From: "Gareth J. Greenaway" Date: Wed, 22 Apr 2020 17:25:32 -0700 Subject: [PATCH 5/5] Fixing type for disabled_requisites in valid_opts. --- salt/config/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/salt/config/__init__.py b/salt/config/__init__.py index 4d315385fd18..ac4d2ab5c281 100644 --- a/salt/config/__init__.py +++ b/salt/config/__init__.py @@ -925,7 +925,7 @@ def _gather_buffer_space(): # Allow raw_shell option when using the ssh # client via the Salt API "netapi_allow_raw_shell": bool, - "disabled_requisites": [], + "disabled_requisites": (six.string_types, list), } )