Make reporting handlers configurable.

And configure the LogHandler by default.

Change-Id: Ic3a1cebbe8684033fb8d46bf4baebb55a43fc890
This commit is contained in:
Daniel Watkins 2015-07-20 10:19:18 +01:00
parent b5f5a16704
commit af671321af
2 changed files with 94 additions and 16 deletions

View File

@ -18,6 +18,14 @@ from cloudinit.registry import DictRegistry
FINISH_EVENT_TYPE = 'finish'
START_EVENT_TYPE = 'start'
DEFAULT_CONFIG = {
'logging': {'type': 'log'},
}
instantiated_handler_registry = DictRegistry()
available_handlers = DictRegistry()
class ReportingEvent(object):
"""Encapsulation of event formatting."""
@ -65,8 +73,12 @@ class LogHandler(ReportingHandler):
logger.info(event.as_string())
handler_registry = DictRegistry()
handler_registry.register_item('_logging', LogHandler())
def add_configuration(config):
for handler_name, handler_config in config.items():
handler_config = handler_config.copy()
cls = available_handlers.registered_items[handler_config.pop('type')]
instance = cls(**handler_config)
instantiated_handler_registry.register_item(handler_name, instance)
def report_event(event):
@ -79,7 +91,7 @@ def report_event(event):
The type of the event; this should be a constant from the
reporting module.
"""
for _, handler in handler_registry.registered_items.items():
for _, handler in instantiated_handler_registry.registered_items.items():
handler.publish_event(event)
@ -104,3 +116,7 @@ def report_start_event(event_name, event_description):
"""
event = ReportingEvent(START_EVENT_TYPE, event_name, event_description)
return report_event(event)
available_handlers.register_item('log', LogHandler)
add_configuration(DEFAULT_CONFIG)

View File

@ -17,15 +17,16 @@ def _fake_registry():
class TestReportStartEvent(unittest.TestCase):
@mock.patch('cloudinit.reporting.handler_registry',
@mock.patch('cloudinit.reporting.instantiated_handler_registry',
new_callable=_fake_registry)
def test_report_start_event_passes_something_with_as_string_to_handlers(
self, handler_registry):
self, instantiated_handler_registry):
event_name, event_description = 'my_test_event', 'my description'
reporting.report_start_event(event_name, event_description)
expected_string_representation = ': '.join(
['start', event_name, event_description])
for _, handler in handler_registry.registered_items.items():
for _, handler in (
instantiated_handler_registry.registered_items.items()):
self.assertEqual(1, handler.publish_event.call_count)
event = handler.publish_event.call_args[0][0]
self.assertEqual(expected_string_representation, event.as_string())
@ -46,37 +47,40 @@ class TestReportFinishEvent(unittest.TestCase):
event = handler.publish_event.call_args[0][0]
self.assertEqual(expected_as_string, event.as_string())
@mock.patch('cloudinit.reporting.handler_registry',
@mock.patch('cloudinit.reporting.instantiated_handler_registry',
new_callable=_fake_registry)
def test_report_finish_event_passes_something_with_as_string_to_handlers(
self, handler_registry):
self, instantiated_handler_registry):
event_name, event_description = self._report_finish_event()
expected_string_representation = ': '.join(
['finish', event_name, event_description])
self.assertHandlersPassedObjectWithAsString(
handler_registry.registered_items, expected_string_representation)
instantiated_handler_registry.registered_items,
expected_string_representation)
@mock.patch('cloudinit.reporting.handler_registry',
@mock.patch('cloudinit.reporting.instantiated_handler_registry',
new_callable=_fake_registry)
def test_reporting_successful_finish_has_sensible_string_repr(
self, handler_registry):
self, instantiated_handler_registry):
event_name, event_description = self._report_finish_event(
successful=True)
expected_string_representation = ': '.join(
['finish', event_name, 'success', event_description])
self.assertHandlersPassedObjectWithAsString(
handler_registry.registered_items, expected_string_representation)
instantiated_handler_registry.registered_items,
expected_string_representation)
@mock.patch('cloudinit.reporting.handler_registry',
@mock.patch('cloudinit.reporting.instantiated_handler_registry',
new_callable=_fake_registry)
def test_reporting_unsuccessful_finish_has_sensible_string_repr(
self, handler_registry):
self, instantiated_handler_registry):
event_name, event_description = self._report_finish_event(
successful=False)
expected_string_representation = ': '.join(
['finish', event_name, 'fail', event_description])
self.assertHandlersPassedObjectWithAsString(
handler_registry.registered_items, expected_string_representation)
instantiated_handler_registry.registered_items,
expected_string_representation)
class TestReportingEvent(unittest.TestCase):
@ -125,8 +129,66 @@ class TestLogHandler(TestCase):
class TestDefaultRegisteredHandler(TestCase):
def test_log_handler_registered_by_default(self):
for _, item in reporting.handler_registry.registered_items.items():
registered_items = (
reporting.instantiated_handler_registry.registered_items)
for _, item in registered_items.items():
if isinstance(item, reporting.LogHandler):
break
else:
self.fail('No reporting LogHandler registered by default.')
class TestReportingConfiguration(TestCase):
@mock.patch.object(reporting, 'instantiated_handler_registry')
def test_empty_configuration_doesnt_add_handlers(
self, instantiated_handler_registry):
reporting.add_configuration({})
self.assertEqual(
0, instantiated_handler_registry.register_item.call_count)
@mock.patch.object(
reporting, 'instantiated_handler_registry', reporting.DictRegistry())
@mock.patch.object(reporting, 'available_handlers')
def test_looks_up_handler_by_type_and_adds_it(self, available_handlers):
handler_type_name = 'test_handler'
handler_cls = mock.Mock()
available_handlers.registered_items = {handler_type_name: handler_cls}
handler_name = 'my_test_handler'
reporting.add_configuration(
{handler_name: {'type': handler_type_name}})
self.assertEqual(
{handler_name: handler_cls.return_value},
reporting.instantiated_handler_registry.registered_items)
@mock.patch.object(
reporting, 'instantiated_handler_registry', reporting.DictRegistry())
@mock.patch.object(reporting, 'available_handlers')
def test_uses_non_type_parts_of_config_dict_as_kwargs(
self, available_handlers):
handler_type_name = 'test_handler'
handler_cls = mock.Mock()
available_handlers.registered_items = {handler_type_name: handler_cls}
extra_kwargs = {'foo': 'bar', 'bar': 'baz'}
handler_config = extra_kwargs.copy()
handler_config.update({'type': handler_type_name})
handler_name = 'my_test_handler'
reporting.add_configuration({handler_name: handler_config})
self.assertEqual(
handler_cls.return_value,
reporting.instantiated_handler_registry.registered_items[
handler_name])
self.assertEqual([mock.call(**extra_kwargs)],
handler_cls.call_args_list)
@mock.patch.object(
reporting, 'instantiated_handler_registry', reporting.DictRegistry())
@mock.patch.object(reporting, 'available_handlers')
def test_handler_config_not_modified(self, available_handlers):
handler_type_name = 'test_handler'
handler_cls = mock.Mock()
available_handlers.registered_items = {handler_type_name: handler_cls}
handler_config = {'type': handler_type_name, 'foo': 'bar'}
expected_handler_config = handler_config.copy()
reporting.add_configuration({'my_test_handler': handler_config})
self.assertEqual(expected_handler_config, handler_config)