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 _commit_package_changes(self): """ Commit cached APT operations and give feedback on the results as a string. """ # XXX we cannot use io.StringIO() here with Python 2 as there is a # string literal written in apt.progress.text.TextProgress._write() # which is not recognized as unicode by io.StringIO() with Python 2. fetch_output = StringIO() # Redirect stdout and stderr to a file. We need to work with the # file descriptors, rather than sys.stdout/stderr, since dpkg is # run in a subprocess. fd, install_output_path = tempfile.mkstemp() old_stdout = os.dup(1) old_stderr = os.dup(2) os.dup2(fd, 1) os.dup2(fd, 2) install_progress = LandscapeInstallProgress() try: # Since others (charms) might be installing packages on this system # We need to retry a bit in case dpkg is locked in progress dpkg_tries = 0 while dpkg_tries <= self.max_dpkg_retries: error = None if dpkg_tries > 0: # Yeah, sleeping isn't kosher according to Twisted, but # this code is run in the package-changer, which doesn't # have any concurrency going on. time.sleep(self.dpkg_retry_sleep) logging.warning( "dpkg process might be in use. " "Retrying package changes. %d retries remaining." % (self.max_dpkg_retries - dpkg_tries)) dpkg_tries += 1 try: self._cache.commit( fetch_progress=LandscapeAcquireProgress(fetch_output), install_progress=install_progress) if not install_progress.dpkg_exited: raise SystemError("dpkg didn't exit cleanly.") except SystemError as exc: result_text = (fetch_output.getvalue() + read_text_file(install_output_path)) error = TransactionError(exc.args[0] + "\n\nPackage operation log:\n" + result_text) # No need to retry SystemError, since it's most # likely a permanent error. break except apt.cache.LockFailedException as exception: result_text = (fetch_output.getvalue() + read_text_file(install_output_path)) error = TransactionError(exception.args[0] + "\n\nPackage operation log:\n" + result_text) else: result_text = (fetch_output.getvalue() + read_text_file(install_output_path)) break if error is not None: raise error finally: # Restore stdout and stderr. os.dup2(old_stdout, 1) os.dup2(old_stderr, 2) os.remove(install_output_path) return result_text
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])
def _run(self, result): protocol = TestProtocolServer(result) output = subprocess.Popen([self.script], stdout=subprocess.PIPE).communicate()[0] protocol.readFrom(StringIO(output))