1# Copyright 2014 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 6""" Utilities for dealing with builder names. This module obtains its attributes 7dynamically from builder_name_schema.json. """ 8 9 10import json 11import os 12 13 14# All of these global variables are filled in by _LoadSchema(). 15 16# The full schema. 17BUILDER_NAME_SCHEMA = None 18 19# Character which separates parts of a builder name. 20BUILDER_NAME_SEP = None 21 22# Builder roles. 23BUILDER_ROLE_CANARY = 'Canary' 24BUILDER_ROLE_BUILD = 'Build' 25BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper' 26BUILDER_ROLE_INFRA = 'Infra' 27BUILDER_ROLE_PERF = 'Perf' 28BUILDER_ROLE_TEST = 'Test' 29BUILDER_ROLES = (BUILDER_ROLE_CANARY, 30 BUILDER_ROLE_BUILD, 31 BUILDER_ROLE_HOUSEKEEPER, 32 BUILDER_ROLE_INFRA, 33 BUILDER_ROLE_PERF, 34 BUILDER_ROLE_TEST) 35 36# Suffix which distinguishes trybots from normal bots. 37TRYBOT_NAME_SUFFIX = None 38 39 40def _LoadSchema(): 41 """ Load the builder naming schema from the JSON file. """ 42 43 def _UnicodeToStr(obj): 44 """ Convert all unicode strings in obj to Python strings. """ 45 if isinstance(obj, unicode): 46 return str(obj) 47 elif isinstance(obj, dict): 48 return dict(map(_UnicodeToStr, obj.iteritems())) 49 elif isinstance(obj, list): 50 return list(map(_UnicodeToStr, obj)) 51 elif isinstance(obj, tuple): 52 return tuple(map(_UnicodeToStr, obj)) 53 else: 54 return obj # pragma: no cover 55 56 builder_name_json_filename = os.path.join( 57 os.path.dirname(__file__), 'builder_name_schema.json') 58 builder_name_schema_json = json.load(open(builder_name_json_filename)) 59 60 global BUILDER_NAME_SCHEMA 61 BUILDER_NAME_SCHEMA = _UnicodeToStr( 62 builder_name_schema_json['builder_name_schema']) 63 64 global BUILDER_NAME_SEP 65 BUILDER_NAME_SEP = _UnicodeToStr( 66 builder_name_schema_json['builder_name_sep']) 67 68 global TRYBOT_NAME_SUFFIX 69 TRYBOT_NAME_SUFFIX = _UnicodeToStr( 70 builder_name_schema_json['trybot_name_suffix']) 71 72 # Since the builder roles are dictionary keys, just assert that the global 73 # variables above account for all of them. 74 assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA) 75 for role in BUILDER_ROLES: 76 assert role in BUILDER_NAME_SCHEMA 77 78 79_LoadSchema() 80 81 82def MakeBuilderName(role, extra_config=None, is_trybot=False, 83 **kwargs): # pragma: no cover 84 schema = BUILDER_NAME_SCHEMA.get(role) 85 if not schema: # pragma: no cover 86 raise ValueError('%s is not a recognized role.' % role) 87 for k, v in kwargs.iteritems(): 88 if BUILDER_NAME_SEP in v: # pragma: no cover 89 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, v)) 90 if not k in schema: # pragma: no cover 91 raise ValueError('Schema does not contain "%s": %s' %(k, schema)) 92 if extra_config and BUILDER_NAME_SEP in extra_config: # pragma: no cover 93 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, 94 extra_config)) 95 name_parts = [role] 96 name_parts.extend([kwargs[attribute] for attribute in schema]) 97 if extra_config: 98 name_parts.append(extra_config) 99 if is_trybot: 100 name_parts.append(TRYBOT_NAME_SUFFIX) 101 return BUILDER_NAME_SEP.join(name_parts) 102 103 104def IsTrybot(builder_name): # pragma: no cover 105 """ Returns true if builder_name refers to a trybot (as opposed to a 106 waterfall bot). """ 107 return builder_name.endswith(TRYBOT_NAME_SUFFIX) 108 109 110def GetWaterfallBot(builder_name): # pragma: no cover 111 """Returns the name of the waterfall bot for this builder. If it is not a 112 trybot, builder_name is returned unchanged. If it is a trybot the name is 113 returned without the trybot suffix.""" 114 if not IsTrybot(builder_name): 115 return builder_name 116 return _WithoutSuffix(builder_name, BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX) 117 118 119def TrybotName(builder_name): # pragma: no cover 120 """Returns the name of the trybot clone of this builder. 121 122 If the given builder is a trybot, the name is returned unchanged. If not, the 123 TRYBOT_NAME_SUFFIX is appended. 124 """ 125 if builder_name.endswith(TRYBOT_NAME_SUFFIX): 126 return builder_name 127 return builder_name + BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX 128 129 130def _WithoutSuffix(string, suffix): # pragma: no cover 131 """ Returns a copy of string 'string', but with suffix 'suffix' removed. 132 Raises ValueError if string does not end with suffix. """ 133 if not string.endswith(suffix): 134 raise ValueError('_WithoutSuffix: string %s does not end with suffix %s' % ( 135 string, suffix)) 136 return string[:-len(suffix)] 137 138 139def DictForBuilderName(builder_name): 140 """Makes a dictionary containing details about the builder from its name.""" 141 split_name = builder_name.split(BUILDER_NAME_SEP) 142 143 def pop_front(): 144 try: 145 return split_name.pop(0) 146 except: # pragma: no cover 147 raise ValueError('Invalid builder name: %s' % builder_name) 148 149 result = {'is_trybot': False} 150 151 if split_name[-1] == TRYBOT_NAME_SUFFIX: 152 result['is_trybot'] = True 153 split_name.pop() 154 155 if split_name[0] in BUILDER_NAME_SCHEMA.keys(): 156 key_list = BUILDER_NAME_SCHEMA[split_name[0]] 157 result['role'] = pop_front() 158 for key in key_list: 159 result[key] = pop_front() 160 if split_name: 161 result['extra_config'] = pop_front() 162 if split_name: # pragma: no cover 163 raise ValueError('Invalid builder name: %s' % builder_name) 164 else: # pragma: no cover 165 raise ValueError('Invalid builder name: %s' % builder_name) 166 return result 167 168 169