예제 #1
0
    def processDeathRow(self, archive):
        """Process death-row for the given archive.

        It handles the current DB transaction according to the results of
        the operation just executed, i.e, commits successful runs and aborts
        runs with errors. It also respects 'dry-run' command-line option.
        """
        death_row = getDeathRow(archive, self.logger, self.options.pool_root)
        self.logger.debug("Unpublishing death row for %s." %
                          archive.displayname)
        set_request_started(request_statements=LimitedList(10000),
                            txn=self.txn,
                            enable_timeout=False)
        try:
            death_row.reap(self.options.dry_run)
        except Exception:
            self.logger.exception(
                "Unexpected exception while doing death-row unpublish")
            self.txn.abort()
        else:
            if self.options.dry_run:
                self.logger.info("Dry run mode; rolling back.")
                self.txn.abort()
            else:
                self.logger.debug("Committing")
                self.txn.commit()
        finally:
            clear_request_started()
예제 #2
0
    def main(self):
        """See `LaunchpadScript`."""
        self.validateOptions()
        self.logOptions()

        for distribution in self.findDistros():
            allowed_suites = self.findAllowedSuites(distribution)
            for archive in self.getTargetArchives(distribution):
                set_request_started(
                    request_statements=LimitedList(10000),
                    txn=self.txn, enable_timeout=False)
                try:
                    if archive.status == ArchiveStatus.DELETING:
                        publisher = self.getPublisher(
                            distribution, archive, allowed_suites)
                        work_done = self.deleteArchive(archive, publisher)
                    elif archive.can_be_published:
                        publisher = self.getPublisher(
                            distribution, archive, allowed_suites)
                        self.publishArchive(archive, publisher)
                        work_done = True
                    else:
                        work_done = False
                finally:
                    clear_request_started()

                if work_done:
                    self.txn.commit()

        self.logger.debug("Ciao")
예제 #3
0
 def test_reduced_timeout(self):
     """reduced_timeout caps the available timeout in various ways."""
     self.addCleanup(set_default_timeout_function, None)
     with reduced_timeout(1.0):
         self.assertIsNone(get_default_timeout_function()())
     with reduced_timeout(1.0, default=5.0):
         self.assertEqual(5.0, get_default_timeout_function()())
     set_default_timeout_function(lambda: 5.0)
     with reduced_timeout(1.0):
         self.assertEqual(4.0, get_default_timeout_function()())
     with reduced_timeout(1.0, default=5.0, webapp_max=2.0):
         self.assertEqual(4.0, get_default_timeout_function()())
     with reduced_timeout(1.0, default=5.0, webapp_max=6.0):
         self.assertEqual(4.0, get_default_timeout_function()())
     with reduced_timeout(6.0, default=5.0, webapp_max=2.0):
         self.assertEqual(5.0, get_default_timeout_function()())
     LaunchpadTestRequest()
     set_request_started()
     try:
         with reduced_timeout(1.0):
             self.assertEqual(4.0, get_default_timeout_function()())
         with reduced_timeout(1.0, default=5.0, webapp_max=2.0):
             self.assertEqual(2.0, get_default_timeout_function()())
         with reduced_timeout(1.0, default=5.0, webapp_max=6.0):
             self.assertEqual(4.0, get_default_timeout_function()())
         with reduced_timeout(6.0, default=5.0, webapp_max=2.0):
             self.assertEqual(2.0, get_default_timeout_function()())
     finally:
         clear_request_started()
