Adding docs and integration tests.

This commit is contained in:
Gareth J. Greenaway 2017-11-06 09:57:54 -07:00
parent 3ffab16a4d
commit 58d22a169c
No known key found for this signature in database
GPG key ID: 10B62F8A7CAD7A41
8 changed files with 612 additions and 1 deletions

View file

@ -209,6 +209,13 @@ Each direct requisite also has a corresponding requisite_in:
* ``onchanges_in``
* ``onfail_in``
There are several corresponding requisite_any statements:
* ``require_any``
* ``watch_any``
* ``onchanges_any``
* ``onfail_any``
All of the requisites define specific relationships and always work with the
dependency logic defined above.
@ -245,6 +252,44 @@ This will add all of the state declarations found in the given sls file. This me
that every state in sls `foo` will be required. This makes it very easy to batch
large groups of states easily in any requisite statement.
.. _requisites-require_any:
require_any
~~~~~~~~~~~
.. versionadded:: Oxygen
The use of ``require_any`` demands that one of the required state executes before the
dependent state. The state containing the ``require_any`` requisite is defined as the
dependent state. The states specified in the ``require_any`` statement are defined as the
required states. If at least one of the required state's execution succeeds, the dependent state
will then execute. If at least one of the required state's execution fails, the dependent state
will not execute.
.. code-block:: yaml
A:
cmd.run:
- name: echo A
- require_any:
- cmd: B
- cmd: C
- cmd: D
B:
cmd.run:
- name: echo B
C:
cmd.run:
- name: /bin/false
D:
cmd.run:
- name: echo D
In this example `A` will run because at least one of the requirements specified,
`B`, `C`, or `D` will succeed.
.. _requisites-watch:
watch
@ -332,6 +377,50 @@ to Salt ensuring that the service is running.
- name: /etc/ntp.conf
- source: salt://ntp/files/ntp.conf
watch_any
~~~~~~~~~
.. versionadded:: Oxygen
The state containing the ``watch_any`` requisite is defined as the watching
state. The states specified in the ``watch_any`` statement are defined as the watched
states. When the watched states execute, they will return a dictionary containing
a key named "changes".
If the "result" of any of the watched states is ``True``, the watching state *will
execute normally*, and if all of them are ``False``, the watching state will never run.
This part of ``watch`` mirrors the functionality of the ``require`` requisite.
If the "result" of any of the watched states is ``True`` *and* the "changes"
key contains a populated dictionary (changes occurred in the watched state),
then the ``watch`` requisite can add additional behavior. This additional
behavior is defined by the ``mod_watch`` function within the watching state
module. If the ``mod_watch`` function exists in the watching state module, it
will be called *in addition to* the normal watching state. The return data
from the ``mod_watch`` function is what will be returned to the master in this
case; the return data from the main watching function is discarded.
If the "changes" key contains an empty dictionary, the ``watch`` requisite acts
exactly like the ``require`` requisite (the watching state will execute if
"result" is ``True``, and fail if "result" is ``False`` in the watched state).
.. code-block:: yaml
apache2:
service.running:
- watch_any:
- file: /etc/apache2/sites-available/site1.conf
- file: /etc/apache2/sites-available/site2.conf
file.managed:
- name: /etc/apache2/sites-available/site1.conf
- source: salt://apache2/files/site1.conf
file.managed:
- name: /etc/apache2/sites-available/site2.conf
- source: salt://apache2/files/site2.conf
In this example, the service will be reloaded/restarted if the either of the state
has a result of True and has changes.
.. _requisites-prereq:
prereq
@ -423,6 +512,43 @@ The ``onfail`` requisite is applied in the same way as ``require`` as ``watch``:
.. _Issue #22370: https://github.com/saltstack/salt/issues/22370
.. _requisites-onfail:
onfail_any
~~~~~~~~~~
.. versionadded:: Oxygen
The ``onfail_any`` requisite allows for reactions to happen strictly as a response
to the failure of at least one other state. This can be used in a number of ways, such as
executing a second attempt to set up a service or begin to execute a separate
thread of states because of a failure.
The ``onfail_any`` requisite is applied in the same way as ``require_any`` as ``watch_any``:
.. code-block:: yaml
primary_mount:
mount.mounted:
- name: /mnt/share
- device: 10.0.0.45:/share
- fstype: nfs
secondary_mount:
mount.mounted:
- name: /mnt/code
- device: 10.0.0.45:/code
- fstype: nfs
backup_mount:
mount.mounted:
- name: /mnt/share
- device: 192.168.40.34:/share
- fstype: nfs
- onfail:
- mount: primary_mount
- mount: secondary_mount
.. _requisites-onchanges:
onchanges
@ -482,6 +608,37 @@ if any of the watched states changes.
- onchanges:
- file: /etc/myservice/myservice.conf
.. _requisites-onchanges_any:
onchanges_any
~~~~~~~~~~~~~
.. versionadded:: Oxygen
The ``onchanges_any`` requisite makes a state only apply one of the required states
generates changes, and if one of the watched state's "result" is ``True``. This can be
a useful way to execute a post hook after changing aspects of a system.
.. code-block:: yaml
myservice:
pkg.installed:
- name: myservice
- name: yourservice
file.managed:
- name: /etc/myservice/myservice.conf
- source: salt://myservice/files/myservice.conf
- mode: 600
file.managed:
- name: /etc/yourservice/yourservice.conf
- source: salt://yourservice/files/yourservice.conf
- mode: 600
cmd.run:
- name: /usr/libexec/myservice/post-changes-hook.sh
- onchanges:
- file: /etc/myservice/myservice.conf
- file: /etc/your_service/yourservice.conf
use
~~~

