Пример #1
0
    def is_approved(self, review_request, prev_approved, prev_failure):
        """Check if a review request is approved to land

        We will completely override the checks done by Review Board and
        provide our own (to keep approval simpler and explicit).

        If True is returned by this function it will indicate that
        review request may be autolanded - care should be taken
        when modifying the logic.
        """
        # TODO: We should consider rejecting review requests which
        # currently have a draft (to prevent autolanding incorrect
        # things)
        try:
            if not is_pushed(review_request):
                return False, 'Manually uploaded requests cannot be approved.'

            if not review_request.public:
                return False, 'The review request is not public.'

            if is_parent(review_request):
                return self.is_approved_parent(review_request)

            return self.is_approved_child(review_request)
        except Exception as e:
            # We catch all exceptions because any error will make
            # Review Board revert to it's default behaviour which
            # is much more relaxed than ours.
            logger.error('Failed to calculate approval for review '
                         'request %s: %s' % (review_request.id, e))
            return False, "Error when calculating approval."
Пример #2
0
    def is_approved(self, review_request, prev_approved, prev_failure):
        """Check if a review request is approved to land

        We will completely override the checks done by Review Board and
        provide our own (to keep approval simpler and explicit).

        If True is returned by this function it will indicate that
        review request may be autolanded - care should be taken
        when modifying the logic.
        """
        # TODO: We should consider rejecting review requests which
        # currently have a draft (to prevent autolanding incorrect
        # things)
        try:
            if not is_pushed(review_request):
                return False, 'Manually uploaded requests cannot be approved.'

            if not review_request.public:
                return False, 'The review request is not public.'

            if is_parent(review_request):
                return self.is_approved_parent(review_request)

            return self.is_approved_child(review_request)
        except Exception as e:
            # We catch all exceptions because any error will make
            # Review Board revert to it's default behaviour which
            # is much more relaxed than ours.
            logger.error('Failed to calculate approval for review '
                         'request %s: %s' % (review_request.id, e))
            return False, "Error when calculating approval."
Пример #3
0
    def on_draft_changed(self, sender, **kwargs):
        instance = kwargs["instance"]
        rr = instance.get_review_request()

        if is_pushed(instance) and not is_parent(rr):
            parent_rr = get_parent_rr(rr)
            parent_rr_draft = parent_rr.get_draft()

            if parent_rr_draft is None:
                parent_rr_draft = ReviewRequestDraft.create(parent_rr)

            update_parent_rr_reviewers(parent_rr_draft)
def on_review_publishing(user, review, **kwargs):
    """Comment in the bug and potentially r+ or clear a review flag.

    Note that a reviewer *must* have editbugs to set an attachment flag on
    someone else's attachment (i.e. the standard BMO review process).

    TODO: Report lack-of-editbugs properly; see bug 1119065.
    """
    review_request = review.review_request
    logger.info('Publishing review for user: %s review id: %s '
                'review request id: %s' % (user, review.id,
                                            review_request.id))

    # skip review requests that were not pushed
    if not is_pushed(review_request):
        logger.info('Did not publish review: %s: for user: %d: review not '
                    'pushed.' % (user, review.id))
        return

    site = Site.objects.get_current()
    siteconfig = SiteConfiguration.objects.get_current()
    comment = build_plaintext_review(review,
                                     get_obj_url(review, site,
                                                 siteconfig),
                                     {"user": user})
    b = Bugzilla(get_bugzilla_api_key(user))

    # TODO: Update all attachments in one call.  This is not possible right
    # now because we have to potentially mix changing and creating flags.

    if is_parent(review_request):
        # Mirror the comment to the bug, unless it's a ship-it, in which
        # case throw an error.  Ship-its are allowed only on child commits.
        if review.ship_it:
            raise ParentShipItError

        [b.post_comment(int(bug_id), comment) for bug_id in
         review_request.get_bug_list()]
    else:
        diff_url = '%sdiff/#index_header' % get_obj_url(review_request)
        bug_id = int(review_request.get_bug_list()[0])

        if review.ship_it:
            commented = b.r_plus_attachment(bug_id, review.user.email,
                                            diff_url, comment)
        else:
            commented = b.cancel_review_request(bug_id, review.user.email,
                                                diff_url, comment)

        if comment and not commented:
            b.post_comment(bug_id, comment)
Пример #5
0
def on_reply_publishing(user, reply, **kwargs):
    review_request = reply.review_request
    logger.info('Posting bugzilla reply for review request %s' %
                (review_request.id))

    # skip review requests that were not pushed
    if not is_pushed(review_request):
        return

    bug_id = int(review_request.get_bug_list()[0])
    b = Bugzilla(get_bugzilla_api_key(user))

    url = get_reply_url(reply)
    comment = build_plaintext_review(reply, url, {"user": user})
    b.post_comment(bug_id, comment, get_diff_url(review_request), True)
def on_reply_publishing(user, reply, **kwargs):
    review_request = reply.review_request
    logger.info('Posting bugzilla reply for review request %s' % (
                review_request.id))

    # skip review requests that were not pushed
    if not is_pushed(review_request):
        return

    bug_id = int(review_request.get_bug_list()[0])
    b = Bugzilla(get_bugzilla_api_key(user))

    url = get_reply_url(reply)
    comment = build_plaintext_review(reply, url, {"user": user})
    b.post_comment(bug_id, comment)
Пример #7
0
def ensure_parent_draft(draft):
    """Ensure parent draft exists when child has a draft.

    This is intended to be called in the post_save signal for the
    ReviewRequestDraft model and ensure the parent review request
    has a draft if a child draft is saved. We need to do this so
    that the parent may always be published when a child requires
    publishing.

    Particularly we update our own reviewer information in the
    parent to make sure that a reviewer change on a child request
    will create a parent draft - even if the reviewer change does
    not alter the overall set of reviewers for the series.
    """
    rr = draft.get_review_request()

    if is_pushed(draft) and not is_parent(rr):
        parent_rr = get_parent_rr(rr)
        parent_rr_draft = parent_rr.get_draft()

        if parent_rr_draft is None:
            parent_rr_draft = ReviewRequestDraft.create(parent_rr)

        update_parent_rr_reviewers(parent_rr_draft)
