| OLD | NEW |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 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 from datetime import datetime | 5 from datetime import datetime |
| 6 import logging | 6 import logging |
| 7 | 7 |
| 8 from google.appengine.ext import ndb | 8 from google.appengine.ext import ndb |
| 9 | 9 |
| 10 from common import appengine_util | 10 from common import appengine_util |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 74 | 74 |
| 75 need_new_try_job = need_new_try_job or failure_need_try_job | 75 need_new_try_job = need_new_try_job or failure_need_try_job |
| 76 last_pass = (failure_last_pass if failure_last_pass and | 76 last_pass = (failure_last_pass if failure_last_pass and |
| 77 failure_last_pass < last_pass else last_pass) | 77 failure_last_pass < last_pass else last_pass) |
| 78 | 78 |
| 79 return targeted_tests, need_new_try_job, last_pass | 79 return targeted_tests, need_new_try_job, last_pass |
| 80 | 80 |
| 81 | 81 |
| 82 @ndb.transactional | 82 @ndb.transactional |
| 83 def _NeedANewTryJob( | 83 def _NeedANewTryJob( |
| 84 master_name, builder_name, build_number, failed_steps, failure_result_map): | 84 master_name, builder_name, build_number, failed_steps, failure_result_map, |
| 85 force_try_job=False): |
| 85 """Checks if a new try_job is needed.""" | 86 """Checks if a new try_job is needed.""" |
| 86 need_new_try_job = False | 87 need_new_try_job = False |
| 87 last_pass = build_number | 88 last_pass = build_number |
| 88 | 89 |
| 89 if 'compile' in failed_steps: | 90 if 'compile' in failed_steps: |
| 90 try_job_type = TryJobType.COMPILE | 91 try_job_type = TryJobType.COMPILE |
| 91 targeted_tests = None | 92 targeted_tests = None |
| 92 need_new_try_job, last_pass = _CheckFailureForTryJobKey( | 93 need_new_try_job, last_pass = _CheckFailureForTryJobKey( |
| 93 master_name, builder_name, build_number, | 94 master_name, builder_name, build_number, |
| 94 failure_result_map, TryJobType.COMPILE, failed_steps['compile']) | 95 failure_result_map, TryJobType.COMPILE, failed_steps['compile']) |
| 95 else: | 96 else: |
| 96 try_job_type = TryJobType.TEST | 97 try_job_type = TryJobType.TEST |
| 97 targeted_tests, need_new_try_job, last_pass = ( | 98 targeted_tests, need_new_try_job, last_pass = ( |
| 98 _CheckIfNeedNewTryJobForTestFailure( | 99 _CheckIfNeedNewTryJobForTestFailure( |
| 99 'step', master_name, builder_name, build_number, failure_result_map, | 100 'step', master_name, builder_name, build_number, failure_result_map, |
| 100 failed_steps)) | 101 failed_steps)) |
| 101 | 102 |
| 102 if need_new_try_job: | 103 if need_new_try_job: |
| 103 try_job = WfTryJob.Get(master_name, builder_name, build_number) | 104 try_job = WfTryJob.Get(master_name, builder_name, build_number) |
| 104 | 105 |
| 105 if try_job: | 106 if try_job: |
| 106 if try_job.failed: | 107 if try_job.failed or force_try_job: |
| 107 try_job.status = analysis_status.PENDING | 108 try_job.status = analysis_status.PENDING |
| 108 try_job.put() | 109 try_job.put() |
| 109 else: | 110 else: |
| 110 need_new_try_job = False | 111 need_new_try_job = False |
| 111 else: | 112 else: |
| 112 try_job = WfTryJob.Create(master_name, builder_name, build_number) | 113 try_job = WfTryJob.Create(master_name, builder_name, build_number) |
| 113 try_job.put() | 114 try_job.put() |
| 114 | 115 |
| 115 return need_new_try_job, last_pass, try_job_type, targeted_tests | 116 return need_new_try_job, last_pass, try_job_type, targeted_tests |
| 116 | 117 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 142 if not heuristic_result: | 143 if not heuristic_result: |
| 143 return list(suspected_revisions) | 144 return list(suspected_revisions) |
| 144 for failure in heuristic_result.get('failures', []): | 145 for failure in heuristic_result.get('failures', []): |
| 145 for cl in failure['suspected_cls']: | 146 for cl in failure['suspected_cls']: |
| 146 suspected_revisions.add(cl['revision']) | 147 suspected_revisions.add(cl['revision']) |
| 147 return list(suspected_revisions) | 148 return list(suspected_revisions) |
| 148 | 149 |
| 149 | 150 |
| 150 def _ShouldBailOutForOutdatedBuild(build): | 151 def _ShouldBailOutForOutdatedBuild(build): |
| 151 return (datetime.utcnow() - build.start_time).days > 0 | 152 return (datetime.utcnow() - build.start_time).days > 0 |
| 152 | |
| 153 | 153 |
| 154 def ScheduleTryJobIfNeeded(failure_info, signals, heuristic_result): | 154 |
| 155 def ScheduleTryJobIfNeeded(failure_info, signals, heuristic_result, |
| 156 force_try_job=False): |
| 155 master_name = failure_info['master_name'] | 157 master_name = failure_info['master_name'] |
| 156 builder_name = failure_info['builder_name'] | 158 builder_name = failure_info['builder_name'] |
| 157 build_number = failure_info['build_number'] | 159 build_number = failure_info['build_number'] |
| 158 failed_steps = failure_info.get('failed_steps', []) | 160 failed_steps = failure_info.get('failed_steps', []) |
| 159 builds = failure_info.get('builds', {}) | 161 builds = failure_info.get('builds', {}) |
| 160 | 162 |
| 161 # Bail out if the build data's timestamp is more than 24 hours old to | |
| 162 # avoid using outdated revisions. TODO(lijeffrey): This will also disallow | |
| 163 # manually triggering try jobs more than a day old using build_completed=1. | |
| 164 # Need to implement a flag to force try jobs regardless of timestamp. | |
| 165 build = WfBuild.Get(master_name, builder_name, build_number) | |
| 166 if _ShouldBailOutForOutdatedBuild(build): | |
| 167 logging.error( | |
| 168 'Build time is more than 24 hours old. Try job will not be triggered.') | |
| 169 return {} | |
| 170 | |
| 171 tryserver_mastername, tryserver_buildername = ( | 163 tryserver_mastername, tryserver_buildername = ( |
| 172 waterfall_config.GetTrybotForWaterfallBuilder(master_name, builder_name)) | 164 waterfall_config.GetTrybotForWaterfallBuilder(master_name, builder_name)) |
| 173 | 165 |
| 174 if not tryserver_mastername or not tryserver_buildername: | 166 if not tryserver_mastername or not tryserver_buildername: |
| 175 logging.info('%s, %s is not supported yet.', master_name, builder_name) | 167 logging.info('%s, %s is not supported yet.', master_name, builder_name) |
| 176 return {} | 168 return {} |
| 177 elif (failure_info['failure_type'] == failure_type.TEST and | 169 |
| 170 if not force_try_job: |
| 171 build = WfBuild.Get(master_name, builder_name, build_number) |
| 172 |
| 173 if _ShouldBailOutForOutdatedBuild(build): |
| 174 logging.error('Build time %s is more than 24 hours old. ' |
| 175 'Try job will not be triggered.' % build.start_time) |
| 176 return {} |
| 177 |
| 178 if (failure_info['failure_type'] == failure_type.TEST and |
| 178 waterfall_config.ShouldSkipTestTryJobs(master_name, builder_name)): | 179 waterfall_config.ShouldSkipTestTryJobs(master_name, builder_name)): |
| 179 logging.info('Test try jobs on %s, %s are not supported yet.', | 180 logging.info('Test try jobs on %s, %s are not supported yet.', |
| 180 master_name, builder_name) | 181 master_name, builder_name) |
| 181 return {} | 182 return {} |
| 182 | 183 |
| 183 failure_result_map = {} | 184 failure_result_map = {} |
| 184 need_new_try_job, last_pass, try_job_type, targeted_tests = ( | 185 need_new_try_job, last_pass, try_job_type, targeted_tests = ( |
| 185 _NeedANewTryJob(master_name, builder_name, build_number, | 186 _NeedANewTryJob(master_name, builder_name, build_number, |
| 186 failed_steps, failure_result_map)) | 187 failed_steps, failure_result_map, force_try_job)) |
| 187 | 188 |
| 188 if need_new_try_job: | 189 if need_new_try_job: |
| 189 compile_targets = (_GetFailedTargetsFromSignals( | 190 compile_targets = (_GetFailedTargetsFromSignals( |
| 190 signals, master_name, builder_name) | 191 signals, master_name, builder_name) |
| 191 if try_job_type == TryJobType.COMPILE else None) | 192 if try_job_type == TryJobType.COMPILE else None) |
| 192 suspected_revisions = _GetSuspectsFromHeuristicResult(heuristic_result) | 193 suspected_revisions = _GetSuspectsFromHeuristicResult(heuristic_result) |
| 193 | 194 |
| 194 pipeline = ( | 195 pipeline = ( |
| 195 swarming_tasks_to_try_job_pipeline.SwarmingTasksToTryJobPipeline( | 196 swarming_tasks_to_try_job_pipeline.SwarmingTasksToTryJobPipeline( |
| 196 master_name, builder_name, build_number, | 197 master_name, builder_name, build_number, |
| 197 builds[str(last_pass)]['chromium_revision'], | 198 builds[str(last_pass)]['chromium_revision'], |
| 198 builds[str(build_number)]['chromium_revision'], | 199 builds[str(build_number)]['chromium_revision'], |
| 199 builds[str(build_number)]['blame_list'], | 200 builds[str(build_number)]['blame_list'], |
| 200 try_job_type, compile_targets, targeted_tests, suspected_revisions)) | 201 try_job_type, compile_targets, targeted_tests, suspected_revisions, |
| 202 force_try_job)) |
| 201 | 203 |
| 202 pipeline.target = appengine_util.GetTargetNameForModule( | 204 pipeline.target = appengine_util.GetTargetNameForModule( |
| 203 constants.WATERFALL_BACKEND) | 205 constants.WATERFALL_BACKEND) |
| 204 pipeline.start(queue_name=constants.WATERFALL_TRY_JOB_QUEUE) | 206 pipeline.start(queue_name=constants.WATERFALL_TRY_JOB_QUEUE) |
| 205 | 207 |
| 206 if try_job_type == TryJobType.TEST: # pragma: no cover | 208 if try_job_type == TryJobType.TEST: # pragma: no cover |
| 207 logging_str = ( | 209 logging_str = ( |
| 208 'Trying to schedule swarming task(s) for build %s, %s, %s: %s' | 210 'Trying to schedule swarming task(s) for build %s, %s, %s: %s' |
| 209 ' because of %s failure. A try job may be triggered if some reliable' | 211 ' because of %s failure. A try job may be triggered if some reliable' |
| 210 ' failure is detected in task(s).') % ( | 212 ' failure is detected in task(s).') % ( |
| 211 master_name, builder_name, build_number, | 213 master_name, builder_name, build_number, |
| 212 pipeline.pipeline_status_path, try_job_type) | 214 pipeline.pipeline_status_path, try_job_type) |
| 213 else: # pragma: no cover | 215 else: # pragma: no cover |
| 214 logging_str = ( | 216 logging_str = ( |
| 215 'Try job was scheduled for build %s, %s, %s: %s because of %s ' | 217 'Try job was scheduled for build %s, %s, %s: %s because of %s ' |
| 216 'failure.') % ( | 218 'failure.') % ( |
| 217 master_name, builder_name, build_number, | 219 master_name, builder_name, build_number, |
| 218 pipeline.pipeline_status_path, try_job_type) | 220 pipeline.pipeline_status_path, try_job_type) |
| 219 logging.info(logging_str) | 221 logging.info(logging_str) |
| 220 | 222 |
| 221 return failure_result_map | 223 return failure_result_map |
| OLD | NEW |