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

Side by Side Diff: appengine/findit/waterfall/flake/flake_analysis_service.py

Issue 2396283002: [Findit] Hook up analysis for CQ flakes. (Closed)
Patch Set: Fix nit Created 4 years, 2 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 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 _MergeNewRequestIntoExistingOne(new_request, previous_request):
38 """Merges the new request into the previous request.
39
40 Args:
41 new_request (FlakeAnalysisRequest): The request to analyze a flake.
42 previous_request (FlakeAnalysisRequest): The previous request in record.
43
44 Returns:
45 (version_number, build_step)
46 version_number (int): The version of the FlakeAnalysisRequest if a new
47 analysis is needed; otherwise 0.
48 build_step (BuildStep): a BuildStep instance if a new analysis is needed;
49 otherwise None.
50 """
51 # If no bug is attached to the previous analysis or the new request, or both
52 # are attached to the same bug, start a new analysis with a different
53 # configuration. For a configuration that was analyzed 7 days ago, reset it
54 # to use the new reported step of the same configuration.
55 # TODO: move this setting to config.
56 seconds_n_days = 7 * 24 * 60 * 60 # 7 days.
57 candidate_supported_steps = []
58 need_updating = False
59 for step in new_request.build_steps:
60 existing_step = None
61 for s in previous_request.build_steps:
62 if (step.master_name == s.master_name and
63 step.builder_name == s.builder_name):
64 existing_step = s
65 break
66
67 if existing_step:
68 # If last reported flake at the existing step was too long ago, drop it
69 # so that the new one is recorded.
70 time_diff = step.reported_time - existing_step.reported_time
71 if time_diff.total_seconds() > seconds_n_days:
72 previous_request.build_steps.remove(existing_step)
73 existing_step = None
74
75 if not existing_step:
76 need_updating = True
77 previous_request.build_steps.append(step)
78 if step.supported:
79 candidate_supported_steps.append(step)
80
81 if not candidate_supported_steps:
82 # Find some existing configuration that is not analyzed yet.
83 for s in previous_request.build_steps:
84 if not s.scheduled and s.supported:
85 candidate_supported_steps.append(s)
86
87 supported_build_step = None
88 if candidate_supported_steps:
89 supported_build_step = candidate_supported_steps[0]
90 previous_request.swarmed = (previous_request.swarmed or
91 supported_build_step.swarmed)
92 previous_request.supported = True
93 need_updating = True
94
95 if supported_build_step and not previous_request.is_step:
96 supported_build_step.scheduled = True # This will be analyzed.
97
98 if not previous_request.bug_id: # No bug was attached before.
99 previous_request.bug_id = new_request.bug_id
100 need_updating = True
101
102 previous_request.user_emails = sorted(
103 set(previous_request.user_emails + new_request.user_emails))
104
105 if need_updating:
106 # TODO: update in a transaction.
107 previous_request.put()
108
109 if not supported_build_step or previous_request.is_step:
110 # No new analysis if:
111 # 1. All analyzed steps are fresh enough and cover all the steps in the
112 # request.
113 # 2. No representative step is Swarmed Gtest.
114 # 3. The flake is a step-level one.
115 return 0, None
116
117 return previous_request.version_number, supported_build_step
118
119
120 def _CheckForNewAnalysis(request):
121 """Checks if a new analysis is needed for the requested flake.
122
123 Args:
124 request (FlakeAnalysisRequest): The request to analyze a flake.
125
126 Returns:
127 (version_number, build_step)
128 version_number (int): The version of the FlakeAnalysisRequest if a new
129 analysis is needed; otherwise 0.
130 build_step (BuildStep): a BuildStep instance if a new analysis is needed;
131 otherwise None.
132 """
133 previous_request = FlakeAnalysisRequest.GetVersion(key=request.name)
134 if not previous_request or (previous_request.bug_id and request.bug_id and
135 previous_request.bug_id != request.bug_id):
136 # If no existing analysis or last analysis was for a different bug, randomly
137 # pick one configuration for a new analysis.
138 if previous_request:
139 # Make a copy to preserve the version number of previous analysis and
140 # prevent concurrent analyses of the same flake.
141 previous_request.CopyFrom(request)
142 request = previous_request
143
144 swarmed, supported, supported_build_step = _CheckFlakeSwarmedAndSupported(
145 request)
146 request.swarmed = swarmed
147 request.supported = supported
148
149 if supported_build_step and not request.is_step:
150 supported_build_step.scheduled = True # This step will be analyzed.
151
152 # For unsupported or step-level flakes, still save them for monitoring.
153 _, saved = request.Save(retry_on_conflict=False) # Create a new version.
154
155 if not saved or not supported_build_step or request.is_step:
156 # No new analysis if:
157 # 1. Another analysis was just triggered.
158 # 2. No representative step is Swarmed Gtest.
159 # 3. The flake is a step-level one.
160 return 0, None
161
162 return request.version_number, supported_build_step
163 else:
164 # If no bug is attached to the previous analysis or the new request, or both
165 # are attached to the same bug, start a new analysis with a different
166 # configuration. For a configuration that was analyzed 7 days ago, reset it
167 # to use the new reported step of the same configuration.
168 # TODO: move this setting to config.
169 return _MergeNewRequestIntoExistingOne(request, previous_request)
170
171
172 def _IsAuthorizedUser(user_email):
173 """Returns True if the given user email account is authorized for access."""
174 return user_email and (
175 user_email in constants.WHITELISTED_APP_ACCOUNTS or
176 user_email.endswith('@google.com'))
177
178
179 def ScheduleAnalysisForFlake(request, user_email, is_admin):
8 """Schedules an analysis on the flake in the given request if needed. 180 """Schedules an analysis on the flake in the given request if needed.
9 181
10 Args: 182 Args:
11 request (FlakeAnalysisRequest): The request to analyze a flake. 183 request (FlakeAnalysisRequest): The request to analyze a flake.
12 user_email (str): The email of the requester. 184 user_email (str): The email of the requester.
13 is_admin (bool): Whether the requester is an admin. 185 is_admin (bool): Whether the requester is an admin.
14 186
15 Returns: 187 Returns:
16 An instance of MasterFlakeAnalysis if an analysis was scheduled; otherwise 188 True if an analysis was scheduled; False if a new analysis is not needed;
17 None if no analysis was scheduled before and the user has no permission to. 189 None if the user has no permission to.
18 """ 190 """
19 # TODO (stgao): hook up with analysis. 191 assert len(request.build_steps) > 0, 'At least 1 build step is needed!'
192
193 if not is_admin and not _IsAuthorizedUser(user_email):
194 return None
195 request.user_emails = [user_email]
196
197 manually_triggered = user_email.endswith('@google.com')
198
199 for build_step in request.build_steps:
200 step_mapper.FindMatchingWaterfallStep(build_step)
201
202 version_number, build_step = _CheckForNewAnalysis(request)
203 if version_number and build_step:
204 # A new analysis is needed.
205 logging.info('A new analysis is needed for: %s', build_step)
206 analysis = initialize_flake_pipeline.ScheduleAnalysisIfNeeded(
207 build_step.wf_master_name, build_step.wf_builder_name,
208 build_step.wf_build_number, build_step.wf_step_name,
209 request.name, allow_new_analysis=True,
210 manually_triggered=manually_triggered,
211 queue_name=constants.WATERFALL_ANALYSIS_QUEUE)
212 if analysis:
213 # TODO: put this in a transaction.
214 request = FlakeAnalysisRequest.GetVersion(
215 key=request.name, version=version_number)
216 request.analyses.append(analysis.key)
217 request.put()
218 logging.info('A new analysis was triggered successfully: %s',
219 analysis.key)
220 return True
221 else:
222 logging.info('But no new analysis was not triggered!')
223 else:
224 logging.info('No new analysis is needed: %s', request)
225
20 return False 226 return False
OLDNEW
« no previous file with comments | « appengine/findit/model/flake/test/flake_analysis_request_test.py ('k') | appengine/findit/waterfall/flake/step_mapper.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698