def ensure_parent_draft(draft):
    """Ensure parent draft exists when child has a draft.

    This is intended to be called in the post_save signal for the
    ReviewRequestDraft model and ensure the parent review request
    has a draft if a child draft is saved. We need to do this so
    that the parent may always be published when a child requires
    publishing.

    Particularly we update our own reviewer information in the
    parent to make sure that a reviewer change on a child request
    will create a parent draft - even if the reviewer change does
    not alter the overall set of reviewers for the series.
    """
    rr = draft.get_review_request()

    if is_pushed(draft) and not is_parent(rr):
        parent_rr = get_parent_rr(rr)
        parent_rr_draft = parent_rr.get_draft()

        if parent_rr_draft is None:
            parent_rr_draft = ReviewRequestDraft.create(parent_rr)

        update_parent_rr_reviewers(parent_rr_draft)
Пример #9
0
def isPush(review_request):
    return is_pushed(review_request)
Пример #10
0
    def create(self, request, review_request_id, try_syntax, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        if not try_syntax.startswith("try: "):
            return INVALID_FORM_DATA, {"fields": {"try_syntax": ["The provided try syntax was invalid"]}}

        commit_data = fetch_commit_data(rr)

        if not is_pushed(rr, commit_data) or not is_parent(rr, commit_data):
            logger.error(
                "Failed triggering Autoland because the review "
                "request is not pushed, or not the parent review "
                "request."
            )
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        target_repository = rr.repository.extra_data.get("try_repository_url")

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message("Autoland has not been configured with a proper try URL.")

        last_revision = json.loads(commit_data.extra_data.get(COMMITS_KEY))[-1][0]

        ext = get_extension_manager().get_enabled_extension("mozreview.extension.MozReviewExtension")

        logger.info(
            "Submitting a request to Autoland for review request "
            "ID %s for revision %s destination try" % (review_request_id, last_revision)
        )

        autoland_url = ext.get_settings("autoland_url")
        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.get_settings("autoland_user")
        autoland_password = ext.get_settings("autoland_password")

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        lock_id = get_autoland_lock_id(rr.id, target_repository, last_revision)
        if not acquire_lock(lock_id):
            return AUTOLAND_REQUEST_IN_PROGRESS

        try:
            # We use a hard-coded destination here. If we ever open this up
            # to make the destination a parameter to this resource, we need to
            # verify that the destination is in fact an "scm_level_1"
            # repository to ensure that people don't try to land to inbound
            # using this resource.
            response = requests.post(
                autoland_url + "/autoland",
                data=json.dumps(
                    {
                        "ldap_username": request.mozreview_profile.ldap_username,
                        "tree": rr.repository.name,
                        "pingback_url": pingback_url,
                        "rev": last_revision,
                        "destination": TRY_AUTOLAND_DESTINATION,
                        "trysyntax": try_syntax,
                    }
                ),
                headers={"content-type": "application/json"},
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password),
            )
        except requests.exceptions.RequestException:
            logger.error("We hit a RequestException when submitting a " "request to Autoland")
            release_lock(lock_id)
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logger.error("We timed out when submitting a request to " "Autoland")
            release_lock(lock_id)
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            release_lock(lock_id)
            return AUTOLAND_ERROR, {"status_code": response.status_code, "message": response.json().get("error")}

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get("request_id", 0))
        finally:
            if autoland_request_id is None:
                release_lock(lock_id)
                return AUTOLAND_ERROR, {"status_code": response.status_code, "request_id": None}

        autoland_request = AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            repository_url=target_repository,
            review_request_id=rr.id,
            user_id=request.user.id,
            extra_data=json.dumps({"try_syntax": try_syntax}),
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED, autoland_request_id=autoland_request_id
        )

        self.save_autolandrequest_id("p2rb.autoland_try", rr, autoland_request_id)

        return 200, {}
Пример #11
0
    def create(self, request, review_request_id, commit_descriptions, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        commit_data = fetch_commit_data(rr)

        if not is_pushed(rr, commit_data) or not is_parent(rr, commit_data):
            logger.error(
                "Failed triggering Autoland because the review "
                "request is not pushed, or not the parent review "
                "request."
            )
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        target_repository = rr.repository.extra_data.get("landing_repository_url")
        push_bookmark = rr.repository.extra_data.get("landing_bookmark")

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                "Autoland has not been configured with a proper landing URL."
            )

        last_revision = json.loads(commit_data.extra_data.get(COMMITS_KEY))[-1][0]

        ext = get_extension_manager().get_enabled_extension("mozreview.extension.MozReviewExtension")

        logger.info(
            "Submitting a request to Autoland for review request "
            "ID %s for revision %s destination %s" % (review_request_id, last_revision, target_repository)
        )

        autoland_url = ext.get_settings("autoland_url")
        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.get_settings("autoland_user")
        autoland_password = ext.get_settings("autoland_password")

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        lock_id = get_autoland_lock_id(rr.id, target_repository, last_revision)
        if not acquire_lock(lock_id):
            return AUTOLAND_REQUEST_IN_PROGRESS
        try:
            response = requests.post(
                autoland_url + "/autoland",
                data=json.dumps(
                    {
                        "ldap_username": request.mozreview_profile.ldap_username,
                        "tree": rr.repository.name,
                        "pingback_url": pingback_url,
                        "rev": last_revision,
                        "destination": target_repository,
                        "push_bookmark": push_bookmark,
                        "commit_descriptions": json.loads(commit_descriptions),
                    }
                ),
                headers={"content-type": "application/json"},
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password),
            )
        except requests.exceptions.RequestException:
            logger.error("We hit a RequestException when submitting a " "request to Autoland")
            release_lock(lock_id)
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logger.error("We timed out when submitting a request to " "Autoland")
            release_lock(lock_id)
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            release_lock(lock_id)
            return AUTOLAND_ERROR, {"status_code": response.status_code, "message": response.json().get("error")}

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get("request_id", 0))
        finally:
            if autoland_request_id is None:
                release_lock(lock_id)
                return AUTOLAND_ERROR, {"status_code": response.status_code, "request_id": None}

        autoland_request = AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            repository_url=target_repository,
            review_request_id=rr.id,
            user_id=request.user.id,
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED, autoland_request_id=autoland_request_id
        )

        self.save_autolandrequest_id("p2rb.autoland", rr, autoland_request_id)

        return 200, {}
