Ejemplo n.º 1
0
    def testRevertCulprit(self, mock_revert, *_):
        repo_name = 'chromium'
        revision = 'rev1'
        build_key = 'm/b/123'

        revert_cl = RevertCL()
        revert_cl.revert_cl_url = 'url'
        revert_cl.created_time = datetime.datetime(2018, 6, 20, 0, 0, 0)

        mock_revert.return_value = (constants.CREATED_BY_FINDIT, revert_cl,
                                    None)

        culprit = WfSuspectedCL.Create(repo_name, revision, 1)
        culprit.put()

        pipeline_input = CreateRevertCLParameters(
            cl_key=culprit.key.urlsafe(),
            build_key=build_key,
            failure_type=failure_type.COMPILE)
        self.assertEqual(
            constants.CREATED_BY_FINDIT,
            culprit_action.RevertCulprit(pipeline_input, 'pipeline_id'))
        culprit = WfSuspectedCL.Get(repo_name, revision)
        self.assertEqual(revert_cl, culprit.revert_cl)
        self.assertEqual(analysis_status.COMPLETED, culprit.revert_status)
Ejemplo n.º 2
0
def RevertCulprit(urlsafe_key, build_id, build_failure_type, sample_step_name,
                  codereview_info):
    """Creates a revert of a culprit and adds reviewers.

  Args:
    urlsafe_key (str): Key to the ndb model.
    build_id (str): Id of the sample failed build.
    build_failure_type (int): Failure type: compile, test or flake.
    sample_step_name (str): Sample failed step in the failed build.
    codereview_info (dict): Code review information about the culprit.

  Returns:
    (int, string, string):
      - Status of the reverting;
      - change_id of the revert;
      - reason why revert is skipped if it is skipped.
  """

    culprit = entity_util.GetEntityFromUrlsafeKey(urlsafe_key)
    repo_name = culprit.repo_name
    revision = culprit.revision
    # 0. Gets information about this culprit.
    culprit_commit_position = codereview_info['commit_position']
    culprit_change_id = codereview_info['review_change_id']

    codereview = _GetCodeReview(codereview_info)
    print codereview

    if not codereview or not culprit_change_id:  # pragma: no cover
        logging.error('Failed to get change id for %s/%s', repo_name, revision)
        return services_constants.ERROR, None, None

    culprit_cl_info = codereview.GetClDetails(culprit_change_id)
    if not culprit_cl_info:  # pragma: no cover
        logging.error('Failed to get cl_info for %s/%s', repo_name, revision)
        return services_constants.ERROR, None, None

    # Checks if the culprit is a revert. If yes, bail out.
    if _IsCulpritARevert(culprit_cl_info):
        return (services_constants.SKIPPED, None,
                services_constants.CULPRIT_IS_A_REVERT)

    if culprit_cl_info.auto_revert_off:
        return services_constants.SKIPPED, None, services_constants.AUTO_REVERT_OFF

    # 1. Checks if a revert CL by sheriff has been created.
    reverts = culprit_cl_info.GetRevertCLsByRevision(revision)

    if reverts is None:  # pragma: no cover
        # if no reverts, reverts should be [], only when some error happens it will
        # be None.
        logging.error('Failed to find patchset_id for %s/%s' %
                      (repo_name, revision))
        return services_constants.ERROR, None, None

    findit_revert = None
    for revert in reverts:
        if _IsOwnerFindit(revert.reverting_user_email):
            findit_revert = revert
            break

    if reverts and not findit_revert:
        # Sheriff(s) created the revert CL(s).
        return (services_constants.CREATED_BY_SHERIFF, None,
                services_constants.REVERTED_BY_SHERIFF)

    revert_change_id = None
    if findit_revert:
        revert_change_id = findit_revert.reverting_cl.change_id

    # 2. Crreate revert CL.
    # TODO (chanli): Better handle cases where 2 analyses are trying to revert
    # at the same time.
    if not revert_change_id:
        revert_reason = culprit.GenerateRevertReason(build_id,
                                                     culprit_commit_position,
                                                     revision,
                                                     sample_step_name)
        if not revert_reason:  # pragma: no cover.
            logging.error('Failed to get the reason for revert culprit %s',
                          culprit.key.urlsafe() if culprit else '')
            return services_constants.ERROR, None, None
        bug_id = _GetBugIdForCulprit(culprit)
        revert_change_id = codereview.CreateRevert(
            revert_reason,
            culprit_change_id,
            culprit_cl_info.GetPatchsetIdByRevision(revision),
            bug_id=bug_id)

        if not revert_change_id:  # pragma: no cover
            return services_constants.ERROR, None, None

    # Save revert CL info and notification info to culprit.
    revert_cl = None
    if not culprit.revert_cl:
        revert_cl = RevertCL()
        revert_cl.revert_cl_url = codereview.GetCodeReviewUrl(revert_change_id)
        revert_cl.created_time = time_util.GetUTCNow()

    # 3. Add reviewers.
    # If Findit cannot commit the revert, add sheriffs as reviewers and ask them
    # to 'LGTM' and commit the revert.
    action_settings = waterfall_config.GetActionSettings()
    can_commit_revert = (action_settings.get('auto_commit_revert_compile')
                         if build_failure_type == failure_type.COMPILE else
                         action_settings.get('auto_commit_revert_test'))
    if not can_commit_revert:
        success = _AddReviewers(revision, culprit, codereview,
                                revert_change_id, False)
        if not success:  # pragma: no cover
            logging.error('Failed to add reviewers for revert of'
                          ' culprit %s/%s' % (repo_name, revision))
            return services_constants.ERROR, revert_cl, None
    return services_constants.CREATED_BY_FINDIT, revert_cl, None
    def testGenerateFinditMetrics(self):
        reverted_suspected_cl = WfSuspectedCL.Create('chromium', 'r1', 1)
        reverted_revert_cl = RevertCL()
        reverted_revert_cl.status = revert_cl_status.COMMITTED
        reverted_suspected_cl.revert_cl = reverted_revert_cl

        duplicate_fast_suspected_cl = WfSuspectedCL.Create('chromium', 'r2', 2)
        duplicate_revert_cl = RevertCL()
        duplicate_revert_cl.created_time = datetime(2017, 3, 15, 1)
        duplicate_revert_cl.status = revert_cl_status.DUPLICATE
        duplicate_fast_suspected_cl.revert_cl = duplicate_revert_cl
        duplicate_fast_suspected_cl.sheriff_action_time = datetime(
            2017, 3, 15, 2)

        duplicate_slow_suspected_cl = WfSuspectedCL.Create('chromium', 'r3', 3)
        duplicate_revert_cl = RevertCL()
        duplicate_revert_cl.created_time = datetime(2017, 3, 15, 2)
        duplicate_revert_cl.status = revert_cl_status.DUPLICATE
        duplicate_slow_suspected_cl.revert_cl = duplicate_revert_cl
        duplicate_slow_suspected_cl.sheriff_action_time = datetime(
            2017, 3, 15, 1)

        false_positive_suspected_cl = WfSuspectedCL.Create('chromium', 'r4', 4)
        false_positive_revert_cl = RevertCL()
        false_positive_revert_cl.status = revert_cl_status.FALSE_POSITIVE
        false_positive_suspected_cl.revert_cl = false_positive_revert_cl

        slow_suspected_cl = WfSuspectedCL.Create('chromium', 'r5', 5)
        slow_suspected_cl.cr_notification_time = datetime(2017, 3, 15, 2)
        slow_suspected_cl.should_be_reverted = True
        slow_suspected_cl.sheriff_action_time = datetime(2017, 3, 15, 1)

        false_positive_suspected_cl_no_revert = WfSuspectedCL.Create(
            'chromium', 'r6', 6)

        expected_metrics = {
            'revert_cls_detected': 5,
            'revert_cls_created': 4,
            'revert_cls_committed': 1,
            'duplicate_revert_cls': 2,
            'sheriffs_faster': 2,
            'false_positives': 1,
            'findit_faster': 1,
            'faster_than_sheriff_metrics': {
                'ninetieth_percentile': '01:00:00',
                'average': '01:00:00',
                'total': 1,
                'fiftieth_percentile': '01:00:00',
                'seventieth_percentile': '01:00:00'
            },
            'slower_than_sheriff_metrics': {
                'ninetieth_percentile': '01:00:00',
                'average': '01:00:00',
                'total': 2,
                'fiftieth_percentile': '01:00:00',
                'seventieth_percentile': '01:00:00'
            }
        }

        metrics = auto_revert_metrics._GenerateFinditMetrics([
            reverted_suspected_cl, duplicate_fast_suspected_cl,
            duplicate_slow_suspected_cl, false_positive_suspected_cl,
            slow_suspected_cl, false_positive_suspected_cl_no_revert
        ])

        self.assertEqual(expected_metrics, metrics)
