Ejemplo n.º 1
0
    def test_configure(self):
        """Test ErrorReportingUtility.setConfigSection()."""
        utility = ErrorReportingUtility()
        # The ErrorReportingUtility uses the config.error_reports section
        # by default.
        self.assertEqual(config.error_reports.oops_prefix, utility.oops_prefix)
        self.assertEqual(config.error_reports.error_dir,
                         utility._oops_datedir_repo.root)
        # Some external processes may extend the reporter/prefix with
        # extra information.
        utility.configure(section_name='branchscanner')
        self.assertEqual('T-branchscanner', utility.oops_prefix)

        # The default error section can be restored.
        utility.configure()
        self.assertEqual(config.error_reports.oops_prefix, utility.oops_prefix)

        # We should have had three publishers setup:
        oops_config = utility._oops_config
        self.assertEqual(3, len(oops_config.publishers))
        # - a rabbit publisher
        self.assertIsInstance(oops_config.publishers[0], oops_amqp.Publisher)
        # - a datedir publisher wrapped in a publish_new_only wrapper
        datedir_repo = utility._oops_datedir_repo
        publisher = oops_config.publishers[1].func_closure[0].cell_contents
        self.assertEqual(publisher, datedir_repo.publish)
        # - a notify publisher
        self.assertEqual(oops_config.publishers[2], notify_publisher)
Ejemplo n.º 2
0
    def test_raising_with_webservice_request(self):
        # Test ErrorReportingUtility.raising() with a WebServiceRequest
        # request. Only some exceptions result in OOPSes.
        request = TestRequest()
        directlyProvides(request, WebServiceLayer)
        utility = ErrorReportingUtility()
        utility._oops_config.publisher = None

        # Exceptions that don't use error_status result in OOPSes.
        try:
            raise ArbitraryException('xyz\nabc')
        except ArbitraryException:
            self.assertNotEqual(None, utility.raising(sys.exc_info(), request))

        # Exceptions with a error_status in the 500 range result
        # in OOPSes.
        @error_status(httplib.INTERNAL_SERVER_ERROR)
        class InternalServerError(Exception):
            pass

        try:
            raise InternalServerError("")
        except InternalServerError:
            self.assertNotEqual(None, utility.raising(sys.exc_info(), request))

        # Exceptions with any other error_status do not result
        # in OOPSes.
        @error_status(httplib.BAD_REQUEST)
        class BadDataError(Exception):
            pass

        try:
            raise BadDataError("")
        except BadDataError:
            self.assertEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 3
0
    def test_configure(self):
        """Test ErrorReportingUtility.setConfigSection()."""
        utility = ErrorReportingUtility()
        # The ErrorReportingUtility uses the config.error_reports section
        # by default.
        self.assertEqual(config.error_reports.oops_prefix, utility.oops_prefix)
        self.assertEqual(config.error_reports.error_dir,
                         utility._oops_datedir_repo.root)
        # Some external processes may extend the reporter/prefix with
        # extra information.
        utility.configure(section_name='branchscanner')
        self.assertEqual('T-branchscanner', utility.oops_prefix)

        # The default error section can be restored.
        utility.configure()
        self.assertEqual(config.error_reports.oops_prefix, utility.oops_prefix)

        # We should have had two publishers set up:
        self.assertEqual(2, len(utility._all_publishers))
        # - a fallback publisher chaining a rabbit publisher and a datedir
        #   publisher
        self.assertIsInstance(utility._main_publishers[0], oops_amqp.Publisher)
        self.assertEqual(utility._main_publishers[1],
                         utility._oops_datedir_repo.publish)
        # - a notify publisher
        self.assertEqual(utility._all_publishers[1], notify_publisher)
Ejemplo n.º 4
0
def make_error_utility(pid=None):
    """Make an error utility for logging errors from bzr."""
    if pid is None:
        pid = os.getpid()
    error_utility = ErrorReportingUtility()
    error_utility.configure('bzr_lpserve')
    return error_utility