Пример #12
0
    def create(self, request, review_request_id, try_syntax, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        if not try_syntax.startswith('try: '):
            return INVALID_FORM_DATA, {
                'fields': {
                    'try_syntax': ['The provided try syntax was invalid']
                }
            }

        commit_data = fetch_commit_data(rr)

        if not is_pushed(rr, commit_data) or not is_parent(rr, commit_data):
            logger.error('Failed triggering Autoland because the review '
                         'request is not pushed, or not the parent review '
                         'request.')
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        enabled = rr.repository.extra_data.get('autolanding_to_try_enabled')

        if not enabled:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autolanding to try not enabled.')

        target_repository = rr.repository.extra_data.get('try_repository_url')

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autoland has not been configured with a proper try URL.')

        last_revision = json.loads(
            commit_data.extra_data.get(COMMITS_KEY))[-1][0]
        ext = get_extension_manager().get_enabled_extension(
            'mozreview.extension.MozReviewExtension')

        logger.info('Submitting a request to Autoland for review request '
                    'ID %s for revision %s destination try' %
                    (review_request_id, last_revision))

        autoland_url = ext.get_settings('autoland_url')

        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.get_settings('autoland_user')
        autoland_password = ext.get_settings('autoland_password')

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        lock_id = get_autoland_lock_id(rr.id, target_repository, last_revision)

        if not acquire_lock(lock_id):
            return AUTOLAND_REQUEST_IN_PROGRESS

        try:
            # We use a hard-coded destination here. If we ever open this up
            # to make the destination a parameter to this resource, we need to
            # verify that the destination is in fact an "scm_level_1"
            # repository to ensure that people don't try to land to inbound
            # using this resource.
            response = requests.post(
                autoland_url + '/autoland',
                data=json.dumps({
                    'ldap_username': request.mozreview_profile.ldap_username,
                    'tree': rr.repository.name,
                    'pingback_url': pingback_url,
                    'rev': last_revision,
                    'destination': TRY_AUTOLAND_DESTINATION,
                    'trysyntax': try_syntax,
                }),
                headers={
                    'content-type': 'application/json',
                },
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password))
        except requests.exceptions.RequestException:
            logger.error('We hit a RequestException when submitting a '
                         'request to Autoland')
            release_lock(lock_id)
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logger.error('We timed out when submitting a request to '
                         'Autoland')
            release_lock(lock_id)
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            release_lock(lock_id)
            return AUTOLAND_ERROR, {
                'status_code': response.status_code,
                'message': response.json().get('error'),
            }

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get('request_id', 0))
        finally:
            if autoland_request_id is None:
                release_lock(lock_id)
                return AUTOLAND_ERROR, {
                    'status_code': response.status_code,
                    'request_id': None,
                }

        AutolandRequest.objects.create(autoland_id=autoland_request_id,
                                       push_revision=last_revision,
                                       repository_url=target_repository,
                                       review_request_id=rr.id,
                                       user_id=request.user.id,
                                       extra_data=json.dumps(
                                           {'try_syntax': try_syntax}))

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED,
            autoland_request_id=autoland_request_id)

        self.save_autolandrequest_id('p2rb.autoland_try', rr,
                                     autoland_request_id)

        return 200, {}
Пример #13
0
    def create(self, request, review_request_id, try_syntax, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        if not try_syntax.startswith('try: '):
            return INVALID_FORM_DATA, {
                'fields': {
                    'try_syntax': ['The provided try syntax was invalid']
                }
            }

        if not is_pushed(rr) or not is_parent(rr):
            logging.error('Failed triggering Autoland because the review '
                          'request is not pushed, or not the parent review '
                          'request.')
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        if not rr.is_mutable_by(request.user):
            return PERMISSION_DENIED

        target_repository = rr.repository.extra_data.get(
            'try_repository_url')

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autoland has not been configured with a proper try URL.')

        last_revision = json.loads(rr.extra_data.get('p2rb.commits'))[-1][0]

        ext = get_extension_manager().get_enabled_extension(
            'mozreview.extension.MozReviewExtension')

        logging.info('Submitting a request to Autoland for review request '
                     'ID %s for revision %s '
                     % (review_request_id, last_revision))

        autoland_url = ext.settings.get('autoland_url')
        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.settings.get('autoland_user')
        autoland_password = ext.settings.get('autoland_password')

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        logging.info('Telling Autoland to give status updates to %s'
                     % pingback_url)

        lock_id = get_autoland_lock_id(rr.id, target_repository, last_revision)
        if not acquire_lock(lock_id):
            return AUTOLAND_REQUEST_IN_PROGRESS

        try:
            # We use a hard-coded destination here. If we ever open this up
            # to make the destination a parameter to this resource, we need to
            # verify that the destination is in fact an "scm_level_1"
            # repository to ensure that people don't try to land to inbound
            # using this resource.
            response = requests.post(autoland_url + '/autoland',
                data=json.dumps({
                'tree': rr.repository.name,
                'pingback_url': pingback_url,
                'rev': last_revision,
                'destination': TRY_AUTOLAND_DESTINATION,
                'trysyntax': try_syntax,
            }), headers={
                'content-type': 'application/json',
            },
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password))
        except requests.exceptions.RequestException:
            logging.error('We hit a RequestException when submitting a '
                          'request to Autoland')
            release_lock(lock_id)
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logging.error('We timed out when submitting a request to '
                          'Autoland')
            release_lock(lock_id)
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            release_lock(lock_id)
            return AUTOLAND_ERROR, {
                'status_code': response.status_code,
                'message': response.json().get('error'),
            }

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get('request_id', 0))
        finally:
            if autoland_request_id is None:
                release_lock(lock_id)
                return AUTOLAND_ERROR, {
                    'status_code': response.status_code,
                    'request_id': None,
                }

        autoland_request = AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            repository_url=target_repository,
            review_request_id=rr.id,
            user_id=request.user.id,
            extra_data=json.dumps({
                'try_syntax': try_syntax
            })
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED,
            autoland_request_id=autoland_request_id)

        self.save_autolandrequest_id('p2rb.autoland_try', rr,
            autoland_request_id)

        return 200, {}
