Skip to content

Commit

Permalink
Merge pull request #45229 from garethgreenaway/30161_using_unless_onl…
Browse files Browse the repository at this point in the history
…yif_together

[develop]  Allow the onlyif & unless requisites in a state file.
  • Loading branch information
Nicole Thomas authored Jan 8, 2018
2 parents 47bdea2 + b6ba331 commit 9cb6ac3
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 38 deletions.
105 changes: 67 additions & 38 deletions salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -812,51 +812,80 @@ def _run_check(self, low_data):
'''
Check that unless doesn't return 0, and that onlyif returns a 0.
'''
ret = {'result': False}
ret = {'result': False, 'comment': []}
cmd_opts = {}

if 'shell' in self.opts['grains']:
cmd_opts['shell'] = self.opts['grains'].get('shell')

if 'onlyif' in low_data:
if not isinstance(low_data['onlyif'], list):
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)
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})
return ret
_ret = self._run_check_onlyif(low_data, cmd_opts)
ret['result'] = _ret['result']
ret['comment'].append(_ret['comment'])
if 'skip_watch' in _ret:
ret['skip_watch'] = _ret['skip_watch']

if 'unless' in low_data:
if not isinstance(low_data['unless'], list):
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)
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
_ret = self._run_check_unless(low_data, cmd_opts)
# If either result is True, the returned result should be True
ret['result'] = _ret['result'] or ret['result']
ret['comment'].append(_ret['comment'])
if 'skip_watch' in _ret:
# If either result is True, the returned result should be True
ret['skip_watch'] = _ret['skip_watch'] or ret['skip_watch']

return ret

def _run_check_onlyif(self, low_data, cmd_opts):
'''
Check that unless doesn't return 0, and that onlyif returns a 0.
'''
ret = {'result': False}

if not isinstance(low_data['onlyif'], list):
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)
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})
return ret

def _run_check_unless(self, low_data, cmd_opts):
'''
Check that unless doesn't return 0, and that onlyif returns a 0.
'''
ret = {'result': False}

if not isinstance(low_data['unless'], list):
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)
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

# No reason to stop, return ret
return ret
Expand Down
24 changes: 24 additions & 0 deletions tests/integration/files/file/base/issue-30161.sls
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
unless_false_onlyif_true:
file.managed:
- name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('test.txt') }}
- unless: /bin/false
- onlyif: /bin/true

unless_true_onlyif_false:
file.managed:
- name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('test.txt') }}
- unless: /bin/true
- onlyif: /bin/false

unless_true_onlyif_true:
file.managed:
- name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('test.txt') }}
- unless: /bin/true
- onlyif: /bin/true

unless_false_onlyif_false:
file.managed:
- name: {{ salt['runtests_helpers.get_salt_temp_dir_for_path']('test.txt') }}
- contents: test
- unless: /bin/false
- onlyif: /bin/false
47 changes: 47 additions & 0 deletions tests/integration/modules/test_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,48 @@ def test_state_sls_id_test_false_pillar_true(self):
self.assertEqual(val['comment'], 'File /tmp/salt-tests-tmpdir/testfile updated')
self.assertEqual(val['changes']['diff'], 'New file')

def test_issue_30161_unless_and_onlyif_together(self):
'''
test cmd.run using multiple unless options where the first cmd in the
list will pass, but the second will fail. This tests the fix for issue
#35384. (The fix is in PR #35545.)
'''
sls = self.run_function('state.sls', mods='issue-30161')
self.assertSaltTrueReturn(sls)
# We must assert against the comment here to make sure the comment reads that the
# command "echo "hello"" was run. This ensures that we made it to the last unless
# command in the state. If the comment reads "unless condition is true", or similar,
# then the unless state run bailed out after the first unless command succeeded,
# which is the bug we're regression testing for.
_expected = {'file_|-unless_false_onlyif_false_|-{0}/test.txt_|-managed'.format(TMP):
{'comment': 'onlyif condition is false\nunless condition is false',
'name': '{0}/test.txt'.format(TMP),
'skip_watch': True,
'changes': {},
'result': True},
'file_|-unless_false_onlyif_true_|-{0}/test.txt_|-managed'.format(TMP):
{'comment': 'Empty file',
'pchanges': {},
'name': '{0}/test.txt'.format(TMP),
'start_time': '18:10:20.341753',
'result': True,
'changes': {'new': 'file {0}/test.txt created'.format(TMP)}},
'file_|-unless_true_onlyif_false_|-{0}/test.txt_|-managed'.format(TMP):
{'comment': 'onlyif condition is false\nunless condition is true',
'name': '{0}/test.txt'.format(TMP),
'start_time': '18:10:22.936446',
'skip_watch': True,
'changes': {},
'result': True},
'file_|-unless_true_onlyif_true_|-{0}/test.txt_|-managed'.format(TMP):
{'comment': 'onlyif condition is true\nunless condition is true',
'name': '{0}/test.txt'.format(TMP),
'skip_watch': True,
'changes': {},
'result': True}}
for id in _expected:
self.assertEqual(sls[id]['comment'], _expected[id]['comment'])

def tearDown(self):
nonbase_file = '/tmp/nonbase_env'
if os.path.isfile(nonbase_file):
Expand All @@ -1747,3 +1789,8 @@ def tearDown(self):
state_file = os.path.join(TMP, 'testfile')
if os.path.isfile(state_file):
os.remove(state_file)

# remove testfile added in issue-30161.sls state file
state_file = os.path.join(TMP, 'test.txt')
if os.path.isfile(state_file):
os.remove(state_file)

0 comments on commit 9cb6ac3

Please sign in to comment.