Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(151)

Side by Side Diff: appengine/cr-buildbucket/swarming/swarming.py

Issue 2213723002: swarmbucket: allow overriding config (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@sb-cfg-refactoring
Patch Set: security warning Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/cr-buildbucket/protoutil.py ('k') | appengine/cr-buildbucket/swarming/swarmingcfg.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 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 """This module integrates buildbucket with swarming. 5 """This module integrates buildbucket with swarming.
6 6
7 A bucket config may have "swarming" field that specifies how a builder 7 A bucket config may have "swarming" field that specifies how a builder
8 is mapped to a recipe. If build is scheduled for a bucket/builder 8 is mapped to a recipe. If build is scheduled for a bucket/builder
9 with swarming configuration, the integration overrides the default behavior. 9 with swarming configuration, the integration overrides the default behavior.
10 10
11 Prior adding Build to the datastore, a swarming task is created. The definition 11 Prior adding Build to the datastore, a swarming task is created. The definition
(...skipping 20 matching lines...) Expand all
32 import json 32 import json
33 import logging 33 import logging
34 import string 34 import string
35 35
36 from components import auth 36 from components import auth
37 from components import config as component_config 37 from components import config as component_config
38 from components import decorators 38 from components import decorators
39 from components import net 39 from components import net
40 from components import utils 40 from components import utils
41 from components.auth import tokens 41 from components.auth import tokens
42 from components.config import validation
42 from google.appengine.api import app_identity 43 from google.appengine.api import app_identity
43 from google.appengine.ext import ndb 44 from google.appengine.ext import ndb
44 import webapp2 45 import webapp2
45 46
46 from proto import project_config_pb2 47 from proto import project_config_pb2
47 from . import swarmingcfg as swarmingcfg_module 48 from . import swarmingcfg as swarmingcfg_module
48 import config 49 import config
49 import errors 50 import errors
50 import model 51 import model
51 import notifications 52 import notifications
53 import protoutil
54
52 55
53 PUBSUB_TOPIC = 'swarming' 56 PUBSUB_TOPIC = 'swarming'
54 BUILDER_PARAMETER = 'builder_name' 57 BUILDER_PARAMETER = 'builder_name'
55 PARAM_PROPERTIES = 'properties' 58 PARAM_PROPERTIES = 'properties'
56 PARAM_SWARMING = 'swarming' 59 PARAM_SWARMING = 'swarming'
57 PARAM_CHANGES = 'changes' 60 PARAM_CHANGES = 'changes'
58 DEFAULT_URL_FORMAT = 'https://{swarming_hostname}/user/task/{task_id}' 61 DEFAULT_URL_FORMAT = 'https://{swarming_hostname}/user/task/{task_id}'
59 62
60 63
61 ################################################################################ 64 ################################################################################
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
130 133
131 def validate_build_parameters(builder_name, params): 134 def validate_build_parameters(builder_name, params):
132 """Raises errors.InvalidInputError if build parameters are invalid.""" 135 """Raises errors.InvalidInputError if build parameters are invalid."""
133 params = copy.deepcopy(params) 136 params = copy.deepcopy(params)
134 137
135 def bad(fmt, *args): 138 def bad(fmt, *args):
136 raise errors.InvalidInputError(fmt % args) 139 raise errors.InvalidInputError(fmt % args)
137 140
138 params.pop(BUILDER_PARAMETER) # already validated 141 params.pop(BUILDER_PARAMETER) # already validated
139 142
143
144 def assert_object(name, value):
145 if not isinstance(value, dict):
146 bad('%s parameter must be an object' % name)
147
140 properties = params.pop(PARAM_PROPERTIES, None) 148 properties = params.pop(PARAM_PROPERTIES, None)
141 if properties is not None: 149 if properties is not None:
142 if not isinstance(properties, dict): 150 assert_object('properties', properties)
143 bad('properties parameter must be an object')
144 if properties.get('buildername', builder_name) != builder_name: 151 if properties.get('buildername', builder_name) != builder_name:
145 bad('inconsistent builder name') 152 bad('inconsistent builder name')
146 153
147 swarming = params.pop(PARAM_SWARMING, None) 154 swarming = params.pop(PARAM_SWARMING, None)
148 if swarming is not None: 155 if swarming is not None:
149 if not isinstance(swarming, dict): 156 assert_object('swarming', swarming)
150 bad('swarming parameter must be an object')
151 swarming = copy.deepcopy(swarming) 157 swarming = copy.deepcopy(swarming)
152 if 'recipe' in swarming: 158 if 'recipe' in swarming:
159 logging.error(
160 'someone is still using deprecated swarming.recipe parameter')
153 recipe = swarming.pop('recipe') 161 recipe = swarming.pop('recipe')
154 if not isinstance(recipe, dict): 162 assert_object('swarming.recipe', recipe)
155 bad('swarming.recipe parameter must be an object')
156 if 'revision' in recipe: 163 if 'revision' in recipe:
157 revision = recipe.pop('revision') 164 revision = recipe.pop('revision')
158 if not isinstance(revision, basestring): 165 if not isinstance(revision, basestring):
159 bad('swarming.recipe.revision parameter must be a string') 166 bad('swarming.recipe.revision parameter must be a string')
160 if recipe: 167 if recipe:
161 bad('unrecognized keys in swarming.recipe: %r', recipe) 168 bad('unrecognized keys in swarming.recipe: %r', recipe)
162 canary_template = swarming.pop('canary_template', None) 169 canary_template = swarming.pop('canary_template', None)
163 if canary_template not in (True, False, None): 170 if canary_template not in (True, False, None):
164 bad('swarming.canary_template parameter must true, false or null') 171 bad('swarming.canary_template parameter must true, false or null')
172
173 override_builder_cfg_data = swarming.pop('override_builder_cfg', None)
174 if override_builder_cfg_data is not None:
175 assert_object('swarming.override_builder_cfg', override_builder_cfg_data)
176 override_builder_cfg = project_config_pb2.Swarming.Builder()
177 try:
178 protoutil.merge_dict(
179 override_builder_cfg_data, override_builder_cfg)
180 except TypeError as ex:
181 bad('swarming.override_builder_cfg parameter: %s', ex)
182 if override_builder_cfg.name:
183 bad('swarming.override_builder_cfg cannot override builder name')
184 ctx = validation.Context.raise_on_error(
185 exc_type=errors.InvalidInputError,
186 prefix='swarming.override_builder_cfg parameter: ')
187 swarmingcfg_module.validate_builder_cfg(
188 override_builder_cfg, ctx, final=False)
189
165 if swarming: 190 if swarming:
166 bad('unrecognized keys in swarming param: %r', swarming.keys()) 191 bad('unrecognized keys in swarming param: %r', swarming.keys())
167 192
168 changes = params.pop(PARAM_CHANGES, None) 193 changes = params.pop(PARAM_CHANGES, None)
169 if changes is not None: 194 if changes is not None:
170 if not isinstance(changes, list): 195 if not isinstance(changes, list):
171 bad('changes param must be an array') 196 bad('changes param must be an array')
172 for c in changes: # pragma: no branch 197 for c in changes: # pragma: no branch
173 if not isinstance(c, dict): 198 if not isinstance(c, dict):
174 bad('changes param must contain only objects') 199 bad('changes param must contain only objects')
(...skipping 21 matching lines...) Expand all
196 if percentage == 0: 221 if percentage == 0:
197 return False 222 return False
198 identity = { 223 identity = {
199 'bucket': build.bucket, 224 'bucket': build.bucket,
200 'parametres': build.parameters, 225 'parametres': build.parameters,
201 } 226 }
202 digest = hashlib.sha1(json.dumps(identity, sort_keys=True)).digest() 227 digest = hashlib.sha1(json.dumps(identity, sort_keys=True)).digest()
203 return digest[0] < 256 * percentage / 100 228 return digest[0] < 256 * percentage / 100
204 229
205 230
231 def _prepare_builder_config(swarming_cfg, builder_cfg, swarming_param):
232 """Returns final version of builder config to use for |build|.
233
234 Expects arguments to be valid.
235 """
236 swarming_cfg = copy.deepcopy(swarming_cfg)
237 swarmingcfg_module.normalize_swarming_cfg(swarming_cfg)
238
239 # Apply defaults.
240 result = copy.deepcopy(swarming_cfg.builder_defaults)
241 swarmingcfg_module.merge_builder(result, builder_cfg)
242
243 # Apply overrides in the swarming parameter.
244 override_builder_cfg_data = swarming_param.get('override_builder_cfg', {})
245 if override_builder_cfg_data:
246 override_builder_cfg = project_config_pb2.Swarming.Builder()
247 protoutil.merge_dict(override_builder_cfg_data, result)
248 ctx = validation.Context.raise_on_error(
249 exc_type=errors.InvalidInputError,
250 prefix='swarming.override_buider_cfg parameter: ')
251 swarmingcfg_module.merge_builder(result, override_builder_cfg)
252 swarmingcfg_module.validate_builder_cfg(result, ctx)
253 return result
254
255
206 @ndb.tasklet 256 @ndb.tasklet
207 def create_task_def_async(project_id, swarming_cfg, builder_cfg, build): 257 def create_task_def_async(project_id, swarming_cfg, builder_cfg, build):
208 """Creates a swarming task definition for the |build|. 258 """Creates a swarming task definition for the |build|.
209 259
210 Supports build properties that are supported by Buildbot-Buildbucket 260 Supports build properties that are supported by Buildbot-Buildbucket
211 integration. See 261 integration. See
212 https://chromium.googlesource.com/chromium/tools/build/+/eff4ceb/scripts/maste r/buildbucket/README.md#Build-parameters 262 https://chromium.googlesource.com/chromium/tools/build/+/eff4ceb/scripts/maste r/buildbucket/README.md#Build-parameters
213 263
214 Raises: 264 Raises:
215 errors.InvalidInputError if build.parameters are invalid. 265 errors.InvalidInputError if build.parameters are invalid.
216 """ 266 """
217 params = build.parameters or {} 267 params = build.parameters or {}
218 validate_build_parameters(builder_cfg.name, params) 268 validate_build_parameters(builder_cfg.name, params)
219 swarming_param = params.get(PARAM_SWARMING) or {} 269 swarming_param = params.get(PARAM_SWARMING) or {}
220 270
221 # Use canary template? 271 # Use canary template?
222 canary = swarming_param.get('canary_template') 272 canary = swarming_param.get('canary_template')
223 canary_required = bool(canary) 273 canary_required = bool(canary)
224 if canary is None: 274 if canary is None:
225 canary = should_use_canary_template( 275 canary = should_use_canary_template(
226 build, swarming_cfg.task_template_canary_percentage) 276 build, swarming_cfg.task_template_canary_percentage)
227 277
228 # Normalize/merge configs. 278 builder_cfg = _prepare_builder_config(
229 swarming_cfg = copy.deepcopy(swarming_cfg) 279 swarming_cfg, builder_cfg, swarming_param)
230 swarmingcfg_module.normalize_swarming_cfg(swarming_cfg)
231
232 merged = copy.deepcopy(swarming_cfg.builder_defaults)
233 swarmingcfg_module.merge_builder(merged, builder_cfg)
234 builder_cfg = merged
235 280
236 # Render task template. 281 # Render task template.
237 try: 282 try:
238 task_template, canary = yield get_task_template_async( 283 task_template, canary = yield get_task_template_async(
239 canary, canary_required) 284 canary, canary_required)
240 except CanaryTemplateNotFound as ex: 285 except CanaryTemplateNotFound as ex:
241 raise errors.InvalidInputError(ex.message) 286 raise errors.InvalidInputError(ex.message)
242 task_template_params = { 287 task_template_params = {
243 'bucket': build.bucket, 288 'bucket': build.bucket,
244 'builder': builder_cfg.name, 289 'builder': builder_cfg.name,
(...skipping 474 matching lines...) Expand 10 before | Expand all | Expand 10 after
719 def _extend_unique(target, items): 764 def _extend_unique(target, items):
720 for x in items: 765 for x in items:
721 if x not in target: # pragma: no branch 766 if x not in target: # pragma: no branch
722 target.append(x) 767 target.append(x)
723 768
724 769
725 class TaskToken(tokens.TokenKind): 770 class TaskToken(tokens.TokenKind):
726 expiration_sec = 60 * 60 * 24 # 24 hours. 771 expiration_sec = 60 * 60 * 24 # 24 hours.
727 secret_key = auth.SecretKey('swarming_task_token', scope='local') 772 secret_key = auth.SecretKey('swarming_task_token', scope='local')
728 version = 1 773 version = 1
OLDNEW
« no previous file with comments | « appengine/cr-buildbucket/protoutil.py ('k') | appengine/cr-buildbucket/swarming/swarmingcfg.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698