Пример #14
0
 def should_render(self, value):
     return is_pushed(self.review_request_details)
Пример #15
0
 def should_render(self, value):
     return (is_pushed(self.review_request_details) and
             is_parent(self.review_request_details) and
             False) # TODO: Remove and render hg web link to the base commit.
Пример #16
0
def on_review_publishing(user, review, **kwargs):
    """Comment in the bug and potentially r+ or clear a review flag.

    Note that a reviewer *must* have editbugs to set an attachment flag on
    someone else's attachment (i.e. the standard BMO review process).

    TODO: Report lack-of-editbugs properly; see bug 1119065.
    """
    review_request = review.review_request
    logger.info('Publishing review for user: %s review id: %s '
                'review request id: %s' % (user, review.id, review_request.id))

    # skip review requests that were not pushed
    if not is_pushed(review_request):
        logger.info('Did not publish review: %s: for user: %d: review not '
                    'pushed.' % (user, review.id))
        return

    site = Site.objects.get_current()
    siteconfig = SiteConfiguration.objects.get_current()
    comment = build_plaintext_review(review,
                                     get_obj_url(review, site, siteconfig),
                                     {"user": user})
    b = Bugzilla(get_bugzilla_api_key(user))

    if is_parent(review_request):
        # We only support raw comments on parent review requests to prevent
        # confusion.  If the ship-it flag or the review flag was set, throw
        # an error.
        # Otherwise, mirror the comment over, associating it with the first
        # commit.
        if review.ship_it or review.extra_data.get(REVIEW_FLAG_KEY):
            raise ParentShipItError

        # TODO: If we ever allow multiple bugs in a single series, and we want
        # to continue to allow comments on parents, we'll have to pick one
        # child for each unique bug.
        first_child = list(gen_child_rrs(review_request))[0]
        b.post_comment(int(first_child.get_bug_list()[0]), comment,
                       get_diff_url(first_child), False)
    else:
        diff_url = get_diff_url(review_request)
        bug_id = int(review_request.get_bug_list()[0])

        commented = False
        flag = review.extra_data.get(REVIEW_FLAG_KEY)

        if flag is not None:
            commented = b.set_review_flag(bug_id, flag, review.user.email,
                                          diff_url, comment)
        else:
            # If for some reasons we don't have the flag set in extra_data,
            # fall back to ship_it
            logger.warning('Review flag not set on review %s, '
                           'updating attachment based on ship_it' % review.id)
            if review.ship_it:
                commented = b.r_plus_attachment(bug_id, review.user.email,
                                                diff_url, comment)
            else:
                commented = b.cancel_review_request(bug_id, review.user.email,
                                                    diff_url, comment)

        if comment and not commented:
            b.post_comment(bug_id, comment, diff_url, False)
