コード例 #1
0
ファイル: flake_issues.py プロジェクト: eunchong/infra
  def get_flakes(cls, mastername, buildername, buildnumber, step):
    # If test results were invalid, report whole step as flaky.
    steptext = ' '.join(step['text'])
    stepname = normalize_test_type(step['name'])
    if 'TEST RESULTS WERE INVALID' in steptext:
      return [stepname]

    url = TEST_RESULTS_URL_TEMPLATE % {
      'mastername': urllib2.quote(mastername),
      'buildername': urllib2.quote(buildername),
      'buildnumber': urllib2.quote(str(buildnumber)),
      'stepname': urllib2.quote(stepname),
    }

    try:
      result = urlfetch.fetch(url)

      if result.status_code >= 200 and result.status_code < 400:
        json_result = json.loads(result.content)

        _, failed, _ = cls._flatten_tests(
            json_result.get('tests', {}),
            json_result.get('path_delimiter', '/'))
        return failed

      if result.status_code == 404:
        # This is quite a common case (only some failing steps are actually
        # running tests and reporting results to flakiness dashboard).
        logging.info('Failed to retrieve JSON from %s', url)
      else:
        logging.exception('Failed to retrieve JSON from %s', url)
    except Exception:
      logging.exception('Failed to retrieve or parse JSON from %s', url)

    return [stepname]
コード例 #2
0
  def get_flakes(cls, mastername, buildername, buildnumber, step):
    """Returns a list of flakes in a given step.

    It can either be entire step or a list of specific tests.

    Args:
      mastername: Master name on which step has been run.
      buildername: Builder name on which step has been run.
      buildnume: Number of the build in which step has been run.
      step: Step name.

    Returns:
      (flakes, is_step), where flakes is a list of flake names and is_step is
      True when the whole step is a flake, in which case flakes is a list
      containing a single entry - the name of the step.
    """
    # If test results were invalid, report whole step as flaky.
    # For Luci builds, some steps don't have step text anymore. Such steps
    # include 'Failure reason', 'analyze', etc.
    steptext = ' '.join(step['text'] or [])
    stepname = normalize_test_type(step['name'])
    if 'TEST RESULTS WERE INVALID' in steptext:
      return [stepname], True

    url = TEST_RESULTS_URL_TEMPLATE % {
      'mastername': urllib2.quote(mastername),
      'buildername': urllib2.quote(buildername),
      'buildnumber': urllib2.quote(str(buildnumber)),
      'stepname': urllib2.quote(stepname),
    }

    try:
      result = urlfetch.fetch(url)

      if result.status_code >= 200 and result.status_code < 400:
        json_result = json.loads(result.content)

        _, failed, _ = cls._flatten_tests(
            json_result.get('tests', {}),
            json_result.get('path_delimiter', '/'))
        if len(failed) > MAX_INDIVIDUAL_FLAKES_PER_STEP:
          return [stepname], True
        return failed, False

      if result.status_code == 404:
        # This is quite a common case (only some failing steps are actually
        # running tests and reporting results to flakiness dashboard).
        logging.info('Failed to retrieve JSON from %s', url)
      else:
        logging.exception('Failed to retrieve JSON from %s', url)
    except Exception:
      logging.exception('Failed to retrieve or parse JSON from %s', url)

    return [stepname], True
コード例 #3
0
ファイル: search.py プロジェクト: asdfghjjklllllaaa/infra
    def get(self):
        search = self.request.get('q')

        flake = Flake.query().filter(Flake.name == search).get()
        if flake:
            self.redirect('/all_flake_occurrences?key=%s' %
                          flake.key.urlsafe())
            return

        # Users might search using full step name. Try normalizing it before
        # searching. Note that this won't find flakes in a step where
        # chromium-try-flakes was able to determine which test has failed. Instead,
        # users should search using the test name.
        normalized_step_name = normalize_test_type(search)
        flake = Flake.query().filter(Flake.name == normalized_step_name).get()
        if flake:
            self.redirect('/all_flake_occurrences?key=%s' %
                          flake.key.urlsafe())
            return

        self.response.write('No flake entry found for ' + search)
コード例 #4
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_ignores_with_patch_when_asked_to(self):
     self.assertEqual(
         'base_unittests',
         util.normalize_test_type(
             'base_unittests on Windows XP (with patch)',
             ignore_with_patch=True))
