Chromium Code Reviews| 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 import json | 5 import json |
| 6 import re | 6 import re |
| 7 | 7 |
| 8 from . import bisect_results | 8 from . import bisect_results |
| 9 from . import depot_config | 9 from . import depot_config |
| 10 from . import revision_state | 10 from . import revision_state |
| (...skipping 435 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 446 """Waits for all revisions in list to finish.""" | 446 """Waits for all revisions in list to finish.""" |
| 447 while any([r.in_progress for r in revision_list]): | 447 while any([r.in_progress for r in revision_list]): |
| 448 revision_list.remove(self.wait_for_any(revision_list)) | 448 revision_list.remove(self.wait_for_any(revision_list)) |
| 449 | 449 |
| 450 def sleep_until_next_revision_ready(self, revision_list): | 450 def sleep_until_next_revision_ready(self, revision_list): |
| 451 """Produces a single step that sleeps until any revision makes progress. | 451 """Produces a single step that sleeps until any revision makes progress. |
| 452 | 452 |
| 453 A revision is considered to make progress when a build file is uploaded to | 453 A revision is considered to make progress when a build file is uploaded to |
| 454 the appropriate bucket, or when buildbot test job is complete. | 454 the appropriate bucket, or when buildbot test job is complete. |
| 455 """ | 455 """ |
| 456 gsutil_path = self.api.m.gsutil.get_gsutil_path() | 456 |
| 457 name = 'Waiting for revision ' + revision_list[0].revision_string | 457 revision_mapping = {} |
| 458 if len(revision_list) > 1: | 458 gs_jobs = [] |
| 459 name += ' and %d other revision(s).' % (len(revision_list) - 1) | 459 buildbot_jobs = [] |
| 460 | |
| 461 for revision in revision_list: | |
| 462 url = revision.get_next_url() | |
| 463 buildbot_job = revision.get_buildbot_locator() | |
| 464 if url: | |
| 465 gs_jobs.append({'type': 'gs', 'location': url}) | |
| 466 revision_mapping[url] = revision | |
| 467 if buildbot_job: | |
| 468 buildbot_job['type'] = 'buildbot' | |
|
prasadv
2015/09/17 22:33:34
Just curious, why 'type' is not added in get_build
RobertoCN
2015/09/19 00:32:54
Because the 'type': 'gs' is just 2 lines above. Bu
| |
| 469 buildbot_jobs.append(buildbot_job) | |
| 470 revision_mapping[buildbot_job['job_name']] = revision | |
| 471 | |
| 472 jobs_config = {'jobs': buildbot_jobs + gs_jobs} | |
| 473 | |
| 460 script = self.api.resource('wait_for_any.py') | 474 script = self.api.resource('wait_for_any.py') |
| 461 args_list = [gsutil_path] | 475 args_list = [self.api.m.gsutil.get_gsutil_path()] if gs_jobs else [] |
| 462 url_mapping = {r.get_next_url(): r for r in revision_list} | 476 |
| 463 bb_locator_mapping = {r.get_buildbot_locator(): r for r in revision_list} | |
| 464 bb_locator_list = bb_locator_mapping.keys() | |
| 465 url_list = url_mapping.keys() | |
| 466 args_list += [url for url in url_list if url and url is not None] | |
| 467 args_list += [entry for entry in bb_locator_list | |
| 468 if entry and entry is not None] | |
| 469 args_list.append( | |
| 470 '--timeout=%d' % ( | |
| 471 self.get_build_timeout_minutes() * 60)) | |
| 472 try: | 477 try: |
| 478 step_name = 'Waiting for revision ' + revision_list[0].revision_string | |
| 479 if len(revision_list) > 1: | |
| 480 step_name += ' and %d other revision(s).' % (len(revision_list) - 1) | |
| 473 step_result = self.api.m.python( | 481 step_result = self.api.m.python( |
| 474 str(name), | 482 str(step_name), |
| 475 script, | 483 script, |
| 476 args_list, | 484 args_list, |
| 477 stdout=self.api.m.raw_io.output()) | 485 stdout=self.api.m.json.output(), |
| 486 stdin=self.api.m.json.input(jobs_config)) | |
| 478 except self.api.m.step.StepFailure: # pragma: no cover | 487 except self.api.m.step.StepFailure: # pragma: no cover |
| 479 # StepFailure is interpreted as a timeout. | 488 # StepFailure is interpreted as a timeout. |
| 480 for revision in revision_list: | 489 for revision in revision_list: |
| 481 revision.status = revision_state.RevisionState.FAILED | 490 revision.status = revision_state.RevisionState.FAILED |
| 482 return None | 491 return None |
|
prasadv
2015/09/17 22:33:34
When this happens what should be the behavior of w
RobertoCN
2015/09/19 00:32:54
Changed it so that we keep the step red and procee
RobertoCN
2015/09/19 00:32:54
If wait_for_any.py errors out, all revisions will
| |
| 483 step_output = step_result.stdout or 'Build finished: Unknown' | 492 if not step_result.stdout: |
| 484 for line in step_output.splitlines(): | 493 return None |
| 485 if line.startswith('Build finished: '): | 494 step_results = step_result.stdout |
| 486 finished_url = line[len('Build finished: '):].strip() | 495 failed_jobs = step_results.get('failed', []) |
| 487 return url_mapping.get(finished_url) | 496 completed_jobs = step_results.get('completed', []) |
| 488 if line.startswith('Failed build url: '): # pragma: no cover | 497 assert failed_jobs or completed_jobs |
| 489 url = line[len('Failed build url: '):].strip() | 498 # Marked all failed builds as failed |
| 490 step_result.presentation.links['Failed build'] = url | 499 for job in failed_jobs: |
|
prasadv
2015/09/17 22:33:34
This is missing.
RobertoCN
2015/09/19 00:32:54
Done.
| |
| 491 if line.startswith('Build failed: '): # pragma: no cover | 500 last_failed_revision = revision_mapping[str(job.get( |
| 492 failed_build_locator = line[len('Build failed: '):].strip() | 501 'location', job.get('job_name')))] |
| 493 failed_build = bb_locator_mapping.get(failed_build_locator) | 502 last_failed_revision.status = revision_state.RevisionState.FAILED |
| 494 failed_build.status = revision_state.RevisionState.FAILED | 503 |
| 495 return failed_build | 504 # Return a completed job if availavle |
| 505 for job in completed_jobs: | |
| 506 return revision_mapping[str(job.get( | |
| 507 'location', job.get('job_name')))] | |
| 508 | |
| 509 # Or return any of the failed revisions | |
| 510 return last_failed_revision | |
| 496 | 511 |
| 497 def wait_for_any(self, revision_list): | 512 def wait_for_any(self, revision_list): |
| 498 """Waits for any of the revisions in the list to finish its job(s).""" | 513 """Waits for any of the revisions in the list to finish its job(s).""" |
| 499 while True: | 514 while True: |
| 500 if not revision_list or not any( | 515 if not revision_list or not any( |
| 501 r.in_progress or r.tested for r in revision_list): # pragma: no cover | 516 r.in_progress or r.tested for r in revision_list): # pragma: no cover |
| 502 break | 517 break |
| 503 | 518 |
| 504 finished_revision = self.sleep_until_next_revision_ready(revision_list) | 519 finished_revision = self.sleep_until_next_revision_ready(revision_list) |
| 505 | 520 |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 553 recipe_tester_name = self.bisect_config.get('recipe_tester_name') | 568 recipe_tester_name = self.bisect_config.get('recipe_tester_name') |
| 554 original_bot_name = self.bisect_config.get('original_bot_name', '') | 569 original_bot_name = self.bisect_config.get('original_bot_name', '') |
| 555 if recipe_tester_name: | 570 if recipe_tester_name: |
| 556 return recipe_tester_name | 571 return recipe_tester_name |
| 557 elif 'win' in original_bot_name: # pragma: no cover | 572 elif 'win' in original_bot_name: # pragma: no cover |
| 558 return 'win64_nv_tester' | 573 return 'win64_nv_tester' |
| 559 else: # pragma: no cover | 574 else: # pragma: no cover |
| 560 # Reasonable fallback | 575 # Reasonable fallback |
| 561 return 'linux_perf_tester' | 576 return 'linux_perf_tester' |
| 562 | 577 |
| 563 def get_build_timeout_minutes(self): | |
| 564 if 'win' in self.get_perf_tester_name(): | |
| 565 return 4 * 60 | |
| 566 return 2 * 60 | |
| 567 | |
| 568 def get_builder_bot_for_this_platform(self): | 578 def get_builder_bot_for_this_platform(self): |
| 569 # TODO(prasadv): We should refactor these codes to remove hard coded values. | 579 # TODO(prasadv): We should refactor these codes to remove hard coded values. |
| 570 bot_name = self.get_perf_tester_name() | 580 bot_name = self.get_perf_tester_name() |
| 571 if 'win' in bot_name: | 581 if 'win' in bot_name: |
| 572 if any(b in bot_name for b in ['x64', 'gpu']): | 582 if any(b in bot_name for b in ['x64', 'gpu']): |
| 573 return 'winx64_bisect_builder' | 583 return 'winx64_bisect_builder' |
| 574 return 'win_perf_bisect_builder' | 584 return 'win_perf_bisect_builder' |
| 575 | 585 |
| 576 if 'android' in bot_name: | 586 if 'android' in bot_name: |
| 577 if 'nexus9' in bot_name: | 587 if 'nexus9' in bot_name: |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 606 | 616 |
| 607 We have seen on several occasions that the local master branch gets reset | 617 We have seen on several occasions that the local master branch gets reset |
| 608 to previous revisions and also detached head states. Running this should | 618 to previous revisions and also detached head states. Running this should |
| 609 take care of either situation. | 619 take care of either situation. |
| 610 """ | 620 """ |
| 611 # TODO(robertocn): Investigate what causes the states mentioned in the | 621 # TODO(robertocn): Investigate what causes the states mentioned in the |
| 612 # docstring in the first place. | 622 # docstring in the first place. |
| 613 self.api.m.git('update-ref', 'refs/heads/master', | 623 self.api.m.git('update-ref', 'refs/heads/master', |
| 614 'refs/remotes/origin/master') | 624 'refs/remotes/origin/master') |
| 615 self.api.m.git('checkout', 'master', cwd=self.api.m.path['checkout']) | 625 self.api.m.git('checkout', 'master', cwd=self.api.m.path['checkout']) |
| OLD | NEW |