Пример #17
0
def on_review_request_publishing(user, review_request_draft, **kwargs):
    # There have been strange cases (all local, and during development), where
    # when attempting to publish a review request, this handler will fail
    # because the draft does not exist. This is a really strange case, and not
    # one we expect to happen in production. However, since we've seen it
    # locally, we handle it here, and log.
    if not review_request_draft:
        logger.error('Strangely, there was no review request draft on the '
                     'review request we were attempting to publish.')
        return

    # If the review request draft has a new DiffSet we will only allow
    # publishing if that DiffSet has been verified. It is important to
    # do this for every review request, not just pushed ones, because
    # we can't trust the storage mechanism which indicates it was pushed.
    # TODO: This will be fixed when we transition away from extra_data.
    if review_request_draft.diffset:
        try:
            DiffSetVerification.objects.get(
                diffset=review_request_draft.diffset)
        except DiffSetVerification.DoesNotExist:
            logger.error(
                'An attempt was made by User %s to publish an unverified '
                'DiffSet with id %s', user.id, review_request_draft.diffset.id)

            raise PublishError(
                'This review request draft contained a manually uploaded '
                'diff, which is prohibited. Please push to the review server '
                'to create review requests. If you believe you received this '
                'message in error, please file a bug.')

    review_request = review_request_draft.get_review_request()
    commit_data = fetch_commit_data(review_request)

    # skip review requests that were not pushed
    if not is_pushed(review_request, commit_data=commit_data):
        return

    if not is_parent(review_request, commit_data):
        # Send a signal asking for approval to publish this review request.
        # We only want to publish this commit request if we are in the middle
        # of publishing the parent. If the parent is publishing it will be
        # listening for this signal to approve it.
        approvals = commit_request_publishing.send_robust(
            sender=review_request,
            user=user,
            review_request_draft=review_request_draft)

        for receiver, approved in approvals:
            if approved:
                break
        else:
            # This publish is not approved by the parent review request.
            raise CommitPublishProhibited()

    # The reviewid passed through p2rb is, for Mozilla's instance anyway,
    # bz://<bug id>/<irc nick>.
    reviewid = commit_data.draft_extra_data.get(IDENTIFIER_KEY, None)
    m = REVIEWID_RE.match(reviewid)

    if not m:
        raise InvalidBugIdError('<unknown>')

    bug_id = m.group(1)

    try:
        bug_id = int(bug_id)
    except (TypeError, ValueError):
        raise InvalidBugIdError(bug_id)

    siteconfig = SiteConfiguration.objects.get_current()
    using_bugzilla = (siteconfig.settings.get("auth_backend",
                                              "builtin") == "bugzilla")

    if using_bugzilla:
        commit_data = fetch_commit_data(review_request_draft)
        publish_as_id = commit_data.draft_extra_data.get(PUBLISH_AS_KEY)
        if publish_as_id:
            u = User.objects.get(id=publish_as_id)
            b = Bugzilla(get_bugzilla_api_key(u))
        else:
            b = Bugzilla(get_bugzilla_api_key(user))

        try:
            if b.is_bug_confidential(bug_id):
                raise ConfidentialBugError
        except BugzillaError as e:
            # Special cases:
            #   100: Invalid Bug Alias
            #   101: Bug does not exist
            if e.fault_code and (e.fault_code == 100 or e.fault_code == 101):
                raise InvalidBugIdError(bug_id)
            raise

    # Note that the bug ID has already been set when the review was created.

    # If this is a squashed/parent review request, automatically publish all
    # relevant children.
    if is_parent(review_request, commit_data):
        unpublished_rids = map(
            int, json.loads(commit_data.extra_data[UNPUBLISHED_KEY]))
        discard_on_publish_rids = map(
            int, json.loads(commit_data.extra_data[DISCARD_ON_PUBLISH_KEY]))
        child_rrs = list(gen_child_rrs(review_request_draft))

        # Create or update Bugzilla attachments for each draft commit.  This
        # is done before the children are published to ensure that MozReview
        # doesn't get into a strange state if communication with Bugzilla is
        # broken or attachment creation otherwise fails.  The Bugzilla
        # attachments will then, of course, be in a weird state, but that
        # should be fixed by the next successful publish.
        if using_bugzilla:
            children_to_post = []
            children_to_obsolete = []

            for child in child_rrs:
                child_draft = child.get_draft(user=user)

                if child_draft:
                    if child.id in discard_on_publish_rids:
                        children_to_obsolete.append(child)

                    children_to_post.append((child_draft, child))

            if children_to_post or children_to_obsolete:
                update_bugzilla_attachments(b, bug_id, children_to_post,
                                            children_to_obsolete)

        # Publish draft commits. This will already include items that are in
        # unpublished_rids, so we'll remove anything we publish out of
        # unpublished_rids.
        for child in child_rrs:
            if child.get_draft(user=user) or not child.public:

                def approve_publish(sender, user, review_request_draft,
                                    **kwargs):
                    return child is sender

                # Setup the parent signal handler to approve the publish
                # and then publish the child.
                commit_request_publishing.connect(approve_publish,
                                                  sender=child,
                                                  weak=False)
                try:
                    child.publish(user=user)
                except NotModifiedError:
                    # As we create empty drafts as part of allowing reviewer
                    # delegation, delete these empty drafts instead of
                    # throwing an error.
                    child.get_draft(user=user).delete()
                finally:
                    commit_request_publishing.disconnect(
                        receiver=approve_publish, sender=child, weak=False)

                if child.id in unpublished_rids:
                    unpublished_rids.remove(child.id)

        # The remaining unpubished_rids need to be closed as discarded because
        # they have never been published, and they will appear in the user's
        # dashboard unless closed.
        for child in gen_rrs_by_rids(unpublished_rids):
            child.close(ReviewRequest.DISCARDED,
                        user=user,
                        description=NEVER_USED_DESCRIPTION)

        # We also close the discard_on_publish review requests because, well,
        # we don't need them anymore. We use a slightly different message
        # though.
        for child in gen_rrs_by_rids(discard_on_publish_rids):
            child.close(ReviewRequest.DISCARDED,
                        user=user,
                        description=OBSOLETE_DESCRIPTION)

        commit_data.extra_data[UNPUBLISHED_KEY] = '[]'
        commit_data.extra_data[DISCARD_ON_PUBLISH_KEY] = '[]'

    # Copy any drafted CommitData from draft_extra_data to extra_data.
    for key in DRAFTED_COMMIT_DATA_KEYS:
        if key in commit_data.draft_extra_data:
            commit_data.extra_data[key] = commit_data.draft_extra_data[key]

    commit_data.save(update_fields=['extra_data'])

    review_request.save()
def handle_commits_published(extension=None, **kwargs):
    """Handle sending 'mozreview.commits.published'.

    This message is only sent when the parent review request, in a set of
    pushed review requests, is published with new commit information.

    This is a useful message for consumers who care about new or modified
    commits being published for review.
    """
    review_request = kwargs.get('review_request')

    if review_request is None:
        return

    commit_data = fetch_commit_data(review_request)

    if (not is_pushed(review_request, commit_data) or
            not is_parent(review_request, commit_data)):
        return

    # Check the change description and only continue if it contains a change
    # to the commit information. Currently change descriptions won't include
    # information about our extra data field, so we'll look for a change to
    # the diff which is mandatory if the commits changed. TODO: Properly use
    # the commit information once we start populating the change description
    # with it.
    #
    # A change description will not exist if this is the first publish of the
    # review request. In that case we know there must be commits since this
    # is a pushed request.
    cd = kwargs.get('changedesc')
    if (cd is not None and ('diff' not in cd.fields_changed or
                            'added' not in cd.fields_changed['diff'])):
        return

    # We publish both the review repository url as well as the landing
    # ("inbound") repository url. This gives consumers which perform hg
    # operations the option to avoid cloning the review repository, which may
    # be large.
    repo = review_request.repository
    repo_url = repo.path
    landing_repo_url = repo.extra_data.get('landing_repository_url')

    child_rrids = []
    commits = []
    ext_commits = json.loads(commit_data.extra_data.get(COMMITS_KEY, '[]'))

    for rev, rrid in ext_commits:
        child_rrids.append(int(rrid))
        commits.append({
            'rev': rev,
            'review_request_id': int(rrid),
            'diffset_revision': None
        })

    # In order to retrieve the diff revision for each commit we need to fetch
    # their correpsonding child review request.
    review_requests = dict(
        (obj.id, obj) for obj in
        ReviewRequest.objects.filter(pk__in=child_rrids))

    for commit_info in commits:
        # TODO: Every call to get_latest_diffset() makes its own query to the
        # database. It is probably possible to retrieve the diffsets we care
        # about using a single query through Django's ORM, but it's not trivial.
        commit_info['diffset_revision'] = review_requests[
            commit_info['review_request_id']
        ].get_latest_diffset().revision

    msg = base.GenericMessage()
    msg.routing_parts.append('mozreview.commits.published')
    msg.data['parent_review_request_id'] = review_request.id
    msg.data['parent_diffset_revision'] = review_request.get_latest_diffset().revision
    msg.data['commits'] = commits
    msg.data['repository_url'] = repo_url
    msg.data['landing_repository_url'] = landing_repo_url

    # TODO: Make work with RB localsites.
    msg.data['review_board_url'] = get_server_url()

    publish_message(extension, msg)
