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