예제 #4
0
    def processForDistro(self, distribution):
        """Process all queue items for a distribution.

        Commits between items.

        :param distribution: The `Distribution` to process queue items for.
        :return: A list of all successfully processed items' ids.
        """
        processed_queue_ids = []
        for archive in self.getTargetArchives(distribution):
            set_request_started(request_statements=LimitedList(10000),
                                txn=self.txn,
                                enable_timeout=False)
            try:
                for distroseries in distribution.series:

                    self.logger.debug("Processing queue for %s %s" %
                                      (archive.reference, distroseries.name))

                    queue_items = distroseries.getPackageUploads(
                        status=PackageUploadStatus.ACCEPTED, archive=archive)
                    for queue_item in queue_items:
                        if self.processQueueItem(queue_item):
                            processed_queue_ids.append(queue_item.id)
                        # Commit even on error; we may have altered the
                        # on-disk archive, so the partial state must
                        # make it to the DB.
                        self.txn.commit()
                        close_bugs_for_queue_item(queue_item)
                        self.txn.commit()
            finally:
                clear_request_started()
        return processed_queue_ids
예제 #5
0
    def rewriteLine(self, resource_location):
        """Rewrite 'resource_location' to a more concrete location.

        We use the 'translatePath' BranchFileSystemClient method.  There are
        three cases:

         (1) The request is for something within the .bzr directory of a
             branch.

             In this case we rewrite the request to the location from which
             branches are served by ID.

         (2) The request is for something within a branch, but not the .bzr
             directory.

             In this case, we hand the request off to codebrowse.

         (3) The branch is not found.  Two sub-cases: the request is for a
             product control directory or the we don't know how to translate
             the path.

             In both these cases we return 'NULL' which indicates to Apache
             that we don't know how to rewrite the request (and so it should
             go on to generate a 404 response).

        Other errors are allowed to propagate, on the assumption that the
        caller will catch and log them.
        """
        # Codebrowse generates references to its images and stylesheets
        # starting with "/static", so pass them on unthinkingly.
        T = time.time()
        # Tell the webapp adapter that we are in a request, so that DB
        # statement timeouts will be applied.
        set_request_started()
        try:
            cached = None
            if resource_location.startswith('/static/'):
                r = self._codebrowse_url(resource_location)
                cached = 'N/A'
            else:
                branch_id, trailing, cached = self._getBranchIdAndTrailingPath(
                    resource_location)
                if branch_id is None:
                    if resource_location.startswith('/' +
                                                    BRANCH_ID_ALIAS_PREFIX):
                        r = 'NULL'
                    else:
                        r = self._codebrowse_url(resource_location)
                else:
                    if trailing.startswith('/.bzr'):
                        r = urlutils.join(
                            config.codehosting.internal_branch_by_id_root,
                            branch_id_to_path(branch_id), trailing[1:])
                    else:
                        r = self._codebrowse_url(resource_location)
        finally:
            clear_request_started()
        self.logger.info("%r -> %r (%fs, cache: %s)", resource_location, r,
                         time.time() - T, cached)
        return r
예제 #6
0
    def endRequest(self, request, object):
        superclass = zope.app.publication.browser.BrowserPublication
        superclass.endRequest(self, request, object)

        da.clear_request_started()

        getUtility(IOpenLaunchBag).clear()

        # Maintain operational statistics.
        if getattr(request, '_wants_retry', False):
            OpStats.stats['retries'] += 1
        else:
            OpStats.stats['requests'] += 1

            # Increment counters for HTTP status codes we track individually
            # NB. We use IBrowserRequest, as other request types such as
            # IXMLRPCRequest use IHTTPRequest as a superclass.
            # This should be fine as Launchpad only deals with browser
            # and XML-RPC requests.
            if IBrowserRequest.providedBy(request):
                OpStats.stats['http requests'] += 1
                status = request.response.getStatus()
                if status == 404:  # Not Found
                    OpStats.stats['404s'] += 1
                elif status == 500:  # Unhandled exceptions
                    OpStats.stats['500s'] += 1
                elif status == 503:  # Timeouts
                    OpStats.stats['503s'] += 1

                # Increment counters for status code groups.
                status_group = str(status)[0] + 'XXs'
                OpStats.stats[status_group] += 1

                # Increment counter for 5XXs_b.
                if is_browser(request) and status_group == '5XXs':
                    OpStats.stats['5XXs_b'] += 1

        # Make sure our databases are in a sane state for the next request.
        thread_name = threading.currentThread().getName()
        for name, store in getUtility(IZStorm).iterstores():
            try:
                assert store._connection._state != STATE_DISCONNECTED, (
                    "Bug #504291: Store left in a disconnected state.")
            except AssertionError:
                # The Store is in a disconnected state. This should
                # not happen, as store.rollback() should have been called
                # by now. Log an OOPS so we know about this. This
                # is Bug #504291 happening.
                getUtility(IErrorReportingUtility).raising(
                    sys.exc_info(), request)
                # Repair things so the server can remain operational.
                store.rollback()
            # Reset all Storm stores when not running the test suite.
            # We could reset them when running the test suite but
            # that'd make writing tests a much more painful task. We
            # still reset the slave stores though to minimize stale
            # cache issues.
            if thread_name != 'MainThread' or name.endswith('-slave'):
                store.reset()