def on_review_request_publishing(user, review_request_draft, **kwargs):
    # There have been strange cases (all local, and during development), where
    # when attempting to publish a review request, this handler will fail
    # because the draft does not exist. This is a really strange case, and not
    # one we expect to happen in production. However, since we've seen it
    # locally, we handle it here, and log.
    if not review_request_draft:
        logger.error('Strangely, there was no review request draft on the '
                     'review request we were attempting to publish.')
        return

    # If the review request draft has a new DiffSet we will only allow
    # publishing if that DiffSet has been verified. It is important to
    # do this for every review request, not just pushed ones, because
    # we can't trust the storage mechanism which indicates it was pushed.
    # TODO: This will be fixed when we transition away from extra_data.
    if review_request_draft.diffset:
        try:
            DiffSetVerification.objects.get(
                diffset=review_request_draft.diffset)
        except DiffSetVerification.DoesNotExist:
            logger.error(
                'An attempt was made by User %s to publish an unverified '
                'DiffSet with id %s',
                user.id,
                review_request_draft.diffset.id)

            raise PublishError(
                'This review request draft contained a manually uploaded '
                'diff, which is prohibited. Please push to the review server '
                'to create review requests. If you believe you received this '
                'message in error, please file a bug.')

    review_request = review_request_draft.get_review_request()
    commit_data = fetch_commit_data(review_request)

    # skip review requests that were not pushed
    if not is_pushed(review_request, commit_data=commit_data):
        return

    if not is_parent(review_request, commit_data):
        # Send a signal asking for approval to publish this review request.
        # We only want to publish this commit request if we are in the middle
        # of publishing the parent. If the parent is publishing it will be
        # listening for this signal to approve it.
        approvals = commit_request_publishing.send_robust(
            sender=review_request,
            user=user,
            review_request_draft=review_request_draft)

        for receiver, approved in approvals:
            if approved:
                break
        else:
            # This publish is not approved by the parent review request.
            raise CommitPublishProhibited()

    # The reviewid passed through p2rb is, for Mozilla's instance anyway,
    # bz://<bug id>/<irc nick>.
    reviewid = commit_data.draft_extra_data.get(IDENTIFIER_KEY, None)
    m = REVIEWID_RE.match(reviewid)

    if not m:
        raise InvalidBugIdError('<unknown>')

    bug_id = m.group(1)

    try:
        bug_id = int(bug_id)
    except (TypeError, ValueError):
        raise InvalidBugIdError(bug_id)

    siteconfig = SiteConfiguration.objects.get_current()
    using_bugzilla = (
        siteconfig.settings.get("auth_backend", "builtin") == "bugzilla")

    if using_bugzilla:
        b = Bugzilla(get_bugzilla_api_key(user))

        try:
            if b.is_bug_confidential(bug_id):
                raise ConfidentialBugError
        except BugzillaError as e:
            # Special cases:
            #   100: Invalid Bug Alias
            #   101: Bug does not exist
            if e.fault_code and (e.fault_code == 100 or e.fault_code == 101):
                raise InvalidBugIdError(bug_id)
            raise

    # Note that the bug ID has already been set when the review was created.

    # If this is a squashed/parent review request, automatically publish all
    # relevant children.
    if is_parent(review_request, commit_data):
        unpublished_rids = map(int, json.loads(
            commit_data.extra_data[UNPUBLISHED_KEY]))
        discard_on_publish_rids = map(int, json.loads(
            commit_data.extra_data[DISCARD_ON_PUBLISH_KEY]))
        child_rrs = list(gen_child_rrs(review_request_draft))

        # Create or update Bugzilla attachments for each draft commit.  This
        # is done before the children are published to ensure that MozReview
        # doesn't get into a strange state if communication with Bugzilla is
        # broken or attachment creation otherwise fails.  The Bugzilla
        # attachments will then, of course, be in a weird state, but that
        # should be fixed by the next successful publish.
        if using_bugzilla:
            for child in child_rrs:
                child_draft = child.get_draft(user=user)

                if child_draft:
                    if child.id in discard_on_publish_rids:
                        b.obsolete_review_attachments(
                            bug_id, get_obj_url(child))
                    post_bugzilla_attachment(b, bug_id, child_draft, child)

        # Publish draft commits. This will already include items that are in
        # unpublished_rids, so we'll remove anything we publish out of
        # unpublished_rids.
        for child in child_rrs:
            if child.get_draft(user=user) or not child.public:
                def approve_publish(sender, user, review_request_draft,
                                    **kwargs):
                    return child is sender

                # Setup the parent signal handler to approve the publish
                # and then publish the child.
                commit_request_publishing.connect(approve_publish,
                                                  sender=child,
                                                  weak=False)
                try:
                    child.publish(user=user)
                finally:
                    commit_request_publishing.disconnect(
                        receiver=approve_publish,
                        sender=child,
                        weak=False)

                if child.id in unpublished_rids:
                    unpublished_rids.remove(child.id)

        # The remaining unpubished_rids need to be closed as discarded because
        # they have never been published, and they will appear in the user's
        # dashboard unless closed.
        for child in gen_rrs_by_rids(unpublished_rids):
            child.close(ReviewRequest.DISCARDED,
                        user=user,
                        description=NEVER_USED_DESCRIPTION)

        # We also close the discard_on_publish review requests because, well,
        # we don't need them anymore. We use a slightly different message
        # though.
        for child in gen_rrs_by_rids(discard_on_publish_rids):
            child.close(ReviewRequest.DISCARDED,
                        user=user,
                        description=OBSOLETE_DESCRIPTION)

        commit_data.extra_data[UNPUBLISHED_KEY] = '[]'
        commit_data.extra_data[DISCARD_ON_PUBLISH_KEY] = '[]'

    # Copy any drafted CommitData from draft_extra_data to extra_data.
    for key in DRAFTED_COMMIT_DATA_KEYS:
        if key in commit_data.draft_extra_data:
            commit_data.extra_data[key] = commit_data.draft_extra_data[key]

    commit_data.save(update_fields=['extra_data'])

    review_request.save()
