| 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 442 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 """Waits for all revisions in list to finish.""" | 453 """Waits for all revisions in list to finish.""" |
| 454 while any([r.in_progress for r in revision_list]): | 454 while any([r.in_progress for r in revision_list]): |
| 455 revision_list.remove(self.wait_for_any(revision_list)) | 455 revision_list.remove(self.wait_for_any(revision_list)) |
| 456 | 456 |
| 457 def sleep_until_next_revision_ready(self, revision_list): | 457 def sleep_until_next_revision_ready(self, revision_list): |
| 458 """Produces a single step that sleeps until any revision makes progress. | 458 """Produces a single step that sleeps until any revision makes progress. |
| 459 | 459 |
| 460 A revision is considered to make progress when a build file is uploaded to | 460 A revision is considered to make progress when a build file is uploaded to |
| 461 the appropriate bucket, or when buildbot test job is complete. | 461 the appropriate bucket, or when buildbot test job is complete. |
| 462 """ | 462 """ |
| 463 gsutil_path = self.api.m.gsutil.get_gsutil_path() | 463 api = self.api |
| 464 name = 'Waiting for revision ' + revision_list[0].revision_string | 464 |
| 465 if len(revision_list) > 1: | 465 revision_mapping = {} |
| 466 name += ' and %d other revision(s).' % (len(revision_list) - 1) | 466 gs_jobs = [] |
| 467 script = self.api.resource('wait_for_any.py') | 467 buildbot_jobs = [] |
| 468 args_list = [gsutil_path] | 468 |
| 469 url_mapping = {r.get_next_url(): r for r in revision_list} | 469 for revision in revision_list: |
| 470 bb_locator_mapping = {r.get_buildbot_locator(): r for r in revision_list} | 470 url = revision.get_next_url() |
| 471 bb_locator_list = bb_locator_mapping.keys() | 471 buildbot_job = revision.get_buildbot_locator() |
| 472 url_list = url_mapping.keys() | 472 if url: |
| 473 args_list += [url for url in url_list if url and url is not None] | 473 gs_jobs.append({'type': 'gs', 'location': url}) |
| 474 args_list += [entry for entry in bb_locator_list | 474 revision_mapping[url] = revision |
| 475 if entry and entry is not None] | 475 if buildbot_job: |
| 476 args_list.append( | 476 buildbot_jobs.append(buildbot_job) |
| 477 '--timeout=%d' % ( | 477 revision_mapping[buildbot_job['job_name']] = revision |
| 478 self.get_build_timeout_minutes() * 60)) | 478 |
| 479 jobs_config = {'jobs': buildbot_jobs + gs_jobs} |
| 480 |
| 481 script = api.resource('wait_for_any.py') |
| 482 args_list = [api.m.gsutil.get_gsutil_path()] if gs_jobs else [] |
| 483 |
| 479 try: | 484 try: |
| 480 step_result = self.api.m.python( | 485 step_name = 'Waiting for revision ' + revision_list[0].revision_string |
| 481 str(name), | 486 if len(revision_list) > 1: |
| 487 step_name += ' and %d other revision(s).' % (len(revision_list) - 1) |
| 488 step_result = api.m.python( |
| 489 str(step_name), |
| 482 script, | 490 script, |
| 483 args_list, | 491 args_list, |
| 484 stdout=self.api.m.raw_io.output()) | 492 stdout=api.m.json.output(), |
| 485 except self.api.m.step.StepFailure: # pragma: no cover | 493 stdin=api.m.json.input(jobs_config), |
| 486 # StepFailure is interpreted as a timeout. | 494 ok_ret={0,1}) |
| 487 for revision in revision_list: | 495 except api.m.step.StepFailure as sf: # pragma: no cover |
| 488 revision.status = revision_state.RevisionState.FAILED | 496 if sf.retcode == 2: # 6 days and no builds finished. |
| 497 for revision in revision_list: |
| 498 revision.status = revision_state.RevisionState.FAILED |
| 499 return None # All builds are failed, no point in returning one. |
| 500 else: # Something else went wrong. |
| 501 raise |
| 502 |
| 503 step_results = api.m.step.active_result.stdout |
| 504 build_failed = api.m.step.active_result.retcode |
| 505 |
| 506 if build_failed: # Explicitly making the step red |
| 507 api.m.step.active_result.presentation.status = api.m.step.FAILURE |
| 508 |
| 509 if not step_results: # For most recipe_simulation_test cases. |
| 489 return None | 510 return None |
| 490 step_output = step_result.stdout or 'Build finished: Unknown' | 511 |
| 491 for line in step_output.splitlines(): | 512 failed_jobs = step_results.get('failed', []) |
| 492 if line.startswith('Build finished: '): | 513 completed_jobs = step_results.get('completed', []) |
| 493 finished_url = line[len('Build finished: '):].strip() | 514 assert failed_jobs or completed_jobs |
| 494 return url_mapping.get(finished_url) | 515 # Marked all failed builds as failed |
| 495 if line.startswith('Failed build url: '): # pragma: no cover | 516 for job in failed_jobs: |
| 496 url = line[len('Failed build url: '):].strip() | 517 last_failed_revision = revision_mapping[str(job.get( |
| 497 step_result.presentation.links['Failed build'] = url | 518 'location', job.get('job_name')))] |
| 498 if line.startswith('Build failed: '): # pragma: no cover | 519 if 'job_url' in job: |
| 499 failed_build_locator = line[len('Build failed: '):].strip() | 520 url = job['job_url'] |
| 500 failed_build = bb_locator_mapping.get(failed_build_locator) | 521 api.m.step.active_result.presentation.links['Failed build'] = url |
| 501 failed_build.status = revision_state.RevisionState.FAILED | 522 last_failed_revision.status = revision_state.RevisionState.FAILED |
| 502 return failed_build | 523 |
| 524 # Return a completed job if availavle |
| 525 for job in completed_jobs: |
| 526 if 'job_url' in job: # pragma: no cover |
| 527 url = job['job_url'] |
| 528 api.m.step.active_result.presentation.links['Completed build'] = url |
| 529 return revision_mapping[str(job.get( |
| 530 'location', job.get('job_name')))] |
| 531 |
| 532 # Or return any of the failed revisions |
| 533 return last_failed_revision |
| 503 | 534 |
| 504 def wait_for_any(self, revision_list): | 535 def wait_for_any(self, revision_list): |
| 505 """Waits for any of the revisions in the list to finish its job(s).""" | 536 """Waits for any of the revisions in the list to finish its job(s).""" |
| 506 while True: | 537 while True: |
| 507 if not revision_list or any(r.status == revision_state.RevisionState.NEW | 538 if not revision_list or any(r.status == revision_state.RevisionState.NEW |
| 508 for r in revision_list): # pragma: no cover | 539 for r in revision_list): # pragma: no cover |
| 509 # We want to avoid waiting forever for revisions that are not started, | 540 # We want to avoid waiting forever for revisions that are not started, |
| 510 # or for an empty list. Hence we fail fast. | 541 # or for an empty list. Hence we fail fast. |
| 511 assert False | 542 assert False |
| 512 | 543 |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 559 recipe_tester_name = self.bisect_config.get('recipe_tester_name') | 590 recipe_tester_name = self.bisect_config.get('recipe_tester_name') |
| 560 original_bot_name = self.bisect_config.get('original_bot_name', '') | 591 original_bot_name = self.bisect_config.get('original_bot_name', '') |
| 561 if recipe_tester_name: | 592 if recipe_tester_name: |
| 562 return recipe_tester_name | 593 return recipe_tester_name |
| 563 elif 'win' in original_bot_name: # pragma: no cover | 594 elif 'win' in original_bot_name: # pragma: no cover |
| 564 return 'win64_nv_tester' | 595 return 'win64_nv_tester' |
| 565 else: # pragma: no cover | 596 else: # pragma: no cover |
| 566 # Reasonable fallback | 597 # Reasonable fallback |
| 567 return 'linux_perf_tester' | 598 return 'linux_perf_tester' |
| 568 | 599 |
| 569 def get_build_timeout_minutes(self): | |
| 570 if 'win' in self.get_perf_tester_name(): | |
| 571 return 4 * 60 | |
| 572 return 2 * 60 | |
| 573 | |
| 574 def get_builder_bot_for_this_platform(self): | 600 def get_builder_bot_for_this_platform(self): |
| 575 # TODO(prasadv): We should refactor these codes to remove hard coded values. | 601 # TODO(prasadv): We should refactor these codes to remove hard coded values. |
| 576 bot_name = self.get_perf_tester_name() | 602 bot_name = self.get_perf_tester_name() |
| 577 if 'win' in bot_name: | 603 if 'win' in bot_name: |
| 578 if any(b in bot_name for b in ['x64', 'gpu']): | 604 if any(b in bot_name for b in ['x64', 'gpu']): |
| 579 return 'winx64_bisect_builder' | 605 return 'winx64_bisect_builder' |
| 580 return 'win_perf_bisect_builder' | 606 return 'win_perf_bisect_builder' |
| 581 | 607 |
| 582 if 'android' in bot_name: | 608 if 'android' in bot_name: |
| 583 if 'nexus9' in bot_name: | 609 if 'nexus9' in bot_name: |
| (...skipping 28 matching lines...) Expand all Loading... |
| 612 | 638 |
| 613 We have seen on several occasions that the local master branch gets reset | 639 We have seen on several occasions that the local master branch gets reset |
| 614 to previous revisions and also detached head states. Running this should | 640 to previous revisions and also detached head states. Running this should |
| 615 take care of either situation. | 641 take care of either situation. |
| 616 """ | 642 """ |
| 617 # TODO(robertocn): Investigate what causes the states mentioned in the | 643 # TODO(robertocn): Investigate what causes the states mentioned in the |
| 618 # docstring in the first place. | 644 # docstring in the first place. |
| 619 self.api.m.git('update-ref', 'refs/heads/master', | 645 self.api.m.git('update-ref', 'refs/heads/master', |
| 620 'refs/remotes/origin/master') | 646 'refs/remotes/origin/master') |
| 621 self.api.m.git('checkout', 'master', cwd=self.api.m.path['checkout']) | 647 self.api.m.git('checkout', 'master', cwd=self.api.m.path['checkout']) |
| OLD | NEW |