예제 #7
0
    def endRequest(self, request, object):
        superclass = zope.app.publication.browser.BrowserPublication
        superclass.endRequest(self, request, object)

        da.clear_request_started()

        getUtility(IOpenLaunchBag).clear()

        # Maintain operational statistics.
        if getattr(request, '_wants_retry', False):
            OpStats.stats['retries'] += 1
        else:
            OpStats.stats['requests'] += 1

            # Increment counters for HTTP status codes we track individually
            # NB. We use IBrowserRequest, as other request types such as
            # IXMLRPCRequest use IHTTPRequest as a superclass.
            # This should be fine as Launchpad only deals with browser
            # and XML-RPC requests.
            if IBrowserRequest.providedBy(request):
                OpStats.stats['http requests'] += 1
                status = request.response.getStatus()
                if status == 404:  # Not Found
                    OpStats.stats['404s'] += 1
                elif status == 500:  # Unhandled exceptions
                    OpStats.stats['500s'] += 1
                elif status == 503:  # Timeouts
                    OpStats.stats['503s'] += 1

                # Increment counters for status code groups.
                status_group = str(status)[0] + 'XXs'
                OpStats.stats[status_group] += 1

                # Increment counter for 5XXs_b.
                if is_browser(request) and status_group == '5XXs':
                    OpStats.stats['5XXs_b'] += 1

        # Make sure our databases are in a sane state for the next request.
        thread_name = threading.currentThread().getName()
        for name, store in getUtility(IZStorm).iterstores():
            try:
                assert store._connection._state != STATE_DISCONNECTED, (
                    "Bug #504291: Store left in a disconnected state.")
            except AssertionError:
                # The Store is in a disconnected state. This should
                # not happen, as store.rollback() should have been called
                # by now. Log an OOPS so we know about this. This
                # is Bug #504291 happening.
                getUtility(IErrorReportingUtility).raising(
                    sys.exc_info(), request)
                # Repair things so the server can remain operational.
                store.rollback()
            # Reset all Storm stores when not running the test suite.
            # We could reset them when running the test suite but
            # that'd make writing tests a much more painful task. We
            # still reset the slave stores though to minimize stale
            # cache issues.
            if thread_name != 'MainThread' or name.endswith('-slave'):
                store.reset()
예제 #8
0
 def runJobHandleError(self, job, fallback=None):
     set_request_started(enable_timeout=False,
                         detail_filter=job.timeline_detail_filter)
     try:
         return super(BaseJobRunner,
                      self).runJobHandleError(job, fallback=fallback)
     finally:
         clear_request_started()