コード例 #5
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_removes_instrumentation_test_prefix(self):
     self.assertEqual(
         'content_shell_test_apk (with patch)',
         util.normalize_test_type(
             'Instrumentation test content_shell_test_apk (with patch)'))
コード例 #6
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_without_patch_is_discarded(self):
     self.assertEqual(
         'base_unittests',
         util.normalize_test_type(
             'base_unittests on ATI GPU on Windows (without patch) on Windows'
         ))
コード例 #7
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_on_platform_with_parens_with_patch_even_more_noise(self):
     self.assertEqual(
         'base_unittests (with patch)',
         util.normalize_test_type(
             'base_unittests (ATI GPU) on Windows (with patch) on Windows'))
コード例 #8
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_on_platform_with_patch(self):
     self.assertEqual(
         'base_unittests (with patch)',
         util.normalize_test_type(
             'base_unittests on Windows XP (with patch)'))
コード例 #9
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_on_platform(self):
     self.assertEqual(
         'base_unittests',
         util.normalize_test_type('base_unittests on Windows XP'))
コード例 #10
0
ファイル: util_test.py プロジェクト: asdfghjjklllllaaa/infra
 def test_already_clean_name(self):
     self.assertEqual('base_unittests',
                      util.normalize_test_type('base_unittests'))
コード例 #11
0
  def post(self):
    if (not self.request.get('failure_run_key') or
        not self.request.get('success_run_key')):
      self.response.set_status(400, 'Invalid request parameters')
      return

    failure_run = ndb.Key(urlsafe=self.request.get('failure_run_key')).get()
    success_run = ndb.Key(urlsafe=self.request.get('success_run_key')).get()

    flaky_run = FlakyRun(
        failure_run=failure_run.key,
        failure_run_time_started=failure_run.time_started,
        failure_run_time_finished=failure_run.time_finished,
        success_run=success_run.key)

    failure_time = failure_run.time_finished
    patchset_builder_runs = failure_run.key.parent().get()

    master = BuildRun.removeMasterPrefix(patchset_builder_runs.master)
    url = ('https://chrome-build-extract.appspot.com/p/' + master +
           '/builders/' + patchset_builder_runs.builder +'/builds/' +
           str(failure_run.buildnumber) + '?json=1')
    urlfetch.set_default_fetch_deadline(60)
    logging.info('get_flaky_run_reason ' + url)
    response = urlfetch.fetch(url)
    if response.status_code >= 400 and response.status_code <= 599:
      logging.error('The request to %s has returned %d: %s', url,
                    response.status_code, response.content)
      self.response.set_status(500, 'Failed to fetch build.')
      return
    json_result = json.loads(response.content)
    steps = json_result['steps']

    failed_steps = []
    passed_steps = []
    for step in steps:
      result = step['results'][0]
      if build_result.isResultSuccess(result):
        passed_steps.append(step)
        continue
      if not build_result.isResultFailure(result):
        continue
      step_name = step['name']
      step_text = ' '.join(step['text'])
      if step_name in IGNORED_STEPS:
        continue

      # Custom (non-trivial) rules for ignoring flakes in certain steps:
      #  - [swarming] ...: summary step would also be red (do not double count)
      #  - Patch failure: ingore non-infra failures as they are typically due to
      #    changes in the code on HEAD
      #  - bot_update PATCH FAILED: Duplicates failure in 'Patch failure' step.
      #  - ... (retry summary): this is an artificial step to fail the build due
      #    to another step that has failed earlier (do not double count).
      if (step_name.startswith('[swarming]') or
          (step_name == 'Patch failure' and result != build_result.EXCEPTION) or
          (step_name == 'bot_update' and 'PATCH FAILED' in step_text)):
        continue

      failed_steps.append(step)

    steps_to_ignore = []
    for step in failed_steps:
      step_name = step['name']
      if '(with patch)' in step_name:
        # Ignore any steps from the same test suite, which is determined by the
        # normalized step name. Additionally, if the step fails without patch,
        # ignore the original step as well because tree is busted.
        normalized_step_name = normalize_test_type(step_name, True)
        for other_step in failed_steps:
          if other_step == step:
            continue
          normalized_other_step_name = normalize_test_type(
              other_step['name'], True)
          if normalized_other_step_name == normalized_step_name:
            steps_to_ignore.append(other_step['name'])
            if '(without patch)' in other_step['name']:
              steps_to_ignore.append(step['name'])

    flakes_to_update = []
    for step in failed_steps:
      step_name = step['name']
      if step_name in steps_to_ignore:
        continue
      flakes, is_step = self.get_flakes(
          master, patchset_builder_runs.builder, failure_run.buildnumber, step)
      for flake in flakes:
        flake_occurrence = FlakeOccurrence(name=step_name, failure=flake)
        flaky_run.flakes.append(flake_occurrence)
        flakes_to_update.append((flake, is_step))

    # Do not create FlakyRuns if all failed steps have been ignored.
    if not flaky_run.flakes:
      return

    flaky_run_key = flaky_run.put()
    for flake, is_step in flakes_to_update:
      self.add_failure_to_flake(flake, flaky_run_key, failure_time, is_step)
    self.flaky_runs.increment_by(1)
