Skip to content

Commit

Permalink
Merge pull request saltstack#51846 from gtmanfred/develop
Browse files Browse the repository at this point in the history
Allow using salt modules in onlyif and unless
  • Loading branch information
dwoz authored and mchugh19 committed Oct 13, 2019
1 parent 8ce2511 commit 1dfcb27
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 18 deletions.
42 changes: 40 additions & 2 deletions doc/ref/states/requisites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,22 @@ For example:
In the above case, ``some_check`` will be run prior to _each_ name -- once for
``first_deploy_cmd`` and a second time for ``second_deploy_cmd``.

.. versionchanged:: Neon
The ``unless`` requisite can take a module as a dictionary field in unless.
The dictionary must contain an argument ``fun`` which is the module that is
being run, and everything else passed in will be kwargs passed to the module
function.

.. code-block:: yaml
install apache on debian based distros:
cmd.run:
- name: make install
- cwd: /path/to/dir/whatever-2.1.5/
- unless:
- fun: file.file_exists
path: /usr/local/bin/whatever
.. _onlyif-requisite:

Onlyif
Expand Down Expand Up @@ -962,8 +978,30 @@ concept of ``True`` and ``False``.
The above example ensures that the stop_volume and delete modules only run
if the gluster commands return a 0 ret value.

Listen/Listen_in
----------------
.. versionchanged:: Neon
The ``onlyif`` requisite can take a module as a dictionary field in onlyif.
The dictionary must contain an argument ``fun`` which is the module that is
being run, and everything else passed in will be kwargs passed to the module
function.

.. code-block:: yaml
install apache on redhat based distros:
pkg.latest:
- name: httpd
- onlyif:
- fun: match.grain
tgt: 'os_family: RedHat'
install apache on debian based distros:
pkg.latest:
- name: apache2
- onlyif:
- fun: match.grain
tgt: 'os_family: Debian'
runas
~~~~~

.. versionadded:: 2014.7.0

Expand Down
68 changes: 52 additions & 16 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -880,20 +880,39 @@ def _run_check_onlyif(self, low_data, cmd_opts):
low_data_onlyif = [low_data['onlyif']]
else:
low_data_onlyif = low_data['onlyif']
for entry in low_data_onlyif:
if not isinstance(entry, six.string_types):
ret.update({'comment': 'onlyif execution failed, bad type passed', 'result': False})
return ret
cmd = self.functions['cmd.retcode'](
entry, ignore_retcode=True, python_shell=True, **cmd_opts)
log.debug('Last command return code: %s', cmd)

def _check_cmd(cmd):
if cmd != 0 and ret['result'] is False:
ret.update({'comment': 'onlyif condition is false',
'skip_watch': True,
'result': True})
return ret
elif cmd == 0:
ret.update({'comment': 'onlyif condition is true', 'result': False})

for entry in low_data_onlyif:
if isinstance(entry, six.string_types):
cmd = self.functions['cmd.retcode'](
entry, ignore_retcode=True, python_shell=True, **cmd_opts)
log.debug('Last command return code: %s', cmd)
_check_cmd(cmd)
elif isinstance(entry, dict):
if 'fun' not in entry:
ret['comment'] = 'no `fun` argument in onlyif: {0}'.format(entry)
log.warning(ret['comment'])
return ret
result = self.functions[entry.pop('fun')](**entry)
if self.state_con.get('retcode', 0):
_check_cmd(self.state_con['retcode'])
elif not result:
ret.update({'comment': 'onlyif condition is false',
'skip_watch': True,
'result': True})
else:
ret.update({'comment': 'onlyif condition is true',
'result': False})

else:
ret.update({'comment': 'onlyif execution failed, bad type passed', 'result': False})
return ret

def _run_check_unless(self, low_data, cmd_opts):
Expand All @@ -906,20 +925,37 @@ def _run_check_unless(self, low_data, cmd_opts):
low_data_unless = [low_data['unless']]
else:
low_data_unless = low_data['unless']
for entry in low_data_unless:
if not isinstance(entry, six.string_types):
ret.update({'comment': 'unless condition is false, bad type passed', 'result': False})
return ret
cmd = self.functions['cmd.retcode'](
entry, ignore_retcode=True, python_shell=True, **cmd_opts)
log.debug('Last command return code: %s', cmd)

def _check_cmd(cmd):
if cmd == 0 and ret['result'] is False:
ret.update({'comment': 'unless condition is true',
'skip_watch': True,
'result': True})
elif cmd != 0:
ret.update({'comment': 'unless condition is false', 'result': False})
return ret