View file

@ -2357,7 +2357,15 @@ class State(object):
tag = _gen_tag(low)
if not low.get(u'prerequired'):
self.active.add(tag)
requisites = [u'require', u'watch', u'prereq', u'onfail', u'onchanges']
requisites = [u'require',
u'require_any',
u'watch',
u'watch_any',
u'prereq',
u'onfail',
u'onfail_any',
u'onchanges',
u'onchanges_any']
if not low.get(u'__prereq__'):
requisites.append(u'prerequired')
status, reqs = self.check_requisite(low, running, chunks, pre=True)

View file

@ -0,0 +1,34 @@
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
# Should succeed since at least one will have changes
test_one_changing_states:
cmd.run:
- name: echo "Success!"
- onchanges_any:
- cmd: changing_state
- cmd: another_changing_state
- pip: non_changing_state
- pip: another_non_changing_state
test_two_non_changing_states:
cmd.run:
- name: echo "Should not run"
- onchanges_any:
- pip: non_changing_state
- pip: another_non_changing_state

View file

@ -0,0 +1,39 @@
a:
cmd.run:
- name: exit 0
b:
cmd.run:
- name: exit 1
c:
cmd.run:
- name: exit 0
d:
cmd.run:
- name: echo itworked
- onfail_any:
- cmd: a
- cmd: b
- cmd: c
e:
cmd.run:
- name: exit 0
f:
cmd.run:
- name: exit 0
g:
cmd.run:
- name: exit 0
h:
cmd.run:
- name: echo itworked
- onfail_any:
- cmd: e
- cmd: f
- cmd: g

View file

@ -0,0 +1,34 @@
# 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 fail since both E & F fail
D:
cmd.run:
- name: echo D
- require_any:
- cmd: E
- cmd: F
- cmd: G
E:
cmd.run:
- name: 'false'
F:
cmd.run:
- name: 'false'
G:
cmd.run:
- name: 'false'

View file

@ -0,0 +1,39 @@
A:
cmd.wait:
- name: 'true'
- watch_any:
- cmd: B
- cmd: C
- cmd: D
B:
cmd.run:
- name: 'true'
C:
cmd.run:
- name: 'false'
D:
cmd.run:
- name: 'true'
E:
cmd.wait:
- name: 'true'
- watch_any:
- cmd: F
- cmd: G
- cmd: H
F:
cmd.run:
- name: 'true'
G:
cmd.run:
- name: 'false'
H:
cmd.run:
- name: 'false'

View file

@ -0,0 +1,19 @@
A:
cmd.wait:
- name: 'true'
- watch_any:
- cmd: B
- cmd: C
- cmd: D
B:
cmd.run:
- name: 'false'
C:
cmd.run:
- name: 'false'
D:
cmd.run:
- name: 'false'

View file

