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