Index: appengine/cr-buildbucket/swarming/swarming.py |
diff --git a/appengine/cr-buildbucket/swarming/swarming.py b/appengine/cr-buildbucket/swarming/swarming.py |
index 24149d1986dd86be125fb9bf17d6f8de1eb245ba..845d7bf3b8d6027700b55659e1c0c340ee2f29d8 100644 |
--- a/appengine/cr-buildbucket/swarming/swarming.py |
+++ b/appengine/cr-buildbucket/swarming/swarming.py |
@@ -1,4 +1,4 @@ |
-# Copyright 2014 The Chromium Authors. All rights reserved. |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
# Use of this source code is governed by a BSD-style license that can be |
# found in the LICENSE file. |
@@ -39,6 +39,7 @@ from components import decorators |
from components import net |
from components import utils |
from components.auth import tokens |
+from components.config import validation |
from google.appengine.api import app_identity |
from google.appengine.ext import ndb |
import webapp2 |
@@ -49,6 +50,8 @@ import config |
import errors |
import model |
import notifications |
+import protoutil |
+ |
PUBSUB_TOPIC = 'swarming' |
BUILDER_PARAMETER = 'builder_name' |
@@ -137,22 +140,26 @@ def validate_build_parameters(builder_name, params): |
params.pop(BUILDER_PARAMETER) # already validated |
+ |
+ def assert_object(name, value): |
+ if not isinstance(value, dict): |
+ bad('%s parameter must be an object' % name) |
+ |
properties = params.pop(PARAM_PROPERTIES, None) |
if properties is not None: |
- if not isinstance(properties, dict): |
- bad('properties parameter must be an object') |
+ assert_object('properties', properties) |
if properties.get('buildername', builder_name) != builder_name: |
bad('inconsistent builder name') |
swarming = params.pop(PARAM_SWARMING, None) |
if swarming is not None: |
- if not isinstance(swarming, dict): |
- bad('swarming parameter must be an object') |
+ assert_object('swarming', swarming) |
swarming = copy.deepcopy(swarming) |
if 'recipe' in swarming: |
+ logging.error( |
+ 'someone is still using deprecated swarming.recipe parameter') |
recipe = swarming.pop('recipe') |
- if not isinstance(recipe, dict): |
- bad('swarming.recipe parameter must be an object') |
+ assert_object('swarming.recipe', recipe) |
if 'revision' in recipe: |
revision = recipe.pop('revision') |
if not isinstance(revision, basestring): |
@@ -162,6 +169,24 @@ def validate_build_parameters(builder_name, params): |
canary_template = swarming.pop('canary_template', None) |
if canary_template not in (True, False, None): |
bad('swarming.canary_template parameter must true, false or null') |
+ |
+ override_builder_cfg_data = swarming.pop('override_builder_cfg', None) |
+ if override_builder_cfg_data is not None: |
+ assert_object('swarming.override_builder_cfg', override_builder_cfg_data) |
+ override_builder_cfg = project_config_pb2.Swarming.Builder() |
+ try: |
+ protoutil.merge_dict( |
+ override_builder_cfg_data, override_builder_cfg) |
+ except TypeError as ex: |
+ bad('swarming.override_builder_cfg parameter: %s', ex) |
+ if override_builder_cfg.name: |
+ bad('swarming.override_builder_cfg cannot override builder name') |
+ ctx = validation.Context.raise_on_error( |
+ exc_type=errors.InvalidInputError, |
+ prefix='swarming.override_builder_cfg parameter: ') |
+ swarmingcfg_module.validate_builder_cfg( |
+ override_builder_cfg, ctx, final=False) |
+ |
if swarming: |
bad('unrecognized keys in swarming param: %r', swarming.keys()) |
@@ -203,6 +228,31 @@ def should_use_canary_template(build, percentage): # pragma: no cover |
return digest[0] < 256 * percentage / 100 |
+def _prepare_builder_config(swarming_cfg, builder_cfg, swarming_param): |
+ """Returns final version of builder config to use for |build|. |
+ |
+ Expects arguments to be valid. |
+ """ |
+ swarming_cfg = copy.deepcopy(swarming_cfg) |
+ swarmingcfg_module.normalize_swarming_cfg(swarming_cfg) |
+ |
+ # Apply defaults. |
+ result = copy.deepcopy(swarming_cfg.builder_defaults) |
+ swarmingcfg_module.merge_builder(result, builder_cfg) |
+ |
+ # Apply overrides in the swarming parameter. |
+ override_builder_cfg_data = swarming_param.get('override_builder_cfg', {}) |
+ if override_builder_cfg_data: |
+ override_builder_cfg = project_config_pb2.Swarming.Builder() |
+ protoutil.merge_dict(override_builder_cfg_data, result) |
+ ctx = validation.Context.raise_on_error( |
+ exc_type=errors.InvalidInputError, |
+ prefix='swarming.override_buider_cfg parameter: ') |
+ swarmingcfg_module.merge_builder(result, override_builder_cfg) |
+ swarmingcfg_module.validate_builder_cfg(result, ctx) |
+ return result |
+ |
+ |
@ndb.tasklet |
def create_task_def_async(project_id, swarming_cfg, builder_cfg, build): |
"""Creates a swarming task definition for the |build|. |
@@ -225,13 +275,8 @@ def create_task_def_async(project_id, swarming_cfg, builder_cfg, build): |
canary = should_use_canary_template( |
build, swarming_cfg.task_template_canary_percentage) |
- # Normalize/merge configs. |
- swarming_cfg = copy.deepcopy(swarming_cfg) |
- swarmingcfg_module.normalize_swarming_cfg(swarming_cfg) |
- |
- merged = copy.deepcopy(swarming_cfg.builder_defaults) |
- swarmingcfg_module.merge_builder(merged, builder_cfg) |
- builder_cfg = merged |
+ builder_cfg = _prepare_builder_config( |
+ swarming_cfg, builder_cfg, swarming_param) |
# Render task template. |
try: |