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 import logging | |
| 5 | 6 |
| 6 def ScheduleAnalysisForFlake( | 7 from common import constants |
| 7 _request, _user_email, _is_admin): # pragma: no cover. | 8 from model.flake.flake_analysis_request import FlakeAnalysisRequest |
| 9 from waterfall.flake import initialize_flake_pipeline | |
| 10 from waterfall.flake import step_mapper | |
| 11 | |
| 12 | |
| 13 def _CheckFlakeSwarmedAndSupported(request): | |
| 14 """Checks if the flake is Swarmed and supported in any build step. | |
| 15 | |
| 16 Args: | |
| 17 request (FlakeAnalysisRequest): The request to analyze a flake. | |
| 18 | |
| 19 Returns: | |
| 20 (swarmed, supported, build_step) | |
| 21 swarmed(bool): True if any step is Swarmed. | |
| 22 supported(bool): True if any step is supported (Swarmed Gtest). | |
| 23 build_step(BuildStep): The representative step that is Swarmed Gtest. | |
| 24 """ | |
| 25 build_step = None | |
| 26 swarmed = False | |
| 27 supported = False | |
| 28 for step in request.build_steps: | |
| 29 swarmed = swarmed or step.swarmed | |
| 30 supported = supported or step.supported | |
| 31 if step.supported: | |
| 32 build_step = step | |
| 33 break | |
| 34 return swarmed, supported, build_step | |
| 35 | |
| 36 | |
| 37 def _NeedNewAnalysis(request): | |
|
chanli
2016/10/07 21:40:37
Nit: This kind of name indicates a True/False retu
stgao
2016/10/07 23:07:06
Renamed to CheckForNewAnalyais. Hope it is better,
| |
| 38 """Checks if a new analysis is needed for the requested flake. | |
| 39 | |
| 40 Args: | |
| 41 request (FlakeAnalysisRequest): The request to analyze a flake. | |
| 42 | |
| 43 Returns: | |
| 44 (version_number, build_step) | |
| 45 version_number(int): The version of the FlakeAnalysisRequest if a new | |
| 46 analysis is needed; otherwise 0. | |
|
lijeffrey
2016/10/07 20:45:07
nit: space before (. Same with build_step
stgao
2016/10/07 23:07:05
Done.
| |
| 47 build_step(BuildStep): a BuildStep instance if a new analysis is needed; | |
| 48 otherwise None. | |
| 49 """ | |
| 50 previous_request = FlakeAnalysisRequest.GetVersion(key=request.name) | |
| 51 if not previous_request or (previous_request.bug_id and request.bug_id and | |
| 52 previous_request.bug_id != request.bug_id): | |
| 53 # If no existing analysis or last analysis was for a different bug, randomly | |
| 54 # pick one configuration for a new analysis. | |
| 55 if previous_request: | |
| 56 # Make a copy to preserve the version number of previous analysis and | |
| 57 # prevent concurrent analyses of the same flake. | |
| 58 previous_request.CopyFrom(request) | |
| 59 request = previous_request | |
| 60 | |
| 61 swarmed, supported, supported_build_step = _CheckFlakeSwarmedAndSupported( | |
| 62 request) | |
| 63 request.swarmed = swarmed | |
| 64 request.supported = supported | |
| 65 | |
| 66 if supported_build_step and not request.is_step: | |
| 67 supported_build_step.analyzed = True # This step will be analyzed. | |
|
chanli
2016/10/07 21:40:37
Based on flake_analysis_request.py, analyzed 'Indi
stgao
2016/10/07 23:07:06
Good point.
Renamed to "scheduled".
| |
| 68 # For unsupported or step-level flakes, still save them for monitoring. | |
|
lijeffrey
2016/10/07 20:45:07
nit: add 1 empty line before the comment
stgao
2016/10/07 23:07:06
Done.
| |
| 69 _, saved = request.Save(retry_on_conflict=False) # Create a new version. | |
| 70 | |
| 71 if not saved or not supported_build_step or request.is_step: | |
| 72 # No new analysis if: | |
| 73 # 1. Another analysis was just triggered. | |
| 74 # 2. No representative step is Swarmed Gtest. | |
| 75 # 3. The flake is a step-level one. | |
| 76 return 0, None | |
| 77 | |
| 78 return request.version_number, supported_build_step | |
| 79 else: | |
| 80 # If no bug is attached to the existing analysis or the new request, or both | |
| 81 # are attached to the same bug, start a new analysis with a different | |
| 82 # configuration. For a configuration that was analyzed 7 days ago, reset it | |
| 83 # to use the new reported step of the same configuration. | |
| 84 # TODO: move this setting to config. | |
| 85 seconds_n_days = 7 * 24 * 60 * 60 # 7 days. | |
| 86 candidate_supported_steps = [] | |
| 87 need_updating = False | |
| 88 for step in request.build_steps: | |
|
lijeffrey
2016/10/07 20:45:07
is it possible to move this for loop to a separate
stgao
2016/10/07 23:07:06
I just moved the else branch to another function.
| |
| 89 existing_step = None | |
| 90 for s in previous_request.build_steps: | |
| 91 if (step.master_name == s.master_name and | |
| 92 step.builder_name == s.builder_name): | |
| 93 existing_step = s | |
| 94 break | |
| 95 | |
| 96 if existing_step: | |
| 97 # If last reported flake at the existing step was too long ago, drop it | |
| 98 # so that the new one is recorded. | |
| 99 time_diff = step.reported_time - existing_step.reported_time | |
| 100 if time_diff.total_seconds() > seconds_n_days: | |
| 101 previous_request.build_steps.remove(existing_step) | |
| 102 existing_step = None | |
| 103 | |
| 104 if not existing_step: | |
| 105 need_updating = True | |
| 106 previous_request.build_steps.append(step) | |
| 107 if step.supported: | |
| 108 candidate_supported_steps.append(step) | |
| 109 | |
| 110 if not candidate_supported_steps: | |
| 111 # Find some existing configuration that is not analyzed yet. | |
| 112 for s in previous_request.build_steps: | |
| 113 if not s.analyzed and s.supported: | |
| 114 candidate_supported_steps.append(s) | |
| 115 | |
| 116 supported_build_step = None | |
| 117 if candidate_supported_steps: | |
| 118 supported_build_step = candidate_supported_steps[0] | |
| 119 previous_request.swarmed = (previous_request.swarmed or | |
| 120 supported_build_step.swarmed) | |
| 121 previous_request.supported = True | |
| 122 need_updating = True | |
| 123 | |
| 124 if supported_build_step and not previous_request.is_step: | |
| 125 supported_build_step.analyzed = True # This will be analyzed. | |
| 126 | |
| 127 if not previous_request.bug_id: # No bug was attached before. | |
| 128 previous_request.bug_id = request.bug_id | |
| 129 need_updating = True | |
| 130 | |
| 131 if need_updating: | |
| 132 # TODO: update in a transaction. | |
| 133 previous_request.put() | |
| 134 | |
| 135 if not supported_build_step or previous_request.is_step: | |
| 136 # No new analysis if: | |
| 137 # 1. All analyzed steps are fresh enough and cover all the steps in the | |
| 138 # request. | |
| 139 # 2. No representative step is Swarmed Gtest. | |
| 140 # 3. The flake is a step-level one. | |
| 141 return 0, None | |
| 142 | |
| 143 return previous_request.version_number, supported_build_step | |
|
chanli
2016/10/07 21:40:37
Just to be clear:
If there is not previous analysi
stgao
2016/10/07 23:07:06
Assume only one bug is files for the same flake. A
| |
| 144 | |
| 145 | |
| 146 def ScheduleAnalysisForFlake(request, user_email, is_admin): | |
| 8 """Schedules an analysis on the flake in the given request if needed. | 147 """Schedules an analysis on the flake in the given request if needed. |
| 9 | 148 |
| 10 Args: | 149 Args: |
| 11 request (FlakeAnalysisRequest): The request to analyze a flake. | 150 request (FlakeAnalysisRequest): The request to analyze a flake. |
| 12 user_email (str): The email of the requester. | 151 user_email (str): The email of the requester. |
| 13 is_admin (bool): Whether the requester is an admin. | 152 is_admin (bool): Whether the requester is an admin. |
| 14 | 153 |
| 15 Returns: | 154 Returns: |
| 16 An instance of MasterFlakeAnalysis if an analysis was scheduled; otherwise | 155 An instance of MasterFlakeAnalysis if an analysis was scheduled; otherwise |
| 17 None if no analysis was scheduled before and the user has no permission to. | 156 None if no analysis was scheduled before and the user has no permission to. |
| 18 """ | 157 """ |
| 19 # TODO (stgao): hook up with analysis. | 158 assert len(request.build_steps) > 0, 'At least 1 build step is needed!' |
| 159 | |
| 160 # TODO (stgao): maybe move this to config. | |
| 161 whitelisted_accounts = [ | |
|
chanli
2016/10/07 21:40:36
Move this to constant for now?
stgao
2016/10/07 23:07:06
Done.
| |
| 162 'chromium-try-flakes@appspot.gserviceaccount.com', | |
|
lijeffrey
2016/10/07 21:22:47
does findit-for-me@appspot.gserviceaccount.com cou
stgao
2016/10/07 23:07:06
No. Admin means admin of the project.
| |
| 163 ] | |
| 164 | |
| 165 # Check whether the requester is authorized for access. | |
| 166 if not user_email or not ( | |
|
lijeffrey
2016/10/07 20:45:07
nit: I think this should be a separate function _I
stgao
2016/10/07 23:07:06
Done.
| |
| 167 user_email.endswith('@google.com') or | |
| 168 user_email in whitelisted_accounts or is_admin): | |
| 169 return None | |
| 170 request.user_emails = [user_email] | |
| 171 | |
| 172 manually_triggered = user_email.endswith('@google.com') | |
| 173 | |
| 174 for build_step in request.build_steps: | |
| 175 step_mapper.FindMatchingWaterfallStep(build_step) | |
| 176 | |
| 177 version_number, build_step = _NeedNewAnalysis(request) | |
| 178 if version_number and build_step: | |
| 179 # A new analysis is needed. | |
| 180 logging.info('A new analysis is needed for: %s', build_step) | |
| 181 analysis = initialize_flake_pipeline.ScheduleAnalysisIfNeeded( | |
| 182 build_step.wf_master_name, build_step.wf_builder_name, | |
| 183 build_step.wf_build_number, build_step.wf_step_name, | |
| 184 request.name, allow_new_analysis=True, | |
| 185 manually_triggered=manually_triggered, | |
| 186 queue_name=constants.WATERFALL_ANALYSIS_QUEUE) | |
| 187 if analysis: | |
| 188 # TODO: put this in a transaction. | |
| 189 request = FlakeAnalysisRequest.GetVersion( | |
| 190 key=request.name, version=version_number) | |
| 191 request.analyses.append(analysis.key) | |
| 192 request.put() | |
| 193 logging.info('A new analysis was triggered successfully: %s', | |
| 194 analysis.key) | |
| 195 return True | |
|
chanli
2016/10/07 21:40:37
Shouldn't return an instance of MasterFlakeAnalysi
stgao
2016/10/07 23:07:06
Updated docstring.
| |
| 196 else: | |
| 197 logging.info('But no new analysis was not triggered!') | |
| 198 else: | |
| 199 logging.info('No new analysis is needed: %s', request) | |
| 200 | |
| 20 return False | 201 return False |
|
chanli
2016/10/07 21:40:37
Return None?
stgao
2016/10/07 23:07:06
Acknowledged.
| |
| OLD | NEW |