Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(335)

Side by Side Diff: commit-queue/verification/try_job_on_rietveld.py

Issue 135363007: Delete public commit queue to avoid confusion after move to internal repo (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # coding=utf8
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5 """Sends patches to the Try server and reads back results.
6
7 - RietveldTryJobs contains RietveldTryJob, one per try job on a builder.
8 - TryRunnerRietveld uses Rietveld to signal and poll job results.
9 """
10
11 import collections
12 import errno
13 import logging
14 import re
15 import socket
16 import time
17 import urllib2
18
19 import buildbot_json
20 import model
21 from verification import base
22 from verification import try_job_steps
23
24 # A build running for longer than this is considered to be timed out.
25 TIMED_OUT = 12 * 60 * 60
26
27
28 def is_job_expired(now, revision, timestamp, checkout):
29 """Returns False if the job result is still somewhat valid.
30
31 A job that occured more than 4 days ago or more than 200 commits behind
32 is 'expired'.
33 """
34 if timestamp < (now - 4*24*60*60):
35 return True
36 if checkout.revisions(revision, None) >= 200:
37 return True
38 return False
39
40
41 TryJobProperties = collections.namedtuple(
42 'TryJobProperties',
43 ['key', 'parent_key', 'builder', 'build', 'buildnumber', 'properties'])
44
45
46 def filter_jobs(try_job_results, watched_builders, current_irrelevant_keys,
47 status):
48 """For each try jobs results, query the Try Server for updated status and
49 returns details about each job in a TryJobProperties.
50
51 Returns a list of namedtuple describing the updated results and and the new
52 list of irrelevant keys.
53
54 It adds the build to the ignored list if the build doesn't exist on the Try
55 Server anymore (usually it's too old) or if the try job was not triggered by
56 the Commit Queue itself.
57 """
58 irrelevant = set(current_irrelevant_keys)
59 try_jobs_with_props = []
60 for result in try_job_results:
61 key = result['key']
62 assert key
63 if key in current_irrelevant_keys:
64 continue
65 builder = result['builder']
66 try:
67 buildnumber = int(result['buildnumber'])
68 except (TypeError, ValueError):
69 continue
70 if buildnumber < 0:
71 logging.debug('Ignoring %s/%d; invalid', builder, buildnumber)
72 irrelevant.add(key)
73 continue
74
75 if builder not in watched_builders:
76 logging.debug('Ignoring %s/%d; no step verifier is examining it', builder,
77 buildnumber)
78 irrelevant.add(key)
79 continue
80
81 # Constructing the object itself doesn't throw an exception, it's reading
82 # its properties that throws.
83 build = status.builders[builder].builds[buildnumber]
84 try:
85 props = build.properties_as_dict
86 except IOError:
87 logging.info(
88 'Build %s/%s is not on the try server anymore',
89 builder, buildnumber)
90 irrelevant.add(key)
91 continue
92 parent_key = props.get('parent_try_job_key')
93 if parent_key:
94 # Triggered build
95 key = '%s/%d_triggered_%s' % (builder, buildnumber, parent_key)
96 elif props.get('try_job_key') != key:
97 # not triggered, not valid
98 logging.debug(
99 'Ignoring %s/%d; not from rietveld', builder, buildnumber)
100 irrelevant.add(key)
101 continue
102
103 try_jobs_with_props.append(
104 TryJobProperties(key, parent_key, builder, build, buildnumber, props))
105
106 # Sort the non-triggered builds first so triggered jobs
107 # can expect their parent to be added to self.try_jobs
108 try_jobs_with_props.sort(key=lambda tup: tup.parent_key)
109
110 return try_jobs_with_props, list(irrelevant)
111
112
113 def _is_skip_try_job(pending):
114 """Returns True if a description contains NOTRY=true."""
115 match = re.search(r'^NOTRY=(.*)$', pending.description, re.MULTILINE)
116 return match and match.group(1).lower() == 'true'
117
118
119 class RietveldTryJobPending(model.PersistentMixIn):
120 """Represents a pending try job for a pending commit that we care about.
121
122 It is immutable.
123 """
124 builder = unicode
125 revision = (None, unicode, int)
126 requested_steps = list
127 clobber = bool
128 # Number of retries for this configuration. Initial try is 1.
129 tries = int
130 init_time = float
131
132 def __init__(self, **kwargs):
133 required = set(self._persistent_members())
134 actual = set(kwargs)
135 assert required == actual, (required - actual, required, actual)
136 super(RietveldTryJobPending, self).__init__(**kwargs)
137 # Then mark it read-only.
138 self._read_only = True
139
140
141 class RietveldTryJob(model.PersistentMixIn):
142 """Represents a try job for a pending commit that we care about.
143
144 This data can be regenerated by parsing all the try job names but it is a bit
145 hard on the try server.
146
147 It is immutable.
148 """
149 builder = unicode
150 build = int
151 revision = (None, unicode, int)
152 requested_steps = list
153 # The timestamp when the build started. buildbot_json returns int.
154 started = int
155 steps_passed = list
156 steps_failed = list
157 clobber = bool
158 completed = bool
159 # Number of retries for this configuration. Initial try is 1.
160 tries = int
161 parent_key = (None, unicode)
162 init_time = float
163
164 def __init__(self, **kwargs):
165 required = set(self._persistent_members())
166 actual = set(kwargs)
167 assert required == actual, (required - actual, required, actual)
168 super(RietveldTryJob, self).__init__(**kwargs)
169 # Then mark it read-only.
170 self._read_only = True
171
172 @property
173 @model.immutable
174 def result(self):
175 if self.steps_failed:
176 return buildbot_json.FAILURE
177 if self.completed:
178 return buildbot_json.SUCCESS
179 return None
180
181
182 class RietveldTryJobs(base.IVerifierStatus):
183 """A set of try jobs that were sent for a specific patch.
184
185 Multiple concurrent try jobs can be sent on a single builder. For example, a
186 previous valid try job could have been triggered by the user but was not
187 completed so another was sent with the missing tests.
188 Also, a try job is sent as soon as a test failure is detected.
189 """
190 # An dict of RietveldTryJob objects per key.
191 try_jobs = dict
192 # The try job keys we ignore because they can't be used to give a good
193 # signal: either they are too old (old revision) or they were not triggerd
194 # by Rietveld, so we don't know if the diff is 100% good.
195 irrelevant = list
196 # When NOTRY=true is specified.
197 skipped = bool
198 # List of test verifiers. All the logic to decide when they are
199 # and what bots they trigger is hidden inside.
200 step_verifiers = list
201 # Jobs that have been sent but are not found yet. Likely a builder is fully
202 # utilized or the try server hasn't polled Rietveld yet. list of
203 # RietveldTryJobPending() instances.
204 pendings = list
205
206 @model.immutable
207 def get_state(self):
208 """Returns the state of this verified.
209
210 Failure can be from:
211 - For each entry in self.step_verifiers:
212 - A Try Job in self.try_jobs has been retried too often.
213
214 In particular, there is no need to wait for every Try Job to complete.
215 """
216 if self.error_message:
217 return base.FAILED
218 if not self.tests_waiting_for_result():
219 return base.SUCCEEDED
220 return base.PROCESSING
221
222 @model.immutable
223 def tests_need_to_be_run(self, now):
224 """Returns which tests need to be run.
225
226 These are the tests that are not pending on any try job, either running or
227 in the pending list.
228 """
229 # Skipped or failed, nothing to do.
230 if self.skipped or self.error_message:
231 return {}
232
233 # What originally needed to be run.
234 # All_tests is {builder_name: set(test_name*)}
235 all_tests = {}
236 for verifier in self.step_verifiers:
237 (builder, tests) = verifier.need_to_trigger(self.try_jobs, now)
238 if tests:
239 all_tests.setdefault(builder, set()).update(tests)
240
241 # Removes what is queued to be run but hasn't started yet.
242 for try_job in self.pendings:
243 if try_job.builder in all_tests:
244 all_tests[try_job.builder] -= set(try_job.requested_steps)
245
246 return dict(
247 (builder, sorted(tests)) for builder, tests in all_tests.iteritems()
248 if tests)
249
250 @model.immutable
251 def tests_waiting_for_result(self):
252 """Returns the tests that we are waiting for results on pending or running
253 builds.
254 """
255 # Skipped or failed, nothing to do.
256 if self.skipped or self.error_message:
257 return {}
258
259 # What originally needed to be run.
260 all_tests = {}
261 for verification in self.step_verifiers:
262 (builder, tests) = verification.waiting_for(self.try_jobs)
263 if tests:
264 all_tests.setdefault(builder, set()).update(tests)
265
266 # Removes what was run.
267 for try_job in self.try_jobs.itervalues():
268 if try_job.builder in all_tests:
269 all_tests[try_job.builder] -= set(try_job.steps_passed)
270
271 return dict(
272 (builder, list(tests)) for builder, tests in all_tests.iteritems()
273 if tests)
274
275 @model.immutable
276 def watched_builders(self):
277 """Marks all the jobs that the step_verifiers don't examine as
278 irrelevant.
279 """
280 # Generate the list of builders to keep.
281 watched_builders = set()
282 for step_verifier in self.step_verifiers:
283 watched_builders.add(step_verifier.builder_name)
284 if isinstance(step_verifier, try_job_steps.TryJobTriggeredSteps):
285 watched_builders.add(step_verifier.trigger_name)
286
287 return watched_builders
288
289 def update_jobs_from_rietveld(
290 self, data, status, checkout, now):
291 """Retrieves the jobs statuses from rietveld and updates its state.
292
293 Args:
294 owner: Owner of the CL.
295 data: Patchset properties as returned from Rietveld.
296 status: A buildbot_json.Buildbot instance.
297 checkout: A depot_tools' Checkout instance.
298 now: epoch time of what should be considered to be 'now'.
299
300 Returns:
301 Keys which were updated.
302 """
303 updated = []
304 try_job_results = data.get('try_job_results', [])
305 logging.debug('Found %d entries', len(try_job_results))
306
307 try_jobs_with_props, self.irrelevant = filter_jobs(
308 try_job_results, self.watched_builders() , self.irrelevant, status)
309
310 # Ensure that all irrelevant jobs have been removed from the set of valid
311 # try jobs.
312 for irrelevant_key in self.irrelevant:
313 if irrelevant_key in self.try_jobs:
314 del self.try_jobs[irrelevant_key]
315 if irrelevant_key + '_old' in self.try_jobs:
316 del self.try_jobs[irrelevant_key + '_old']
317
318 for i in try_jobs_with_props:
319 if self._update_try_job_status(checkout, i, now):
320 updated.append(i.key)
321 return updated
322
323 def _update_try_job_status(self, checkout, try_job_properties, now):
324 """Updates status of a specific RietveldTryJob.
325
326 try_job_property is an instance of TryJobProperties.
327
328 Returns True if it was updated.
329 """
330 key = try_job_properties.key
331 builder = try_job_properties.builder
332 buildnumber = try_job_properties.buildnumber
333 if key in self.irrelevant:
334 logging.debug('Ignoring %s/%d; irrelevant', builder, buildnumber)
335 return False
336 if (try_job_properties.parent_key and
337 try_job_properties.parent_key not in self.try_jobs):
338 logging.debug('Ignoring %s, parent unknown', key)
339 return False
340
341 requested_steps = []
342 # Set it to 0 as the default value since when the job is new and previous
343 # try jobs are found, we don't want to count them as tries.
344 tries = 0
345 job = self.try_jobs.get(key)
346 build = try_job_properties.build
347 if job:
348 if job.completed:
349 logging.debug('Ignoring %s/%d; completed', builder, buildnumber)
350 return False
351 else:
352 if now - job.started > TIMED_OUT:
353 # Flush it and start over.
354 self.irrelevant.append(key)
355 del self.try_jobs[key]
356 return False
357 requested_steps = job.requested_steps
358 tries = job.tries
359 init_time = job.init_time
360 else:
361 # This try job is new. See if we triggered it previously by
362 # looking in self.pendings.
363 for index, pending_job in enumerate(self.pendings):
364 if pending_job.builder == builder:
365 # Reuse its item.
366 requested_steps = pending_job.requested_steps
367 tries = pending_job.tries
368 self.pendings.pop(index)
369 break
370 else:
371 # Is this a good build? It must not be too old and triggered by
372 # rietveld.
373 if is_job_expired(now, build.revision, build.start_time, checkout):
374 logging.debug('Ignoring %s/%d; expired', builder, buildnumber)
375 self.irrelevant.append(key)
376 return False
377 init_time = now
378
379 passed = [s.name for s in build.steps if s.simplified_result]
380 failed = [s.name for s in build.steps if s.simplified_result is False]
381 # The steps in neither passed or failed were skipped.
382 new_job = RietveldTryJob(
383 init_time=init_time,
384 builder=builder,
385 build=buildnumber,
386 revision=build.revision,
387 requested_steps=requested_steps,
388 started=build.start_time,
389 steps_passed=passed,
390 steps_failed=failed,
391 clobber=bool(try_job_properties.properties.get('clobber')),
392 completed=build.completed,
393 tries=tries,
394 parent_key=try_job_properties.parent_key)
395 if job and job.build and new_job.build and job.build != new_job.build:
396 # It's tricky because 'key' is the same for both. The trick is to create
397 # a fake key for the old build and mark it as completed. Note that
398 # Rietveld is confused by it too.
399 logging.warning(
400 'Try Server was restarted and restarted builds with the same keys. '
401 'I\'m confused. %s: %d != %d', job.builder, job.build, new_job.build)
402 # Resave the old try job and mark it as completed.
403 self.try_jobs[key + '_old'] = RietveldTryJob(
404 init_time=job.init_time,
405 builder=job.builder,
406 build=job.build,
407 revision=job.revision,
408 requested_steps=job.requested_steps,
409 started=build.start_time,
410 steps_passed=job.steps_passed,
411 steps_failed=job.steps_failed,
412 clobber=job.clobber,
413 completed=True,
414 tries=job.tries,
415 parent_key=job.parent_key)
416 if not job or not model.is_equivalent(new_job, job):
417 logging.info(
418 'Job update: %s: %s/%d',
419 try_job_properties.properties.get('issue'),
420 builder,
421 buildnumber)
422 self.try_jobs[key] = new_job
423 return key
424
425 def signal_as_failed_if_needed(self, job, url, now):
426 """Detects if the RietveldTryJob instance is in a state where it is
427 impossible to make progress.
428
429 If so, mark ourself as failed by setting self.error_message and return True.
430 """
431 if self.skipped or self.error_message:
432 return False
433 # Figure out steps that should be retried for this builder.
434 missing_tests = self.tests_need_to_be_run(now).get(job.builder, [])
435 if not missing_tests:
436 return False
437 if job.tries > 2:
438 self.error_message = (
439 'Retried try job too often on %s for step(s) %s\n%s' %
440 (job.builder, ', '.join(missing_tests), url))
441 logging.info(self.error_message)
442 return True
443 return False
444
445 @model.immutable
446 def why_not(self):
447 # Skipped or failed, nothing to do.
448 if self.skipped or self.error_message:
449 return None
450 waiting = self.tests_waiting_for_result()
451 if waiting:
452 out = 'Waiting for the following jobs:\n'
453 for builder in sorted(waiting):
454 out += ' %s: %s\n' % (builder, ','.join(waiting[builder]))
455 return out
456
457
458 class TryRunnerRietveld(base.VerifierCheckout):
459 """Stateless communication with a try server.
460
461 Uses Rietveld to trigger the try job and reads try job status with the json
462 API.
463
464 Analysis goes as following:
465 - compile step itself is not flaky. compile.py already takes care of most
466 flakiness and clobber build is done by default. If compile step fails, try
467 again with clobber=True
468 - test steps are flaky and can be retried as necessary.
469
470 1. For each existing try jobs from rietveld.
471 1. Fetch result from try server.
472 2. If try job was generated from rietveld;
473 1. If not is_job_expired();
474 1. Skip any scheduled test that succeeded on this builder.
475 2. For each builder with tests scheduled;
476 1. If no step waiting to be triggered, skip this builder completely.
477 2. For each non succeeded job;
478 1. Send try jobs to rietveld.
479
480 Note: It needs rietveld, hence it uses VerifierCheckout, but it doesn't need a
481 checkout.
482 """
483 name = 'try job rietveld'
484
485 # Only updates a job status once every 60 seconds.
486 update_latency = 60
487
488 def __init__(
489 self, context_obj, try_server_url, commit_user, step_verifiers,
490 ignored_steps, solution):
491 super(TryRunnerRietveld, self).__init__(context_obj)
492 self.try_server_url = try_server_url.rstrip('/')
493 self.commit_user = commit_user
494 # TODO(maruel): Have it be overridden by presubmit_support.DoGetTrySlaves.
495 self.step_verifiers = step_verifiers
496 self.ignored_steps = set(ignored_steps)
497 # Time to poll the Try Server, and not Rietveld.
498 self.last_update = time.time() - self.update_latency
499 self.solution = solution
500
501 def verify(self, pending):
502 """Sends a try job to the try server and returns a RietveldTryJob list.
503
504 This function is called synchronously.
505 """
506 jobs = pending.verifications.setdefault(self.name, RietveldTryJobs())
507 if _is_skip_try_job(pending):
508 # Do not run try job for it.
509 jobs.skipped = True
510 return
511
512 # Overridde any previous list from the last restart.
513 jobs.step_verifiers = []
514 for step in self.step_verifiers:
515 if isinstance(step, try_job_steps.TryJobTriggeredOrNormalSteps):
516 # Since the steps are immutable, create a new step so that swarm
517 # can be enabled.
518 jobs.step_verifiers.append(try_job_steps.TryJobTriggeredOrNormalSteps(
519 builder_name=step.builder_name,
520 trigger_name=step.trigger_name,
521 steps=step.steps,
522 trigger_bot_steps=step.trigger_bot_steps,
523 use_triggered_bot=True))
524 else:
525 jobs.step_verifiers.append(step)
526
527 # First, update the status of the current try jobs on Rietveld.
528 now = time.time()
529 self._update_jobs_from_rietveld(pending, jobs, False, now)
530
531 # Add anything that is missing.
532 self._send_jobs(pending, jobs, now)
533
534 # Slightly postpone next check.
535 self.last_update = min(now, self.last_update + (self.update_latency / 4))
536
537 def update_status(self, queue):
538 """Grabs the current status of all try jobs and update self.queue.
539
540 Note: it would be more efficient to be event based.
541 """
542 if not queue:
543 logging.debug('The list is empty, nothing to do')
544 return
545
546 # Hard code 'now' to the value before querying and sending them. This will
547 # cause some issues when querying state or sending the jobs takes a
548 # non-trivial amount of time but in general it will be fine.
549 now = time.time()
550 if now - self.last_update < self.update_latency:
551 logging.debug('TS: Throttling updates')
552 return
553 self.last_update = now
554
555 # Update the status of the current pending CLs on Rietveld.
556 for pending, jobs in self.loop(queue, RietveldTryJobs, True):
557 # Update 'now' since querying the try jobs may take a significant amount
558 # of time.
559 now = time.time()
560 if self._update_jobs_from_rietveld(pending, jobs, True, now):
561 # Send any necessary job. Noop if not needed.
562 self._send_jobs(pending, jobs, now)
563
564 def _add_pending_job_and_send_if_needed(self, builder, steps, jobs,
565 send_job, pending, now):
566 # Find if there was a previous try.
567 previous_jobs = [
568 job for job in jobs.try_jobs.itervalues() if job.builder == builder
569 ]
570 if previous_jobs:
571 tries = max(job.tries for job in previous_jobs)
572 clobber = max(
573 (job.clobber or 'compile' in job.steps_failed)
574 for job in previous_jobs)
575 else:
576 tries = 0
577 clobber = False
578 if tries > 4:
579 # Fail safe.
580 jobs.error_message = (
581 ( 'The commit queue went berserk retrying too often for a\n'
582 'seemingly flaky test on builder %s:\n%s') %
583 ( builder,
584 '\n'.join(self._build_status_url(j) for j in previous_jobs)))
585 return False
586
587 # Don't always send the job (triggered bots don't need to send there own
588 # request).
589 if send_job:
590 logging.debug(
591 'Sending job %s for %s: %s', pending.issue, builder, ','.join(steps))
592 try:
593 self.context.rietveld.trigger_try_jobs(
594 pending.issue, pending.patchset, 'CQ', clobber, 'HEAD',
595 {builder: steps})
596 except urllib2.HTTPError as e:
597 if e.code == 400:
598 # This probably mean a new patchset was uploaded since the last poll,
599 # so it's better to drop the CL.
600 jobs.error_message = 'Failed to trigger a try job on %s\n%s' % (
601 builder, e)
602 return False
603 else:
604 raise
605
606 # Set the status of this pending job here and on the CQ page.
607 jobs.pendings.append(
608 RietveldTryJobPending(
609 init_time=now,
610 builder=builder,
611 revision=None,
612 requested_steps=steps,
613 clobber=clobber,
614 tries=tries + 1))
615 # Update the status on the AppEngine status to signal a new try job was
616 # sent.
617 info = {
618 'builder': builder,
619 'clobber': clobber,
620 'job_name': 'CQ',
621 'revision': None, #revision,
622 }
623 self.send_status(pending, info)
624 return True
625
626 def _get_triggered_bots(self, builder, steps):
627 """Returns a dict of all the (builder, steps) pairs of bots that will get
628 triggered by the given builder steps combination."""
629 triggered_bots = {}
630 for verifier in self.step_verifiers:
631 builder, steps = verifier.get_triggered_steps(builder, steps)
632 if steps:
633 triggered_bots[builder] = steps
634
635 return triggered_bots
636
637 def _send_jobs(self, pending, jobs, now):
638 """Prepares the RietveldTryJobs instance |jobs| to send try jobs to the try
639 server.
640 """
641 if jobs.error_message:
642 # Too late.
643 return
644 remaining = jobs.tests_need_to_be_run(now)
645 if not remaining:
646 return
647 # Send them in order to simplify testing.
648 for builder in sorted(remaining):
649 tests = remaining[builder]
650 if not self._add_pending_job_and_send_if_needed(builder, tests, jobs,
651 True, pending, now):
652 # If the main job wasn't sent, we can't skip the triggered jobs since
653 # they won't get triggered.
654 continue
655
656 # Add any pending bots that will be triggered from this build.
657 triggered_bots = self._get_triggered_bots(builder, tests)
658 for builder, steps in triggered_bots.iteritems():
659 self._add_pending_job_and_send_if_needed(builder, steps, jobs, False,
660 pending, now)
661
662 @model.immutable
663 def _build_status_url(self, job):
664 """Html url for this try job."""
665 assert job.build is not None, str(job)
666 return '%s/buildstatus?builder=%s&number=%s' % (
667 self.try_server_url, job.builder, job.build)
668
669 @model.immutable
670 def _update_dashboard(self, pending, job):
671 """Updates the CQ dashboard with the current Try Job state as known to the
672 CQ.
673 """
674 logging.debug('_update_dashboard(%s/%s)', job.builder, job.build)
675 info = {
676 'build': job.build,
677 'builder': job.builder,
678 'job_name': 'CQ',
679 'result': job.result,
680 'revision': job.revision,
681 'url': self._build_status_url(job),
682 }
683 self.send_status(pending, info)
684
685 def _update_jobs_from_rietveld(self, pending, jobs, handle, now):
686 """Grabs data from Rietveld and pass it to
687 RietveldTryJobs.update_jobs_from_rietveld().
688
689 Returns True on success.
690 """
691 status = buildbot_json.Buildbot(self.try_server_url)
692 try:
693 try:
694 data = self.context.rietveld.get_patchset_properties(
695 pending.issue, pending.patchset)
696 except urllib2.HTTPError as e:
697 if e.code == 404:
698 # TODO(phajdan.jr): Maybe generate a random id to correlate the user's
699 # error message and exception in the logs.
700 # Don't put exception traceback in the user-visible message to avoid
701 # leaking sensitive CQ data (passwords etc).
702 jobs.error_message = ('Failed to get patchset properties (patchset '
703 'not found?)')
704 logging.error(str(e))
705 return False
706 else:
707 raise
708
709 # Update the RietvedTryJobs object.
710 keys = jobs.update_jobs_from_rietveld(
711 data,
712 status,
713 self.context.checkout,
714 now)
715 except urllib2.HTTPError as e:
716 if e.code in (500, 502, 503):
717 # Temporary AppEngine hiccup. Just log it and return failure.
718 logging.warning('%s while accessing %s. Ignoring error.' % (
719 str(e), e.url))
720 return False
721 else:
722 raise
723 except urllib2.URLError as e:
724 if 'timed out' in e.reason:
725 # Handle timeouts gracefully.
726 logging.warning('%s while updating tryserver status for '
727 'rietveld issue %s', e, pending.issue)
728 return False
729 else:
730 raise
731 except socket.error as e:
732 # Temporary AppEngine hiccup. Just log it and return failure.
733 if e.errno == errno.ECONNRESET:
734 logging.warning(
735 '%s while updating tryserver status for rietveld issue %s.' % (
736 str(e), str(pending.issue)))
737 return False
738 else:
739 raise
740 except IOError as e:
741 # Temporary AppEngine hiccup. Just log it and return failure.
742 if e.errno == 'socket error':
743 logging.warning(
744 '%s while updating tryserver status for rietveld issue %s.' % (
745 str(e), str(pending.issue)))
746 return False
747 raise
748
749 if handle:
750 for updated_key in keys:
751 job = jobs.try_jobs[updated_key]
752 self._update_dashboard(pending, job)
753 jobs.signal_as_failed_if_needed(job, self._build_status_url(job), now)
754
755 return True
OLDNEW
« no previous file with comments | « commit-queue/verification/trigger_experimental_try_job.py ('k') | commit-queue/verification/try_job_steps.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698