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 from datetime import datetime | 5 from datetime import datetime |
| 6 import logging | 6 import logging |
| 7 import textwrap | 7 import textwrap |
| 8 | 8 |
| 9 from google.appengine.ext import ndb | 9 from google.appengine.ext import ndb |
| 10 | 10 |
| 11 from common.git_repository import GitRepository | 11 from common.git_repository import GitRepository |
| 12 from common.http_client_appengine import HttpClientAppengine as HttpClient | 12 from common.http_client_appengine import HttpClientAppengine as HttpClient |
| 13 from common.pipeline_wrapper import BasePipeline | 13 from common.pipeline_wrapper import BasePipeline |
| 14 from common.rietveld import Rietveld | 14 from common.rietveld import Rietveld |
| 15 from model import analysis_status as status | 15 from model import analysis_status as status |
| 16 from model.wf_culprit import WfCulprit | 16 from model.wf_culprit import WfCulprit |
| 17 from waterfall import waterfall_config | |
| 17 | 18 |
| 18 | 19 |
| 19 @ndb.transactional | 20 @ndb.transactional |
| 20 def _ShouldSendNotification( | 21 def _ShouldSendNotification( |
| 21 master_name, builder_name, build_number, repo_name, revision): | 22 master_name, builder_name, build_number, repo_name, revision, |
| 23 build_num_threshold, pass_time_limit): | |
| 22 """Returns True if a notification for the culprit should be sent.""" | 24 """Returns True if a notification for the culprit should be sent.""" |
| 23 culprit = (WfCulprit.Get(repo_name, revision) or | 25 culprit = (WfCulprit.Get(repo_name, revision) or |
| 24 WfCulprit.Create(repo_name, revision)) | 26 WfCulprit.Create(repo_name, revision)) |
| 25 if [master_name, builder_name, build_number] in culprit.builds: | 27 if [master_name, builder_name, build_number] in culprit.builds: |
| 26 return False | 28 return False |
| 27 | 29 |
| 28 culprit.builds.append([master_name, builder_name, build_number]) | 30 culprit.builds.append([master_name, builder_name, build_number]) |
| 29 # Send notification when the culprit is for 2+ failures in two different | 31 # Send notification only when: |
| 30 # builds to avoid false positive due to flakiness. | 32 # 1. It was not processed yet. |
| 31 # TODO(stgao): move to config. | 33 # 2. The culprit is for multiple failures in different builds to avoid false |
| 34 # positive due to flakiness. | |
| 35 # 3. It is not too late after the culprit was committed. | |
| 32 should_send = not (culprit.cr_notification_processed or | 36 should_send = not (culprit.cr_notification_processed or |
| 33 len(culprit.builds) < 2) | 37 len(culprit.builds) < build_num_threshold or |
| 38 pass_time_limit) | |
| 34 if should_send: | 39 if should_send: |
| 35 culprit.cr_notification_status = status.RUNNING | 40 culprit.cr_notification_status = status.RUNNING |
| 36 culprit.put() | 41 culprit.put() |
| 37 return should_send | 42 return should_send |
| 38 | 43 |
| 39 | 44 |
| 40 @ndb.transactional | 45 @ndb.transactional |
| 41 def _UpdateNotificationStatus(repo_name, revision, new_status): | 46 def _UpdateNotificationStatus(repo_name, revision, new_status): |
| 42 culprit = WfCulprit.Get(repo_name, revision) | 47 culprit = WfCulprit.Get(repo_name, revision) |
| 43 culprit.cr_notification_status = new_status | 48 culprit.cr_notification_status = new_status |
| 44 if culprit.cr_notified: | 49 if culprit.cr_notified: |
| 45 culprit.cr_notification_time = datetime.utcnow() | 50 culprit.cr_notification_time = datetime.utcnow() |
| 46 culprit.put() | 51 culprit.put() |
| 47 | 52 |
| 48 | 53 |
| 49 def _SendNotificationForCulprit(repo_name, revision): | 54 def _SendNotificationForCulprit(repo_name, revision, code_review_url): |
| 50 # TODO(stgao): get repo url at runtime based on the given repo name. | |
| 51 repo = GitRepository( | |
| 52 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | |
| 53 change_log = repo.GetChangeLog(revision) | |
| 54 sent = False | 55 sent = False |
| 55 if change_log.code_review_url: | 56 if code_review_url: |
| 56 # Occasionally, a commit was not uploaded for code-review. | 57 # Occasionally, a commit was not uploaded for code-review. |
| 57 culprit = WfCulprit.Get(repo_name, revision) | 58 culprit = WfCulprit.Get(repo_name, revision) |
| 58 rietveld = Rietveld() | 59 rietveld = Rietveld() |
| 59 message = textwrap.dedent(""" | 60 message = textwrap.dedent(""" |
| 60 Findit Try-job identified this CL (revision %s) as the culprit for failures | 61 Findit Try-job identified this CL (revision %s) as the culprit for failures |
| 61 in the build cycle(s) as shown on: | 62 in the build cycle(s) as shown on: |
| 62 https://findit-for-me.appspot.com/waterfall/culprit?key=%s | 63 https://findit-for-me.appspot.com/waterfall/culprit?key=%s |
| 63 """) % (revision, culprit.key.urlsafe()) | 64 """) % (revision, culprit.key.urlsafe()) |
| 64 sent = rietveld.PostMessage(change_log.code_review_url, message) | 65 sent = rietveld.PostMessage(code_review_url, message) |
| 65 else: | 66 else: |
| 66 logging.error('Can not get code-review url for %s/%s', repo_name, revision) | 67 logging.error('No code-review url for %s/%s', repo_name, revision) |
| 67 | 68 |
| 68 _UpdateNotificationStatus(repo_name, revision, | 69 _UpdateNotificationStatus(repo_name, revision, |
| 69 status.COMPLETED if sent else status.ERROR) | 70 status.COMPLETED if sent else status.ERROR) |
| 70 return sent | 71 return sent |
| 71 | 72 |
| 72 | 73 |
| 74 def _GetCulpritInfo(repo_name, revision): | |
| 75 """Returns commit time and code-review url of the given revision.""" | |
| 76 # TODO(stgao): get repo url at runtime based on the given repo name. | |
| 77 # unused arg - pylint: disable=W0612,W0613 | |
| 78 repo = GitRepository( | |
| 79 'https://chromium.googlesource.com/chromium/src.git', HttpClient()) | |
| 80 change_log = repo.GetChangeLog(revision) | |
| 81 return change_log.committer_time, change_log.code_review_url | |
| 82 | |
| 83 | |
| 84 def _PassNotificationTimeLimit(commit_time, latency_limit_seconds): | |
|
lijeffrey
2016/06/30 01:02:48
nit: I would rename this _NotificationTimeLimitPas
stgao
2016/06/30 17:35:33
hm, this is actually not a timeout.
But the rename
| |
| 85 """Returns True if it is too late to send notification.""" | |
| 86 latency_seconds = (datetime.utcnow() - commit_time).total_seconds() | |
| 87 return latency_seconds > latency_limit_seconds | |
|
chanli
2016/06/30 20:23:41
Though I think this is just an implementation deta
stgao
2016/06/30 22:36:57
We could do minutes. Done.
| |
| 88 | |
| 89 | |
| 73 class SendNotificationForCulpritPipeline(BasePipeline): | 90 class SendNotificationForCulpritPipeline(BasePipeline): |
| 74 | 91 |
| 75 # Arguments number differs from overridden method - pylint: disable=W0221 | 92 # Arguments number differs from overridden method - pylint: disable=W0221 |
| 76 def run(self, master_name, builder_name, build_number, repo_name, revision): | 93 def run(self, master_name, builder_name, build_number, repo_name, revision): |
| 94 action_settings = waterfall_config.GetActionSettings() | |
| 95 build_threshold = action_settings.get( | |
| 96 'cr_notification_build_threshold', 100000) | |
| 97 latency_limit_seconds = action_settings.get( | |
| 98 'cr_notification_latency_limit_seconds', 1) | |
|
chanli
2016/06/30 20:23:41
are 100000 and 1 default values or some impossible
stgao
2016/06/30 22:36:57
Yes, added a comment on this.
| |
| 99 | |
| 100 commit_time, code_review_url = _GetCulpritInfo(repo_name, revision) | |
| 101 pass_time_limit = _PassNotificationTimeLimit( | |
| 102 commit_time, latency_limit_seconds) | |
| 103 | |
| 77 if not _ShouldSendNotification( | 104 if not _ShouldSendNotification( |
| 78 master_name, builder_name, build_number, repo_name, revision): | 105 master_name, builder_name, build_number, |
| 106 repo_name, revision, build_threshold, pass_time_limit): | |
| 79 return False | 107 return False |
| 80 return _SendNotificationForCulprit(repo_name, revision) | 108 return _SendNotificationForCulprit(repo_name, revision, code_review_url) |
| OLD | NEW |