@ -702,6 +702,287 @@ class StateModuleTest(ModuleCase, SaltReturnAssertsMixin):
['A recursive requisite was found, SLS "requisites.require_recursion_error1" ID "B" ID "A"']
)
def test_requisites_require_any(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
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_|-/bin/false_|-run': {
'__run_num__': 1,
'comment': 'Command "/bin/false" run',
'result': False,
'changes': True,
},
'cmd_|-D_|-echo D_|-run': {
'__run_num__': 2,
'comment': 'Command "echo D" run',
'result': True,
'changes': True,
},
}
ret = self.run_function('state.sls', mods='requisites.require_any')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_require_any_fail(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
expected_result = {
'cmd_|-D_|-echo D_|-run': {
'__run_num__': 3,
'comment': 'One or more requisite failed: requisites.require_any_fail.F, requisites.require_any_fail.G, requisites.require_any_fail.E',
'result': False,
'changes': False,
},
'cmd_|-E_|-false_|-run': {
'__run_num__': 0,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-F_|-false_|-run': {
'__run_num__': 1,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-G_|-false_|-run': {
'__run_num__': 2,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
}
ret = self.run_function('state.sls', mods='requisites.require_any_fail')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_watch_any(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
expected_result = {
'cmd_|-A_|-true_|-wait': {
'__run_num__': 4,
'comment': 'Command "true" run',
'result': True,
'changes': True,
},
'cmd_|-B_|-true_|-run': {
'__run_num__': 0,
'comment': 'Command "true" run',
'result': True,
'changes': True,
},
'cmd_|-C_|-false_|-run': {
'__run_num__': 1,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-D_|-true_|-run': {
'__run_num__': 2,
'comment': 'Command "true" run',
'result': True,
'changes': True,
},
'cmd_|-E_|-true_|-wait': {
'__run_num__': 9,
'comment': 'Command "true" run',
'result': True,
'changes': True,
},
'cmd_|-F_|-true_|-run': {
'__run_num__': 5,
'comment': 'Command "true" run',
'result': True,
'changes': True,
},
'cmd_|-G_|-false_|-run': {
'__run_num__': 6,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-H_|-false_|-run': {
'__run_num__': 7,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
}
ret = self.run_function('state.sls', mods='requisites.watch_any')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_watch_any_fail(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
expected_result = {
'cmd_|-A_|-true_|-wait': {
'__run_num__': 3,
'comment': 'One or more requisite failed: requisites.watch_any_fail.B, requisites.watch_any_fail.C, requisites.watch_any_fail.D',
'result': False,
'changes': False,
},
'cmd_|-B_|-false_|-run': {
'__run_num__': 0,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-C_|-false_|-run': {
'__run_num__': 1,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
'cmd_|-D_|-false_|-run': {
'__run_num__': 2,
'comment': 'Command "false" run',
'result': False,
'changes': True,
},
}
ret = self.run_function('state.sls', mods='requisites.watch_any_fail')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_onchanges_any(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
expected_result = {
'cmd_|-another_changing_state_|-echo "Changed!"_|-run': {
'__run_num__': 1,
'changes': True,
'comment': 'Command "echo "Changed!"" run',
'result': True
},
'cmd_|-changing_state_|-echo "Changed!"_|-run': {
'__run_num__': 0,
'changes': True,
'comment': 'Command "echo "Changed!"" run',
'result': True
},
'cmd_|-test_one_changing_states_|-echo "Success!"_|-run': {
'__run_num__': 4,
'changes': True,
'comment': 'Command "echo "Success!"" run',
'result': True
},
'cmd_|-test_two_non_changing_states_|-echo "Should not run"_|-run': {
'__run_num__': 5,
'changes': False,
'comment': 'State was not run because none of the onchanges reqs changed',
'result': True
},
'pip_|-another_non_changing_state_|-mock_|-installed': {
'__run_num__': 3,
'changes': False,
'comment': 'Python package mock was already installed\nAll packages were successfully installed',
'result': True
},
'pip_|-non_changing_state_|-mock_|-installed': {
'__run_num__': 2,
'changes': False,
'comment': 'Python package mock was already installed\nAll packages were successfully installed',
'result': True
}
}
ret = self.run_function('state.sls', mods='requisites.onchanges_any')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_onfail_any(self):
'''
Call sls file containing several require_in and require.
Ensure that some of them are failing and that the order is right.
'''
expected_result = {
'cmd_|-a_|-exit 0_|-run': {
'__run_num__': 0,
'changes': True,
'comment': 'Command "exit 0" run',
'result': True
},
'cmd_|-b_|-exit 1_|-run': {
'__run_num__': 1,
'changes': True,
'comment': 'Command "exit 1" run',
'result': False
},
'cmd_|-c_|-exit 0_|-run': {
'__run_num__': 2,
'changes': True,
'comment': 'Command "exit 0" run',
'result': True
},
'cmd_|-d_|-echo itworked_|-run': {
'__run_num__': 3,
'changes': True,
'comment': 'Command "echo itworked" run',
'result': True},
'cmd_|-e_|-exit 0_|-run': {
'__run_num__': 4,
'changes': True,
'comment': 'Command "exit 0" run',
'result': True
},
'cmd_|-f_|-exit 0_|-run': {
'__run_num__': 5,
'changes': True,
'comment': 'Command "exit 0" run',
'result': True
},
'cmd_|-g_|-exit 0_|-run': {
'__run_num__': 6,
'changes': True,
'comment': 'Command "exit 0" run',
'result': True
},
'cmd_|-h_|-echo itworked_|-run': {
'__run_num__': 7,
'changes': False,
'comment': 'State was not run because onfail req did not change',
'result': True
}
}
ret = self.run_function('state.sls', mods='requisites.onfail_any')
result = self.normalize_ret(ret)
self.assertReturnNonEmptySaltType(ret)
self.assertEqual(expected_result, result)
def test_requisites_full_sls(self):
'''
Teste the sls special command in requisites