コード例 #12
0
  def post(self):
    if (not self.request.get('failure_run_key') or
        not self.request.get('success_run_key')):
      self.response.set_status(400, 'Invalid request parameters')
      return

    failure_run = ndb.Key(urlsafe=self.request.get('failure_run_key')).get()
    success_run = ndb.Key(urlsafe=self.request.get('success_run_key')).get()

    flaky_run = FlakyRun(
        failure_run=failure_run.key,
        failure_run_time_started=failure_run.time_started,
        failure_run_time_finished=failure_run.time_finished,
        success_run=success_run.key)

    failure_time = failure_run.time_finished
    patchset_builder_runs = failure_run.key.parent().get()

    master = BuildRun.removeMasterPrefix(patchset_builder_runs.master)
    url = ('https://luci-milo.appspot.com/'
           'prpc/milo.Buildbot/GetBuildbotBuildJSON')
    request = json.dumps({
        'master': master,
        'builder': patchset_builder_runs.builder,
        'buildNum': failure_run.buildnumber,
    })
    headers = {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
    }
    urlfetch.set_default_fetch_deadline(60)
    logging.info('get_flaky_run_reason: %s, %s', url, request)
    response = urlfetch.fetch(
        url, payload=request, method=urlfetch.POST, headers=headers,
        validate_certificate=True)
    if response.status_code != 200:
      logging.error('The request to %s has returned %d: %s', url,
                    response.status_code, response.content)
      self.response.set_status(500, 'Failed to fetch build.')
      return
    content = response.content
    if content.startswith(_MILO_RESPONSE_PREFIX):
      content = content[len(_MILO_RESPONSE_PREFIX):]
    data = json.loads(content)['data']
    json_result = json.loads(base64.b64decode(data))
    steps = json_result['steps']

    failed_steps = []
    passed_steps = []
    for step in steps:
      result = step['results'][0]
      if build_result.isResultSuccess(result):
        passed_steps.append(step)
        continue
      if not build_result.isResultFailure(result):
        continue
      # For Luci builds, some steps don't have step text anymore. Such steps
      # include 'Failure reason', 'analyze', etc.
      step_text = ' '.join(step['text'] or [])
      step_name = step['name']
      if step_name in IGNORED_STEPS:
        continue

      # Custom (non-trivial) rules for ignoring flakes in certain steps:
      #  - [swarming] ...: summary step would also be red (do not double count)
      #  - Patch failure: ingore non-infra failures as they are typically due to
      #    changes in the code on HEAD
      #  - bot_update PATCH FAILED: Duplicates failure in 'Patch failure' step.
      #  - ... (retry summary): this is an artificial step to fail the build due
      #    to another step that has failed earlier (do not double count).
      if (step_name.startswith('[swarming]') or
          (step_name == 'Patch failure' and result != build_result.EXCEPTION) or
          (step_name == 'bot_update' and 'PATCH FAILED' in step_text)):
        continue

      failed_steps.append(step)

    steps_to_ignore = []
    for step in failed_steps:
      step_name = step['name']
      if '(with patch)' in step_name:
        # Ignore any steps from the same test suite, which is determined by the
        # normalized step name. Additionally, if the step fails without patch,
        # ignore the original step as well because tree is busted.
        normalized_step_name = normalize_test_type(step_name, True)
        for other_step in failed_steps:
          if other_step == step:
            continue
          normalized_other_step_name = normalize_test_type(
              other_step['name'], True)
          if normalized_other_step_name == normalized_step_name:
            steps_to_ignore.append(other_step['name'])
            if '(without patch)' in other_step['name']:
              steps_to_ignore.append(step['name'])

    flakes_to_update = []
    for step in failed_steps:
      step_name = step['name']
      if step_name in steps_to_ignore:
        continue
      flakes, is_step = self.get_flakes(
          master, patchset_builder_runs.builder, failure_run.buildnumber, step)
      if is_step and not is_infra_step_flake(step_name):
        continue  # Ignore flakes of non-infra steps.
      for flake in flakes:
        flake_occurrence = FlakeOccurrence(name=step_name, failure=flake)
        flaky_run.flakes.append(flake_occurrence)
        flakes_to_update.append((flake, is_step))

    # Do not create FlakyRuns if all failed steps have been ignored.
    if not flaky_run.flakes:
      return

    flaky_run_key = flaky_run.put()
    for flake, is_step in flakes_to_update:
      if self.is_duplicate_occurrence(flake, flaky_run):
        logging.info('Not adding duplicate occurrence for the same CL')
        continue
      self.add_failure_to_flake(flake, flaky_run_key, failure_time, is_step)
    self.flaky_runs.increment_by(1)