Ejemplo n.º 5
0
 def test_raising_unauthorized_without_request(self):
     """Unauthorized exceptions are logged when there's no request."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     try:
         raise Unauthorized('xyz')
     except Unauthorized:
         oops = utility.raising(sys.exc_info())
     self.assertNotEqual(None, oops)
Ejemplo n.º 6
0
 def test_raising_unauthorized_with_authenticated_principal(self):
     """Unauthorized exceptions are logged when the request has an
     authenticated principal."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     request = TestRequestWithPrincipal()
     try:
         raise Unauthorized('xyz')
     except Unauthorized:
         self.assertNotEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 7
0
 def test_raising_unauthorized_without_principal(self):
     """Unauthorized exceptions are logged when the request has no
     principal."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     request = ScriptRequest([('name2', 'value2')])
     try:
         raise Unauthorized('xyz')
     except Unauthorized:
         self.assertNotEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 8
0
 def test_ignored_exceptions_for_offsite_referer(self):
     # Exceptions caused by bad URLs that may not be an Lp code issue.
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     errors = set([
         GoneError.__name__, InvalidBatchSizeError.__name__,
         NotFound.__name__
     ])
     self.assertEqual(errors,
                      utility._ignored_exceptions_for_offsite_referer)
Ejemplo n.º 9
0
    def process(self):
        """Process an upload that is the result of a build.

        The name of the leaf is the build id of the build.
        Build uploads always contain a single package per leaf.
        """
        logger = BufferLogger()
        if self.build.status != BuildStatus.UPLOADING:
            self.processor.log.warn(
                "Expected build status to be 'UPLOADING', was %s. Ignoring." %
                self.build.status.name)
            return
        try:
            # The recipe may have been deleted so we need to flag that here
            # and will handle below. We check so that we don't go to the
            # expense of doing an unnecessary upload. We don't just exit here
            # because we want the standard cleanup to occur.
            recipe_deleted = (ISourcePackageRecipeBuild.providedBy(self.build)
                              and self.build.recipe is None)
            if recipe_deleted:
                result = UploadStatusEnum.FAILED
            else:
                self.processor.log.debug("Build %s found" % self.build.id)
                [changes_file] = self.locateChangesFiles()
                logger.debug("Considering changefile %s" % changes_file)
                result = self.processChangesFile(changes_file, logger)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            info = sys.exc_info()
            message = ('Exception while processing upload %s' %
                       self.upload_path)
            properties = [('error-explanation', message)]
            request = ScriptRequest(properties)
            error_utility = ErrorReportingUtility()
            error_utility.raising(info, request)
            logger.error('%s (%s)' % (message, request.oopsid))
            result = UploadStatusEnum.FAILED
        if (result != UploadStatusEnum.ACCEPTED
                or not self.build.verifySuccessfulUpload()):
            self.build.updateStatus(BuildStatus.FAILEDTOUPLOAD)
        if self.build.status != BuildStatus.FULLYBUILT:
            if recipe_deleted:
                # For a deleted recipe, no need to notify that uploading has
                # failed - we just log a warning.
                self.processor.log.warn(
                    "Recipe for build %s was deleted. Ignoring." % self.upload)
            else:
                self.build.storeUploadLog(logger.getLogBuffer())
                self.build.notify(extra_info="Uploading build %s failed." %
                                  self.upload)
        else:
            self.build.notify()
        self.processor.ztm.commit()
        self.moveProcessedUpload(result, logger)
Ejemplo n.º 10
0
 def test_404_without_referer_is_ignored(self):
     # If a 404 is generated and there is no HTTP referer, we don't produce
     # an OOPS.
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     report = {
         'type': 'NotFound',
         'url': 'http://example.com',
         'req_vars': {}
     }
     self.assertEqual(None, utility._oops_config.publish(report))
Ejemplo n.º 11
0
 def test_ignored_exceptions_for_offsite_referer_not_reported(self):
     # Oopses are not reported when Launchpad is not the referer.
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     # There is no HTTP_REFERER header in this request
     request = TestRequest(
         environ={'SERVER_URL': 'http://launchpad.dev/fnord'})
     try:
         raise GoneError('fnord')
     except GoneError:
         self.assertEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 12
0
 def test_marked_exception_is_ignored(self):
     # If an exception has been marked as ignorable, then it is ignored in
     # the report.
     utility = ErrorReportingUtility()
     try:
         raise ArbitraryException('xyz\nabc')
     except ArbitraryException:
         exc_info = sys.exc_info()
         directlyProvides(exc_info[1], IUnloggedException)
     report = utility._oops_config.create(dict(exc_info=exc_info))
     self.assertTrue(report['ignore'])
Ejemplo n.º 13
0
def report_oops(message=None,
                properties=None,
                info=None,
                transaction_manager=None):
    """Record an oops for the current exception.

    This must only be called while handling an exception.

    Searches for 'URL', 'url', or 'baseurl' properties, in order of
    preference, to use as the linked URL of the OOPS report.

    :param message: custom explanatory error message. Do not use
        str(exception) to fill in this parameter, it should only be
        set when a human readable error has been explicitly generated.

    :param properties: Properties to record in the OOPS report.
    :type properties: An iterable of (name, value) tuples.

    :param info: Exception info.
    :type info: The return value of `sys.exc_info()`.

    :param transaction_manager: A transaction manager. If specified,
        further commit() calls will be logged.
    """
    # Get the current exception info first of all.
    if info is None:
        info = sys.exc_info()

    # Collect properties to report.
    if properties is None:
        properties = []
    else:
        properties = list(properties)

    if message is not None:
        properties.append(('error-explanation', message))

    # Find a candidate for the request URL.
    def find_url():
        for name in 'URL', 'url', 'baseurl':
            for key, value in properties:
                if key == name:
                    return value
        return None

    url = find_url()

    # Create the dummy request object.
    request = ScriptRequest(properties, url)
    error_utility = ErrorReportingUtility()
    error_utility.configure(section_name='checkwatches')
    error_utility.raising(info, request)
    return request
Ejemplo n.º 14
0
 def test_raising_with_xmlrpc_request(self):
     # Test ErrorReportingUtility.raising() with an XML-RPC request.
     request = TestRequest()
     directlyProvides(request, IXMLRPCRequest)
     request.getPositionalArguments = lambda: (1, 2)
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     try:
         raise ArbitraryException('xyz\nabc')
     except ArbitraryException:
         report = utility.raising(sys.exc_info(), request)
     self.assertEqual("(1, 2)", report['req_vars']['xmlrpc args'])
Ejemplo n.º 15
0
 def test__makeErrorReport_includes_oops_messages(self):
     """The error report should include the oops messages."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     with utility.oopsMessage(dict(a='b', c='d')):
         try:
             raise ArbitraryException('foo')
         except ArbitraryException:
             info = sys.exc_info()
             oops = utility._oops_config.create(dict(exc_info=info))
             self.assertEqual({'<oops-message-0>': "{'a': 'b', 'c': 'd'}"},
                              oops['req_vars'])
