Example #1
0
def parse_cq_data(json_data):
    logging_output = []
    for result in json_data.get('results', {}):
        fields = result.get('fields', [])
        if not 'action' in fields:
            logging.warning('Missing field action in status record')
            parsing_errors.increment_by(1)
            continue

        action = fields.get('action')
        if action != 'verifier_jobs_update':
            continue

        if fields.get('verifier') != 'try job':
            continue

        # At the moment, much of the parsing logic assumes this is a Chromium
        # tryjob.
        project = fields.get('project')
        if project != 'chromium/chromium/src':
            logging.info('project not chromium: %s', project)
            continue

        job_states = fields.get('jobs', {})

        for job in itertools.chain.from_iterable(job_states.values()):
            try:
                builder = job['builder']
                result = job['result']
                timestamp_tz = dateutil.parser.parse(
                    job.get('created_ts') or job['timestamp'])
                # We assume timestamps from chromium-cq-status are already in UTC.
                timestamp = timestamp_tz.replace(tzinfo=None)
            except KeyError:
                logging.warning('Failed to parse job details', exc_info=True)
                parsing_errors.increment_by(1)
                continue

            if build_result.isResultPending(result):
                continue

            build_properties = job.get('build_properties')
            if not build_properties:
                logging.warning(
                    'Missing field build_properties in job details')
                parsing_errors.increment_by(1)
                continue

            issue = -1
            patchset = -1
            time_started = 0

            try:
                buildnumber = get_int_value(build_properties, 'buildnumber')
                if 'patch_issue' in build_properties:
                    issue = get_int_value(build_properties, 'patch_issue')
                else:  # pragma: no cover
                    logging.warning('no issue')

                if 'patch_set' in build_properties:
                    patchset = get_int_value(build_properties, 'patch_set')
                else:  # pragma: no cover
                    logging.warning('no patchset')

                if 'attempt_start_ts' in build_properties:
                    attempt_start_ts = get_int_value(build_properties,
                                                     'attempt_start_ts')
                    time_started = datetime.datetime.utcfromtimestamp(
                        attempt_start_ts / 1000000)
                else:  # pragma: no cover
                    logging.warning('no attempt_start_ts')
                    continue

                # For builds through Buildbucket, job['master'] is actually the bucket
                # name. For buildbot-based builds, it just happens to be the same as the
                # master name. For Luci-based builds, it is different from the master
                # name, and the master name is set as a build property instead.
                # https://chromium.googlesource.com/chromium/src/+/infra/config/cr-buildbucket.cfg#115
                # So in either case, the "real" master name is in the build properties.
                master = build_properties['mastername']

            except (ValueError, KeyError):
                logging.warning('Failed to parse build properties',
                                exc_info=True)
                parsing_errors.increment_by(1)
                continue

            # At this point, only success or failure.
            success = build_result.isResultSuccess(result)

            patchset_builder_runs = get_patchset_builder_runs(
                issue=issue, patchset=patchset, master=master, builder=builder)

            build_run = BuildRun(parent=patchset_builder_runs.key,
                                 buildnumber=buildnumber,
                                 result=result,
                                 time_started=time_started,
                                 time_finished=timestamp)

            previous_runs = BuildRun.query(
                ancestor=patchset_builder_runs.key).fetch()

            duplicate = False
            for previous_run in previous_runs:
                # We saw this build run already or there are multiple green runs,
                # in which case we ignore subsequent ones to avoid showing failures
                # multiple times.
                if (previous_run.buildnumber == buildnumber) or \
                   (build_run.is_success and previous_run.is_success) :
                    duplicate = True
                    break

            if duplicate:
                continue

            build_run.put()

            for previous_run in previous_runs:
                if previous_run.is_success == build_run.is_success:
                    continue
                if success:
                    # We saw the flake and then the pass.
                    failure_run = previous_run
                    success_run = build_run
                else:
                    # We saw the pass and then the failure. Could happen when fetching
                    # historical data, or for the bot_update step (patch can't be
                    # applied cleanly anymore).
                    failure_run = build_run
                    success_run = previous_run

                logging_output.append(failure_run.key.parent().get().builder +
                                      str(failure_run.buildnumber))

                # Queue a task to fetch the error of this failure and create FlakyRun.
                flakes_metric.increment_by(1)
                taskqueue.add(queue_name='issue-updates',
                              url='/issues/create_flaky_run',
                              params={
                                  'failure_run_key': failure_run.key.urlsafe(),
                                  'success_run_key': success_run.key.urlsafe()
                              })

    return logging_output
