| OLD | NEW |
| 1 # Copyright 2016 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 """API for interacting with the buildbucket service directly. | 5 """API for interacting with the buildbucket service directly. |
| 6 | 6 |
| 7 Instead of triggering jobs by emitting annotations then handled by the master, | 7 Instead of triggering jobs by emitting annotations then handled by the master, |
| 8 this module allows slaves to directly post requests to buildbucket. | 8 this module allows slaves to directly post requests to buildbucket. |
| 9 """ | 9 """ |
| 10 | 10 |
| 11 import json | 11 import json |
| 12 import os | 12 import os |
| 13 import uuid | 13 import uuid |
| 14 | 14 |
| 15 from recipe_engine import recipe_api | 15 from recipe_engine import recipe_api |
| 16 | 16 |
| 17 | 17 |
| 18 class BuildbucketApi(recipe_api.RecipeApi): | 18 class BuildbucketApi(recipe_api.RecipeApi): |
| 19 """A module for interacting with buildbucket.""" | 19 """A module for interacting with buildbucket.""" |
| 20 | 20 |
| 21 def __init__(self, *args, **kwargs): |
| 22 super(BuildbucketApi, self).__init__(*args, **kwargs) |
| 23 self._properties = None |
| 24 |
| 21 def get_config_defaults(self): | 25 def get_config_defaults(self): |
| 22 if self.m.platform.is_win: | 26 if self.m.platform.is_win: |
| 23 return {'PLATFORM': 'win'} | 27 return {'PLATFORM': 'win'} |
| 24 return {'PLATFORM': 'default'} | 28 return {'PLATFORM': 'default'} |
| 25 | 29 |
| 26 def _configure_defaults(self): | 30 def _configure_defaults(self): |
| 27 """Apply default configuration if no configuration has been set. | 31 """Apply default configuration if no configuration has been set. |
| 28 | 32 |
| 29 Ideally whoever uses this api will explicitly set the configuration by | 33 Ideally whoever uses this api will explicitly set the configuration by |
| 30 doing `api.buildbucket.set_config('production_buildbucket')`, but to make | 34 doing `api.buildbucket.set_config('production_buildbucket')`, but to make |
| 31 this module usable even in case they don't configure it, we set the default | 35 this module usable even in case they don't configure it, we set the default |
| 32 to the production instance of buildbucket.""" | 36 to the production instance of buildbucket.""" |
| 33 # There's only two items in this module's configuration, the path to the | 37 # There's only two items in this module's configuration, the path to the |
| 34 # buildbucket cli client binary and the buildbucket hostname, this default | 38 # buildbucket cli client binary and the buildbucket hostname, this default |
| 35 # configuration will override them. | 39 # configuration will override them. |
| 36 if not self.c or not self.c.complete(): | 40 if not self.c or not self.c.complete(): |
| 37 self.set_config('production_buildbucket') | 41 self.set_config('production_buildbucket') |
| 38 | 42 |
| 39 def _tags_for_build(self, bucket, parameters, override_tags=None): | 43 def _tags_for_build(self, bucket, parameters, override_tags=None): |
| 40 buildbucket_info = self.m.properties.get('buildbucket', {}) | 44 buildbucket_info = self.properties or {} |
| 41 if isinstance(buildbucket_info, basestring): # pragma: no cover | |
| 42 buildbucket_info = json.loads(buildbucket_info) | |
| 43 original_tags_list = buildbucket_info.get('build', {}).get('tags', []) | 45 original_tags_list = buildbucket_info.get('build', {}).get('tags', []) |
| 44 | 46 |
| 45 original_tags = dict(t.split(':', 1) for t in original_tags_list) | 47 original_tags = dict(t.split(':', 1) for t in original_tags_list) |
| 46 new_tags = {'user_agent': 'recipe'} | 48 new_tags = {'user_agent': 'recipe'} |
| 47 | 49 |
| 48 if 'buildset' in original_tags: | 50 if 'buildset' in original_tags: |
| 49 new_tags['buildset'] = original_tags['buildset'] | 51 new_tags['buildset'] = original_tags['buildset'] |
| 50 builder_name = parameters.get('builder_name') | 52 builder_name = parameters.get('builder_name') |
| 51 if builder_name: | 53 if builder_name: |
| 52 new_tags['builder'] = builder_name | 54 new_tags['builder'] = builder_name |
| 53 if bucket.startswith('master.'): | 55 if bucket.startswith('master.'): |
| 54 new_tags['master'] = bucket[7:] | 56 new_tags['master'] = bucket[7:] |
| 55 | 57 |
| 56 new_tags.update(override_tags or {}) | 58 new_tags.update(override_tags or {}) |
| 57 return sorted([':'.join((x, y)) for x, y in new_tags.iteritems()]) | 59 return sorted([':'.join((x, y)) for x, y in new_tags.iteritems()]) |
| 58 | 60 |
| 61 @property |
| 62 def properties(self): |
| 63 """Returns (dict-like or None): The BuildBucket properties, if present.""" |
| 64 if self._properties is None: |
| 65 # Not cached, load and deserialize from properties. |
| 66 props = self.m.properties.get('buildbucket') |
| 67 if props is not None: |
| 68 if isinstance(props, basestring): |
| 69 props = json.loads(props) |
| 70 self._properties = props |
| 71 return self._properties |
| 59 | 72 |
| 60 def put(self, builds, service_account=None, **kwargs): | 73 def put(self, builds, service_account=None, **kwargs): |
| 61 """Puts a batch of builds. | 74 """Puts a batch of builds. |
| 62 | 75 |
| 63 Args: | 76 Args: |
| 64 builds (list): A list of dicts, where keys are: | 77 builds (list): A list of dicts, where keys are: |
| 65 'bucket': (required) name of the bucket for the request. | 78 'bucket': (required) name of the bucket for the request. |
| 66 'parameters' (dict): (required) arbitrary json-able parameters that a | 79 'parameters' (dict): (required) arbitrary json-able parameters that a |
| 67 build system would be able to interpret. | 80 build system would be able to interpret. |
| 68 'tags': (optional) a dict(str->str) of tags for the build. These will | 81 'tags': (optional) a dict(str->str) of tags for the build. These will |
| (...skipping 30 matching lines...) Expand all Loading... |
| 99 def _call_service(self, command, args, service_account=None, **kwargs): | 112 def _call_service(self, command, args, service_account=None, **kwargs): |
| 100 # TODO: Deploy buildbucket client using cipd. | 113 # TODO: Deploy buildbucket client using cipd. |
| 101 self._configure_defaults() | 114 self._configure_defaults() |
| 102 step_name = kwargs.pop('name', 'buildbucket.' + command) | 115 step_name = kwargs.pop('name', 'buildbucket.' + command) |
| 103 if service_account: | 116 if service_account: |
| 104 args = ['--service-account-json', service_account] + args | 117 args = ['--service-account-json', service_account] + args |
| 105 args = [str(self.c.buildbucket_client_path), command, '--host', | 118 args = [str(self.c.buildbucket_client_path), command, '--host', |
| 106 self.c.buildbucket_host] + args | 119 self.c.buildbucket_host] + args |
| 107 return self.m.step( | 120 return self.m.step( |
| 108 step_name, args, stdout=self.m.json.output(), **kwargs) | 121 step_name, args, stdout=self.m.json.output(), **kwargs) |
| OLD | NEW |