Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import json | |
| 6 import os | |
| 7 | |
| 8 import collections | |
| 9 import logging | |
| 10 | |
| 11 from common import cache_decorator | |
| 12 from common.http_client_appengine import HttpClientAppEngine as HttpClient | |
| 13 from waterfall import buildbot | |
| 14 from waterfall import swarming_util | |
| 15 from waterfall.flake import trybots | |
| 16 | |
| 17 | |
| 18 @cache_decorator.Cached( | |
| 19 namespace='trybots', cacher=cache_decorator.CompressedMemCacher()) | |
| 20 def _LoadTrybots(): # pragma: no cover. | |
| 21 """Returns the mapping of Commit Queue trybots to Waterfall buildbots.""" | |
| 22 with open(os.path.join(os.path.dirname(__file__), 'trybots.json'), 'r') as f: | |
| 23 return json.load(f) | |
| 24 | |
| 25 | |
| 26 def _GetMatchingBuildbots(cq_master_name, cq_builder_name): # pragma: no cover. | |
| 27 """Returns a list of matching builder/tester buildbots on Waterfall.""" | |
| 28 trybot_map = _LoadTrybots() | |
| 29 builders = trybot_map.get(cq_master_name, {}).get('builders', {}) | |
| 30 return builders.get(cq_builder_name, {}).get('bot_ids', []) | |
| 31 | |
| 32 | |
| 33 def _GetMatchingWaterfallBuildStep( | |
| 34 cq_build_step, http_client): # pragma: no cover. | |
| 35 """Returns the matching Waterfall build step of the given CQ one. | |
| 36 | |
| 37 Args: | |
| 38 cq_build_step (BuildStep): A build step on Commit Queue. | |
| 39 http_client (RetryHttpClient): A http client to send http requests. | |
| 40 | |
| 41 Returns: | |
| 42 (master_name, builder_name, build_number, step_name) | |
| 43 or | |
| 44 None | |
| 45 """ | |
| 46 no_matching_result = (None, None, None, None) | |
| 47 | |
| 48 def GetTagValue(tag_name, tags): | |
| 49 """Returns the value of a tag in a Swarming task.""" | |
| 50 tag_name_prefix = '%s:' % tag_name | |
| 51 for tag in tags: | |
| 52 if tag.startswith(tag_name_prefix): | |
| 53 return tag[len(tag_name_prefix):] | |
| 54 return None | |
| 55 | |
| 56 # 1. Map a cq trybot to the matching waterfall buildbots. | |
| 57 buildbots = _GetMatchingBuildbots( | |
| 58 cq_build_step.master_name, cq_build_step.builder_name) | |
| 59 if not buildbots: | |
| 60 logging.info('%s/%s has no matching Waterfall buildbot', | |
| 61 cq_build_step.master_name, cq_build_step.builder_name) | |
| 62 return no_matching_result # No matching Waterfall buildbots. | |
| 63 | |
| 64 # 2. Get "name" of the CQ trybot step in the tags of a Swarming task. | |
| 65 tasks = swarming_util.ListSwarmingTasksDataByTags( | |
| 66 cq_build_step.master_name, cq_build_step.builder_name, | |
| 67 cq_build_step.build_number, http_client, | |
| 68 {'stepname': cq_build_step.step_name}) | |
| 69 if not tasks: | |
| 70 logging.info( | |
| 71 '%s/%s/%s is not Swarmed yet.', | |
| 72 cq_build_step.master_name, cq_build_step.builder_name, | |
| 73 cq_build_step.build_step) | |
| 74 return no_matching_result # Not on Swarm yet. | |
| 75 | |
| 76 # Name of the step in the tags of a Swarming task. | |
| 77 # Can't use step name, as cq one is with "(with patch)" while waterfall one | |
| 78 # without. | |
| 79 name = GetTagValue('name', tasks[0].get('tags', [])) | |
| 80 if not name: | |
| 81 logging.error( | |
| 82 'Swarming task has no name tag: %s' % tasks[0].get('task_id')) | |
| 83 return no_matching_result # No name of the step. | |
| 84 | |
| 85 for bot in buildbots: | |
| 86 wf_master_name = bot['mastername'] | |
| 87 # Assume Swarmed gtests run on tester bot instead of the builder bot. | |
| 88 wf_builder_name = bot.get('tester') or bot.get('buildername') | |
| 89 # TODO: cache and throttle QPS to the same master. | |
| 90 # 3. Retrieve latest completed build cycle on the buildbot. | |
| 91 builds = buildbot.GetRecentCompletedBuilds( | |
| 92 wf_master_name, wf_builder_name, http_client) | |
| 93 if not builds: | |
| 94 logging.error('Failed to retrieve recent builds on %s', wf_master_name) | |
|
chanli
2016/10/07 01:08:37
There is logging at GetRecentCompletedBuilds side
stgao
2016/10/07 01:19:33
Good catch. Removed here.
| |
| 95 return # No recent builds for the buildbot. | |
|
chanli
2016/10/07 01:04:52
return no_matching_result? Or maybe just continue
stgao
2016/10/07 01:19:33
Good catch! Let's continue with next configuration
| |
| 96 | |
| 97 # 4. Check whether there is matching step. | |
|
chanli
2016/10/07 01:04:52
I think we should. If a test is os specifically fl
stgao
2016/10/07 01:19:33
Done.
| |
| 98 # TODO: we might have to check OS or dimension too. | |
| 99 tasks = swarming_util.ListSwarmingTasksDataByTags( | |
| 100 wf_master_name, wf_builder_name, builds[0], http_client, {'name': name}) | |
| 101 if tasks: # One matching buildbot is found. | |
| 102 wf_step_name = GetTagValue('stepname', tasks[0].get('tags', [])) | |
| 103 return wf_master_name, wf_builder_name, builds[0], wf_step_name | |
| 104 | |
| 105 return no_matching_result | |
| 106 | |
| 107 | |
| 108 def FindMatchingWaterfallStep(build_step): # pragma: no cover. | |
|
chanli
2016/10/07 01:04:52
Is there any return value for this function? Or do
stgao
2016/10/07 01:19:33
No return value. Information will be updated direc
| |
| 109 """Finds the matching Waterfall step and checks whether it is supported. | |
| 110 | |
| 111 Only Swarmed and gtest-based steps are supported at the moment. | |
| 112 | |
| 113 Args: | |
| 114 build_step (BuildStep): A build step on Waterfall or Commit Queue. It | |
| 115 will be updated with the matching Waterfall step and whether it is | |
| 116 Swarmed and supported. | |
| 117 """ | |
| 118 # TODO (chanli): re-implement this hack after step metadata is added. | |
| 119 | |
| 120 build_step.swarmed = False | |
| 121 build_step.supported = False | |
| 122 | |
| 123 wf_master_name = None | |
| 124 wf_builder_name = None | |
| 125 wf_build_number = None | |
| 126 wf_step_name = None | |
| 127 | |
| 128 http_client = HttpClient() | |
| 129 | |
| 130 if not build_step.master_name.startswith('tryserver.'): | |
| 131 wf_master_name = build_step.master_name | |
| 132 wf_builder_name = build_step.builder_name | |
| 133 wf_build_number = build_step.build_number | |
| 134 wf_step_name = build_step.step_name | |
| 135 else: | |
| 136 step_info = _GetMatchingWaterfallBuildStep(build_step, http_client) | |
| 137 wf_master_name, wf_builder_name, wf_build_number, wf_step_name = step_info | |
| 138 | |
| 139 build_step.wf_master_name = wf_master_name | |
| 140 build_step.wf_builder_name = wf_builder_name | |
| 141 build_step.wf_build_number = wf_build_number | |
| 142 build_step.wf_step_name = wf_step_name | |
| 143 | |
| 144 if not build_step.has_matching_waterfall_step: | |
| 145 return | |
| 146 | |
| 147 # Query Swarming for isolated data. | |
| 148 step_isolated_data = swarming_util.GetIsolatedDataForStep( | |
| 149 build_step.master_name, build_step.builder_name, build_step.build_number, | |
| 150 build_step.step_name, http_client) | |
| 151 build_step.swarmed = len(step_isolated_data) > 0 | |
| 152 | |
| 153 if build_step.swarmed: | |
| 154 # Retrieve a sample output from Isolate. | |
| 155 output = swarming_util.RetrieveShardedTestResultsFromIsolatedServer( | |
| 156 step_isolated_data[:1], http_client) | |
| 157 if output: | |
| 158 # Guess from the format. | |
| 159 build_step.supported = ( | |
| 160 isinstance(output, dict) and | |
| 161 isinstance(output.get('all_tests'), list) and | |
| 162 isinstance(output.get('per_iteration_data'), list) and | |
| 163 all(isinstance(i, dict) for i in output.get('per_iteration_data')) | |
| 164 ) | |
| OLD | NEW |