def test_stop_reactor_even_when_sync_exception_from_sysinfo_run(self): """ Even when there's a synchronous exception from run_sysinfo, the reactor should be stopped. """ self.log_helper.ignore_errors(ZeroDivisionError) reactor = FakeReactor() sysinfo = SysInfoPluginRegistry() sysinfo.run = lambda: 1 / 0 d = run(["--sysinfo-plugins", "TestPlugin"], reactor=reactor, sysinfo=sysinfo) for x in reactor.queued_calls: x() self.assertEqual(reactor.scheduled_calls, [(0, reactor.stop, (), {})]) return self.assertFailure(d, ZeroDivisionError)
def test_output_is_only_displayed_once_deferred_fires(self): deferred = Deferred() # We mock the sysinfo.run() to return a Deferred but still # run the actual sysinfo.run() to gather the results from all # the plugins. We cannot easily combine return_value and # side_effect because side_effect operates on the return_value, # thus firing the callback and writing sysinfo out to stdout. sysinfo = SysInfoPluginRegistry() original_sysinfo_run = sysinfo.run def wrapped_sysinfo_run(*args, **kwargs): original_sysinfo_run(*args, **kwargs) return deferred sysinfo.run = mock.Mock(side_effect=wrapped_sysinfo_run) run(["--sysinfo-plugins", "TestPlugin"], sysinfo=sysinfo) sysinfo.run.assert_called_once_with() self.assertNotIn("Test note", self.stdout.getvalue()) deferred.callback(None) self.assertIn("Test note", self.stdout.getvalue())
class SysInfoPluginRegistryTest(HelperTestCase): def setUp(self): super(SysInfoPluginRegistryTest, self).setUp() self.sysinfo = SysInfoPluginRegistry() self.sysinfo_logfile = StringIO() self.handler = StreamHandler(self.sysinfo_logfile) self.logger = getLogger("landscape-sysinfo") self.logger.addHandler(self.handler) def tearDown(self): super(SysInfoPluginRegistryTest, self).tearDown() self.logger.removeHandler(self.handler) def test_is_plugin_registry(self): self.assertTrue(isinstance(self.sysinfo, PluginRegistry)) def test_add_and_get_headers(self): self.sysinfo.add_header("Memory usage", "65%") self.sysinfo.add_header("Swap usage", "None") self.assertEqual(self.sysinfo.get_headers(), [("Memory usage", "65%"), ("Swap usage", "None")]) self.assertEqual(self.sysinfo.get_notes(), []) self.assertEqual(self.sysinfo.get_footnotes(), []) def test_add_same_header_twice(self): self.sysinfo.add_header("Header1", "Value1") self.sysinfo.add_header("Header2", "Value2") self.sysinfo.add_header("Header3", "Value3") self.sysinfo.add_header("Header2", "Value4") self.assertEqual(self.sysinfo.get_headers(), [("Header1", "Value1"), ("Header2", "Value4"), ("Header3", "Value3")]) def test_add_header_with_none_value(self): self.sysinfo.add_header("Header1", "Value1") self.sysinfo.add_header("Header2", None) self.sysinfo.add_header("Header3", "Value3") self.assertEqual(self.sysinfo.get_headers(), [("Header1", "Value1"), ("Header3", "Value3")]) self.sysinfo.add_header("Header2", "Value2") self.assertEqual(self.sysinfo.get_headers(), [("Header1", "Value1"), ("Header2", "Value2"), ("Header3", "Value3")]) def test_add_and_get_notes(self): self.sysinfo.add_note("Your laptop is burning!") self.sysinfo.add_note("Oh, your house too, btw.") self.assertEqual( self.sysinfo.get_notes(), ["Your laptop is burning!", "Oh, your house too, btw."]) self.assertEqual(self.sysinfo.get_headers(), []) self.assertEqual(self.sysinfo.get_footnotes(), []) def test_add_and_get_footnotes(self): self.sysinfo.add_footnote("Graphs available at http://graph") self.sysinfo.add_footnote("Go! Go!") self.assertEqual(self.sysinfo.get_footnotes(), ["Graphs available at http://graph", "Go! Go!"]) self.assertEqual(self.sysinfo.get_headers(), []) self.assertEqual(self.sysinfo.get_notes(), []) def test_run(self): class Plugin(object): def __init__(self, deferred): self._deferred = deferred def register(self, registry): pass def run(self): return self._deferred plugin_deferred1 = Deferred() plugin_deferred2 = Deferred() plugin1 = Plugin(plugin_deferred1) plugin2 = Plugin(plugin_deferred2) self.sysinfo.add(plugin1) self.sysinfo.add(plugin2) def check_result(result): self.assertEqual(result, [123, 456]) deferred = self.sysinfo.run() deferred.addBoth(check_result) self.assertEqual(deferred.called, False) plugin_deferred1.callback(123) self.assertEqual(deferred.called, False) plugin_deferred2.callback(456) self.assertEqual(deferred.called, True) plugin_exception_message = ( "There were exceptions while processing one or more plugins. " "See %s/sysinfo.log for more information.") def test_plugins_run_after_synchronous_error(self): """ Even when a plugin raises a synchronous error, other plugins will continue to be run. """ self.log_helper.ignore_errors(ZeroDivisionError) plugins_what_run = [] class BadPlugin(object): def register(self, registry): pass def run(self): plugins_what_run.append(self) 1 / 0 class GoodPlugin(object): def register(self, registry): pass def run(self): plugins_what_run.append(self) return succeed(None) plugin1 = BadPlugin() plugin2 = GoodPlugin() self.sysinfo.add(plugin1) self.sysinfo.add(plugin2) self.sysinfo.run() self.assertEqual(plugins_what_run, [plugin1, plugin2]) log = self.sysinfo_logfile.getvalue() message = "BadPlugin plugin raised an exception." self.assertIn(message, log) self.assertIn("1 / 0", log) self.assertIn("ZeroDivisionError", log) path = os.path.expanduser("~/.landscape") self.assertEqual(self.sysinfo.get_notes(), [self.plugin_exception_message % path]) def test_asynchronous_errors_logged(self): self.log_helper.ignore_errors(ZeroDivisionError) class BadPlugin(object): def register(self, registry): pass def run(self): return fail(ZeroDivisionError("yay")) plugin = BadPlugin() self.sysinfo.add(plugin) self.sysinfo.run() log = self.sysinfo_logfile.getvalue() message = "BadPlugin plugin raised an exception." self.assertIn(message, log) self.assertIn("ZeroDivisionError: yay", log) path = os.path.expanduser("~/.landscape") self.assertEqual(self.sysinfo.get_notes(), [self.plugin_exception_message % path]) def test_multiple_exceptions_get_one_note(self): self.log_helper.ignore_errors(ZeroDivisionError) class RegularBadPlugin(object): def register(self, registry): pass def run(self): 1 / 0 class AsyncBadPlugin(object): def register(self, registry): pass def run(self): return fail(ZeroDivisionError("Hi")) plugin1 = RegularBadPlugin() plugin2 = AsyncBadPlugin() self.sysinfo.add(plugin1) self.sysinfo.add(plugin2) self.sysinfo.run() path = os.path.expanduser("~/.landscape") self.assertEqual(self.sysinfo.get_notes(), [self.plugin_exception_message % path]) @mock.patch("os.getuid", return_value=0) def test_exception_running_as_privileged_user(self, uid_mock): """ If a Plugin fails while running and the sysinfo binary is running with a uid of 0, Landscape sysinfo should write to the system logs directory. """ class AsyncBadPlugin(object): def register(self, registry): pass def run(self): return fail(ZeroDivisionError("Hi")) self.log_helper.ignore_errors(ZeroDivisionError) plugin = AsyncBadPlugin() self.sysinfo.add(plugin) self.sysinfo.run() uid_mock.assert_called_with() path = "/var/log/landscape" self.assertEqual(self.sysinfo.get_notes(), [self.plugin_exception_message % path])