Example #2
0
def parse_cq_data(json_data):
  logging_output = []
  for result in json_data.get('results', {}):
    fields = result.get('fields', [])
    if not 'action' in fields:
      continue

    action = fields.get('action')
    if action != 'verifier_jobs_update':
      continue

    if fields.get('verifier') != 'try job':
      continue

    # At the moment, much of the parsing logic assumes this is a Chromium
    # tryjob.
    if fields.get('project') != 'chromium':
      continue

    job_states = fields.get('jobs', [])
    for state in job_states:
      # Just go by |result|.
      #if state not in ['JOB_SUCCEEDED', 'JOB_FAILED', 'JOB_TIMED_OUT']:
      #  continue

      for job in job_states[state]:
        build_properties = job.get('build_properties')
        if not build_properties:
          continue

        try:
          master = job['master']
          builder = job['builder']
          result = job['result']
          timestamp_tz = dateutil.parser.parse(job['timestamp'])
          # We assume timestamps from chromium-cq-status are already in UTC.
          timestamp = timestamp_tz.replace(tzinfo=None)
        except KeyError:
          continue

        try:
          buildnumber = get_int_value(build_properties, 'buildnumber')
          issue = get_int_value(build_properties, 'issue')
          patchset = get_int_value(build_properties, 'patchset')
          attempt_start_ts = get_int_value(build_properties, 'attempt_start_ts')
          time_started = datetime.datetime.utcfromtimestamp(
              attempt_start_ts / 1000000)
        except ValueError:
          continue

        if build_result.isResultPending(result):
          continue

        # At this point, only success or failure.
        success = build_result.isResultSuccess(result)

        patchset_builder_runs = get_patchset_builder_runs(issue=issue,
                                                          patchset=patchset,
                                                          master=master,
                                                          builder=builder)

        build_run = BuildRun(parent=patchset_builder_runs.key,
                             buildnumber=buildnumber,
                             result=result,
                             time_started=time_started,
                             time_finished=timestamp)

        previous_runs = BuildRun.query(
            ancestor=patchset_builder_runs.key).fetch()

        duplicate = False
        for previous_run in previous_runs:
          # We saw this build run already or there are multiple green runs,
          # in which case we ignore subsequent ones to avoid showing failures
          # multiple times.
          if (previous_run.buildnumber == buildnumber) or \
             (build_run.is_success and previous_run.is_success) :
            duplicate = True
            break

        if duplicate:
          continue

        build_run.put()

        for previous_run in previous_runs:
          if previous_run.is_success == build_run.is_success:
            continue
          if success:
            # We saw the flake and then the pass.
            failure_run = previous_run
            success_run = build_run
          else:
            # We saw the pass and then the failure. Could happen when fetching
            # historical data, or for the bot_update step (patch can't be
            # applied cleanly anymore).
            failure_run = build_run
            success_run = previous_run

          logging_output.append(failure_run.key.parent().get().builder +
                                str(failure_run.buildnumber))

          # Queue a task to fetch the error of this failure and create FlakyRun.
          flakes_metric.increment_by(1)
          taskqueue.add(
              queue_name='issue-updates',
              url='/issues/create_flaky_run',
              params={'failure_run_key': failure_run.key.urlsafe(),
                      'success_run_key': success_run.key.urlsafe()})

  return logging_output
Example #3
0
def parse_cq_data(json_data):
  logging_output = []
  for result in json_data['results']:
    fields = result['fields']
    if not 'action' in fields:
      continue

    action = fields['action']
    if action != 'verifier_jobs_update':
      continue

    if fields['verifier'] != 'try job':
      continue

    job_states = fields['jobs']
    for state in job_states:
      # Just go by |result|.
      #if state not in ['JOB_SUCCEEDED', 'JOB_FAILED', 'JOB_TIMED_OUT']:
      #  continue

      for job in job_states[state]:
        build_properties = job['build_properties']
        if not build_properties:
          continue

        master = job['master']
        builder = job['builder']
        result = job['result']
        timestamp = datetime.datetime.strptime(job['timestamp'],
                                               '%Y-%m-%d %H:%M:%S.%f')
        try:
          buildnumber = get_int_value(build_properties, 'buildnumber')
          issue = get_int_value(build_properties, 'issue')
          patchset = get_int_value(build_properties, 'patchset')
        except ValueError as e:
          continue

        if build_result.isResultPending(result):
          continue

        # At this point, only success or failure.
        success = build_result.isResultSuccess(result)

        patchset_builder_runs = get_patchset_builder_runs(issue=issue,
                                                          patchset=patchset,
                                                          master=master,
                                                          builder=builder)

        build_run = BuildRun(parent=patchset_builder_runs.key,
                             buildnumber=buildnumber,
                             result=result,
                             time_finished=timestamp)

        previous_runs = BuildRun.query(
            ancestor=patchset_builder_runs.key).fetch()

        duplicate = False
        for previous_run in previous_runs:
          # We saw this build run already or there are multiple green runs,
          # in which case we ignore subsequent ones to avoid showing failures
          # multiple times.
          if (previous_run.buildnumber == buildnumber) or \
             (build_run.is_success and previous_run.is_success) :
            duplicate = True
            break

        if duplicate:
          continue

        build_run.put()

        for previous_run in previous_runs:
          if previous_run.is_success == build_run.is_success:
            continue
          if success:
            # We saw the flake and then the pass.
            flaky_run = FlakyRun(
                failure_run=previous_run.key,
                failure_run_time_finished=previous_run.time_finished,
                success_run=build_run.key)
            flaky_run.put()
            logging_output.append(previous_run.key.parent().get().builder +
                                  str(previous_run.buildnumber))
          else:
            # We saw the pass and then the failure. Could happen when fetching
            # historical data.
            flaky_run = FlakyRun(
                failure_run=build_run.key,
                failure_run_time_finished=build_run.time_finished,
                success_run=previous_run.key)
            flaky_run.put()
            logging_output.append(build_run.key.parent().get().builder +
                                  str(build_run.buildnumber))

          # Queue a task to fetch the error of this failure.
          deferred.defer(get_flaky_run_reason, flaky_run.key)

  return logging_output