예제 #9
0
    def test_disconnect_logs_oops(self):
        # Ensure that OOPS reports are generated for database
        # disconnections, as per Bug #373837.
        request = LaunchpadTestRequest()
        publication = WebServicePublication(None)
        dbadapter.set_request_started()
        try:
            raise DisconnectionError('Fake')
        except DisconnectionError:
            self.assertRaises(Retry, publication.handleException, None,
                              request, sys.exc_info(), True)
        dbadapter.clear_request_started()
        self.assertEqual(1, len(self.oopses))
        oops = self.oopses[0]

        # Ensure the OOPS mentions the correct exception
        self.assertEqual(oops['type'], "DisconnectionError")
예제 #10
0
 def endRequest(self, path='/', exception=None, pageid=None, work=None):
     start_event = self._get_start_event(path)
     da.set_request_started()
     profile.start_request(start_event)
     request = start_event.request
     if pageid is not None:
         request.setInWSGIEnvironment('launchpad.pageid', pageid)
     if work is not None:
         work()
     request.response.setResult(EXAMPLE_HTML)
     context = object()
     event = EndRequestEvent(context, request)
     if exception is not None:
         self.eru.raising((type(exception), exception, None), event.request)
     profile.end_request(event)
     da.clear_request_started()
     return event.request
예제 #11
0
 def endRequest(self, path='/', exception=None, pageid=None, work=None):
     start_event = self._get_start_event(path)
     da.set_request_started()
     profile.start_request(start_event)
     request = start_event.request
     if pageid is not None:
         request.setInWSGIEnvironment('launchpad.pageid', pageid)
     if work is not None:
         work()
     request.response.setResult(EXAMPLE_HTML)
     context = object()
     event = EndRequestEvent(context, request)
     if exception is not None:
         self.eru.raising(
             (type(exception), exception, None), event.request)
     profile.end_request(event)
     da.clear_request_started()
     return event.request
예제 #12
0
    def test_disconnect_logs_oops(self):
        # Ensure that OOPS reports are generated for database
        # disconnections, as per Bug #373837.
        request = LaunchpadTestRequest()
        publication = WebServicePublication(None)
        dbadapter.set_request_started()
        try:
            raise DisconnectionError('Fake')
        except DisconnectionError:
            self.assertRaises(
                Retry,
                publication.handleException,
                None, request, sys.exc_info(), True)
        dbadapter.clear_request_started()
        self.assertEqual(1, len(self.oopses))
        oops = self.oopses[0]

        # Ensure the OOPS mentions the correct exception
        self.assertEqual(oops['type'], "DisconnectionError")
예제 #13
0
    def beginErrorHandlingTransaction(self, request, ob, note):
        """Hook for when a new view is started to handle an exception.

        We need to add an additional behavior to the usual Zope behavior.
        We must restart the request timer.  Otherwise we can get OOPS errors
        from our exception views inappropriately.
        """
        super(LaunchpadBrowserPublication,
              self).beginErrorHandlingTransaction(request, ob, note)
        # XXX: gary 2008-11-04 bug=293614: As the bug describes, we want to
        # only clear the SQL records and timeout when we are preparing for a
        # view (or a side effect). Otherwise, we don't want to clear the
        # records because they are what the error reporting utility uses to
        # create OOPS reports with the SQL commands that led up to the error.
        # At the moment, we can only distinguish based on the "note" argument:
        # an undocumented argument of this undocumented method.
        if note in ('application error-handling',
                    'application error-handling side-effect'):
            da.clear_request_started()
            da.set_request_started()
예제 #14
0
    def beginErrorHandlingTransaction(self, request, ob, note):
        """Hook for when a new view is started to handle an exception.

        We need to add an additional behavior to the usual Zope behavior.
        We must restart the request timer.  Otherwise we can get OOPS errors
        from our exception views inappropriately.
        """
        super(LaunchpadBrowserPublication,
              self).beginErrorHandlingTransaction(request, ob, note)
        # XXX: gary 2008-11-04 bug=293614: As the bug describes, we want to
        # only clear the SQL records and timeout when we are preparing for a
        # view (or a side effect). Otherwise, we don't want to clear the
        # records because they are what the error reporting utility uses to
        # create OOPS reports with the SQL commands that led up to the error.
        # At the moment, we can only distinguish based on the "note" argument:
        # an undocumented argument of this undocumented method.
        if note in ('application error-handling',
                    'application error-handling side-effect'):
            da.clear_request_started()
            da.set_request_started()
