From a5607d5e07c0e48470d19a54258451c81081c998 Mon Sep 17 00:00:00 2001 From: mattmb Date: Tue, 16 Jun 2015 15:02:11 +0100 Subject: [PATCH] Multiple onchanges requisite behaviour Change the onchanges requisite so that if you list multiple onchanges requisites then only one must change to trigger the state. Currently all the watched states must change to cause the trigger. This fixes #19592 --- doc/ref/states/requisites.rst | 3 ++ salt/state.py | 8 ++-- .../base/requisites/onchanges_multiple.sls | 38 +++++++++++++++++++ tests/integration/modules/state.py | 28 +++++++++++++- 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 tests/integration/files/file/base/requisites/onchanges_multiple.sls diff --git a/doc/ref/states/requisites.rst b/doc/ref/states/requisites.rst index c3a90e67210c..82cdd22234ba 100644 --- a/doc/ref/states/requisites.rst +++ b/doc/ref/states/requisites.rst @@ -326,6 +326,9 @@ The ``onchanges`` requisite makes a state only apply if the required states generate changes, and if the watched state's "result" is ``True``. This can be a useful way to execute a post hook after changing aspects of a system. +If a state has multiple ``onchanges`` requisites then the state will trigger +if any of the watched states changes. + use ~~~ diff --git a/salt/state.py b/salt/state.py index 060fdb1c4c12..4f80b5cbf48d 100644 --- a/salt/state.py +++ b/salt/state.py @@ -1746,7 +1746,9 @@ def check_requisite(self, low, running, chunks, pre=False): if r_state == 'onchanges': if not run_dict[tag]['changes']: fun_stats.add('onchanges') - continue + else: + fun_stats.add('onchangesmet') + continue if r_state == 'watch' and run_dict[tag]['changes']: fun_stats.add('change') continue @@ -1768,7 +1770,7 @@ def check_requisite(self, low, running, chunks, pre=False): status = 'pre' elif 'onfail' in fun_stats: status = 'onfail' - elif 'onchanges' in fun_stats: + elif 'onchanges' in fun_stats and not 'onchangesmet' in fun_stats: status = 'onchanges' elif 'change' in fun_stats: status = 'change' @@ -1992,7 +1994,7 @@ def call_chunk(self, low, running, chunks): elif status == 'onchanges': running[tag] = {'changes': {}, 'result': True, - 'comment': 'State was not run because onchanges req did not change', + 'comment': 'State was not run because none of the onchanges reqs changed', '__run_num__': self.__run_num, '__sls__': low['__sls__']} self.__run_num += 1 diff --git a/tests/integration/files/file/base/requisites/onchanges_multiple.sls b/tests/integration/files/file/base/requisites/onchanges_multiple.sls new file mode 100644 index 000000000000..e4f20f89c8ab --- /dev/null +++ b/tests/integration/files/file/base/requisites/onchanges_multiple.sls @@ -0,0 +1,38 @@ +changing_state: + cmd.run: + - name: echo "Changed!" + +another_changing_state: + cmd.run: + - name: echo "Changed!" + +# mock is installed with salttesting, so it should already be +# present on the system, resulting in no changes +non_changing_state: + pip.installed: + - name: mock + +another_non_changing_state: + pip.installed: + - name: mock + +test_two_changing_states: + cmd.run: + - name: echo "Success!" + - onchanges: + - cmd: changing_state + - cmd: another_changing_state + +test_two_non_changing_states: + cmd.run: + - name: echo "Should not run" + - onchanges: + - pip: non_changing_state + - pip: another_non_changing_state + +test_one_changing_state: + cmd.run: + - name: echo "Success!" + - onchanges: + - cmd: changing_state + - pip: non_changing_state diff --git a/tests/integration/modules/state.py b/tests/integration/modules/state.py index 1c7b93a48a8a..d6bdf2ec7308 100644 --- a/tests/integration/modules/state.py +++ b/tests/integration/modules/state.py @@ -926,7 +926,31 @@ def test_onchanges_requisite(self): # Then, test the result of the state run when changes are not expected to happen test_data = state_run['cmd_|-test_non_changing_state_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because onchanges req did not change' + expected_result = 'State was not run because none of the onchanges reqs changed' + self.assertIn(expected_result, test_data) + + def test_onchanges_requisite_multiple(self): + ''' + Tests a simple state using the onchanges requisite + ''' + + # Only run the state once and keep the return data + state_run = self.run_function('state.sls', + mods='requisites.onchanges_multiple') + + # First, test the result of the state run when two changes are expected to happen + test_data = state_run['cmd_|-test_two_changing_states_|-echo "Success!"_|-run']['comment'] + expected_result = 'Command "echo "Success!"" run' + self.assertIn(expected_result, test_data) + + # Then, test the result of the state run when two changes are not expected to happen + test_data = state_run['cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run']['comment'] + expected_result = 'State was not run because none of the onchanges reqs changed' + self.assertIn(expected_result, test_data) + + # Finally, test the result of the state run when only one of the onchanges requisites changes. + test_data = state_run['cmd_|-test_one_changing_state_|-echo "Success!"_|-run']['comment'] + expected_result = 'Command "echo "Success!"" run' self.assertIn(expected_result, test_data) def test_onchanges_in_requisite(self): @@ -944,7 +968,7 @@ def test_onchanges_in_requisite(self): # Then, test the result of the state run when changes are not expected to happen test_data = state_run['cmd_|-test_changes_not_expected_|-echo "Should not run"_|-run']['comment'] - expected_result = 'State was not run because onchanges req did not change' + expected_result = 'State was not run because none of the onchanges reqs changed' self.assertIn(expected_result, test_data) # onfail tests