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

Side by Side Diff: dashboard/dashboard/update_bug_with_results.py

Issue 1924143002: Make sure that email alerts are sent on bisect fyi jobs failures. (Closed) Base URL: https://github.com/catapult-project/catapult.git@master
Patch Set: Created 4 years, 7 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 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 """URL endpoint for a cron job to update bugs after bisects.""" 5 """URL endpoint for a cron job to update bugs after bisects."""
6 6
7 import datetime 7 import datetime
8 import json 8 import json
9 import logging 9 import logging
10 import re 10 import re
11 import traceback 11 import traceback
12 12
13 from google.appengine.api import mail 13 from google.appengine.api import mail
14 from google.appengine.ext import ndb 14 from google.appengine.ext import ndb
15 15
16 from dashboard import bisect_fyi 16 from dashboard import bisect_fyi
17 from dashboard import bisect_report 17 from dashboard import bisect_report
18 from dashboard import buildbucket_service
18 from dashboard import datastore_hooks 19 from dashboard import datastore_hooks
19 from dashboard import email_template 20 from dashboard import email_template
20 from dashboard import issue_tracker_service 21 from dashboard import issue_tracker_service
21 from dashboard import layered_cache 22 from dashboard import layered_cache
22 from dashboard import quick_logger 23 from dashboard import quick_logger
23 from dashboard import request_handler 24 from dashboard import request_handler
24 from dashboard import utils 25 from dashboard import utils
25 from dashboard.models import anomaly 26 from dashboard.models import anomaly
26 from dashboard.models import bug_data 27 from dashboard.models import bug_data
27 from dashboard.models import try_job 28 from dashboard.models import try_job
(...skipping 10 matching lines...) Expand all
38 === Auto-CCing suspected CL author %(author)s === 39 === Auto-CCing suspected CL author %(author)s ===
39 40
40 Hi %(author)s, the bisect results pointed to your CL below as possibly 41 Hi %(author)s, the bisect results pointed to your CL below as possibly
41 causing a regression. Please have a look at this info and see whether 42 causing a regression. Please have a look at this info and see whether
42 your CL be related. 43 your CL be related.
43 44
44 """ 45 """
45 46
46 _CONFIDENCE_LEVEL_TO_CC_AUTHOR = 95 47 _CONFIDENCE_LEVEL_TO_CC_AUTHOR = 95
47 48
49 _BUILD_FAILURE_REASON = {
50 'BUILD_FAILURE': 'the build has failed',
51 'INFRA_FAILURE': 'the build has failed due to infrastructure failure.',
52 'BUILDBUCKET_FAILURE': 'the buildbucket service failure.',
53 'INVALID_BUILD_DEFINITION': 'incorrect bisect configuation.',
54 'CANCELED_EXPLICITLY': 'the build was canceled explicitly.',
55 'TIMEOUT': 'the build was canceled by buildbot on timeout.',
56 }
57
58 class BisectJobFailure(Exception):
59 pass
60
48 61
49 class BugUpdateFailure(Exception): 62 class BugUpdateFailure(Exception):
50 pass 63 pass
51 64
52 65
53 class UpdateBugWithResultsHandler(request_handler.RequestHandler): 66 class UpdateBugWithResultsHandler(request_handler.RequestHandler):
54 """URL endpoint for a cron job to update bugs after bisects.""" 67 """URL endpoint for a cron job to update bugs after bisects."""
55 68
56 def get(self): 69 def get(self):
57 """The get handler method is called from a cron job. 70 """The get handler method is called from a cron job.
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
92 job: A TryJob entity, which represents one bisect try job. 105 job: A TryJob entity, which represents one bisect try job.
93 issue_tracker: An issue_tracker_service.IssueTrackerService instance. 106 issue_tracker: An issue_tracker_service.IssueTrackerService instance.
94 """ 107 """
95 if _IsStale(job): 108 if _IsStale(job):
96 job.SetStaled() 109 job.SetStaled()
97 # TODO(chrisphan): Add a staled TryJob log. 110 # TODO(chrisphan): Add a staled TryJob log.
98 # TODO(chrisphan): Do we want to send a FYI Bisect email here? 111 # TODO(chrisphan): Do we want to send a FYI Bisect email here?
99 return 112 return
100 113
101 results_data = job.results_data 114 results_data = job.results_data
102 if not results_data or results_data['status'] not in [COMPLETED, FAILED]: 115 # Skip this check for bisect fyi jobs, because if the job is fails due to
116 # bisect recipe or infra failures then an alert message should be sent to the
117 # team.
118 if ((not results_data or results_data['status'] not in [COMPLETED, FAILED])
119 and job.job_type != 'bisect-fyi'):
103 return 120 return
104 121
105 if job.job_type == 'perf-try': 122 if job.job_type == 'perf-try':
106 _SendPerfTryJobEmail(job) 123 _SendPerfTryJobEmail(job)
107 elif job.job_type == 'bisect-fyi': 124 elif job.job_type == 'bisect-fyi':
108 _CheckFYIBisectJob(job, issue_tracker) 125 _CheckFYIBisectJob(job, issue_tracker)
109 else: 126 else:
110 _CheckBisectJob(job, issue_tracker) 127 _CheckBisectJob(job, issue_tracker)
111 128
112 if results_data['status'] == COMPLETED: 129 if results_data['status'] == COMPLETED:
113 job.SetCompleted() 130 job.SetCompleted()
114 else: 131 else:
115 job.SetFailed() 132 job.SetFailed()
116 133
117 134
118 def _CheckBisectJob(job, issue_tracker): 135 def _CheckBisectJob(job, issue_tracker):
119 results_data = job.results_data 136 results_data = job.results_data
120 has_partial_result = ('revision_data' in results_data and 137 has_partial_result = ('revision_data' in results_data and
121 results_data['revision_data']) 138 results_data['revision_data'])
122 if results_data['status'] == FAILED and not has_partial_result: 139 if results_data['status'] == FAILED and not has_partial_result:
123 return 140 return
124 _PostResult(job, issue_tracker) 141 _PostResult(job, issue_tracker)
125 142
126 143
127 def _CheckFYIBisectJob(job, issue_tracker): 144 def _CheckFYIBisectJob(job, issue_tracker):
128 try: 145 try:
146 if not _IsBisectJobCompleted(job):
147 return
148 error_message = bisect_fyi.VerifyBisectFYIResults(job)
129 _PostResult(job, issue_tracker) 149 _PostResult(job, issue_tracker)
130 error_message = bisect_fyi.VerifyBisectFYIResults(job)
131 if not bisect_fyi.IsBugUpdated(job, issue_tracker): 150 if not bisect_fyi.IsBugUpdated(job, issue_tracker):
132 error_message += '\nFailed to update bug with bisect results.' 151 error_message += '\nFailed to update bug with bisect results.'
152 except BisectJobFailure as e:
153 error_message = 'Bisect job failed because, %s' % e
133 except BugUpdateFailure as e: 154 except BugUpdateFailure as e:
134 error_message = 'Failed to update bug with bisect results: %s' % e 155 error_message = 'Failed to update bug with bisect results: %s' % e
135 if job.results_data['status'] == FAILED or error_message: 156 if job.results_data['status'] == FAILED or error_message:
157 job.SetFailed()
136 _SendFYIBisectEmail(job, error_message) 158 _SendFYIBisectEmail(job, error_message)
137 159
138 160
139 def _SendPerfTryJobEmail(job): 161 def _SendPerfTryJobEmail(job):
140 """Sends an email to the user who started the perf try job.""" 162 """Sends an email to the user who started the perf try job."""
141 if not job.email: 163 if not job.email:
142 return 164 return
143 email_report = email_template.GetPerfTryJobEmailReport(job) 165 email_report = email_template.GetPerfTryJobEmailReport(job)
144 if not email_report: 166 if not email_report:
145 return 167 return
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 return 347 return
326 formatter = quick_logger.Formatter() 348 formatter = quick_logger.Formatter()
327 logger = quick_logger.QuickLogger('bisect_result', job.bug_id, formatter) 349 logger = quick_logger.QuickLogger('bisect_result', job.bug_id, formatter)
328 if job.log_record_id: 350 if job.log_record_id:
329 logger.Log(report, record_id=job.log_record_id) 351 logger.Log(report, record_id=job.log_record_id)
330 logger.Save() 352 logger.Save()
331 else: 353 else:
332 job.log_record_id = logger.Log(report) 354 job.log_record_id = logger.Log(report)
333 logger.Save() 355 logger.Save()
334 job.put() 356 job.put()
357
358
359 def _IsBisectJobCompleted(job):
360 return _ValidateBuildbucketResponse(
361 buildbucket_service.GetJobStatus(job.buildbucket_job_id), job)
362
363
364 def _ValidateBuildbucketResponse(job_info, job):
365 """Checks and validates the response from the buildbucket service for bisect.
366
367 Args:
368 job_info: A dictionary containing the response from the buildbucket service.
369
370 Returns:
371 True if bisect job is completed successfully and False for pending job.
372
373 Raises:
374 BisectJobFailure: When job is completed but build is failed or cancelled.
375 """
376 job_info = job_info['build']
377 json_response = json.dumps(job_info)
378 if not job_info:
379 raise BisectJobFailure('No response from Buildbucket.')
380
381 if job_info.get('status') in ['SCHEDULED', 'STARTED']:
382 return False
383
384 if not job.results_data:
385 job.results_data = {}
386 job.results_data['buildbot_log_url'] = str(job_info.get('url'))
387
388 if job_info.get('result') is None:
389 raise BisectJobFailure('No "result" in try job results. '
390 'Buildbucket response: %s' % json_response)
391
392 # There are various failure and cancellation reasons for a buildbucket
393 # job to fail as listed in https://goto.google.com/bb_status.
394 if (job_info.get('status') == 'COMPLETED' and
395 job_info.get('result') != 'SUCCESS'):
396 reason = (job_info.get('cancelation_reason') or
397 job_info.get('failure_reason'))
398 raise BisectJobFailure(_BUILD_FAILURE_REASON.get(reason))
399 return True
OLDNEW
« no previous file with comments | « dashboard/dashboard/email_template.py ('k') | dashboard/dashboard/update_bug_with_results_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698