for entry in low_data_unless:
if isinstance(entry, six.string_types):
cmd = self.functions['cmd.retcode'](entry, ignore_retcode=True, python_shell=True, **cmd_opts)
log.debug('Last command return code: %s', cmd)
_check_cmd(cmd)
elif isinstance(entry, dict):
if 'fun' not in entry:
ret['comment'] = 'no `fun` argument in onlyif: {0}'.format(entry)
log.warning(ret['comment'])
return ret
result = self.functions[entry.pop('fun')](**entry)
if self.state_con.get('retcode', 0):
_check_cmd(self.state_con['retcode'])
elif result:
ret.update({'comment': 'unless condition is true',
'skip_watch': True,
'result': True})
else:
ret.update({'comment': 'unless condition is false',
'result': False})
else:
ret.update({'comment': 'unless condition is false, bad type passed', 'result': False})

# No reason to stop, return ret
return ret
Expand Down
138 changes: 138 additions & 0 deletions tests/integration/modules/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,144 @@ def test_requisites_use_no_state_module(self):
for item, descr in six.iteritems(ret):
self.assertEqual(descr['comment'], 'onlyif condition is false')

def test_onlyif_req(self):
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='onlyif test',
onlyif=[
{}
],
)['test_|-onlyif test_|-onlyif test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertEqual(ret['comment'], 'Success!')
ret = self.run_function(
'state.single',
fun='test.fail_with_changes',
name='onlyif test',
onlyif=[
{'fun': 'test.false'},
],
)['test_|-onlyif test_|-onlyif test_|-fail_with_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'onlyif condition is false')
ret = self.run_function(
'state.single',
fun='test.fail_with_changes',
name='onlyif test',
onlyif=[
{'fun': 'test.true'},
],
)['test_|-onlyif test_|-onlyif test_|-fail_with_changes']
self.assertFalse(ret['result'])
self.assertTrue(ret['changes'])
self.assertEqual(ret['comment'], 'Failure!')
ret = self.run_function(
'state.single',
fun='test.succeed_without_changes',
name='onlyif test',
onlyif=[
{'fun': 'test.true'},
],
)['test_|-onlyif test_|-onlyif test_|-succeed_without_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'Success!')

def test_onlyif_req_retcode(self):
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='onlyif test',
onlyif=[
{'fun': 'test.retcode'},
],
)['test_|-onlyif test_|-onlyif test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'onlyif condition is false')
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='onlyif test',
onlyif=[
{'fun': 'test.retcode', 'code': 0},
],
)['test_|-onlyif test_|-onlyif test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertTrue(ret['changes'])
self.assertEqual(ret['comment'], 'Success!')

def test_unless_req(self):
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='unless test',
unless=[
{}
],
)['test_|-unless test_|-unless test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertEqual(ret['comment'], 'Success!')
ret = self.run_function(
'state.single',
fun='test.fail_with_changes',
name='unless test',
unless=[
{'fun': 'test.true'},
],
)['test_|-unless test_|-unless test_|-fail_with_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'unless condition is true')
ret = self.run_function(
'state.single',
fun='test.fail_with_changes',
name='unless test',
unless=[
{'fun': 'test.false'},
],
)['test_|-unless test_|-unless test_|-fail_with_changes']
self.assertFalse(ret['result'])
self.assertTrue(ret['changes'])
self.assertEqual(ret['comment'], 'Failure!')
ret = self.run_function(
'state.single',
fun='test.succeed_without_changes',
name='unless test',
unless=[
{'fun': 'test.false'},
],
)['test_|-unless test_|-unless test_|-succeed_without_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'Success!')

def test_unless_req_retcode(self):
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='unless test',
unless=[
{'fun': 'test.retcode'},
],
)['test_|-unless test_|-unless test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertTrue(ret['changes'])
self.assertEqual(ret['comment'], 'Success!')
ret = self.run_function(
'state.single',
fun='test.succeed_with_changes',
name='unless test',
unless=[
{'fun': 'test.retcode', 'code': 0},
],
)['test_|-unless test_|-unless test_|-succeed_with_changes']
self.assertTrue(ret['result'])
self.assertFalse(ret['changes'])
self.assertEqual(ret['comment'], 'unless condition is true')

def test_get_file_from_env_in_top_match(self):
tgt = os.path.join(TMP, 'prod-cheese-file')
try:
Expand Down

0 comments on commit 1dfcb27

Please sign in to comment.