| 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 2. Analysis of flakes on Commit Queue. |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 110 | 110 |
| 111 class _FlakeAnalysis(messages.Message): | 111 class _FlakeAnalysis(messages.Message): |
| 112 analysis_triggered = messages.BooleanField(1, required=True) | 112 analysis_triggered = messages.BooleanField(1, required=True) |
| 113 | 113 |
| 114 | 114 |
| 115 def _TriggerNewAnalysesOnDemand(builds): | 115 def _TriggerNewAnalysesOnDemand(builds): |
| 116 """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.""" |
| 117 target = appengine_util.GetTargetNameForModule(constants.WATERFALL_BACKEND) | 117 target = appengine_util.GetTargetNameForModule(constants.WATERFALL_BACKEND) |
| 118 payload = json.dumps({'builds': builds}) | 118 payload = json.dumps({'builds': builds}) |
| 119 taskqueue.add( | 119 taskqueue.add( |
| 120 url=constants.WATERFALL_TRIGGER_ANALYSIS_URL, payload=payload, | 120 url=constants.WATERFALL_TRIGGER_ANALYSIS_URL, |
| 121 target=target, queue_name=constants.WATERFALL_SERIAL_QUEUE) | 121 payload=payload, target=target, |
| 122 queue_name=constants.WATERFALL_FAILURE_ANALYSIS_REQUEST_QUEUE) |
| 122 | 123 |
| 123 | 124 |
| 124 # Create a Cloud Endpoints API. | 125 # Create a Cloud Endpoints API. |
| 125 # https://cloud.google.com/appengine/docs/python/endpoints/create_api | 126 # https://cloud.google.com/appengine/docs/python/endpoints/create_api |
| 126 @endpoints.api(name='findit', version='v1', description='FindIt API') | 127 @endpoints.api(name='findit', version='v1', description='FindIt API') |
| 127 class FindItApi(remote.Service): | 128 class FindItApi(remote.Service): |
| 128 """FindIt API v1.""" | 129 """FindIt API v1.""" |
| 129 | 130 |
| 130 def _GenerateBuildFailureAnalysisResult( | 131 def _GenerateBuildFailureAnalysisResult( |
| 131 self, build, suspected_cls_in_result, step_name, | 132 self, build, suspected_cls_in_result, step_name, |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 Returns: | 242 Returns: |
| 242 _BuildFailureAnalysisResultCollection | 243 _BuildFailureAnalysisResultCollection |
| 243 A list of analysis results for the given build failures. | 244 A list of analysis results for the given build failures. |
| 244 """ | 245 """ |
| 245 results = [] | 246 results = [] |
| 246 supported_builds = [] | 247 supported_builds = [] |
| 247 | 248 |
| 248 for build in request.builds: | 249 for build in request.builds: |
| 249 master_name = buildbot.GetMasterNameFromUrl(build.master_url) | 250 master_name = buildbot.GetMasterNameFromUrl(build.master_url) |
| 250 if not (master_name and waterfall_config.MasterIsSupported(master_name)): | 251 if not (master_name and waterfall_config.MasterIsSupported(master_name)): |
| 252 logging.info('%s/%s/%s is not supported', |
| 253 build.master_url, build.builder_name, build.build_number) |
| 251 continue | 254 continue |
| 252 | 255 |
| 253 supported_builds.append({ | 256 supported_builds.append({ |
| 254 'master_name': master_name, | 257 'master_name': master_name, |
| 255 'builder_name': build.builder_name, | 258 'builder_name': build.builder_name, |
| 256 'build_number': build.build_number, | 259 'build_number': build.build_number, |
| 257 'failed_steps': build.failed_steps, | 260 'failed_steps': build.failed_steps, |
| 258 }) | 261 }) |
| 259 | 262 |
| 260 # If the build failure was already analyzed and a new analysis is | 263 # If the build failure was already analyzed and a new analysis is |
| (...skipping 11 matching lines...) Expand all Loading... |
| 272 | 275 |
| 273 self._GenerateResultsForBuild(build, heuristic_analysis, results) | 276 self._GenerateResultsForBuild(build, heuristic_analysis, results) |
| 274 | 277 |
| 275 logging.info('%d build failure(s), while %d are supported', | 278 logging.info('%d build failure(s), while %d are supported', |
| 276 len(request.builds), len(supported_builds)) | 279 len(request.builds), len(supported_builds)) |
| 277 try: | 280 try: |
| 278 _TriggerNewAnalysesOnDemand(supported_builds) | 281 _TriggerNewAnalysesOnDemand(supported_builds) |
| 279 except Exception: # pragma: no cover. | 282 except Exception: # pragma: no cover. |
| 280 # If we fail to post a task to the task queue, we ignore and wait for next | 283 # If we fail to post a task to the task queue, we ignore and wait for next |
| 281 # request. | 284 # request. |
| 282 logging.exception('Failed to trigger new analyses on demand.') | 285 logging.exception('Failed to add analysis request to task queue: %s', |
| 286 repr(supported_builds)) |
| 283 | 287 |
| 284 return _BuildFailureAnalysisResultCollection(results=results) | 288 return _BuildFailureAnalysisResultCollection(results=results) |
| 285 | 289 |
| 286 @endpoints.method(_Flake, _FlakeAnalysis, path='flake', name='flake') | 290 @endpoints.method(_Flake, _FlakeAnalysis, path='flake', name='flake') |
| 287 def AnalyzeFlake(self, request): | 291 def AnalyzeFlake(self, request): |
| 288 """Analyze a flake on Commit Queue. Currently only supports flaky tests.""" | 292 """Analyze a flake on Commit Queue. Currently only supports flaky tests.""" |
| 289 user_email = auth_util.GetUserEmail() | 293 user_email = auth_util.GetUserEmail() |
| 290 is_admin = auth_util.IsCurrentUserAdmin() | 294 is_admin = auth_util.IsCurrentUserAdmin() |
| 291 | 295 |
| 292 def CreateFlakeAnalysisRequest(flake): | 296 def CreateFlakeAnalysisRequest(flake): |
| 293 analysis_request = FlakeAnalysisRequest.Create( | 297 analysis_request = FlakeAnalysisRequest.Create( |
| 294 flake.name, flake.is_step, flake.bug_id) | 298 flake.name, flake.is_step, flake.bug_id) |
| 295 for step in flake.build_steps: | 299 for step in flake.build_steps: |
| 296 analysis_request.AddBuildStep(step.master_name, step.builder_name, | 300 analysis_request.AddBuildStep(step.master_name, step.builder_name, |
| 297 step.build_number, step.step_name, | 301 step.build_number, step.step_name, |
| 298 time_util.GetUTCNow()) | 302 time_util.GetUTCNow()) |
| 299 return analysis_request | 303 return analysis_request |
| 300 | 304 |
| 301 logging.info('Flake: %s', CreateFlakeAnalysisRequest(request)) | 305 logging.info('Flake: %s', CreateFlakeAnalysisRequest(request)) |
| 302 analysis_triggered = flake_analysis_service.ScheduleAnalysisForFlake( | 306 analysis_triggered = flake_analysis_service.ScheduleAnalysisForFlake( |
| 303 CreateFlakeAnalysisRequest(request), user_email, is_admin) | 307 CreateFlakeAnalysisRequest(request), user_email, is_admin) |
| 304 | 308 |
| 305 if analysis_triggered is None: | 309 if analysis_triggered is None: |
| 306 raise endpoints.UnauthorizedException( | 310 raise endpoints.UnauthorizedException( |
| 307 'No permission for a new analysis! User is %s' % user_email) | 311 'No permission for a new analysis! User is %s' % user_email) |
| 308 | 312 |
| 309 return _FlakeAnalysis(analysis_triggered=analysis_triggered) | 313 return _FlakeAnalysis(analysis_triggered=analysis_triggered) |
| OLD | NEW |