コード例 #13
0
def show_all_flakes(flake, show_all):
  from_index = 0 if show_all else -MAX_OCCURRENCES_DEFAULT
  occurrences = filterNone(ndb.get_multi(flake.occurrences[from_index:]))

  failure_runs_keys = []
  patchsets_keys = []
  flakes = []
  step_names = set()
  for o in occurrences:
    failure_runs_keys.append(o.failure_run)
    patchsets_keys.append(o.failure_run.parent())
    matching_flakes = [f for f in o.flakes if f.failure == flake.name]
    flakes.append(matching_flakes)
    step_names.update(normalize_test_type(f.name) for f in matching_flakes)

  failure_runs = filterNone(ndb.get_multi(failure_runs_keys))
  patchsets = filterNone(ndb.get_multi(patchsets_keys))

  class FailureRunExtended:
    def __init__(self, url, milo_url, patchset_url, builder, formatted_time,
                 issue_ids, time_finished):
      self.url = url
      self.milo_url = milo_url
      self.patchset_url = patchset_url
      self.builder = builder
      self.formatted_time = formatted_time
      self.issue_ids = issue_ids
      self.time_finished = time_finished

  failure_runs_extended = []
  for index, fr in enumerate(failure_runs):
    failure_runs_extended.append(FailureRunExtended(
      fr.getURL(),
      fr.getMiloURL(),
      patchsets[index].getURL(),
      patchsets[index].builder,
      fr.time_finished.strftime('%Y-%m-%d %H:%M:%S UTC'),
      set([f.issue_id for f in flakes[index] if f.issue_id > 0]),
      fr.time_finished,
    ))

  # Do simple sorting to make reading easier.
  failure_runs_extended = sorted(
      failure_runs_extended, key=RunsSortFunction, reverse=True)

  # Group flaky runs into periods separated by at least 3 days.
  grouped_runs = []
  if failure_runs_extended:
    current_group = [failure_runs_extended[0]]
    for f in failure_runs_extended[1:]:
      if current_group[-1].time_finished - f.time_finished < MAX_GROUP_DISTANCE:
        current_group.append(f)
      else:
        grouped_runs.append(current_group)
        current_group = [f]
    grouped_runs.append(current_group)

  show_all_link = (len(flake.occurrences) > MAX_OCCURRENCES_DEFAULT and
                   not show_all)
  data = {
    'flake': flake,
    'grouped_runs': grouped_runs,
    'show_all_link': show_all_link,
    'time_now': datetime.datetime.utcnow(),
  }

  if not flake.is_step:
    data['flakiness_dashboard_urls'] = [
      {
        'url': FLAKINESS_DASHBOARD_URL % {
          'normalized_step_name': step_name,
          'test_name': flake.name
        },
        'step_name': step_name,
      } for step_name in step_names
    ]

  return data