Ejemplo n.º 16
0
 def test_offsite_404_ignored(self):
     # A request originating from another site that generates a NotFound
     # (404) is ignored (i.e., no OOPS is logged).
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     report = {
         'type': 'NotFound',
         'url': 'http://example.com',
         'req_vars': {
             'HTTP_REFERER': 'example.com'
         }
     }
     self.assertEqual(None, utility._oops_config.publish(report))
Ejemplo n.º 17
0
 def test_session_queries_filtered(self):
     """Test that session queries are filtered."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     timeline = Timeline()
     timeline.start("SQL-session", "SELECT 'gone'").finish()
     try:
         raise ArbitraryException('foo')
     except ArbitraryException:
         info = sys.exc_info()
         oops = utility._oops_config.create(
             dict(exc_info=info, timeline=timeline))
     self.assertEqual("SELECT '%s'", oops['timeline'][0][3])
Ejemplo n.º 18
0
 def test_onsite_404_not_ignored(self):
     # A request originating from a local site that generates a NotFound
     # (404) produces an OOPS.
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     report = {
         'type': 'NotFound',
         'url': 'http://example.com',
         'req_vars': {
             'HTTP_REFERER': 'http://launchpad.dev/'
         }
     }
     self.assertNotEqual(None, utility._oops_config.publish(report))
Ejemplo n.º 19
0
 def setUp(self):
     super(BaseRequestEndHandlerTest, self).setUp()
     self.profile_dir = self.makeTemporaryDirectory()
     self.memory_profile_log = os.path.join(self.profile_dir, 'memory_log')
     self.pushConfig('profiling', profile_dir=self.profile_dir)
     eru = queryUtility(IErrorReportingUtility)
     if eru is None:
         # Register an Error reporting utility for this layer.
         # This will break tests when run with an ERU already registered.
         self.eru = ErrorReportingUtility()
         sm = getSiteManager()
         sm.registerUtility(self.eru)
         self.addCleanup(sm.unregisterUtility, self.eru)
Ejemplo n.º 20
0
    def test_raising_no_referrer_error(self):
        """Test ErrorReportingUtility.raising() with a NoReferrerError
        exception.

        An OOPS is not recorded when a NoReferrerError exception is
        raised.
        """
        utility = ErrorReportingUtility()
        utility._oops_config.publisher = None
        try:
            raise NoReferrerError('xyz')
        except NoReferrerError:
            self.assertEqual(None, utility.raising(sys.exc_info()))
Ejemplo n.º 21
0
 def test_ignored_exceptions_for_offsite_referer_reported(self):
     # Oopses are reported when Launchpad is the referer for a URL
     # that caused an exception.
     utility = ErrorReportingUtility()
     del utility._oops_config.publishers[:]
     request = TestRequest(
         environ={
             'SERVER_URL': 'http://launchpad.dev/fnord',
             'HTTP_REFERER': 'http://launchpad.dev/snarf'
         })
     try:
         raise GoneError('fnord')
     except GoneError:
         self.assertNotEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 22
0
 def test_ignored_exceptions_for_criss_cross_vhost_referer_reported(self):
     # Oopses are reported when a Launchpad referer for a bad URL on a
     # vhost that caused an exception.
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     request = TestRequest(
         environ={
             'SERVER_URL': 'http://bazaar.launchpad.dev/fnord',
             'HTTP_REFERER': 'http://launchpad.dev/snarf'
         })
     try:
         raise GoneError('fnord')
     except GoneError:
         self.assertNotEqual(None, utility.raising(sys.exc_info(), request))
Ejemplo n.º 23
0
 def test_raising_non_utf8_request_param_key_bug_896959(self):
     # When a form has a nonutf8 request param, the key in req_vars must
     # still be unicode (or utf8).
     request = TestRequest(form={'foo\x85': 'bar'})
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     try:
         raise ArbitraryException('foo')
     except ArbitraryException:
         report = utility.raising(sys.exc_info(), request)
     for key in report['req_vars'].keys():
         if isinstance(key, str):
             key.decode('utf8')
         else:
             self.assertIsInstance(key, unicode)
Ejemplo n.º 24
0
 def test__makeErrorReport_combines_request_and_error_vars(self):
     """The oops messages should be distinct from real request vars."""
     utility = ErrorReportingUtility()
     utility._oops_config.publisher = None
     request = ScriptRequest([('c', 'd')])
     with utility.oopsMessage(dict(a='b')):
         try:
             raise ArbitraryException('foo')
         except ArbitraryException:
             info = sys.exc_info()
             oops = utility._oops_config.create(
                 dict(exc_info=info, http_request=request))
             self.assertEqual({
                 '<oops-message-0>': "{'a': 'b'}",
                 'c': 'd'
             }, oops['req_vars'])
Ejemplo n.º 25
0
    def test_raising_translation_unavailable(self):
        """Test ErrorReportingUtility.raising() with a TranslationUnavailable
        exception.

        An OOPS is not recorded when a TranslationUnavailable exception is
        raised.
        """
        utility = ErrorReportingUtility()
        utility._oops_config.publisher = None
        self.assertTrue(
            TranslationUnavailable.__name__ in utility._ignored_exceptions,
            'TranslationUnavailable is not in _ignored_exceptions.')
        try:
            raise TranslationUnavailable('xyz')
        except TranslationUnavailable:
            self.assertEqual(None, utility.raising(sys.exc_info()))
Ejemplo n.º 26
0
    def test_raising_with_unprintable_exception(self):
        class UnprintableException(Exception):
            def __str__(self):
                raise RuntimeError('arrgh')

            __repr__ = __str__

        utility = ErrorReportingUtility()
        utility._oops_config.publisher = None
        try:
            raise UnprintableException()
        except UnprintableException:
            report = utility.raising(sys.exc_info())

        unprintable = '<unprintable UnprintableException object>'
        self.assertEqual(unprintable, report['value'])
        self.assertIn('UnprintableException: ' + unprintable,
                      report['tb_text'])
Ejemplo n.º 27
0
def report_oops(file_alias_url=None, error_msg=None):
    """Record an OOPS for the current exception and return the OOPS ID."""
    info = sys.exc_info()
    properties = []
    if file_alias_url is not None:
        properties.append(('Sent message', file_alias_url))
    if error_msg is not None:
        properties.append(('Error message', error_msg))
    request = ScriptRequest(properties)
    request.principal = get_current_principal()
    errorUtility = ErrorReportingUtility()
    # Report all exceptions: the mail handling code doesn't expect any in
    # normal operation.
    errorUtility._ignored_exceptions = set()
    report = errorUtility.raising(info, request)
    # Note that this assert is arguably bogus: raising is permitted to filter
    # reports.
    assert report is not None, ('No OOPS generated.')
    return report['id']
Ejemplo n.º 28
0
def log_exception(message, *args):
    """Write the current exception traceback into the Mailman log file.

    This is really just a convenience function for a refactored chunk of
    common code.

    :param message: The message to appear in the xmlrpc and error logs. It
        may be a format string.
    :param args: Optional arguments to be interpolated into a format string.
    """
    error_utility = ErrorReportingUtility()
    error_utility.configure(section_name='mailman')
    error_utility.raising(sys.exc_info())
    out_file = StringIO()
    traceback.print_exc(file=out_file)
    traceback_text = out_file.getvalue()
    syslog('xmlrpc', message, *args)
    syslog('error', message, *args)
    syslog('error', traceback_text)
Ejemplo n.º 29
0
    def test_raising_with_request(self):
        """Test ErrorReportingUtility.raising() with a request"""
        utility = ErrorReportingUtility()
        utility._main_publishers[0].__call__ = lambda report: []

        request = TestRequestWithPrincipal(environ={
            'SERVER_URL': 'http://localhost:9000/foo',
            'HTTP_COOKIE': 'lp=cookies_hidden_for_security_reasons',
            'name1': 'value1',
        },
                                           form={
                                               'name1': 'value3 \xa7',
                                               'name2': 'value2',
                                               u'\N{BLACK SQUARE}': u'value4',
                                           })
        request.setInWSGIEnvironment('launchpad.pageid', 'IFoo:+foo-template')

        try:
            raise ArbitraryException('xyz\nabc')
        except ArbitraryException:
            report = utility.raising(sys.exc_info(), request)

        # topic is obtained from the request
        self.assertEqual('IFoo:+foo-template', report['topic'])
        self.assertEqual(u'Login, 42, title, description |\u25a0|',
                         report['username'])
        self.assertEqual('http://localhost:9000/foo', report['url'])
        self.assertEqual(
            {
                'CONTENT_LENGTH': '0',
                'GATEWAY_INTERFACE': 'TestFooInterface/1.0',
                'HTTP_COOKIE': '<hidden>',
                'HTTP_HOST': '127.0.0.1',
                'SERVER_URL': 'http://localhost:9000/foo',
                u'\u25a0': 'value4',
                'lp': '<hidden>',
                'name1': 'value3 \xa7',
                'name2': 'value2',
            }, report['req_vars'])
        # verify that the oopsid was set on the request
        self.assertEqual(request.oopsid, report['id'])
        self.assertEqual(request.oops, report)
Ejemplo n.º 30
0
    def test_raising_with_string_as_traceback(self):
        # ErrorReportingUtility.raising() can be called with a string in the
        # place of a traceback. This is useful when the original traceback
        # object is unavailable - e.g. when logging a failure reported by a
        # non-oops-enabled service.

        try:
            raise RuntimeError('hello')
        except RuntimeError:
            exc_type, exc_value, exc_tb = sys.exc_info()
            # Turn the traceback into a string. When the traceback itself
            # cannot be passed to ErrorReportingUtility.raising, a string like
            # one generated by format_exc is sometimes passed instead.
            exc_tb = traceback.format_exc()

        utility = ErrorReportingUtility()
        utility._oops_config.publisher = None
        report = utility.raising((exc_type, exc_value, exc_tb))
        # traceback is what we supplied.
        self.assertEqual(exc_tb, report['tb_text'])