Пример #20
0
    def create(self, request, review_request_id, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        if not is_pushed(rr) or not is_parent(rr):
            logging.error(
                "Failed triggering Autoland because the review "
                "request is not pushed, or not the parent review "
                "request."
            )
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        if not rr.is_mutable_by(request.user):
            return PERMISSION_DENIED

        last_revision = json.loads(rr.extra_data.get("p2rb.commits"))[-1][0]

        ext = get_extension_manager().get_enabled_extension("mozreview.extension.MozReviewExtension")

        logging.info(
            "Submitting a request to Autoland for review request "
            "ID %s for revision %s " % (review_request_id, last_revision)
        )

        autoland_url = ext.settings.get("autoland_url")
        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.settings.get("autoland_user")
        autoland_password = ext.settings.get("autoland_password")

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        logging.info("Telling Autoland to give status updates to %s" % pingback_url)

        try:
            # Rather than hard coding the destination it would make sense
            # to extract it from metadata about the repository. That will have
            # to wait until we fix Bug 1168486.
            response = requests.post(
                autoland_url + "/autoland",
                data=json.dumps(
                    {
                        "tree": rr.repository.name,
                        "pingback_url": pingback_url,
                        "rev": last_revision,
                        "destination": INBOUND_AUTOLAND_DESTINATION,
                    }
                ),
                headers={"content-type": "application/json"},
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password),
            )
        except requests.exceptions.RequestException:
            logging.error("We hit a RequestException when submitting a " "request to Autoland")
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logging.error("We timed out when submitting a request to " "Autoland")
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            return AUTOLAND_ERROR, {"status_code": response.status_code, "message": response.json().get("error")}

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get("request_id", 0))
        finally:
            if autoland_request_id is None:
                return AUTOLAND_ERROR, {"status_code": response.status_code, "request_id": None}

        autoland_request = AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            review_request_id=rr.id,
            user_id=request.user.id,
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED, autoland_request_id=autoland_request_id
        )

        self.save_autolandrequest_id("p2rb.autoland_inbound", rr, autoland_request_id)

        return 200, {}
Пример #21
0
 def should_render(self, value):
     return is_pushed(self.review_request_details)
Пример #22
0
def isPush(review_request):
    return is_pushed(review_request)
Пример #23
0
 def should_render(self, value):
     return (is_pushed(self.review_request_details) and
             is_parent(self.review_request_details) and
             False) # TODO: Remove and render hg web link to the base commit.
Пример #24
0
    def create(self, request, review_request_id, commit_descriptions, *args,
               **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        commit_data = fetch_commit_data(rr)

        if not is_pushed(rr, commit_data) or not is_parent(rr, commit_data):
            logger.error('Failed triggering Autoland because the review '
                         'request is not pushed, or not the parent review '
                         'request.')
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        enabled = rr.repository.extra_data.get('autolanding_enabled')

        if not enabled:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autolanding not enabled.')

        target_repository = rr.repository.extra_data.get(
            'landing_repository_url')
        push_bookmark = rr.repository.extra_data.get('landing_bookmark')

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autoland has not been configured with a proper landing URL.')

        last_revision = json.loads(
            commit_data.extra_data.get(COMMITS_KEY))[-1][0]
        ext = get_extension_manager().get_enabled_extension(
            'mozreview.extension.MozReviewExtension')

        logger.info('Submitting a request to Autoland for review request '
                    'ID %s for revision %s destination %s' %
                    (review_request_id, last_revision, target_repository))

        autoland_url = ext.get_settings('autoland_url')

        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.get_settings('autoland_user')
        autoland_password = ext.get_settings('autoland_password')

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)
        lock_id = get_autoland_lock_id(rr.id, target_repository, last_revision)

        if not acquire_lock(lock_id):
            return AUTOLAND_REQUEST_IN_PROGRESS

        try:
            response = requests.post(
                autoland_url + '/autoland',
                data=json.dumps({
                    'ldap_username':
                    request.mozreview_profile.ldap_username,
                    'tree':
                    rr.repository.name,
                    'pingback_url':
                    pingback_url,
                    'rev':
                    last_revision,
                    'destination':
                    target_repository,
                    'push_bookmark':
                    push_bookmark,
                    'commit_descriptions':
                    json.loads(commit_descriptions),
                }),
                headers={
                    'content-type': 'application/json',
                },
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password))
        except requests.exceptions.RequestException:
            logger.error('We hit a RequestException when submitting a '
                         'request to Autoland')
            release_lock(lock_id)
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logger.error('We timed out when submitting a request to '
                         'Autoland')
            release_lock(lock_id)
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            release_lock(lock_id)

            try:
                error_message = response.json().get('error')
            except ValueError:
                error_message = response.text

            return AUTOLAND_ERROR, {
                'status_code': response.status_code,
                'message': error_message,
            }

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get('request_id', 0))
        finally:
            if autoland_request_id is None:
                release_lock(lock_id)
                return AUTOLAND_ERROR, {
                    'status_code': response.status_code,
                    'request_id': None,
                }

        AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            repository_url=target_repository,
            review_request_id=rr.id,
            user_id=request.user.id,
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED,
            autoland_request_id=autoland_request_id)

        self.save_autolandrequest_id('p2rb.autoland', rr, autoland_request_id)

        return 200, {}
