Handle better state errors

This fixes #9983.
This commit is contained in:
Mathieu Le Marec - Pasquet 2014-01-28 13:40:03 +01:00
parent cb65f44864
commit 165b5e4af2
5 changed files with 99 additions and 23 deletions

View file

@ -1371,16 +1371,30 @@ class State(object):
self.verify_ret(ret)
except Exception:
trb = traceback.format_exc()
# There are a number of possibilities to not have the cdata
# populated with what we might have expected, so just be enought
# smart to not raise another KeyError as the name is easily
# guessable and fallback in all cases to present the real
# exception to the user
if len(cdata['args']) > 0:
name = cdata['args'][0]
elif 'name' in cdata['kwargs']:
name = cdata['kwargs'].get(
'name',
low.get('name',
low.get('__id__'))
)
ret = {
'result': False,
'name': cdata['args'][0],
'name': name,
'changes': {},
'comment': 'An exception occurred in this state: {0}'.format(
trb)
}
}
finally:
if low.get('__prereq__'):
sys.modules[self.states[cdata['full']].__module__].__opts__['test'] = test
sys.modules[self.states[cdata['full']].__module__].__opts__[
'test'] = test
# If format_call got any warnings, let's show them to the user
if 'warnings' in cdata:

View file

@ -427,16 +427,18 @@ class TestDaemon(object):
if sync_needed:
# Wait for minions to "sync_all"
sync_minions = multiprocessing.Process(
target=self.sync_minion_modules,
args=(self.minion_targets, self.MINIONS_SYNC_TIMEOUT)
)
sync_minions.start()
sync_minions.join()
if sync_minions.exitcode > 0:
return False
sync_minions.terminate()
del sync_minions
for target in [self.sync_minion_modules,
self.sync_minion_states]:
sync_minions = multiprocessing.Process(
target=target,
args=(self.minion_targets, self.MINIONS_SYNC_TIMEOUT)
)
sync_minions.start()
sync_minions.join()
if sync_minions.exitcode > 0:
return False
sync_minions.terminate()
del sync_minions
return True
@ -596,26 +598,30 @@ class TestDaemon(object):
print_header('=', sep='=', inline=True)
raise SystemExit()
def sync_minion_modules(self, targets, timeout=120):
def sync_minion_modules_(self, modules_kind, targets, timeout=None):
if not timeout:
timeout = 120
# Let's sync all connected minions
print(
' {LIGHT_BLUE}*{ENDC} Syncing minion\'s modules '
'(saltutil.sync_modules)'.format(
' {LIGHT_BLUE}*{ENDC} Syncing minion\'s {1} '
'(saltutil.sync_{1})'.format(
', '.join(targets),
modules_kind,
**self.colors
)
)
syncing = set(targets)
jid_info = self.client.run_job(
list(targets), 'saltutil.sync_modules',
list(targets), 'saltutil.sync_{0}'.format(modules_kind),
expr_form='list',
timeout=9999999999999999,
)
if self.wait_for_jid(targets, jid_info['jid'], timeout) is False:
print(
' {RED_BOLD}*{ENDC} WARNING: Minions failed to sync modules. '
'Tests requiring these modules WILL fail'.format(**self.colors)
' {RED_BOLD}*{ENDC} WARNING: Minions failed to sync {0}. '
'Tests requiring these {0} WILL fail'.format(
modules_kind, **self.colors)
)
raise SystemExit()
@ -631,15 +637,20 @@ class TestDaemon(object):
if isinstance(output['ret'], salt._compat.string_types):
# An errors has occurred
print(
' {RED_BOLD}*{ENDC} {0} Failed so sync modules: '
'{1}'.format(name, output['ret'], **self.colors)
' {RED_BOLD}*{ENDC} {0} Failed so sync {2}: '
'{1}'.format(
name, output['ret'],
modules_kind,
**self.colors)
)
return False
print(
' {LIGHT_GREEN}*{ENDC} Synced {0} modules: '
' {LIGHT_GREEN}*{ENDC} Synced {0} {2}: '
'{1}'.format(
name, ', '.join(output['ret']), **self.colors
name,
', '.join(output['ret']),
modules_kind, **self.colors
)
)
# Synced!
@ -652,6 +663,12 @@ class TestDaemon(object):
)
return True
def sync_minion_states(self, targets, timeout=None):
self.sync_minion_modules_('states', targets, timeout=timeout)
def sync_minion_modules(self, targets, timeout=None):
self.sync_minion_modules_('modules', targets, timeout=timeout)
class AdaptedConfigurationTestCaseMixIn(object):

View file

@ -0,0 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__docformat__ = 'restructuredtext en'
def hello(name):
return "hello " + name
# vim:set et sts=4 ts=4 tw=80:

View file

@ -0,0 +1,2 @@
issue-9983-handleerror:
salttest.hello: []

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
'''
tests for host state
'''
# Import Salt Testing libs
from salttesting.helpers import ensure_in_syspath
ensure_in_syspath('../../')
# Import salt libs
import integration
class HandleErrorTest(integration.ModuleCase):
'''
Validate that ordering works correctly
'''
def test_handle_error(self):
'''
Test how an error can be recovered
'''
# without sync_states, the custom state may not be installed
# (resulting in :
# State salttest.hello found in sls issue-... is unavailable
ret = self.run_function('state.sls', ['issue-9983-handleerror'])
self.assertTrue(
'An exception occurred in this state: Traceback'
in ret[[a for a in ret][0]]['comment'])
if __name__ == '__main__':
from integration import run_tests
run_tests(HandleErrorTest)