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

Side by Side Diff: scripts/slave/recipe_modules/auto_bisect/revision_state.py

Issue 1758603004: Checking for failure in requested builds. (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/build.git@master
Patch Set: Created 4 years, 9 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 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 """An interface for holding state and result of revisions in a bisect job. 5 """An interface for holding state and result of revisions in a bisect job.
6 6
7 When implementing support for tests other than perf, one should extend this 7 When implementing support for tests other than perf, one should extend this
8 class so that the bisect module and recipe can use it. 8 class so that the bisect module and recipe can use it.
9 9
10 See perf_revision_state for an example. 10 See perf_revision_state for an example.
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
84 self.deps = dict(base_revision.deps) 84 self.deps = dict(base_revision.deps)
85 self.deps[self.depot_name] = self.commit_hash 85 self.deps[self.depot_name] = self.commit_hash
86 else: 86 else:
87 self.needs_patch = False 87 self.needs_patch = False
88 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix() 88 self.build_url = self.bisector.get_platform_gs_prefix() + self._gs_suffix()
89 self.values = [] 89 self.values = []
90 self.mean_value = None 90 self.mean_value = None
91 self.std_dev = None 91 self.std_dev = None
92 self.repeat_count = MINIMUM_SAMPLE_SIZE 92 self.repeat_count = MINIMUM_SAMPLE_SIZE
93 self._test_config = None 93 self._test_config = None
94 self.build_number = None
94 95
95 @property 96 @property
96 def tested(self): 97 def tested(self):
97 return self.status in (RevisionState.TESTED,) 98 return self.status in (RevisionState.TESTED,)
98 99
99 @property 100 @property
100 def in_progress(self): 101 def in_progress(self):
101 return self.status in (RevisionState.BUILDING, RevisionState.TESTING, 102 return self.status in (RevisionState.BUILDING, RevisionState.TESTING,
102 RevisionState.NEED_MORE_DATA) 103 RevisionState.NEED_MORE_DATA)
103 104
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
212 213
213 def update_status(self): 214 def update_status(self):
214 """Checks on the pending jobs and updates status accordingly. 215 """Checks on the pending jobs and updates status accordingly.
215 216
216 This method will check for the build to complete and then trigger the test, 217 This method will check for the build to complete and then trigger the test,
217 or will wait for the test as appropriate. 218 or will wait for the test as appropriate.
218 219
219 To wait for the test we try to get the buildbot job url from GS, and if 220 To wait for the test we try to get the buildbot job url from GS, and if
220 available, we query the status of such job. 221 available, we query the status of such job.
221 """ 222 """
222 if self.status == RevisionState.BUILDING and self._is_build_archived(): 223 if self.status == RevisionState.BUILDING:
223 self.start_job() 224 if self._is_build_archived():
225 self.start_job()
226 elif self._is_build_failed():
227 self.status = RevisionState.FAILED
224 elif (self.status in (RevisionState.TESTING, RevisionState.NEED_MORE_DATA) 228 elif (self.status in (RevisionState.TESTING, RevisionState.NEED_MORE_DATA)
225 and self._results_available()): 229 and self._results_available()):
226 # If we have already decided whether the revision is good or bad we 230 # If we have already decided whether the revision is good or bad we
227 # shouldn't check again 231 # shouldn't check again
228 check_revision_goodness = not(self.good or self.bad) 232 check_revision_goodness = not(self.good or self.bad)
229 self._read_test_results( 233 self._read_test_results(
230 check_revision_goodness=check_revision_goodness) 234 check_revision_goodness=check_revision_goodness)
231 # We assume _read_test_results may have changed the status to a broken 235 # We assume _read_test_results may have changed the status to a broken
232 # state such as FAILED or ABORTED. 236 # state such as FAILED or ABORTED.
233 if self.status in (RevisionState.TESTING, RevisionState.NEED_MORE_DATA): 237 if self.status in (RevisionState.TESTING, RevisionState.NEED_MORE_DATA):
234 self.status = RevisionState.TESTED 238 self.status = RevisionState.TESTED
235 239
236 def _is_build_archived(self): 240 def _is_build_archived(self):
237 """Checks if the revision is already built and archived.""" 241 """Checks if the revision is already built and archived."""
238 if not self.build_archived: 242 if not self.build_archived:
239 api = self.bisector.api 243 api = self.bisector.api
240 self.build_archived = api.gsutil_file_exists(self.build_url) 244 self.build_archived = api.gsutil_file_exists(self.build_url)
241 245
242 if self.bisector.dummy_builds: 246 if self.bisector.dummy_builds:
243 self.build_archived = self.in_progress 247 self.build_archived = self.in_progress
244 248
245 return self.build_archived 249 return self.build_archived
246 250
251 def _is_build_failed(self):
252 api = self.bisector.api
253 current_build = None
254 base_url = '%sjson/builders/%s'% (
255 os.environ.get('BUILDBOT_URL', 'http://localhost:8041/'),
qyearsley 2016/03/04 22:06:28 Why http://localhost:8041/?
RobertoCN 2016/03/14 01:00:25 The reasonable assumption if this env variable is
256 self.bisector.get_builder_bot_for_this_platform())
257 if self.build_number is None:
qyearsley 2016/03/04 22:06:28 Does this mean no build started yet, or we're not
RobertoCN 2016/03/14 01:00:25 It means we don't know the build number yet. The b
258 try:
259 builder_state_url = base_url + '?as_text=1'
260 fetch_step = api.m.url.fetch_to_file(
261 builder_state_url, None,
262 step_name='fetch builder state',
263 stdout=api.m.raw_io.output())
264 builder_state = fetch_step.stdout
265 builder_state = json.loads(builder_state or '{}')
266 for build_number in builder_state.get('cachedBuilds', []):
267 build_url = '%s/builds/%s?as_text=1' % (base_url, build_number)
268 fetch_result = api.m.url.fetch_to_file(
269 build_url, None,
270 step_name='fetch build details',
271 stdout=api.m.raw_io.output())
272 build = json.loads(fetch_result.stdout or '{}')
273 for p in build.get('properties', []):
274 if p[0] == 'build_archive_url' and p[1] == self.build_url:
275 current_build = build
276 self.build_number = build_number
277 break
qyearsley 2016/03/04 22:06:28 Is build.get('properties') expected to be a list o
RobertoCN 2016/03/14 01:00:25 Thanks! I don't know how I didn't see it. Done.
278 if self.build_number:
279 break
280 except api.m.step.StepFailure: # pragma: no cover
281 # If we cannot get json from buildbot, we cannot determine if a build is
282 # failed, hence we consider it in progress until it times out.
283 return False
284 if self.build_number is None:
285 # The build hasn't started yet, therefore it's not failed.
286 return False
287 if not current_build:
288 build_url = '%s/builds/%s?as_text=1' % (base_url, self.build_number)
289 fetch_result = api.m.url.fetch_to_file(
290 build_url, None,
291 step_name='fetch build details',
292 stdout=api.m.raw_io.output())
293 current_build = json.loads(fetch_result.stdout or '{}')
qyearsley 2016/03/04 22:06:28 This section of code (where the URL is put togethe
RobertoCN 2016/03/14 01:00:25 Done.
294 return current_build.get('results') in [2, 3, 4]
qyearsley 2016/03/04 22:06:28 These could be set as constants, ideally with some
RobertoCN 2016/03/14 01:00:25 Works for me.
295
247 def _results_available(self): 296 def _results_available(self):
248 """Checks if the results for the test job have been uploaded.""" 297 """Checks if the results for the test job have been uploaded."""
249 api = self.bisector.api 298 api = self.bisector.api
250 result = api.gsutil_file_exists(self.test_results_url) 299 result = api.gsutil_file_exists(self.test_results_url)
251 if self.bisector.dummy_builds: 300 if self.bisector.dummy_builds:
252 return self.in_progress 301 return self.in_progress
253 return result # pragma: no cover 302 return result # pragma: no cover
254 303
255 def _gs_suffix(self): 304 def _gs_suffix(self):
256 """Provides the expected right half of the build filename. 305 """Provides the expected right half of the build filename.
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 file_name = str(api.m.path['tmp_base'].join(build_name + '.diff')) 359 file_name = str(api.m.path['tmp_base'].join(build_name + '.diff'))
311 api.m.file.write('Saving diff patch for ' + self.commit_hash, 360 api.m.file.write('Saving diff patch for ' + self.commit_hash,
312 file_name, self.deps_patch + self.deps_sha_patch) 361 file_name, self.deps_patch + self.deps_sha_patch)
313 return file_name 362 return file_name
314 363
315 def _request_build(self): 364 def _request_build(self):
316 """Posts a request to buildbot to build this revision and archive it.""" 365 """Posts a request to buildbot to build this revision and archive it."""
317 # TODO: Rewrite using the trigger module. 366 # TODO: Rewrite using the trigger module.
318 api = self.bisector.api 367 api = self.bisector.api
319 bot_name = self.bisector.get_builder_bot_for_this_platform() 368 bot_name = self.bisector.get_builder_bot_for_this_platform()
320 if self.bisector.dummy_builds: 369 if self.bisector.bisect_config.get('dummy_job_names'):
321 self.job_name = self.commit_hash + '-build' 370 self.job_name = self.commit_hash + '-build'
322 else: # pragma: no cover 371 else: # pragma: no cover
323 self.job_name = uuid.uuid4().hex 372 self.job_name = uuid.uuid4().hex
324 if self.needs_patch: 373 if self.needs_patch:
325 self.patch_file = self._write_deps_patch_file( 374 self.patch_file = self._write_deps_patch_file(
326 self.job_name) 375 self.job_name)
327 else: 376 else:
328 self.patch_file = '/dev/null' 377 self.patch_file = '/dev/null'
329 378
330 # To allow multiple nested levels, we go to the topmost revision. 379 # To allow multiple nested levels, we go to the topmost revision.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
376 self._test_config = result 425 self._test_config = result
377 return result 426 return result
378 427
379 def _do_test(self): 428 def _do_test(self):
380 """Triggers tests for a revision, either locally or via try job. 429 """Triggers tests for a revision, either locally or via try job.
381 430
382 If local testing is enabled (i.e. director/tester merged) then 431 If local testing is enabled (i.e. director/tester merged) then
383 the test will be run on the same machine. Otherwise, this posts 432 the test will be run on the same machine. Otherwise, this posts
384 a request to buildbot to download and perf-test this build. 433 a request to buildbot to download and perf-test this build.
385 """ 434 """
386 if self.bisector.dummy_builds: 435 if self.bisector.bisect_config.get('dummy_job_names'):
387 self.job_name = self.commit_hash + '-test' 436 self.job_name = self.commit_hash + '-test'
388 else: # pragma: no cover 437 else: # pragma: no cover
389 self.job_name = uuid.uuid4().hex 438 self.job_name = uuid.uuid4().hex
390 api = self.bisector.api 439 api = self.bisector.api
391 top_revision = self 440 top_revision = self
392 while top_revision.base_revision: # pragma: no cover 441 while top_revision.base_revision: # pragma: no cover
393 top_revision = top_revision.base_revision 442 top_revision = top_revision.base_revision
394 perf_test_properties = { 443 perf_test_properties = {
395 'builder_name': self.bisector.get_perf_tester_name(), 444 'builder_name': self.bisector.get_perf_tester_name(),
396 'properties': { 445 'properties': {
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
513 key=lambda x: len(x.values)) 562 key=lambda x: len(x.values))
514 if (len(self.bisector.last_tested_revision.values) == 563 if (len(self.bisector.last_tested_revision.values) ==
515 next_revision_to_test.values): 564 next_revision_to_test.values):
516 self.bisector.last_tested_revision.retest() 565 self.bisector.last_tested_revision.retest()
517 else: 566 else:
518 next_revision_to_test.retest() 567 next_revision_to_test.retest()
519 568
520 def __repr__(self): 569 def __repr__(self):
521 return ('RevisionState(rev=%s, values=%r, mean_value=%r, std_dev=%r)' % ( 570 return ('RevisionState(rev=%s, values=%r, mean_value=%r, std_dev=%r)' % (
522 self.revision_string(), self.values, self.mean_value, self.std_dev)) 571 self.revision_string(), self.values, self.mean_value, self.std_dev))
OLDNEW
« no previous file with comments | « scripts/slave/recipe_modules/auto_bisect/example.expected/windows_x64_bisector.json ('k') | scripts/slave/recipes/bisect.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698