Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | |
|
borenet
2015/07/29 13:38:57
Copied from https://chromium.googlesource.com/chro
| |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 | |
| 6 """ Utilities for dealing with builder names. This module obtains its attributes | |
| 7 dynamically from builder_name_schema.json. """ | |
| 8 | |
| 9 | |
| 10 import json | |
| 11 import os | |
| 12 | |
| 13 | |
| 14 # All of these global variables are filled in by _LoadSchema(). | |
| 15 | |
| 16 # The full schema. | |
| 17 BUILDER_NAME_SCHEMA = None | |
| 18 | |
| 19 # Character which separates parts of a builder name. | |
| 20 BUILDER_NAME_SEP = None | |
| 21 | |
| 22 # Builder roles. | |
| 23 BUILDER_ROLE_CANARY = 'Canary' | |
| 24 BUILDER_ROLE_BUILD = 'Build' | |
| 25 BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper' | |
| 26 BUILDER_ROLE_PERF = 'Perf' | |
| 27 BUILDER_ROLE_TEST = 'Test' | |
| 28 BUILDER_ROLES = (BUILDER_ROLE_CANARY, | |
| 29 BUILDER_ROLE_BUILD, | |
| 30 BUILDER_ROLE_HOUSEKEEPER, | |
| 31 BUILDER_ROLE_PERF, | |
| 32 BUILDER_ROLE_TEST) | |
| 33 | |
| 34 # Suffix which distinguishes trybots from normal bots. | |
| 35 TRYBOT_NAME_SUFFIX = None | |
| 36 | |
| 37 | |
| 38 def _LoadSchema(): | |
| 39 """ Load the builder naming schema from the JSON file. """ | |
| 40 | |
| 41 def _UnicodeToStr(obj): | |
| 42 """ Convert all unicode strings in obj to Python strings. """ | |
| 43 if isinstance(obj, unicode): | |
| 44 return str(obj) | |
| 45 elif isinstance(obj, dict): | |
| 46 return dict(map(_UnicodeToStr, obj.iteritems())) | |
| 47 elif isinstance(obj, list): | |
| 48 return list(map(_UnicodeToStr, obj)) | |
| 49 elif isinstance(obj, tuple): | |
| 50 return tuple(map(_UnicodeToStr, obj)) | |
| 51 else: | |
| 52 return obj | |
| 53 | |
| 54 builder_name_json_filename = os.path.join( | |
| 55 os.path.dirname(__file__), 'builder_name_schema.json') | |
| 56 builder_name_schema_json = json.load(open(builder_name_json_filename)) | |
| 57 | |
| 58 global BUILDER_NAME_SCHEMA | |
| 59 BUILDER_NAME_SCHEMA = _UnicodeToStr( | |
| 60 builder_name_schema_json['builder_name_schema']) | |
| 61 | |
| 62 global BUILDER_NAME_SEP | |
| 63 BUILDER_NAME_SEP = _UnicodeToStr( | |
| 64 builder_name_schema_json['builder_name_sep']) | |
| 65 | |
| 66 global TRYBOT_NAME_SUFFIX | |
| 67 TRYBOT_NAME_SUFFIX = _UnicodeToStr( | |
| 68 builder_name_schema_json['trybot_name_suffix']) | |
| 69 | |
| 70 # Since the builder roles are dictionary keys, just assert that the global | |
| 71 # variables above account for all of them. | |
| 72 assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA) | |
| 73 for role in BUILDER_ROLES: | |
| 74 assert role in BUILDER_NAME_SCHEMA | |
| 75 | |
| 76 | |
| 77 _LoadSchema() | |
| 78 | |
| 79 | |
| 80 def MakeBuilderName(role, extra_config=None, is_trybot=False, **kwargs): | |
| 81 schema = BUILDER_NAME_SCHEMA.get(role) | |
| 82 if not schema: | |
| 83 raise ValueError('%s is not a recognized role.' % role) | |
| 84 for k, v in kwargs.iteritems(): | |
| 85 if BUILDER_NAME_SEP in v: | |
| 86 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, v)) | |
| 87 if not k in schema: | |
| 88 raise ValueError('Schema does not contain "%s": %s' %(k, schema)) | |
| 89 if extra_config and BUILDER_NAME_SEP in extra_config: | |
| 90 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, | |
| 91 extra_config)) | |
| 92 name_parts = [role] | |
| 93 name_parts.extend([kwargs[attribute] for attribute in schema]) | |
| 94 if extra_config: | |
| 95 name_parts.append(extra_config) | |
| 96 if is_trybot: | |
| 97 name_parts.append(TRYBOT_NAME_SUFFIX) | |
| 98 return BUILDER_NAME_SEP.join(name_parts) | |
| 99 | |
| 100 | |
| 101 def BuilderNameFromObject(obj, is_trybot=False): | |
| 102 """Create a builder name based on properties of the given object. | |
| 103 | |
| 104 Args: | |
| 105 obj: the object from which to create the builder name. The object must | |
| 106 have as properties: | |
| 107 - A valid builder role, as defined in the JSON file | |
| 108 - All properties listed in the JSON file for that role | |
| 109 - Optionally, an extra_config property | |
| 110 is_trybot: bool; whether or not the builder is a trybot. | |
| 111 Returns: | |
| 112 string which combines the properties of the given object into a valid | |
| 113 builder name. | |
| 114 """ | |
| 115 schema = BUILDER_NAME_SCHEMA.get(obj.role) | |
| 116 if not schema: | |
| 117 raise ValueError('%s is not a recognized role.' % obj.role) | |
| 118 name_parts = [obj.role] | |
| 119 for attr_name in schema: | |
| 120 attr_val = getattr(obj, attr_name) | |
| 121 name_parts.append(attr_val) | |
| 122 extra_config = getattr(obj, 'extra_config', None) | |
| 123 if extra_config: | |
| 124 name_parts.append(extra_config) | |
| 125 if is_trybot: | |
| 126 name_parts.append(TRYBOT_NAME_SUFFIX) | |
| 127 return BUILDER_NAME_SEP.join(name_parts) | |
| 128 | |
| 129 | |
| 130 def IsTrybot(builder_name): | |
| 131 """ Returns true if builder_name refers to a trybot (as opposed to a | |
| 132 waterfall bot). """ | |
| 133 return builder_name.endswith(TRYBOT_NAME_SUFFIX) | |
| 134 | |
| 135 | |
| 136 def GetWaterfallBot(builder_name): | |
| 137 """Returns the name of the waterfall bot for this builder. If it is not a | |
| 138 trybot, builder_name is returned unchanged. If it is a trybot the name is | |
| 139 returned without the trybot suffix.""" | |
| 140 if not IsTrybot(builder_name): | |
| 141 return builder_name | |
| 142 return _WithoutSuffix(builder_name, BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX) | |
| 143 | |
| 144 | |
| 145 def TrybotName(builder_name): | |
| 146 """Returns the name of the trybot clone of this builder. | |
| 147 | |
| 148 If the given builder is a trybot, the name is returned unchanged. If not, the | |
| 149 TRYBOT_NAME_SUFFIX is appended. | |
| 150 """ | |
| 151 if builder_name.endswith(TRYBOT_NAME_SUFFIX): | |
| 152 return builder_name | |
| 153 return builder_name + BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX | |
| 154 | |
| 155 | |
| 156 def _WithoutSuffix(string, suffix): | |
| 157 """ Returns a copy of string 'string', but with suffix 'suffix' removed. | |
| 158 Raises ValueError if string does not end with suffix. """ | |
| 159 if not string.endswith(suffix): | |
| 160 raise ValueError('_WithoutSuffix: string %s does not end with suffix %s' % ( | |
| 161 string, suffix)) | |
| 162 return string[:-len(suffix)] | |
| 163 | |
| 164 | |
| 165 def DictForBuilderName(builder_name): | |
| 166 """Makes a dictionary containing details about the builder from its name.""" | |
| 167 split_name = builder_name.split(BUILDER_NAME_SEP) | |
| 168 | |
| 169 def pop_front(): | |
| 170 try: | |
| 171 return split_name.pop(0) | |
| 172 except: | |
| 173 raise ValueError('Invalid builder name: %s' % builder_name) | |
| 174 | |
| 175 result = {'is_trybot': False} | |
| 176 | |
| 177 if split_name[-1] == TRYBOT_NAME_SUFFIX: | |
| 178 result['is_trybot'] = True | |
| 179 split_name.pop() | |
| 180 | |
| 181 if split_name[0] in BUILDER_NAME_SCHEMA.keys(): | |
| 182 key_list = BUILDER_NAME_SCHEMA[split_name[0]] | |
| 183 result['role'] = pop_front() | |
| 184 for key in key_list: | |
| 185 result[key] = pop_front() | |
| 186 if split_name: | |
| 187 result['extra_config'] = pop_front() | |
| 188 if split_name: | |
| 189 raise ValueError('Invalid builder name: %s' % builder_name) | |
| 190 else: | |
| 191 raise ValueError('Invalid builder name: %s' % builder_name) | |
| 192 return result | |
| 193 | |
| 194 | |
| OLD | NEW |