예제 #15
0
    def test_timeout(self):
        # The time permitted to get the diff from the librarian may be None,
        # or 2. If there is not 2 seconds left in the request, the number will
        # be 0.01 smaller or the actual remaining time.
        class DiffWithFakeText(Diff):
            diff_text = FakeMethod()

        diff = DiffWithFakeText()
        diff.diff_text.open = FakeMethod()
        diff.diff_text.read = FakeMethod()
        diff.diff_text.close = FakeMethod()
        value = None
        original_timeout_function = get_default_timeout_function()
        set_default_timeout_function(lambda: value)
        try:
            LaunchpadTestRequest()
            set_request_started()
            try:
                diff.text
                self.assertEqual(
                    LIBRARIAN_SERVER_DEFAULT_TIMEOUT,
                    diff.diff_text.open.calls[-1][0][0])
                value = 3.1
                diff.text
                self.assertEqual(2.0, diff.diff_text.open.calls[-1][0][0])
                value = 1.11
                diff.text
                self.assertEqual(1.1, diff.diff_text.open.calls[-1][0][0])
                value = 0.11
                diff.text
                self.assertEqual(0.1, diff.diff_text.open.calls[-1][0][0])
                value = 0.01
                diff.text
                self.assertEqual(0.01, diff.diff_text.open.calls[-1][0][0])
            finally:
                clear_request_started()
        finally:
            set_default_timeout_function(original_timeout_function)
예제 #16
0
 def _statement_logging_stop(self):
     """Stop logging SQL statements."""
     clear_request_started()
예제 #17
0
 def _statement_logging_stop(self):
     """Stop logging SQL statements."""
     clear_request_started()
예제 #18
0
    def rewriteLine(self, resource_location):
        """Rewrite 'resource_location' to a more concrete location.

        We use the 'translatePath' BranchFileSystemClient method.  There are
        three cases:

         (1) The request is for something within the .bzr directory of a
             branch.

             In this case we rewrite the request to the location from which
             branches are served by ID.

         (2) The request is for something within a branch, but not the .bzr
             directory.

             In this case, we hand the request off to codebrowse.

         (3) The branch is not found.  Two sub-cases: the request is for a
             product control directory or the we don't know how to translate
             the path.

             In both these cases we return 'NULL' which indicates to Apache
             that we don't know how to rewrite the request (and so it should
             go on to generate a 404 response).

        Other errors are allowed to propagate, on the assumption that the
        caller will catch and log them.
        """
        # Codebrowse generates references to its images and stylesheets
        # starting with "/static", so pass them on unthinkingly.
        T = time.time()
        # Tell the webapp adapter that we are in a request, so that DB
        # statement timeouts will be applied.
        set_request_started()
        try:
            cached = None
            if resource_location.startswith('/static/'):
                r = self._codebrowse_url(resource_location)
                cached = 'N/A'
            else:
                branch_id, trailing, cached = self._getBranchIdAndTrailingPath(
                    resource_location)
                if branch_id is None:
                    if resource_location.startswith(
                            '/' + BRANCH_ID_ALIAS_PREFIX):
                        r = 'NULL'
                    else:
                        r = self._codebrowse_url(resource_location)
                else:
                    if trailing.startswith('/.bzr'):
                        r = urlutils.join(
                            config.codehosting.internal_branch_by_id_root,
                            branch_id_to_path(branch_id), trailing[1:])
                    else:
                        r = self._codebrowse_url(resource_location)
        finally:
            clear_request_started()
        self.logger.info(
            "%r -> %r (%fs, cache: %s)",
            resource_location, r, time.time() - T, cached)
        return r