Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | 1 # Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | |
| 6 | |
| 5 from common.pipeline_wrapper import BasePipeline | 7 from common.pipeline_wrapper import BasePipeline |
| 6 from model.wf_analysis import WfAnalysis | 8 from common.waterfall import failure_type |
| 7 from waterfall import try_job_util | 9 from waterfall import try_job_util |
| 10 from waterfall.identify_try_job_culprit_pipeline import ( | |
| 11 IdentifyTryJobCulpritPipeline) | |
| 12 from waterfall.monitor_try_job_pipeline import MonitorTryJobPipeline | |
| 13 from waterfall.process_swarming_task_result_pipeline import ( | |
| 14 ProcessSwarmingTaskResultPipeline) | |
| 15 from waterfall.schedule_compile_try_job_pipeline import ( | |
| 16 ScheduleCompileTryJobPipeline) | |
| 17 from waterfall.schedule_test_try_job_pipeline import ( | |
| 18 ScheduleTestTryJobPipeline) | |
| 19 | |
| 20 | |
| 21 def _GetLastPassCompile(build_number, failed_steps): | |
| 22 if (failed_steps.get('compile') and | |
| 23 failed_steps['compile']['first_failure'] == build_number and | |
| 24 failed_steps['compile'].get('last_pass') is not None): | |
| 25 return failed_steps['compile']['last_pass'] | |
| 26 return None | |
| 27 | |
| 28 | |
| 29 def _GetLastPassTest(build_number, failed_steps): | |
| 30 for step_failure in failed_steps.itervalues(): | |
| 31 for test_failure in step_failure['tests'].itervalues(): | |
| 32 if (test_failure['first_failure'] == build_number and | |
| 33 test_failure.get('last_pass') is not None): | |
| 34 return test_failure['last_pass'] | |
| 35 return None | |
| 36 | |
| 37 | |
| 38 def _GetLastPass(build_number, failure_info, try_job_type): | |
| 39 if try_job_type == failure_type.COMPILE: | |
| 40 return _GetLastPassCompile(build_number, failure_info['failed_steps']) | |
| 41 elif try_job_type == failure_type.TEST: | |
| 42 return _GetLastPassTest(build_number, failure_info['failed_steps']) | |
| 43 else: | |
| 44 return None | |
| 45 | |
| 46 | |
| 47 def _GetSuspectsFromHeuristicResult(heuristic_result): | |
| 48 suspected_revisions = set() | |
|
lijeffrey
2016/08/04 10:31:47
nit: why not check heuristic_result first and retu
chanli
2016/08/05 19:53:54
Done.
| |
| 49 if not heuristic_result: | |
| 50 return list(suspected_revisions) | |
| 51 for failure in heuristic_result.get('failures', []): | |
| 52 for cl in failure['suspected_cls']: | |
| 53 suspected_revisions.add(cl['revision']) | |
| 54 return list(suspected_revisions) | |
| 8 | 55 |
| 9 | 56 |
| 10 class StartTryJobOnDemandPipeline(BasePipeline): | 57 class StartTryJobOnDemandPipeline(BasePipeline): |
| 11 | 58 |
| 12 # Arguments number differs from overridden method - pylint: disable=W0221 | 59 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 13 def run(self, failure_info, signals, build_completed, force_try_job, | 60 def run(self, master_name, builder_name, build_number, failure_info, |
| 14 heuristic_result): | 61 signals, heuristic_result, build_completed, force_try_job): |
| 15 """Starts a try job if one is needed for the given failure.""" | 62 """Starts a try job if one is needed for the given failure.""" |
| 63 | |
| 16 if not build_completed: # Only start try-jobs for completed builds. | 64 if not build_completed: # Only start try-jobs for completed builds. |
| 17 return False | 65 return |
| 18 | 66 |
| 19 failure_result_map = try_job_util.ScheduleTryJobIfNeeded( | 67 need_try_job = try_job_util.NeedANewTryJob( |
| 20 failure_info, signals=signals, heuristic_result=heuristic_result, | 68 master_name, builder_name, build_number, failure_info, signals, |
| 21 force_try_job=force_try_job) | 69 heuristic_result, force_try_job) |
| 22 | 70 |
| 23 # Save reference to the try-jobs if any was scheduled. | 71 if not need_try_job: |
| 24 master_name = failure_info['master_name'] | 72 return |
| 25 builder_name = failure_info['builder_name'] | 73 |
| 26 build_number = failure_info['build_number'] | 74 try_job_type = failure_info['failure_type'] |
| 27 analysis = WfAnalysis.Get(master_name, builder_name, build_number) | 75 last_pass = _GetLastPass(build_number, failure_info, try_job_type) |
| 28 analysis.failure_result_map = failure_result_map | 76 if last_pass is None: # pragma: no cover |
| 29 analysis.put() | 77 logging.warning('Couldn"t start try job because last_pass is not found.') |
| 30 return True | 78 return |
| 79 | |
| 80 blame_list = failure_info['builds'][str(build_number)]['blame_list'] | |
| 81 good_revision = failure_info['builds'][str(last_pass)]['chromium_revision'] | |
| 82 bad_revision = failure_info['builds'][str(build_number)][ | |
| 83 'chromium_revision'] | |
| 84 suspected_revisions = _GetSuspectsFromHeuristicResult(heuristic_result) | |
| 85 | |
| 86 if try_job_type == failure_type.COMPILE: | |
| 87 compile_targets = try_job_util.GetFailedTargetsFromSignals( | |
| 88 signals, master_name, builder_name) | |
| 89 try_job_id = yield ScheduleCompileTryJobPipeline( | |
| 90 master_name, builder_name, build_number, good_revision, bad_revision, | |
| 91 try_job_type, compile_targets, suspected_revisions) | |
| 92 try_job_result = yield MonitorTryJobPipeline( | |
| 93 master_name, builder_name, build_number, try_job_type, try_job_id) | |
| 94 yield IdentifyTryJobCulpritPipeline( | |
| 95 master_name, builder_name, build_number, blame_list, try_job_type, | |
| 96 try_job_id, try_job_result) | |
| 97 else: # try_job_type == failure_type.TEST. | |
|
lijeffrey
2016/08/04 10:31:47
nit: should this be removed?
chanli
2016/08/05 19:53:54
Done.
| |
| 98 # if try_job_type is other type, the pipeline has returned. | |
| 99 | |
| 100 # Waits and gets the swarming tasks' results. | |
| 101 reliable_tests = [] | |
| 102 for step_name, step_failure in failure_info['failed_steps'].iteritems(): | |
| 103 step_has_first_time_failure = False | |
| 104 for test_failure in step_failure['tests'].itervalues(): | |
| 105 if test_failure['first_failure'] == build_number: | |
| 106 step_has_first_time_failure = True | |
| 107 break | |
| 108 if not step_has_first_time_failure: | |
| 109 continue | |
| 110 task_result = yield ProcessSwarmingTaskResultPipeline( | |
| 111 master_name, builder_name, build_number, step_name) | |
| 112 reliable_tests.append(task_result) | |
| 113 | |
| 114 try_job_id = yield ScheduleTestTryJobPipeline( | |
| 115 master_name, builder_name, build_number, good_revision, bad_revision, | |
| 116 try_job_type, suspected_revisions, *reliable_tests) | |
| 117 try_job_result = yield MonitorTryJobPipeline( | |
| 118 master_name, builder_name, build_number, try_job_type, try_job_id) | |
| 119 yield IdentifyTryJobCulpritPipeline( | |
| 120 master_name, builder_name, build_number, blame_list, try_job_type, | |
| 121 try_job_id, try_job_result) | |
| OLD | NEW |