Index: commit-queue/verification/try_job_on_rietveld.py |
=================================================================== |
--- commit-queue/verification/try_job_on_rietveld.py (revision 249146) |
+++ commit-queue/verification/try_job_on_rietveld.py (working copy) |
@@ -1,755 +0,0 @@ |
-# coding=utf8 |
-# Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-# Use of this source code is governed by a BSD-style license that can be |
-# found in the LICENSE file. |
-"""Sends patches to the Try server and reads back results. |
- |
-- RietveldTryJobs contains RietveldTryJob, one per try job on a builder. |
-- TryRunnerRietveld uses Rietveld to signal and poll job results. |
-""" |
- |
-import collections |
-import errno |
-import logging |
-import re |
-import socket |
-import time |
-import urllib2 |
- |
-import buildbot_json |
-import model |
-from verification import base |
-from verification import try_job_steps |
- |
-# A build running for longer than this is considered to be timed out. |
-TIMED_OUT = 12 * 60 * 60 |
- |
- |
-def is_job_expired(now, revision, timestamp, checkout): |
- """Returns False if the job result is still somewhat valid. |
- |
- A job that occured more than 4 days ago or more than 200 commits behind |
- is 'expired'. |
- """ |
- if timestamp < (now - 4*24*60*60): |
- return True |
- if checkout.revisions(revision, None) >= 200: |
- return True |
- return False |
- |
- |
-TryJobProperties = collections.namedtuple( |
- 'TryJobProperties', |
- ['key', 'parent_key', 'builder', 'build', 'buildnumber', 'properties']) |
- |
- |
-def filter_jobs(try_job_results, watched_builders, current_irrelevant_keys, |
- status): |
- """For each try jobs results, query the Try Server for updated status and |
- returns details about each job in a TryJobProperties. |
- |
- Returns a list of namedtuple describing the updated results and and the new |
- list of irrelevant keys. |
- |
- It adds the build to the ignored list if the build doesn't exist on the Try |
- Server anymore (usually it's too old) or if the try job was not triggered by |
- the Commit Queue itself. |
- """ |
- irrelevant = set(current_irrelevant_keys) |
- try_jobs_with_props = [] |
- for result in try_job_results: |
- key = result['key'] |
- assert key |
- if key in current_irrelevant_keys: |
- continue |
- builder = result['builder'] |
- try: |
- buildnumber = int(result['buildnumber']) |
- except (TypeError, ValueError): |
- continue |
- if buildnumber < 0: |
- logging.debug('Ignoring %s/%d; invalid', builder, buildnumber) |
- irrelevant.add(key) |
- continue |
- |
- if builder not in watched_builders: |
- logging.debug('Ignoring %s/%d; no step verifier is examining it', builder, |
- buildnumber) |
- irrelevant.add(key) |
- continue |
- |
- # Constructing the object itself doesn't throw an exception, it's reading |
- # its properties that throws. |
- build = status.builders[builder].builds[buildnumber] |
- try: |
- props = build.properties_as_dict |
- except IOError: |
- logging.info( |
- 'Build %s/%s is not on the try server anymore', |
- builder, buildnumber) |
- irrelevant.add(key) |
- continue |
- parent_key = props.get('parent_try_job_key') |
- if parent_key: |
- # Triggered build |
- key = '%s/%d_triggered_%s' % (builder, buildnumber, parent_key) |
- elif props.get('try_job_key') != key: |
- # not triggered, not valid |
- logging.debug( |
- 'Ignoring %s/%d; not from rietveld', builder, buildnumber) |
- irrelevant.add(key) |
- continue |
- |
- try_jobs_with_props.append( |
- TryJobProperties(key, parent_key, builder, build, buildnumber, props)) |
- |
- # Sort the non-triggered builds first so triggered jobs |
- # can expect their parent to be added to self.try_jobs |
- try_jobs_with_props.sort(key=lambda tup: tup.parent_key) |
- |
- return try_jobs_with_props, list(irrelevant) |
- |
- |
-def _is_skip_try_job(pending): |
- """Returns True if a description contains NOTRY=true.""" |
- match = re.search(r'^NOTRY=(.*)$', pending.description, re.MULTILINE) |
- return match and match.group(1).lower() == 'true' |
- |
- |
-class RietveldTryJobPending(model.PersistentMixIn): |
- """Represents a pending try job for a pending commit that we care about. |
- |
- It is immutable. |
- """ |
- builder = unicode |
- revision = (None, unicode, int) |
- requested_steps = list |
- clobber = bool |
- # Number of retries for this configuration. Initial try is 1. |
- tries = int |
- init_time = float |
- |
- def __init__(self, **kwargs): |
- required = set(self._persistent_members()) |
- actual = set(kwargs) |
- assert required == actual, (required - actual, required, actual) |
- super(RietveldTryJobPending, self).__init__(**kwargs) |
- # Then mark it read-only. |
- self._read_only = True |
- |
- |
-class RietveldTryJob(model.PersistentMixIn): |
- """Represents a try job for a pending commit that we care about. |
- |
- This data can be regenerated by parsing all the try job names but it is a bit |
- hard on the try server. |
- |
- It is immutable. |
- """ |
- builder = unicode |
- build = int |
- revision = (None, unicode, int) |
- requested_steps = list |
- # The timestamp when the build started. buildbot_json returns int. |
- started = int |
- steps_passed = list |
- steps_failed = list |
- clobber = bool |
- completed = bool |
- # Number of retries for this configuration. Initial try is 1. |
- tries = int |
- parent_key = (None, unicode) |
- init_time = float |
- |
- def __init__(self, **kwargs): |
- required = set(self._persistent_members()) |
- actual = set(kwargs) |
- assert required == actual, (required - actual, required, actual) |
- super(RietveldTryJob, self).__init__(**kwargs) |
- # Then mark it read-only. |
- self._read_only = True |
- |
- @property |
- @model.immutable |
- def result(self): |
- if self.steps_failed: |
- return buildbot_json.FAILURE |
- if self.completed: |
- return buildbot_json.SUCCESS |
- return None |
- |
- |
-class RietveldTryJobs(base.IVerifierStatus): |
- """A set of try jobs that were sent for a specific patch. |
- |
- Multiple concurrent try jobs can be sent on a single builder. For example, a |
- previous valid try job could have been triggered by the user but was not |
- completed so another was sent with the missing tests. |
- Also, a try job is sent as soon as a test failure is detected. |
- """ |
- # An dict of RietveldTryJob objects per key. |
- try_jobs = dict |
- # The try job keys we ignore because they can't be used to give a good |
- # signal: either they are too old (old revision) or they were not triggerd |
- # by Rietveld, so we don't know if the diff is 100% good. |
- irrelevant = list |
- # When NOTRY=true is specified. |
- skipped = bool |
- # List of test verifiers. All the logic to decide when they are |
- # and what bots they trigger is hidden inside. |
- step_verifiers = list |
- # Jobs that have been sent but are not found yet. Likely a builder is fully |
- # utilized or the try server hasn't polled Rietveld yet. list of |
- # RietveldTryJobPending() instances. |
- pendings = list |
- |
- @model.immutable |
- def get_state(self): |
- """Returns the state of this verified. |
- |
- Failure can be from: |
- - For each entry in self.step_verifiers: |
- - A Try Job in self.try_jobs has been retried too often. |
- |
- In particular, there is no need to wait for every Try Job to complete. |
- """ |
- if self.error_message: |
- return base.FAILED |
- if not self.tests_waiting_for_result(): |
- return base.SUCCEEDED |
- return base.PROCESSING |
- |
- @model.immutable |
- def tests_need_to_be_run(self, now): |
- """Returns which tests need to be run. |
- |
- These are the tests that are not pending on any try job, either running or |
- in the pending list. |
- """ |
- # Skipped or failed, nothing to do. |
- if self.skipped or self.error_message: |
- return {} |
- |
- # What originally needed to be run. |
- # All_tests is {builder_name: set(test_name*)} |
- all_tests = {} |
- for verifier in self.step_verifiers: |
- (builder, tests) = verifier.need_to_trigger(self.try_jobs, now) |
- if tests: |
- all_tests.setdefault(builder, set()).update(tests) |
- |
- # Removes what is queued to be run but hasn't started yet. |
- for try_job in self.pendings: |
- if try_job.builder in all_tests: |
- all_tests[try_job.builder] -= set(try_job.requested_steps) |
- |
- return dict( |
- (builder, sorted(tests)) for builder, tests in all_tests.iteritems() |
- if tests) |
- |
- @model.immutable |
- def tests_waiting_for_result(self): |
- """Returns the tests that we are waiting for results on pending or running |
- builds. |
- """ |
- # Skipped or failed, nothing to do. |
- if self.skipped or self.error_message: |
- return {} |
- |
- # What originally needed to be run. |
- all_tests = {} |
- for verification in self.step_verifiers: |
- (builder, tests) = verification.waiting_for(self.try_jobs) |
- if tests: |
- all_tests.setdefault(builder, set()).update(tests) |
- |
- # Removes what was run. |
- for try_job in self.try_jobs.itervalues(): |
- if try_job.builder in all_tests: |
- all_tests[try_job.builder] -= set(try_job.steps_passed) |
- |
- return dict( |
- (builder, list(tests)) for builder, tests in all_tests.iteritems() |
- if tests) |
- |
- @model.immutable |
- def watched_builders(self): |
- """Marks all the jobs that the step_verifiers don't examine as |
- irrelevant. |
- """ |
- # Generate the list of builders to keep. |
- watched_builders = set() |
- for step_verifier in self.step_verifiers: |
- watched_builders.add(step_verifier.builder_name) |
- if isinstance(step_verifier, try_job_steps.TryJobTriggeredSteps): |
- watched_builders.add(step_verifier.trigger_name) |
- |
- return watched_builders |
- |
- def update_jobs_from_rietveld( |
- self, data, status, checkout, now): |
- """Retrieves the jobs statuses from rietveld and updates its state. |
- |
- Args: |
- owner: Owner of the CL. |
- data: Patchset properties as returned from Rietveld. |
- status: A buildbot_json.Buildbot instance. |
- checkout: A depot_tools' Checkout instance. |
- now: epoch time of what should be considered to be 'now'. |
- |
- Returns: |
- Keys which were updated. |
- """ |
- updated = [] |
- try_job_results = data.get('try_job_results', []) |
- logging.debug('Found %d entries', len(try_job_results)) |
- |
- try_jobs_with_props, self.irrelevant = filter_jobs( |
- try_job_results, self.watched_builders() , self.irrelevant, status) |
- |
- # Ensure that all irrelevant jobs have been removed from the set of valid |
- # try jobs. |
- for irrelevant_key in self.irrelevant: |
- if irrelevant_key in self.try_jobs: |
- del self.try_jobs[irrelevant_key] |
- if irrelevant_key + '_old' in self.try_jobs: |
- del self.try_jobs[irrelevant_key + '_old'] |
- |
- for i in try_jobs_with_props: |
- if self._update_try_job_status(checkout, i, now): |
- updated.append(i.key) |
- return updated |
- |
- def _update_try_job_status(self, checkout, try_job_properties, now): |
- """Updates status of a specific RietveldTryJob. |
- |
- try_job_property is an instance of TryJobProperties. |
- |
- Returns True if it was updated. |
- """ |
- key = try_job_properties.key |
- builder = try_job_properties.builder |
- buildnumber = try_job_properties.buildnumber |
- if key in self.irrelevant: |
- logging.debug('Ignoring %s/%d; irrelevant', builder, buildnumber) |
- return False |
- if (try_job_properties.parent_key and |
- try_job_properties.parent_key not in self.try_jobs): |
- logging.debug('Ignoring %s, parent unknown', key) |
- return False |
- |
- requested_steps = [] |
- # Set it to 0 as the default value since when the job is new and previous |
- # try jobs are found, we don't want to count them as tries. |
- tries = 0 |
- job = self.try_jobs.get(key) |
- build = try_job_properties.build |
- if job: |
- if job.completed: |
- logging.debug('Ignoring %s/%d; completed', builder, buildnumber) |
- return False |
- else: |
- if now - job.started > TIMED_OUT: |
- # Flush it and start over. |
- self.irrelevant.append(key) |
- del self.try_jobs[key] |
- return False |
- requested_steps = job.requested_steps |
- tries = job.tries |
- init_time = job.init_time |
- else: |
- # This try job is new. See if we triggered it previously by |
- # looking in self.pendings. |
- for index, pending_job in enumerate(self.pendings): |
- if pending_job.builder == builder: |
- # Reuse its item. |
- requested_steps = pending_job.requested_steps |
- tries = pending_job.tries |
- self.pendings.pop(index) |
- break |
- else: |
- # Is this a good build? It must not be too old and triggered by |
- # rietveld. |
- if is_job_expired(now, build.revision, build.start_time, checkout): |
- logging.debug('Ignoring %s/%d; expired', builder, buildnumber) |
- self.irrelevant.append(key) |
- return False |
- init_time = now |
- |
- passed = [s.name for s in build.steps if s.simplified_result] |
- failed = [s.name for s in build.steps if s.simplified_result is False] |
- # The steps in neither passed or failed were skipped. |
- new_job = RietveldTryJob( |
- init_time=init_time, |
- builder=builder, |
- build=buildnumber, |
- revision=build.revision, |
- requested_steps=requested_steps, |
- started=build.start_time, |
- steps_passed=passed, |
- steps_failed=failed, |
- clobber=bool(try_job_properties.properties.get('clobber')), |
- completed=build.completed, |
- tries=tries, |
- parent_key=try_job_properties.parent_key) |
- if job and job.build and new_job.build and job.build != new_job.build: |
- # It's tricky because 'key' is the same for both. The trick is to create |
- # a fake key for the old build and mark it as completed. Note that |
- # Rietveld is confused by it too. |
- logging.warning( |
- 'Try Server was restarted and restarted builds with the same keys. ' |
- 'I\'m confused. %s: %d != %d', job.builder, job.build, new_job.build) |
- # Resave the old try job and mark it as completed. |
- self.try_jobs[key + '_old'] = RietveldTryJob( |
- init_time=job.init_time, |
- builder=job.builder, |
- build=job.build, |
- revision=job.revision, |
- requested_steps=job.requested_steps, |
- started=build.start_time, |
- steps_passed=job.steps_passed, |
- steps_failed=job.steps_failed, |
- clobber=job.clobber, |
- completed=True, |
- tries=job.tries, |
- parent_key=job.parent_key) |
- if not job or not model.is_equivalent(new_job, job): |
- logging.info( |
- 'Job update: %s: %s/%d', |
- try_job_properties.properties.get('issue'), |
- builder, |
- buildnumber) |
- self.try_jobs[key] = new_job |
- return key |
- |
- def signal_as_failed_if_needed(self, job, url, now): |
- """Detects if the RietveldTryJob instance is in a state where it is |
- impossible to make progress. |
- |
- If so, mark ourself as failed by setting self.error_message and return True. |
- """ |
- if self.skipped or self.error_message: |
- return False |
- # Figure out steps that should be retried for this builder. |
- missing_tests = self.tests_need_to_be_run(now).get(job.builder, []) |
- if not missing_tests: |
- return False |
- if job.tries > 2: |
- self.error_message = ( |
- 'Retried try job too often on %s for step(s) %s\n%s' % |
- (job.builder, ', '.join(missing_tests), url)) |
- logging.info(self.error_message) |
- return True |
- return False |
- |
- @model.immutable |
- def why_not(self): |
- # Skipped or failed, nothing to do. |
- if self.skipped or self.error_message: |
- return None |
- waiting = self.tests_waiting_for_result() |
- if waiting: |
- out = 'Waiting for the following jobs:\n' |
- for builder in sorted(waiting): |
- out += ' %s: %s\n' % (builder, ','.join(waiting[builder])) |
- return out |
- |
- |
-class TryRunnerRietveld(base.VerifierCheckout): |
- """Stateless communication with a try server. |
- |
- Uses Rietveld to trigger the try job and reads try job status with the json |
- API. |
- |
- Analysis goes as following: |
- - compile step itself is not flaky. compile.py already takes care of most |
- flakiness and clobber build is done by default. If compile step fails, try |
- again with clobber=True |
- - test steps are flaky and can be retried as necessary. |
- |
- 1. For each existing try jobs from rietveld. |
- 1. Fetch result from try server. |
- 2. If try job was generated from rietveld; |
- 1. If not is_job_expired(); |
- 1. Skip any scheduled test that succeeded on this builder. |
- 2. For each builder with tests scheduled; |
- 1. If no step waiting to be triggered, skip this builder completely. |
- 2. For each non succeeded job; |
- 1. Send try jobs to rietveld. |
- |
- Note: It needs rietveld, hence it uses VerifierCheckout, but it doesn't need a |
- checkout. |
- """ |
- name = 'try job rietveld' |
- |
- # Only updates a job status once every 60 seconds. |
- update_latency = 60 |
- |
- def __init__( |
- self, context_obj, try_server_url, commit_user, step_verifiers, |
- ignored_steps, solution): |
- super(TryRunnerRietveld, self).__init__(context_obj) |
- self.try_server_url = try_server_url.rstrip('/') |
- self.commit_user = commit_user |
- # TODO(maruel): Have it be overridden by presubmit_support.DoGetTrySlaves. |
- self.step_verifiers = step_verifiers |
- self.ignored_steps = set(ignored_steps) |
- # Time to poll the Try Server, and not Rietveld. |
- self.last_update = time.time() - self.update_latency |
- self.solution = solution |
- |
- def verify(self, pending): |
- """Sends a try job to the try server and returns a RietveldTryJob list. |
- |
- This function is called synchronously. |
- """ |
- jobs = pending.verifications.setdefault(self.name, RietveldTryJobs()) |
- if _is_skip_try_job(pending): |
- # Do not run try job for it. |
- jobs.skipped = True |
- return |
- |
- # Overridde any previous list from the last restart. |
- jobs.step_verifiers = [] |
- for step in self.step_verifiers: |
- if isinstance(step, try_job_steps.TryJobTriggeredOrNormalSteps): |
- # Since the steps are immutable, create a new step so that swarm |
- # can be enabled. |
- jobs.step_verifiers.append(try_job_steps.TryJobTriggeredOrNormalSteps( |
- builder_name=step.builder_name, |
- trigger_name=step.trigger_name, |
- steps=step.steps, |
- trigger_bot_steps=step.trigger_bot_steps, |
- use_triggered_bot=True)) |
- else: |
- jobs.step_verifiers.append(step) |
- |
- # First, update the status of the current try jobs on Rietveld. |
- now = time.time() |
- self._update_jobs_from_rietveld(pending, jobs, False, now) |
- |
- # Add anything that is missing. |
- self._send_jobs(pending, jobs, now) |
- |
- # Slightly postpone next check. |
- self.last_update = min(now, self.last_update + (self.update_latency / 4)) |
- |
- def update_status(self, queue): |
- """Grabs the current status of all try jobs and update self.queue. |
- |
- Note: it would be more efficient to be event based. |
- """ |
- if not queue: |
- logging.debug('The list is empty, nothing to do') |
- return |
- |
- # Hard code 'now' to the value before querying and sending them. This will |
- # cause some issues when querying state or sending the jobs takes a |
- # non-trivial amount of time but in general it will be fine. |
- now = time.time() |
- if now - self.last_update < self.update_latency: |
- logging.debug('TS: Throttling updates') |
- return |
- self.last_update = now |
- |
- # Update the status of the current pending CLs on Rietveld. |
- for pending, jobs in self.loop(queue, RietveldTryJobs, True): |
- # Update 'now' since querying the try jobs may take a significant amount |
- # of time. |
- now = time.time() |
- if self._update_jobs_from_rietveld(pending, jobs, True, now): |
- # Send any necessary job. Noop if not needed. |
- self._send_jobs(pending, jobs, now) |
- |
- def _add_pending_job_and_send_if_needed(self, builder, steps, jobs, |
- send_job, pending, now): |
- # Find if there was a previous try. |
- previous_jobs = [ |
- job for job in jobs.try_jobs.itervalues() if job.builder == builder |
- ] |
- if previous_jobs: |
- tries = max(job.tries for job in previous_jobs) |
- clobber = max( |
- (job.clobber or 'compile' in job.steps_failed) |
- for job in previous_jobs) |
- else: |
- tries = 0 |
- clobber = False |
- if tries > 4: |
- # Fail safe. |
- jobs.error_message = ( |
- ( 'The commit queue went berserk retrying too often for a\n' |
- 'seemingly flaky test on builder %s:\n%s') % |
- ( builder, |
- '\n'.join(self._build_status_url(j) for j in previous_jobs))) |
- return False |
- |
- # Don't always send the job (triggered bots don't need to send there own |
- # request). |
- if send_job: |
- logging.debug( |
- 'Sending job %s for %s: %s', pending.issue, builder, ','.join(steps)) |
- try: |
- self.context.rietveld.trigger_try_jobs( |
- pending.issue, pending.patchset, 'CQ', clobber, 'HEAD', |
- {builder: steps}) |
- except urllib2.HTTPError as e: |
- if e.code == 400: |
- # This probably mean a new patchset was uploaded since the last poll, |
- # so it's better to drop the CL. |
- jobs.error_message = 'Failed to trigger a try job on %s\n%s' % ( |
- builder, e) |
- return False |
- else: |
- raise |
- |
- # Set the status of this pending job here and on the CQ page. |
- jobs.pendings.append( |
- RietveldTryJobPending( |
- init_time=now, |
- builder=builder, |
- revision=None, |
- requested_steps=steps, |
- clobber=clobber, |
- tries=tries + 1)) |
- # Update the status on the AppEngine status to signal a new try job was |
- # sent. |
- info = { |
- 'builder': builder, |
- 'clobber': clobber, |
- 'job_name': 'CQ', |
- 'revision': None, #revision, |
- } |
- self.send_status(pending, info) |
- return True |
- |
- def _get_triggered_bots(self, builder, steps): |
- """Returns a dict of all the (builder, steps) pairs of bots that will get |
- triggered by the given builder steps combination.""" |
- triggered_bots = {} |
- for verifier in self.step_verifiers: |
- builder, steps = verifier.get_triggered_steps(builder, steps) |
- if steps: |
- triggered_bots[builder] = steps |
- |
- return triggered_bots |
- |
- def _send_jobs(self, pending, jobs, now): |
- """Prepares the RietveldTryJobs instance |jobs| to send try jobs to the try |
- server. |
- """ |
- if jobs.error_message: |
- # Too late. |
- return |
- remaining = jobs.tests_need_to_be_run(now) |
- if not remaining: |
- return |
- # Send them in order to simplify testing. |
- for builder in sorted(remaining): |
- tests = remaining[builder] |
- if not self._add_pending_job_and_send_if_needed(builder, tests, jobs, |
- True, pending, now): |
- # If the main job wasn't sent, we can't skip the triggered jobs since |
- # they won't get triggered. |
- continue |
- |
- # Add any pending bots that will be triggered from this build. |
- triggered_bots = self._get_triggered_bots(builder, tests) |
- for builder, steps in triggered_bots.iteritems(): |
- self._add_pending_job_and_send_if_needed(builder, steps, jobs, False, |
- pending, now) |
- |
- @model.immutable |
- def _build_status_url(self, job): |
- """Html url for this try job.""" |
- assert job.build is not None, str(job) |
- return '%s/buildstatus?builder=%s&number=%s' % ( |
- self.try_server_url, job.builder, job.build) |
- |
- @model.immutable |
- def _update_dashboard(self, pending, job): |
- """Updates the CQ dashboard with the current Try Job state as known to the |
- CQ. |
- """ |
- logging.debug('_update_dashboard(%s/%s)', job.builder, job.build) |
- info = { |
- 'build': job.build, |
- 'builder': job.builder, |
- 'job_name': 'CQ', |
- 'result': job.result, |
- 'revision': job.revision, |
- 'url': self._build_status_url(job), |
- } |
- self.send_status(pending, info) |
- |
- def _update_jobs_from_rietveld(self, pending, jobs, handle, now): |
- """Grabs data from Rietveld and pass it to |
- RietveldTryJobs.update_jobs_from_rietveld(). |
- |
- Returns True on success. |
- """ |
- status = buildbot_json.Buildbot(self.try_server_url) |
- try: |
- try: |
- data = self.context.rietveld.get_patchset_properties( |
- pending.issue, pending.patchset) |
- except urllib2.HTTPError as e: |
- if e.code == 404: |
- # TODO(phajdan.jr): Maybe generate a random id to correlate the user's |
- # error message and exception in the logs. |
- # Don't put exception traceback in the user-visible message to avoid |
- # leaking sensitive CQ data (passwords etc). |
- jobs.error_message = ('Failed to get patchset properties (patchset ' |
- 'not found?)') |
- logging.error(str(e)) |
- return False |
- else: |
- raise |
- |
- # Update the RietvedTryJobs object. |
- keys = jobs.update_jobs_from_rietveld( |
- data, |
- status, |
- self.context.checkout, |
- now) |
- except urllib2.HTTPError as e: |
- if e.code in (500, 502, 503): |
- # Temporary AppEngine hiccup. Just log it and return failure. |
- logging.warning('%s while accessing %s. Ignoring error.' % ( |
- str(e), e.url)) |
- return False |
- else: |
- raise |
- except urllib2.URLError as e: |
- if 'timed out' in e.reason: |
- # Handle timeouts gracefully. |
- logging.warning('%s while updating tryserver status for ' |
- 'rietveld issue %s', e, pending.issue) |
- return False |
- else: |
- raise |
- except socket.error as e: |
- # Temporary AppEngine hiccup. Just log it and return failure. |
- if e.errno == errno.ECONNRESET: |
- logging.warning( |
- '%s while updating tryserver status for rietveld issue %s.' % ( |
- str(e), str(pending.issue))) |
- return False |
- else: |
- raise |
- except IOError as e: |
- # Temporary AppEngine hiccup. Just log it and return failure. |
- if e.errno == 'socket error': |
- logging.warning( |
- '%s while updating tryserver status for rietveld issue %s.' % ( |
- str(e), str(pending.issue))) |
- return False |
- raise |
- |
- if handle: |
- for updated_key in keys: |
- job = jobs.try_jobs[updated_key] |
- self._update_dashboard(pending, job) |
- jobs.signal_as_failed_if_needed(job, self._build_status_url(job), now) |
- |
- return True |