| OLD | NEW |
| 1 # Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 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 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 _, text = yield component_config.get_self_config_async( | 71 _, text = yield component_config.get_self_config_async( |
| 72 'swarming_task_template.json', store_last_good=True) | 72 'swarming_task_template.json', store_last_good=True) |
| 73 raise ndb.Return(json.loads(text) if text else None) | 73 raise ndb.Return(json.loads(text) if text else None) |
| 74 | 74 |
| 75 | 75 |
| 76 @utils.memcache_async( | 76 @utils.memcache_async( |
| 77 'swarming/is_for_swarming', ['bucket_name', 'builder_name'], time=10 * 60) | 77 'swarming/is_for_swarming', ['bucket_name', 'builder_name'], time=10 * 60) |
| 78 @ndb.tasklet | 78 @ndb.tasklet |
| 79 def _is_for_swarming_async(bucket_name, builder_name): | 79 def _is_for_swarming_async(bucket_name, builder_name): |
| 80 """Returns True if swarming is configured for |builder_name|.""" | 80 """Returns True if swarming is configured for |builder_name|.""" |
| 81 cfg = yield config.get_bucket_async(bucket_name) | 81 _, cfg = yield config.get_bucket_async(bucket_name) |
| 82 if cfg and cfg.swarming: # pragma: no branch | 82 if cfg and cfg.swarming: # pragma: no branch |
| 83 for b in cfg.swarming.builders: | 83 for b in cfg.swarming.builders: |
| 84 if b.name == builder_name: | 84 if b.name == builder_name: |
| 85 raise ndb.Return(True) | 85 raise ndb.Return(True) |
| 86 raise ndb.Return(False) | 86 raise ndb.Return(False) |
| 87 | 87 |
| 88 | 88 |
| 89 @ndb.tasklet | 89 @ndb.tasklet |
| 90 def is_for_swarming_async(build): | 90 def is_for_swarming_async(build): |
| 91 """Returns True if |build|'s bucket and builder are designed for swarming.""" | 91 """Returns True if |build|'s bucket and builder are designed for swarming.""" |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 166 props = read_properties(r1) | 166 props = read_properties(r1) |
| 167 props.update(read_properties(r2)) | 167 props.update(read_properties(r2)) |
| 168 recipe = project_config_pb2.Swarming.Recipe( | 168 recipe = project_config_pb2.Swarming.Recipe( |
| 169 repository=r2.repository or r1.repository, | 169 repository=r2.repository or r1.repository, |
| 170 name=r2.name or r1.name, | 170 name=r2.name or r1.name, |
| 171 ) | 171 ) |
| 172 return recipe, props | 172 return recipe, props |
| 173 | 173 |
| 174 | 174 |
| 175 @ndb.tasklet | 175 @ndb.tasklet |
| 176 def create_task_def_async(swarming_cfg, builder_cfg, build): | 176 def create_task_def_async(project_id, swarming_cfg, builder_cfg, build): |
| 177 """Creates a swarming task definition for the |build|. | 177 """Creates a swarming task definition for the |build|. |
| 178 | 178 |
| 179 Supports build properties that are supported by Buildbot-Buildbucket | 179 Supports build properties that are supported by Buildbot-Buildbucket |
| 180 integration. See | 180 integration. See |
| 181 https://chromium.googlesource.com/chromium/tools/build/+/eff4ceb/scripts/maste
r/buildbucket/README.md#Build-parameters | 181 https://chromium.googlesource.com/chromium/tools/build/+/eff4ceb/scripts/maste
r/buildbucket/README.md#Build-parameters |
| 182 | 182 |
| 183 Raises: | 183 Raises: |
| 184 errors.InvalidInputError if build.parameters are invalid. | 184 errors.InvalidInputError if build.parameters are invalid. |
| 185 """ | 185 """ |
| 186 params = build.parameters or {} | 186 params = build.parameters or {} |
| 187 validate_build_parameters(builder_cfg.name, params) | 187 validate_build_parameters(builder_cfg.name, params) |
| 188 swarming_param = params.get(PARAM_SWARMING) or {} | 188 swarming_param = params.get(PARAM_SWARMING) or {} |
| 189 | 189 |
| 190 # Render task template. | 190 # Render task template. |
| 191 task_template = yield get_task_template_async() | 191 task_template = yield get_task_template_async() |
| 192 task_template_params = { | 192 task_template_params = { |
| 193 'bucket': build.bucket, | 193 'bucket': build.bucket, |
| 194 'builder': builder_cfg.name, | 194 'builder': builder_cfg.name, |
| 195 'project': project_id, |
| 195 } | 196 } |
| 196 | 197 |
| 197 is_recipe = ( | 198 is_recipe = ( |
| 198 builder_cfg.HasField('recipe') or swarming_cfg.HasField('common_recipe')) | 199 builder_cfg.HasField('recipe') or swarming_cfg.HasField('common_recipe')) |
| 199 if is_recipe: # pragma: no branch | 200 if is_recipe: # pragma: no branch |
| 200 recipe, build_properties = merge_recipe( | 201 recipe, build_properties = merge_recipe( |
| 201 swarming_cfg.common_recipe, builder_cfg.recipe) | 202 swarming_cfg.common_recipe, builder_cfg.recipe) |
| 202 revision = swarming_param.get('recipe', {}).get('revision') or '' | 203 revision = swarming_param.get('recipe', {}).get('revision') or '' |
| 203 | 204 |
| 204 build_properties['buildername'] = builder_cfg.name | 205 build_properties['buildername'] = builder_cfg.name |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 307 @ndb.tasklet | 308 @ndb.tasklet |
| 308 def create_task_async(build): | 309 def create_task_async(build): |
| 309 """Creates a swarming task for the build and mutates the build. | 310 """Creates a swarming task for the build and mutates the build. |
| 310 | 311 |
| 311 May be called only if is_for_swarming(build) == True. | 312 May be called only if is_for_swarming(build) == True. |
| 312 """ | 313 """ |
| 313 if build.lease_key: | 314 if build.lease_key: |
| 314 raise errors.InvalidInputError( | 315 raise errors.InvalidInputError( |
| 315 'swarming builders do not support creation of leased builds') | 316 'swarming builders do not support creation of leased builds') |
| 316 builder_name = build.parameters[BUILDER_PARAMETER] | 317 builder_name = build.parameters[BUILDER_PARAMETER] |
| 317 bucket_cfg = yield config.get_bucket_async(build.bucket) | 318 project_id, bucket_cfg = yield config.get_bucket_async(build.bucket) |
| 318 builder_cfg = None | 319 builder_cfg = None |
| 319 for b in bucket_cfg.swarming.builders: # pragma: no branch | 320 for b in bucket_cfg.swarming.builders: # pragma: no branch |
| 320 if b.name == builder_name: # pragma: no branch | 321 if b.name == builder_name: # pragma: no branch |
| 321 builder_cfg = b | 322 builder_cfg = b |
| 322 break | 323 break |
| 323 assert builder_cfg, 'Builder %s not found' % builder_name | 324 assert builder_cfg, 'Builder %s not found' % builder_name |
| 324 | 325 |
| 325 task = yield create_task_def_async(bucket_cfg.swarming, builder_cfg, build) | 326 task = yield create_task_def_async( |
| 327 project_id, bucket_cfg.swarming, builder_cfg, build) |
| 326 res = yield _call_api_async( | 328 res = yield _call_api_async( |
| 327 bucket_cfg.swarming.hostname, 'tasks/new', method='POST', payload=task, | 329 bucket_cfg.swarming.hostname, 'tasks/new', method='POST', payload=task, |
| 328 # Higher timeout than normal because if the task creation request | 330 # Higher timeout than normal because if the task creation request |
| 329 # fails, but the task is actually created, later we will receive a | 331 # fails, but the task is actually created, later we will receive a |
| 330 # notification that the task is completed, but we won't have a build | 332 # notification that the task is completed, but we won't have a build |
| 331 # for that task, which results in errors in the log. | 333 # for that task, which results in errors in the log. |
| 332 deadline=30, | 334 deadline=30, |
| 333 # This code path is executed by put and put_batch request handlers. | 335 # This code path is executed by put and put_batch request handlers. |
| 334 # Clients should retry these requests on transient errors, so | 336 # Clients should retry these requests on transient errors, so |
| 335 # do not retry requests to swarming. | 337 # do not retry requests to swarming. |
| (...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 683 def _extend_unique(target, items): | 685 def _extend_unique(target, items): |
| 684 for x in items: | 686 for x in items: |
| 685 if x not in target: # pragma: no branch | 687 if x not in target: # pragma: no branch |
| 686 target.append(x) | 688 target.append(x) |
| 687 | 689 |
| 688 | 690 |
| 689 class TaskToken(tokens.TokenKind): | 691 class TaskToken(tokens.TokenKind): |
| 690 expiration_sec = 60 * 60 * 24 # 24 hours. | 692 expiration_sec = 60 * 60 * 24 # 24 hours. |
| 691 secret_key = auth.SecretKey('swarming_task_token', scope='local') | 693 secret_key = auth.SecretKey('swarming_task_token', scope='local') |
| 692 version = 1 | 694 version = 1 |
| OLD | NEW |