mirror of
https://github.com/valitydev/salt.git
synced 2024-11-07 08:58:59 +00:00
Make returncode friendly to onfail requisites
Ensure that correctly handled states execution (via onfail) will return a 0 code if onfail requisites suceed.
This commit is contained in:
parent
3c108bd95b
commit
fa2f9cf7bd
@ -83,7 +83,7 @@ def _filter_running(runnings):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
def _set_retcode(ret):
|
def _set_retcode(ret, highstate=None):
|
||||||
'''
|
'''
|
||||||
Set the return code based on the data back from the state system
|
Set the return code based on the data back from the state system
|
||||||
'''
|
'''
|
||||||
@ -94,7 +94,8 @@ def _set_retcode(ret):
|
|||||||
if isinstance(ret, list):
|
if isinstance(ret, list):
|
||||||
__context__['retcode'] = 1
|
__context__['retcode'] = 1
|
||||||
return
|
return
|
||||||
if not salt.utils.check_state_result(ret):
|
if not salt.utils.check_state_result(ret, highstate=highstate):
|
||||||
|
|
||||||
__context__['retcode'] = 2
|
__context__['retcode'] = 2
|
||||||
|
|
||||||
|
|
||||||
@ -345,7 +346,7 @@ def high(data, test=None, queue=False, **kwargs):
|
|||||||
st_ = salt.state.State(opts, pillar, pillar_enc=pillar_enc)
|
st_ = salt.state.State(opts, pillar, pillar_enc=pillar_enc)
|
||||||
|
|
||||||
ret = st_.call_high(data)
|
ret = st_.call_high(data)
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, highstate=data)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -393,7 +394,7 @@ def template(tem, queue=False, **kwargs):
|
|||||||
__context__['retcode'] = 1
|
__context__['retcode'] = 1
|
||||||
return errors
|
return errors
|
||||||
ret = st_.state.call_high(high_state)
|
ret = st_.state.call_high(high_state)
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, highstate=high_state)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@ -859,7 +860,7 @@ def highstate(test=None,
|
|||||||
|
|
||||||
serial = salt.payload.Serial(__opts__)
|
serial = salt.payload.Serial(__opts__)
|
||||||
cache_file = os.path.join(__opts__['cachedir'], 'highstate.p')
|
cache_file = os.path.join(__opts__['cachedir'], 'highstate.p')
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, highstate=st_.building_highstate)
|
||||||
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
||||||
# value from before this function was run.
|
# value from before this function was run.
|
||||||
_snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre)
|
_snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre)
|
||||||
@ -1091,7 +1092,7 @@ def sls(mods,
|
|||||||
except (IOError, OSError):
|
except (IOError, OSError):
|
||||||
msg = 'Unable to write to SLS cache file {0}. Check permission.'
|
msg = 'Unable to write to SLS cache file {0}. Check permission.'
|
||||||
log.error(msg.format(cache_file))
|
log.error(msg.format(cache_file))
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, high_)
|
||||||
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
||||||
# value from before this function was run.
|
# value from before this function was run.
|
||||||
__opts__['test'] = orig_test
|
__opts__['test'] = orig_test
|
||||||
@ -1195,7 +1196,7 @@ def top(topfn,
|
|||||||
finally:
|
finally:
|
||||||
st_.pop_active()
|
st_.pop_active()
|
||||||
|
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, highstate=st_.building_highstate)
|
||||||
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
||||||
# value from before this function was run.
|
# value from before this function was run.
|
||||||
_snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre)
|
_snapper_post(opts, kwargs.get('__pub_jid', 'called localy'), snapper_pre)
|
||||||
@ -1396,7 +1397,7 @@ def sls_id(
|
|||||||
if chunk.get('__id__', '') == id_:
|
if chunk.get('__id__', '') == id_:
|
||||||
ret.update(st_.state.call_chunk(chunk, {}, chunks))
|
ret.update(st_.state.call_chunk(chunk, {}, chunks))
|
||||||
|
|
||||||
_set_retcode(ret)
|
_set_retcode(ret, highstate=highstate)
|
||||||
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
# Work around Windows multiprocessing bug, set __opts__['test'] back to
|
||||||
# value from before this function was run.
|
# value from before this function was run.
|
||||||
__opts__['test'] = orig_test
|
__opts__['test'] = orig_test
|
||||||
|
@ -1868,7 +1868,95 @@ def gen_state_tag(low):
|
|||||||
return '{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}'.format(low)
|
return '{0[state]}_|-{0[__id__]}_|-{0[name]}_|-{0[fun]}'.format(low)
|
||||||
|
|
||||||
|
|
||||||
def check_state_result(running, recurse=False):
|
def search_onfail_requisites(sid, highstate):
|
||||||
|
"""
|
||||||
|
For a particular low chunk, search relevant onfail related
|
||||||
|
states
|
||||||
|
"""
|
||||||
|
onfails = []
|
||||||
|
if '_|-' in sid:
|
||||||
|
st = salt.state.split_low_tag(sid)
|
||||||
|
else:
|
||||||
|
st = {'__id__': sid}
|
||||||
|
for fstate, fchunks in six.iteritems(highstate):
|
||||||
|
if fstate == st['__id__']:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for mod_, fchunk in six.iteritems(fchunks):
|
||||||
|
if (
|
||||||
|
not isinstance(mod_, six.string_types) or
|
||||||
|
mod_.startswith('__')
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if not isinstance(fchunk, list):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
for fdata in fchunk:
|
||||||
|
if not isinstance(fdata, dict):
|
||||||
|
continue
|
||||||
|
for knob, fvalue in six.iteritems(fdata):
|
||||||
|
if knob != 'onfail':
|
||||||
|
continue
|
||||||
|
for freqs in fvalue:
|
||||||
|
for fmod, fid in six.iteritems(freqs):
|
||||||
|
if not (
|
||||||
|
fid == st['__id__'] and
|
||||||
|
fmod == st.get('state', fmod)
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
onfails.append((fstate, mod_, fchunk))
|
||||||
|
return onfails
|
||||||
|
|
||||||
|
|
||||||
|
def check_onfail_requisites(state_id, state_result, running, highstate):
|
||||||
|
'''
|
||||||
|
When a state fail and is part of a highstate, check
|
||||||
|
if there is onfail requisites.
|
||||||
|
When we find onfail requisites, we will consider the state failed
|
||||||
|
only if at least one of those onfail requisites also failed
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
True: if onfail handlers suceeded
|
||||||
|
False: if one on those handler failed
|
||||||
|
None: if the state does not have onfail requisites
|
||||||
|
|
||||||
|
'''
|
||||||
|
nret = None
|
||||||
|
if (
|
||||||
|
state_id and state_result and
|
||||||
|
highstate and isinstance(highstate, dict)
|
||||||
|
):
|
||||||
|
onfails = search_onfail_requisites(state_id, highstate)
|
||||||
|
if onfails:
|
||||||
|
for handler in onfails:
|
||||||
|
fstate, mod_, fchunk = handler
|
||||||
|
ofresult = True
|
||||||
|
for rstateid, rstate in six.iteritems(running):
|
||||||
|
if '_|-' in rstateid:
|
||||||
|
st = salt.state.split_low_tag(rstateid)
|
||||||
|
# in case of simple state, try to guess
|
||||||
|
else:
|
||||||
|
id_ = rstate.get('__id__', rstateid)
|
||||||
|
if not id_:
|
||||||
|
raise ValueError('no state id')
|
||||||
|
st = {'__id__': id_, 'state': mod_}
|
||||||
|
if mod_ == st['state'] and fstate == st['__id__']:
|
||||||
|
ofresult = rstate.get('result', _empty)
|
||||||
|
if ofresult in [False, True]:
|
||||||
|
nret = ofresult
|
||||||
|
if ofresult is False:
|
||||||
|
# as soon as we find an errored onfail, we stop
|
||||||
|
break
|
||||||
|
# consider that if we parsed onfailes without changing
|
||||||
|
# the ret, that we have failed
|
||||||
|
if nret is None:
|
||||||
|
nret = False
|
||||||
|
return nret
|
||||||
|
|
||||||
|
|
||||||
|
def check_state_result(running, recurse=False, highstate=None):
|
||||||
'''
|
'''
|
||||||
Check the total return value of the run and determine if the running
|
Check the total return value of the run and determine if the running
|
||||||
dict has any issues
|
dict has any issues
|
||||||
@ -1880,7 +1968,7 @@ def check_state_result(running, recurse=False):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
ret = True
|
ret = True
|
||||||
for state_result in six.itervalues(running):
|
for state_id, state_result in six.iteritems(running):
|
||||||
if not recurse and not isinstance(state_result, dict):
|
if not recurse and not isinstance(state_result, dict):
|
||||||
ret = False
|
ret = False
|
||||||
if ret and isinstance(state_result, dict):
|
if ret and isinstance(state_result, dict):
|
||||||
@ -1889,7 +1977,13 @@ def check_state_result(running, recurse=False):
|
|||||||
ret = False
|
ret = False
|
||||||
# only override return value if we are not already failed
|
# only override return value if we are not already failed
|
||||||
elif result is _empty and isinstance(state_result, dict) and ret:
|
elif result is _empty and isinstance(state_result, dict) and ret:
|
||||||
ret = check_state_result(state_result, recurse=True)
|
ret = check_state_result(
|
||||||
|
state_result, recurse=True, highstate=highstate)
|
||||||
|
# if we detect a fail, check for onfail requisites
|
||||||
|
if not ret:
|
||||||
|
# ret can be None in case of no onfail reqs, recast it to bool
|
||||||
|
ret = bool(check_onfail_requisites(state_id, state_result,
|
||||||
|
running, highstate))
|
||||||
# return as soon as we got a failure
|
# return as soon as we got a failure
|
||||||
if not ret:
|
if not ret:
|
||||||
break
|
break
|
||||||
|
@ -136,6 +136,7 @@ class MockState(object):
|
|||||||
'pillar': {}}
|
'pillar': {}}
|
||||||
|
|
||||||
def __init__(self, opts, pillar=None, *args, **kwargs):
|
def __init__(self, opts, pillar=None, *args, **kwargs):
|
||||||
|
self.building_highstate = {}
|
||||||
self.state = MockState.State(opts,
|
self.state = MockState.State(opts,
|
||||||
pillar=pillar)
|
pillar=pillar)
|
||||||
|
|
||||||
|
@ -452,6 +452,243 @@ class UtilsTestCase(TestCase):
|
|||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
utils.check_state_result(data),
|
utils.check_state_result(data),
|
||||||
msg='{0} failed'.format(test))
|
msg='{0} failed'.format(test))
|
||||||
|
test_invalid_true_ht_states = {
|
||||||
|
'test_onfail_integ2': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('t_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate1_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_ivstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}],
|
||||||
|
't': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_ivstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_ivstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'test_onfail_integ3': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('t_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': True}),
|
||||||
|
('cmd_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate1_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_ivstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}],
|
||||||
|
't': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_ivstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_ivstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'test_onfail_integ4': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('t_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate1_|-echo_|-run', {
|
||||||
|
'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_ivstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}],
|
||||||
|
't': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_ivstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_ivstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'test_onfail': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('test_state0', {'result': False}),
|
||||||
|
('test_state', {'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
None
|
||||||
|
),
|
||||||
|
'test_onfail_d': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('test_state0', {'result': False}),
|
||||||
|
('test_state', {'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for test, testdata in six.iteritems(test_invalid_true_ht_states):
|
||||||
|
data, ht = testdata
|
||||||
|
for t_ in [a for a in data['host1']]:
|
||||||
|
tdata = data['host1'][t_]
|
||||||
|
if '_|-' in t_:
|
||||||
|
t_ = t_.split('_|-')[1]
|
||||||
|
tdata['__id__'] = t_
|
||||||
|
self.assertFalse(
|
||||||
|
utils.check_state_result(data, highstate=ht),
|
||||||
|
msg='{0} failed'.format(test))
|
||||||
|
|
||||||
|
test_valid_true_ht_states = {
|
||||||
|
'test_onfail_integ': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('cmd_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate1_|-echo_|-run', {
|
||||||
|
'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_ivstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_ivstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_ivstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'test_onfail_intega3': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('t_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': True}),
|
||||||
|
('cmd_|-test_ivstate0_|-echo_|-run', {
|
||||||
|
'result': False}),
|
||||||
|
('cmd_|-test_ivstate1_|-echo_|-run', {
|
||||||
|
'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_ivstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}],
|
||||||
|
't': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_ivstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_ivstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'test_onfail_simple': (
|
||||||
|
OrderedDict([
|
||||||
|
('host1',
|
||||||
|
OrderedDict([
|
||||||
|
('test_vstate0', {'result': False}),
|
||||||
|
('test_vstate1', {'result': True}),
|
||||||
|
])),
|
||||||
|
]),
|
||||||
|
{
|
||||||
|
'test_vstate0': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
'run',
|
||||||
|
{'order': 10002}]},
|
||||||
|
'test_vstate1': {
|
||||||
|
'__env__': 'base',
|
||||||
|
'__sls__': u'a',
|
||||||
|
'cmd': [OrderedDict([('name', '/bin/true')]),
|
||||||
|
OrderedDict([
|
||||||
|
('onfail',
|
||||||
|
[OrderedDict([('cmd', 'test_vstate0')])])
|
||||||
|
]),
|
||||||
|
'run',
|
||||||
|
{'order': 10004}]},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for test, testdata in six.iteritems(test_valid_true_ht_states):
|
||||||
|
data, ht = testdata
|
||||||
|
for t_ in [a for a in data['host1']]:
|
||||||
|
tdata = data['host1'][t_]
|
||||||
|
if '_|-' in t_:
|
||||||
|
t_ = t_.split('_|-')[1]
|
||||||
|
tdata['__id__'] = t_
|
||||||
|
self.assertTrue(
|
||||||
|
utils.check_state_result(data, highstate=ht),
|
||||||
|
msg='{0} failed'.format(test))
|
||||||
test_valid_false_state = {'host1': {'test_state': {'result': False}}}
|
test_valid_false_state = {'host1': {'test_state': {'result': False}}}
|
||||||
self.assertFalse(utils.check_state_result(test_valid_false_state))
|
self.assertFalse(utils.check_state_result(test_valid_false_state))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user