| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 import base64 | 5 import base64 |
| 6 import json | 6 import json |
| 7 import os | 7 import os |
| 8 import re | 8 import re |
| 9 import shutil | 9 import shutil |
| 10 import zlib | 10 import zlib |
| (...skipping 14 matching lines...) Expand all Loading... |
| 25 | 25 |
| 26 from twisted.internet import defer, reactor, utils | 26 from twisted.internet import defer, reactor, utils |
| 27 from twisted.mail.smtp import SMTPSenderFactory | 27 from twisted.mail.smtp import SMTPSenderFactory |
| 28 from twisted.python import log | 28 from twisted.python import log |
| 29 | 29 |
| 30 from common.twisted_util.response import StringResponse | 30 from common.twisted_util.response import StringResponse |
| 31 from master import gitiles_poller | 31 from master import gitiles_poller |
| 32 from master.try_job_base import BadJobfile | 32 from master.try_job_base import BadJobfile |
| 33 | 33 |
| 34 | 34 |
| 35 def compress_extra_args(extra_args): |
| 36 """Returns (str): Compressed notation for "extra_args" field. |
| 37 |
| 38 This notation is implemented in the "cros/cbuildbot_tryjob" recipe. Basically, |
| 39 if the "cbb_extra_args" field begins with "z:", the remainder is a |
| 40 GZIP-compressed string containing the JSON-encoded "extra_args" list. |
| 41 |
| 42 The motivation for compressing this parameter is that it will occasionally be |
| 43 too long to store in BulidBot's database. |
| 44 |
| 45 Args: |
| 46 extra_args (list): The extra_args parameter. |
| 47 """ |
| 48 return 'z:' + base64.b64encode(zlib.compress(json.dumps(extra_args))) |
| 49 |
| 50 |
| 35 class CbuildbotConfigs(object): | 51 class CbuildbotConfigs(object): |
| 36 | 52 |
| 37 # Valid 'etc' builder targets. Specifically, this ensures: | 53 # Valid 'etc' builder targets. Specifically, this ensures: |
| 38 # - The build name doesn't begin with a flag ('--') | 54 # - The build name doesn't begin with a flag ('--') |
| 39 # - The build name doesn't contain spaces (to spill into extra args). | 55 # - The build name doesn't contain spaces (to spill into extra args). |
| 40 _ETC_TARGET_RE = re.compile(r'^[a-zA-Z][\w-]+\w$') | 56 _ETC_TARGET_RE = re.compile(r'^[a-zA-Z][\w-]+\w$') |
| 41 | 57 |
| 42 def __init__(self, configs, etc_builder=None): | 58 def __init__(self, configs, etc_builder=None): |
| 43 """Holds base state of the master's try job related configuration. | 59 """Holds base state of the master's try job related configuration. |
| 44 | 60 |
| (...skipping 12 matching lines...) Expand all Loading... |
| 57 mapping it based on its config. | 73 mapping it based on its config. |
| 58 | 74 |
| 59 If an 'etc' builder is configured and the config name is unknown, it will be | 75 If an 'etc' builder is configured and the config name is unknown, it will be |
| 60 mapped to the 'etc' builder if possible. | 76 mapped to the 'etc' builder if possible. |
| 61 | 77 |
| 62 A tryserver BuildBucket build takes the form: | 78 A tryserver BuildBucket build takes the form: |
| 63 - Empty `builder_name` parameter. If one is supplied, it will be ignored. | 79 - Empty `builder_name` parameter. If one is supplied, it will be ignored. |
| 64 - BuildBot changes can be added by including one or more BuildBucket | 80 - BuildBot changes can be added by including one or more BuildBucket |
| 65 `changes` parameters: [{'author': {'email': 'author@google.com'}}]. | 81 `changes` parameters: [{'author': {'email': 'author@google.com'}}]. |
| 66 - `cbb_config` property must be set to the build's cbuildbot config target. | 82 - `cbb_config` property must be set to the build's cbuildbot config target. |
| 67 - `extra_args` property (optional) may be a JSON list of additional | 83 - `cbb_extra_args` property (optional) may be a JSON-encoded list of |
| 68 parameters to pass to the tryjob. | 84 additional parameters to pass to the tryjob. |
| 69 - `slaves_request` property (optional) may be a JSON list of slaves on which | 85 - `slaves_request` property (optional) may be a JSON-encoded list of slaves |
| 70 this build may run. | 86 on which this build may run. |
| 71 - Additional BuildBot properties may be added. | 87 - Additional BuildBot properties may be added. |
| 72 | 88 |
| 73 NOTE: Internally, all of these parameters are converted to BuildBot | 89 NOTE: Internally, all of these parameters are converted to BuildBot |
| 74 properties and referenced as such in other areas of code. The Git poller | 90 properties and referenced as such in other areas of code. The Git poller |
| 75 also constructs the same property set, so code paths converge. | 91 also constructs the same property set, so code paths converge. |
| 76 """ | 92 """ |
| 77 def params_hook(params, _build): | 93 def params_hook(params, _build): |
| 78 # Map `cbb_config` to a builder name. | 94 # Map `cbb_config` to a builder name. |
| 79 properties = params.get('properties', {}) | 95 properties = params.get('properties', {}).copy() |
| 80 config_name = properties.get('cbb_config') | 96 config_name = properties.get('cbb_config') |
| 81 if not config_name: | 97 if not config_name: |
| 82 raise ValueError('Missing required `cbb_config` property.') | 98 raise ValueError('Missing required `cbb_config` property.') |
| 83 params['builder_name'] = self.GetBuilderForConfig(config_name) | 99 params['builder_name'] = self.GetBuilderForConfig(config_name) |
| 84 | 100 |
| 85 # Validate other fields. | 101 # If "cbb_extra_args" is present and a list, convert this to a compressed |
| 86 if not isinstance(properties.get('extra_args', []), list): | 102 # JSON string for the recipe. |
| 87 raise ValueError('`extra_args` property is not a list.') | 103 extra_args = properties.get('cbb_extra_args') |
| 104 if extra_args and isinstance(extra_args, list): |
| 105 properties['cbb_extra_args'] = compress_extra_args(extra_args) |
| 106 |
| 107 # The "slaves_request" field must be a list, if present. |
| 88 if not isinstance(properties.get('slaves_request', []), list): | 108 if not isinstance(properties.get('slaves_request', []), list): |
| 89 raise ValueError('`slaves_request` is not a list.') | 109 raise ValueError('`slaves_request` is not a list.') |
| 90 | 110 |
| 91 # Add mandatory properties to build. | 111 # Add mandatory properties to build. |
| 92 params['properties'] = properties | 112 params['properties'] = properties |
| 93 c['buildbucket_params_hook'] = params_hook | 113 c['buildbucket_params_hook'] = params_hook |
| 94 | 114 |
| 95 def GetBuilderForConfig(self, config_name): | 115 def GetBuilderForConfig(self, config_name): |
| 96 config = self.configs.get(config_name) | 116 config = self.configs.get(config_name) |
| 97 if config: | 117 if config: |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 | 254 |
| 235 props.setProperty('slaves_request', options.get('slaves_request', []), | 255 props.setProperty('slaves_request', options.get('slaves_request', []), |
| 236 self._PROPERTY_SOURCE) | 256 self._PROPERTY_SOURCE) |
| 237 props.setProperty('cbb_config', config, self._PROPERTY_SOURCE) | 257 props.setProperty('cbb_config', config, self._PROPERTY_SOURCE) |
| 238 | 258 |
| 239 extra_args = options.get('extra_args') | 259 extra_args = options.get('extra_args') |
| 240 if extra_args: | 260 if extra_args: |
| 241 # This field can be quite large, and exceed BuildBot property limits. | 261 # This field can be quite large, and exceed BuildBot property limits. |
| 242 # Compress it, Base64 encode it, and prefix it with "z:" so the consumer | 262 # Compress it, Base64 encode it, and prefix it with "z:" so the consumer |
| 243 # knows its size. | 263 # knows its size. |
| 244 extra_args = 'z:' + base64.b64encode(zlib.compress(json.dumps( | 264 props.setProperty('cbb_extra_args', compress_extra_args(extra_args), |
| 245 extra_args))) | |
| 246 props.setProperty('cbb_extra_args', extra_args, | |
| 247 self._PROPERTY_SOURCE) | 265 self._PROPERTY_SOURCE) |
| 248 return props | 266 return props |
| 249 | 267 |
| 250 def create_buildset(self, ssid, parsed_job): | 268 def create_buildset(self, ssid, parsed_job): |
| 251 """Overriding base class method.""" | 269 """Overriding base class method.""" |
| 252 dlist = [] | 270 dlist = [] |
| 253 buildset_name = '%s:%s' % (parsed_job['user'], parsed_job['name']) | 271 buildset_name = '%s:%s' % (parsed_job['user'], parsed_job['name']) |
| 254 for bot in parsed_job['bot']: | 272 for bot in parsed_job['bot']: |
| 255 builder_name = self.cbb.GetBuilderForConfig(bot) | 273 builder_name = self.cbb.GetBuilderForConfig(bot) |
| 256 log.msg("Creating '%s' try job(s) %s for %s" % (builder_name, ssid, bot)) | 274 log.msg("Creating '%s' try job(s) %s for %s" % (builder_name, ssid, bot)) |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 340 | 358 |
| 341 # Load the contents of the modified file. | 359 # Load the contents of the modified file. |
| 342 path = self._GITILES_PATH_TMPL % { | 360 path = self._GITILES_PATH_TMPL % { |
| 343 'repo': poller.repo_path, | 361 'repo': poller.repo_path, |
| 344 'revision': change.revision, | 362 'revision': change.revision, |
| 345 'path': change.files[0], | 363 'path': change.files[0], |
| 346 } | 364 } |
| 347 contents_b64 = yield poller.agent.request('GET', path, retry=5, | 365 contents_b64 = yield poller.agent.request('GET', path, retry=5, |
| 348 protocol=StringResponse.Get) | 366 protocol=StringResponse.Get) |
| 349 defer.returnValue(base64.b64decode(contents_b64)) | 367 defer.returnValue(base64.b64decode(contents_b64)) |
| OLD | NEW |