| 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 """This module is to provide Findit service APIs through Cloud Endpoints: | 5 """This module is to provide Findit service APIs through Cloud Endpoints: |
| 6 | 6 |
| 7 Current APIs include: | 7 Current APIs include: |
| 8 1. Analysis of compile/test failures in Chromium waterfalls. | 8 1. Analysis of compile/test failures in Chromium waterfalls. |
| 9 Analyzes failures and detects suspected CLs. | 9 Analyzes failures and detects suspected CLs. |
| 10 2. Analysis of flakes on Commit Queue. |
| 10 """ | 11 """ |
| 11 | 12 |
| 12 import json | 13 import json |
| 13 import logging | 14 import logging |
| 14 | 15 |
| 15 import endpoints | 16 import endpoints |
| 16 from google.appengine.api import taskqueue | 17 from google.appengine.api import taskqueue |
| 17 from protorpc import messages | 18 from protorpc import messages |
| 18 from protorpc import remote | 19 from protorpc import remote |
| 19 | 20 |
| 20 from common import appengine_util | 21 from common import appengine_util |
| 22 from common import auth_util |
| 21 from common import constants | 23 from common import constants |
| 24 from common import time_util |
| 22 from common.waterfall import failure_type | 25 from common.waterfall import failure_type |
| 26 from model.flake.flake_analysis_request import FlakeAnalysisRequest |
| 23 from model.wf_analysis import WfAnalysis | 27 from model.wf_analysis import WfAnalysis |
| 24 from model.wf_swarming_task import WfSwarmingTask | 28 from model.wf_swarming_task import WfSwarmingTask |
| 25 from model.wf_try_job import WfTryJob | 29 from model.wf_try_job import WfTryJob |
| 26 from waterfall import buildbot | 30 from waterfall import buildbot |
| 27 from waterfall import waterfall_config | 31 from waterfall import waterfall_config |
| 32 from waterfall.flake import flake_analysis_service |
| 28 | 33 |
| 29 | 34 |
| 30 # This is used by the underlying ProtoRpc when creating names for the ProtoRPC | 35 # This is used by the underlying ProtoRpc when creating names for the ProtoRPC |
| 31 # messages below. This package name will show up as a prefix to the message | 36 # messages below. This package name will show up as a prefix to the message |
| 32 # class names in the discovery doc and client libraries. | 37 # class names in the discovery doc and client libraries. |
| 33 package = 'FindIt' | 38 package = 'FindIt' |
| 34 | 39 |
| 35 | 40 |
| 36 # These subclasses of Message are basically definitions of Protocol RPC | 41 # These subclasses of Message are basically definitions of Protocol RPC |
| 37 # messages. https://cloud.google.com/appengine/docs/python/tools/protorpc/ | 42 # messages. https://cloud.google.com/appengine/docs/python/tools/protorpc/ |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 7, variant=messages.Variant.INT32) | 78 7, variant=messages.Variant.INT32) |
| 74 suspected_cls = messages.MessageField(_SuspectedCL, 8, repeated=True) | 79 suspected_cls = messages.MessageField(_SuspectedCL, 8, repeated=True) |
| 75 analysis_approach = messages.EnumField(_AnalysisApproach, 9) | 80 analysis_approach = messages.EnumField(_AnalysisApproach, 9) |
| 76 | 81 |
| 77 | 82 |
| 78 class _BuildFailureAnalysisResultCollection(messages.Message): | 83 class _BuildFailureAnalysisResultCollection(messages.Message): |
| 79 """Represents a response to the client, eg. builder_alerts.""" | 84 """Represents a response to the client, eg. builder_alerts.""" |
| 80 results = messages.MessageField(_BuildFailureAnalysisResult, 1, repeated=True) | 85 results = messages.MessageField(_BuildFailureAnalysisResult, 1, repeated=True) |
| 81 | 86 |
| 82 | 87 |
| 88 class _BuildStep(messages.Message): |
| 89 master_name = messages.StringField(1, required=True) |
| 90 builder_name = messages.StringField(2, required=True) |
| 91 build_number = messages.IntegerField( |
| 92 3, variant=messages.Variant.INT32, required=True) |
| 93 step_name = messages.StringField(4, required=True) |
| 94 |
| 95 |
| 96 class _Flake(messages.Message): |
| 97 name = messages.StringField(1, required=True) |
| 98 is_step = messages.BooleanField(2, required=False, default=False) |
| 99 bug_id = messages.IntegerField( |
| 100 3, variant=messages.Variant.INT32, required=True) |
| 101 build_steps = messages.MessageField(_BuildStep, 4, repeated=True) |
| 102 |
| 103 |
| 104 class _Build(messages.Message): |
| 105 master_name = messages.StringField(1, required=True) |
| 106 builder_name = messages.StringField(2, required=True) |
| 107 build_number = messages.IntegerField( |
| 108 3, variant=messages.Variant.INT32, required=True) |
| 109 |
| 110 |
| 111 class _FlakeAnalysis(messages.Message): |
| 112 analysis_triggered = messages.BooleanField(1, required=True) |
| 113 |
| 114 |
| 83 def _TriggerNewAnalysesOnDemand(builds): | 115 def _TriggerNewAnalysesOnDemand(builds): |
| 84 """Pushes a task to run on the backend to trigger new analyses on demand.""" | 116 """Pushes a task to run on the backend to trigger new analyses on demand.""" |
| 85 target = appengine_util.GetTargetNameForModule(constants.WATERFALL_BACKEND) | 117 target = appengine_util.GetTargetNameForModule(constants.WATERFALL_BACKEND) |
| 86 payload = json.dumps({'builds': builds}) | 118 payload = json.dumps({'builds': builds}) |
| 87 taskqueue.add( | 119 taskqueue.add( |
| 88 url=constants.WATERFALL_TRIGGER_ANALYSIS_URL, payload=payload, | 120 url=constants.WATERFALL_TRIGGER_ANALYSIS_URL, payload=payload, |
| 89 target=target, queue_name=constants.WATERFALL_SERIAL_QUEUE) | 121 target=target, queue_name=constants.WATERFALL_SERIAL_QUEUE) |
| 90 | 122 |
| 91 | 123 |
| 92 # Create a Cloud Endpoints API. | 124 # Create a Cloud Endpoints API. |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 243 logging.info('%d build failure(s), while %d are supported', | 275 logging.info('%d build failure(s), while %d are supported', |
| 244 len(request.builds), len(supported_builds)) | 276 len(request.builds), len(supported_builds)) |
| 245 try: | 277 try: |
| 246 _TriggerNewAnalysesOnDemand(supported_builds) | 278 _TriggerNewAnalysesOnDemand(supported_builds) |
| 247 except Exception: # pragma: no cover. | 279 except Exception: # pragma: no cover. |
| 248 # If we fail to post a task to the task queue, we ignore and wait for next | 280 # If we fail to post a task to the task queue, we ignore and wait for next |
| 249 # request. | 281 # request. |
| 250 logging.exception('Failed to trigger new analyses on demand.') | 282 logging.exception('Failed to trigger new analyses on demand.') |
| 251 | 283 |
| 252 return _BuildFailureAnalysisResultCollection(results=results) | 284 return _BuildFailureAnalysisResultCollection(results=results) |
| 285 |
| 286 @endpoints.method(_Flake, _FlakeAnalysis, path='flake', name='flake') |
| 287 def AnalyzeFlake(self, request): |
| 288 """Analyze a flake on Commit Queue. Currently only supports flaky tests.""" |
| 289 user_email = auth_util.GetUserEmail() |
| 290 is_admin = auth_util.IsCurrentUserAdmin() |
| 291 |
| 292 def CreateFlakeAnalysisRequest(flake): |
| 293 analysis_request = FlakeAnalysisRequest.Create( |
| 294 flake.name, flake.is_step, flake.bug_id) |
| 295 for step in flake.build_steps: |
| 296 analysis_request.AddBuildStep(step.master_name, step.builder_name, |
| 297 step.build_number, step.step_name, |
| 298 time_util.GetUTCNow()) |
| 299 return analysis_request |
| 300 |
| 301 logging.info('Flake: %s', CreateFlakeAnalysisRequest(request)) |
| 302 analysis_triggered = flake_analysis_service.ScheduleAnalysisForFlake( |
| 303 CreateFlakeAnalysisRequest(request), user_email, is_admin) |
| 304 |
| 305 if analysis_triggered is None: |
| 306 raise endpoints.UnauthorizedException( |
| 307 'No permission for a new analysis! User is %s' % user_email) |
| 308 |
| 309 return _FlakeAnalysis(analysis_triggered=analysis_triggered) |
| OLD | NEW |