def _RevertCulprit(master_name, builder_name, build_number, repo_name,
                   revision):

    culprit = _UpdateCulprit(repo_name, revision)

    if culprit.revert_cl and culprit.revert_status == status.COMPLETED:
        return CREATED_BY_FINDIT

    # 0. Gets information about this culprit.
    culprit_info = (suspected_cl_util.GetCulpritInfo(repo_name, revision))

    culprit_commit_position = culprit_info['commit_position']
    culprit_change_id = culprit_info['review_change_id']
    culprit_host = culprit_info['review_server_host']

    codereview = codereview_util.GetCodeReviewForReview(culprit_host)

    if not codereview or not culprit_change_id:  # pragma: no cover
        logging.error('Failed to get change id for %s/%s' %
                      (repo_name, revision))
        return ERROR

    culprit_cl_info = codereview.GetClDetails(culprit_change_id)
    if not culprit_cl_info:  # pragma: no cover
        logging.error('Failed to get cl_info for %s/%s' %
                      (repo_name, revision))
        return ERROR

    # Checks if the culprit is a revert created by Findit. If yes, bail out.
    if _IsOwnerFindit(culprit_cl_info.owner_email):
        _UpdateCulprit(repo_name,
                       revision,
                       status.SKIPPED,
                       skip_revert_reason=CULPRIT_OWNED_BY_FINDIT)
        return SKIPPED

    if culprit_cl_info.auto_revert_off:
        _UpdateCulprit(repo_name,
                       revision,
                       status.SKIPPED,
                       skip_revert_reason=AUTO_REVERT_OFF)
        return SKIPPED

    # 1. Checks if a revert CL by sheriff has been created.
    reverts = culprit_cl_info.GetRevertCLsByRevision(revision)

    if reverts is None:  # pragma: no cover
        # if no reverts, reverts should be [], only when some error happens it will
        # be None.
        logging.error('Failed to find patchset_id for %s/%s' %
                      (repo_name, revision))
        return ERROR

    findit_revert = None
    for revert in reverts:
        if _IsOwnerFindit(revert.reverting_user_email):
            findit_revert = revert
            break

    if reverts and not findit_revert:
        # Sheriff(s) created the revert CL(s).
        return CREATED_BY_SHERIFF

    # 2. Reverts the culprit.
    if not _LatestBuildFailed(master_name, builder_name, build_number):
        # The latest build didn't fail, skip.
        _UpdateCulprit(repo_name,
                       revision,
                       status.SKIPPED,
                       skip_revert_reason=NEWEST_BUILD_GREEN)
        return SKIPPED

    revert_change_id = None
    if findit_revert:
        revert_change_id = findit_revert.reverting_cl.change_id

    # TODO (chanli): Better handle cases where 2 analyses are trying to revert
    # at the same time.
    if not revert_change_id:
        _UpdateCulprit(repo_name, revision, status.RUNNING)
        revert_reason = textwrap.dedent("""
        Findit (https://goo.gl/kROfz5) identified CL at revision %s as the
        culprit for failures in the build cycles as shown on:
        https://findit-for-me.appspot.com/waterfall/culprit?key=%s""") % (
            culprit_commit_position or revision, culprit.key.urlsafe())

        revert_change_id = codereview.CreateRevert(
            revert_reason, culprit_change_id,
            culprit_cl_info.GetPatchsetIdByRevision(revision))

        if not revert_change_id:  # pragma: no cover
            _UpdateCulprit(repo_name, revision, status.ERROR)
            logging.error('Revert for culprit %s/%s failed.' %
                          (repo_name, revision))
            culprit.put()
            return ERROR

    # Save revert CL info and notification info to culprit.
    if not culprit.revert_cl:
        revert_cl = RevertCL()
        revert_cl.revert_cl_url = codereview.GetCodeReviewUrl(revert_change_id)
        revert_cl.created_time = time_util.GetUTCNow()
        _UpdateCulprit(repo_name, revision, None, revert_cl=revert_cl)

    # 3. Add reviewers.
    sheriffs = rotations.current_sheriffs()
    message = textwrap.dedent("""
      Sheriffs:

      Please confirm and "Quick L-G-T-M & CQ" this revert if it is correct.
      If it is a false positive, please close it.

      Findit (https://goo.gl/kROfz5) identified the original CL as the culprit
      for failures in the build cycles as shown on:
      https://findit-for-me.appspot.com/waterfall/culprit?key=%s""") % (
        culprit.key.urlsafe())
    success = codereview.AddReviewers(revert_change_id, sheriffs, message)

    if not success:  # pragma: no cover
        _UpdateCulprit(repo_name, revision, status.ERROR)
        logging.error('Failed to add reviewers for revert of culprit %s/%s' %
                      (repo_name, revision))
        return ERROR

    _UpdateCulprit(repo_name, revision, revert_status=status.COMPLETED)
    return CREATED_BY_FINDIT