Пример #25
0
def handle_commits_published(extension=None, **kwargs):
    """Handle sending 'mozreview.commits.published'.

    This message is only sent when the parent review request, in a set of
    pushed review requests, is published with new commit information.

    This is a useful message for consumers who care about new or modified
    commits being published for review.
    """
    review_request = kwargs.get('review_request')

    if review_request is None:
        return

    commit_data = fetch_commit_data(review_request)

    if (not is_pushed(review_request, commit_data)
            or not is_parent(review_request, commit_data)):
        return

    # Check the change description and only continue if it contains a change
    # to the commit information. Currently change descriptions won't include
    # information about our extra data field, so we'll look for a change to
    # the diff which is mandatory if the commits changed. TODO: Properly use
    # the commit information once we start populating the change description
    # with it.
    #
    # A change description will not exist if this is the first publish of the
    # review request. In that case we know there must be commits since this
    # is a pushed request.
    cd = kwargs.get('changedesc')
    if (cd is not None and ('diff' not in cd.fields_changed
                            or 'added' not in cd.fields_changed['diff'])):
        return

    # We publish both the review repository url as well as the landing
    # ("inbound") repository url. This gives consumers which perform hg
    # operations the option to avoid cloning the review repository, which may
    # be large.
    repo = review_request.repository
    repo_url = repo.path
    landing_repo_url = repo.extra_data.get('landing_repository_url')

    child_rrids = []
    commits = []
    ext_commits = json.loads(commit_data.extra_data.get(COMMITS_KEY, '[]'))

    for rev, rrid in ext_commits:
        child_rrids.append(int(rrid))
        commits.append({
            'rev': rev,
            'review_request_id': int(rrid),
            'diffset_revision': None
        })

    # In order to retrieve the diff revision for each commit we need to fetch
    # their correpsonding child review request.
    review_requests = dict(
        (obj.id, obj)
        for obj in ReviewRequest.objects.filter(pk__in=child_rrids))

    for commit_info in commits:
        # TODO: Every call to get_latest_diffset() makes its own query to the
        # database. It is probably possible to retrieve the diffsets we care
        # about using a single query through Django's ORM, but it's not trivial.
        commit_info['diffset_revision'] = review_requests[
            commit_info['review_request_id']].get_latest_diffset().revision

    msg = base.GenericMessage()
    msg.routing_parts.append('mozreview.commits.published')
    msg.data['parent_review_request_id'] = review_request.id
    msg.data['parent_diffset_revision'] = review_request.get_latest_diffset(
    ).revision
    msg.data['commits'] = commits
    msg.data['repository_url'] = repo_url
    msg.data['landing_repository_url'] = landing_repo_url

    # TODO: Make work with RB localsites.
    msg.data['review_board_url'] = get_server_url()

    publish_message(extension, msg)
Пример #26
0
    def create(self, request, review_request_id, *args, **kwargs):
        try:
            rr = ReviewRequest.objects.get(pk=review_request_id)
        except ReviewRequest.DoesNotExist:
            return DOES_NOT_EXIST

        if not is_pushed(rr) or not is_parent(rr):
            logging.error('Failed triggering Autoland because the review '
                          'request is not pushed, or not the parent review '
                          'request.')
            return NOT_PUSHED_PARENT_REVIEW_REQUEST

        if not rr.is_mutable_by(request.user):
            return PERMISSION_DENIED

        target_repository = rr.repository.extra_data.get(
            'landing_repository_url')
        push_bookmark = rr.repository.extra_data.get('landing_bookmark')

        if not target_repository:
            return AUTOLAND_CONFIGURATION_ERROR.with_message(
                'Autoland has not been configured with a proper landing URL.')

        last_revision = json.loads(rr.extra_data.get('p2rb.commits'))[-1][0]

        ext = get_extension_manager().get_enabled_extension(
            'mozreview.extension.MozReviewExtension')

        logging.info('Submitting a request to Autoland for review request '
                     'ID %s for revision %s '
                     % (review_request_id, last_revision))

        autoland_url = ext.settings.get('autoland_url')
        if not autoland_url:
            return AUTOLAND_CONFIGURATION_ERROR

        autoland_user = ext.settings.get('autoland_user')
        autoland_password = ext.settings.get('autoland_password')

        if not autoland_user or not autoland_password:
            return AUTOLAND_CONFIGURATION_ERROR

        pingback_url = autoland_request_update_resource.get_uri(request)

        logging.info('Telling Autoland to give status updates to %s'
                     % pingback_url)

        try:
            # Rather than hard coding the destination it would make sense
            # to extract it from metadata about the repository. That will have
            # to wait until we fix Bug 1168486.
            response = requests.post(autoland_url + '/autoland',
                data=json.dumps({
                'tree': rr.repository.name,
                'pingback_url': pingback_url,
                'rev': last_revision,
                'destination': target_repository,
                'push_bookmark': push_bookmark,
            }), headers={
                'content-type': 'application/json',
            },
                timeout=AUTOLAND_REQUEST_TIMEOUT,
                auth=(autoland_user, autoland_password))
        except requests.exceptions.RequestException:
            logging.error('We hit a RequestException when submitting a '
                          'request to Autoland')
            return AUTOLAND_ERROR
        except requests.exceptions.Timeout:
            logging.error('We timed out when submitting a request to '
                          'Autoland')
            return AUTOLAND_TIMEOUT

        if response.status_code != 200:
            return AUTOLAND_ERROR, {
                'status_code': response.status_code,
                'message': response.json().get('error'),
            }

        # We succeeded in scheduling the job.
        try:
            autoland_request_id = int(response.json().get('request_id', 0))
        finally:
            if autoland_request_id is None:
                return AUTOLAND_ERROR, {
                    'status_code': response.status_code,
                    'request_id': None,
                }

        autoland_request = AutolandRequest.objects.create(
            autoland_id=autoland_request_id,
            push_revision=last_revision,
            repository_url=target_repository,
            review_request_id=rr.id,
            user_id=request.user.id,
        )

        AutolandEventLogEntry.objects.create(
            status=AutolandEventLogEntry.REQUESTED,
            autoland_request_id=autoland_request_id)

        self.save_autolandrequest_id('p2rb.autoland', rr,
            autoland_request_id)

        return 200, {}