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

Side by Side Diff: appengine/findit/handlers/build_failure.py

Issue 2026283002: [Findit] Adding logic to force try jobs regardless of bailout or previous results (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 4 years, 6 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
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 1 # Copyright 2014 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 collections import defaultdict 5 from collections import defaultdict
6 import copy 6 import copy
7 from datetime import datetime 7 from datetime import datetime
8 import logging 8 import logging
9 import os 9 import os
10 10
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after
140 'last_pass': test_result.get('last_pass'), 140 'last_pass': test_result.get('last_pass'),
141 'supported': supported, 141 'supported': supported,
142 'tests': group['tests'], 142 'tests': group['tests'],
143 'suspected_cls': group['suspected_cls'] 143 'suspected_cls': group['suspected_cls']
144 } 144 }
145 organized_suspected_cls.append(shared_result) 145 organized_suspected_cls.append(shared_result)
146 146
147 return organized_results 147 return organized_results
148 148
149 149
150 def _GetAnalysisResultWithTryJobInfo( 150 def _GetAnalysisResultWithTryJobInfo(show_debug_info, organized_results,
151 organized_results, master_name, builder_name, build_number): 151 master_name, builder_name, build_number):
152 """Reorganizes analysis result and try job result by step_name and culprit. 152 """Reorganizes analysis result and try job result by step_name and culprit.
153 153
154 Returns: 154 Returns:
155 update_result (dict): A dict of classified results. 155 update_result (dict): A dict of classified results.
156 156
157 The format for those dicts are as below: 157 The format for those dicts are as below:
158 { 158 {
159 # A dict of results that contains both 159 # A dict of results that contains both
160 # heuristic analysis results and try job results. 160 # heuristic analysis results and try job results.
161 'reliable_failures': { 161 'reliable_failures': {
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 # such as non-swarming tests or swarming rerun failed. 198 # such as non-swarming tests or swarming rerun failed.
199 'unclassified_failures': {...} 199 'unclassified_failures': {...}
200 } 200 }
201 """ 201 """
202 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list))) 202 updated_results = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
203 203
204 if not organized_results: 204 if not organized_results:
205 return updated_results 205 return updated_results
206 206
207 try_job_info = handlers_util.GetAllTryJobResults( 207 try_job_info = handlers_util.GetAllTryJobResults(
208 master_name, builder_name, build_number) 208 master_name, builder_name, build_number, show_debug_info)
209
209 if not try_job_info: 210 if not try_job_info:
210 return updated_results 211 return updated_results
211 212
212 for step_name, try_jobs in try_job_info.iteritems(): 213 for step_name, try_jobs in try_job_info.iteritems():
213 try_jobs = try_jobs['try_jobs'] 214 try_jobs = try_jobs['try_jobs']
214 step_heuristic_results = organized_results[step_name] 215 step_heuristic_results = organized_results[step_name]
215 step_updated_results = updated_results[step_name]['results'] 216 step_updated_results = updated_results[step_name]['results']
216 217
217 # Finds out try job result index and heuristic result index for each test. 218 # Finds out try job result index and heuristic result index for each test.
218 test_result_map = defaultdict(lambda: defaultdict(int)) 219 test_result_map = defaultdict(lambda: defaultdict(int))
(...skipping 27 matching lines...) Expand all
246 'try_job': try_job_result, 247 'try_job': try_job_result,
247 'heuristic_analysis': { 248 'heuristic_analysis': {
248 'suspected_cls': heuristic_result['suspected_cls'] 249 'suspected_cls': heuristic_result['suspected_cls']
249 }, 250 },
250 'tests': tests if tests != [NON_SWARMING] else [], 251 'tests': tests if tests != [NON_SWARMING] else [],
251 'first_failure': heuristic_result['first_failure'], 252 'first_failure': heuristic_result['first_failure'],
252 'last_pass': heuristic_result['last_pass'], 253 'last_pass': heuristic_result['last_pass'],
253 'supported': heuristic_result['supported'] 254 'supported': heuristic_result['supported']
254 } 255 }
255 256
256 if ('status' not in try_job_result or 257 if (('status' not in try_job_result or
257 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()): 258 try_job_result['status'] in NO_TRY_JOB_REASON_MAP.values()) or
259 (tests == [NON_SWARMING] and show_debug_info)):
258 # There is no try job info but only heuristic result. 260 # There is no try job info but only heuristic result.
259 try_job_result['status'] = try_job_result.get( 261 try_job_result['status'] = try_job_result.get(
260 'status', result_status.UNKNOWN) 262 'status', result_status.UNKNOWN)
261 step_updated_results['unclassified_failures'].append(final_result) 263 step_updated_results['unclassified_failures'].append(final_result)
262 elif try_job_result['status'] == result_status.FLAKY: 264 elif try_job_result['status'] == result_status.FLAKY:
263 step_updated_results['flaky_failures'].append(final_result) 265 step_updated_results['flaky_failures'].append(final_result)
264 else: 266 else:
265 step_updated_results['reliable_failures'].append(final_result) 267 step_updated_results['reliable_failures'].append(final_result)
266 268
267 return updated_results 269 return updated_results
268 270
269 271
270 class BuildFailure(BaseHandler): 272 class BuildFailure(BaseHandler):
271 PERMISSION_LEVEL = Permission.ANYONE 273 PERMISSION_LEVEL = Permission.ANYONE
272 274
273 def _ShowDebugInfo(self): 275 def _ShowDebugInfo(self):
274 # Show debug info only if the app is run locally during development, if the 276 # Show debug info only if the app is run locally during development, if the
275 # currently logged-in user is an admin, or if it is explicitly requested 277 # currently logged-in user is an admin, or if it is explicitly requested
276 # with parameter 'debug=1'. 278 # with parameter 'debug=1'.
277 return ( 279 return users.is_current_user_admin() or self.request.get('debug') == '1'
278 users.is_current_user_admin() or self.request.get('debug') == '1')
279 280
280 def _ShowTriageHelpButton(self): 281 def _ShowTriageHelpButton(self):
281 return users.is_current_user_admin() 282 return users.is_current_user_admin()
282 283
283 def _PrepareCommonDataForFailure(self, analysis): 284 def _PrepareCommonDataForFailure(self, analysis):
284 return { 285 return {
285 'master_name': analysis.master_name, 286 'master_name': analysis.master_name,
286 'builder_name': analysis.builder_name, 287 'builder_name': analysis.builder_name,
287 'build_number': analysis.build_number, 288 'build_number': analysis.build_number,
288 'pipeline_status_path': analysis.pipeline_status_path, 289 'pipeline_status_path': analysis.pipeline_status_path,
(...skipping 18 matching lines...) Expand all
307 return try_job_data # pragma: no cover. 308 return try_job_data # pragma: no cover.
308 309
309 referred_build_keys = analysis.failure_result_map[ 310 referred_build_keys = analysis.failure_result_map[
310 constants.COMPILE_STEP_NAME].split('/') 311 constants.COMPILE_STEP_NAME].split('/')
311 try_job = WfTryJob.Get(*referred_build_keys) 312 try_job = WfTryJob.Get(*referred_build_keys)
312 if not try_job or not try_job.compile_results: 313 if not try_job or not try_job.compile_results:
313 return try_job_data # pragma: no cover. 314 return try_job_data # pragma: no cover.
314 result = try_job.compile_results[-1] 315 result = try_job.compile_results[-1]
315 316
316 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get( 317 try_job_data['status'] = analysis_status.STATUS_TO_DESCRIPTION.get(
317 try_job.status, 'unknown').lower() 318 try_job.status, 'unknown').lower()
318 try_job_data['url'] = result.get('url') 319 try_job_data['url'] = result.get('url')
319 try_job_data['completed'] = try_job.completed 320 try_job_data['completed'] = try_job.completed
320 try_job_data['failed'] = try_job.failed 321 try_job_data['failed'] = try_job.failed
321 try_job_data['culprit'] = result.get( 322 try_job_data['culprit'] = result.get(
322 'culprit', {}).get(constants.COMPILE_STEP_NAME) 323 'culprit', {}).get(constants.COMPILE_STEP_NAME)
323 324
324 return try_job_data 325 return try_job_data
325 326
326 @staticmethod 327 @staticmethod
327 def _PopulateHeuristicDataForCompileFailure(analysis, data): 328 def _PopulateHeuristicDataForCompileFailure(analysis, data):
(...skipping 10 matching lines...) Expand all
338 def _PrepareDataForCompileFailure(self, analysis): 339 def _PrepareDataForCompileFailure(self, analysis):
339 data = self._PrepareCommonDataForFailure(analysis) 340 data = self._PrepareCommonDataForFailure(analysis)
340 341
341 # Check result from heuristic analysis. 342 # Check result from heuristic analysis.
342 self._PopulateHeuristicDataForCompileFailure(analysis, data) 343 self._PopulateHeuristicDataForCompileFailure(analysis, data)
343 # Check result from try job. 344 # Check result from try job.
344 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis) 345 data['try_job'] = self._PrepareTryJobDataForCompileFailure(analysis)
345 346
346 return data 347 return data
347 348
348 def _PrepareDataForTestFailures(self, analysis, build_info): 349 def _PrepareDataForTestFailures(self, analysis, build_info,
350 show_debug_info=False):
349 data = self._PrepareCommonDataForFailure(analysis) 351 data = self._PrepareCommonDataForFailure(analysis)
350 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP 352 data['status_message_map'] = result_status.STATUS_MESSAGE_MAP
351 353
352 organized_results = _GetOrganizedAnalysisResultBySuspectedCL( 354 organized_results = _GetOrganizedAnalysisResultBySuspectedCL(
353 analysis.result) 355 analysis.result)
354 analysis_result = _GetAnalysisResultWithTryJobInfo( 356 analysis_result = _GetAnalysisResultWithTryJobInfo(
355 organized_results, *build_info) 357 show_debug_info, organized_results, *build_info)
356 data['analysis_result'] = analysis_result 358 data['analysis_result'] = analysis_result
357 359
358 return data 360 return data
359 361
360 def HandleGet(self): 362 def HandleGet(self):
361 """Triggers analysis of a build failure on demand and return current result. 363 """Triggers analysis of a build failure on demand and return current result.
362 364
363 If the final analysis result is available, set cache-control to 1 day to 365 If the final analysis result is available, set cache-control to 1 day to
364 avoid overload by unnecessary and frequent query from clients; otherwise 366 avoid overload by unnecessary and frequent query from clients; otherwise
365 set cache-control to 5 seconds to allow repeated query. 367 set cache-control to 5 seconds to allow repeated query.
(...skipping 15 matching lines...) Expand all
381 if not analysis: 383 if not analysis:
382 return BaseHandler.CreateError( 384 return BaseHandler.CreateError(
383 'Master "%s" is not supported yet.' % master_name, 501) 385 'Master "%s" is not supported yet.' % master_name, 501)
384 386
385 if not analysis: 387 if not analysis:
386 # Only allow admin to force a re-run and set the build_completed. 388 # Only allow admin to force a re-run and set the build_completed.
387 force = (users.is_current_user_admin() and 389 force = (users.is_current_user_admin() and
388 self.request.get('force') == '1') 390 self.request.get('force') == '1')
389 build_completed = (users.is_current_user_admin() and 391 build_completed = (users.is_current_user_admin() and
390 self.request.get('build_completed') == '1') 392 self.request.get('build_completed') == '1')
393 force_try_job_rerun = (users.is_current_user_admin() and
394 self.request.get('force_try_job_rerun') == '1')
stgao 2016/06/02 01:25:13 nit: just "force_try_job"?
lijeffrey 2016/06/02 05:36:06 Done.
391 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded( 395 analysis = build_failure_analysis_pipelines.ScheduleAnalysisIfNeeded(
392 master_name, builder_name, build_number, 396 master_name, builder_name, build_number,
393 build_completed=build_completed, force=force, 397 build_completed=build_completed, force=force,
398 force_try_job_rerun=force_try_job_rerun,
394 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) 399 queue_name=constants.WATERFALL_ANALYSIS_QUEUE)
395 400
396 if analysis.failure_type == failure_type.COMPILE: 401 if analysis.failure_type == failure_type.COMPILE:
397 return { 402 return {
398 'template': 'waterfall/compile_failure.html', 403 'template': 'waterfall/compile_failure.html',
399 'data': self._PrepareDataForCompileFailure(analysis), 404 'data': self._PrepareDataForCompileFailure(analysis),
400 } 405 }
401 else: 406 else:
402 return { 407 return {
403 'template': 'build_failure.html', 408 'template': 'build_failure.html',
404 'data': self._PrepareDataForTestFailures(analysis, build_info), 409 'data': self._PrepareDataForTestFailures(analysis, build_info,
410 self._ShowDebugInfo()),
405 } 411 }
406 412
407 def HandlePost(self): # pragma: no cover 413 def HandlePost(self): # pragma: no cover
408 return self.HandleGet() 414 return self.HandleGet()
OLDNEW
« no previous file with comments | « no previous file | appengine/findit/handlers/handlers_util.py » ('j') | appengine/findit/waterfall/test/try_job_util_test.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698