Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 import argparse | 6 import argparse |
| 7 import collections | 7 import collections |
| 8 import contextlib | 8 import contextlib |
| 9 import json | 9 import json |
| 10 import logging | 10 import logging |
| 11 import os | 11 import os |
| 12 import platform | 12 import platform |
| 13 import shutil | 13 import shutil |
| 14 import socket | 14 import socket |
| 15 import subprocess | 15 import subprocess |
| 16 import sys | 16 import sys |
| 17 import tempfile | 17 import tempfile |
| 18 | 18 |
| 19 | 19 |
| 20 # Install Infra build environment. | 20 # Install Infra build environment. |
| 21 BUILD_ROOT = os.path.dirname(os.path.dirname(os.path.dirname( | 21 BUILD_ROOT = os.path.dirname(os.path.dirname(os.path.dirname( |
| 22 os.path.abspath(__file__)))) | 22 os.path.abspath(__file__)))) |
| 23 sys.path.insert(0, os.path.join(BUILD_ROOT, 'scripts')) | 23 sys.path.insert(0, os.path.join(BUILD_ROOT, 'scripts')) |
| 24 | 24 |
| 25 from common import annotator | 25 from common import annotator |
| 26 from common import chromium_utils | 26 from common import chromium_utils |
| 27 from common import env | 27 from common import env |
| 28 from common import master_cfg_utils | 28 from common import master_cfg_utils |
| 29 from slave import gce | |
| 29 | 30 |
| 30 # Logging instance. | 31 # Logging instance. |
| 31 LOGGER = logging.getLogger('annotated_run') | 32 LOGGER = logging.getLogger('annotated_run') |
| 32 | 33 |
| 34 # Return codes used by Butler/Annotee to indicate their failure (as opposed to | |
| 35 # a forwarded return code from the underlying process). | |
| 36 LOGDOG_ERROR_RETURNCODES = ( | |
| 37 # Butler runtime error. | |
| 38 250, | |
| 39 # Annotee runtime error. | |
| 40 251, | |
| 41 ) | |
| 42 | |
| 43 # Sentinel value that, if present in master config, matches all builders | |
| 44 # underneath that master. | |
| 45 WHITELIST_ALL = '*' | |
| 46 | |
| 47 # Whitelist of {master}=>[{builder}|WHITELIST_ALL] whitelisting specific masters | |
| 48 # and builders for experimental LogDog/Annotee export. | |
| 49 LOGDOG_WHITELIST_MASTER_BUILDERS = { | |
| 50 } | |
| 51 | |
| 52 # Configuration for a Pub/Sub topic. | |
| 53 PubSubConfig = collections.namedtuple('PubSubConfig', ('project', 'topic')) | |
| 54 | |
| 55 # LogDogPlatform is the set of platform-specific LogDog bootstrapping | |
| 56 # configuration parameters. | |
| 57 # | |
| 58 # See _logdog_get_streamserver_uri for "streamserver" parameter details. | |
| 59 LogDogPlatform = collections.namedtuple('LogDogPlatform', ( | |
| 60 'butler', 'annotee', 'credential_path', 'streamserver', | |
| 61 )) | |
| 62 | |
| 63 # A CIPD binary description, including the package name, version, and relative | |
| 64 # path of the binary within the package. | |
| 65 CipdBinary = collections.namedtuple('CipdBinary', | |
| 66 ('package', 'version', 'relpath')) | |
| 33 | 67 |
| 34 # RecipeRuntime will probe this for values. | 68 # RecipeRuntime will probe this for values. |
| 35 # - First, (system, platform) | 69 # - First, (system, platform) |
| 36 # - Then, (system,) | 70 # - Then, (system,) |
| 37 # - Finally, (), | 71 # - Finally, (), |
| 38 PLATFORM_CONFIG = { | 72 PLATFORM_CONFIG = { |
| 39 # All systems. | 73 # All systems. |
| 40 (): {}, | 74 (): { |
| 75 'logdog_pubsub': PubSubConfig( | |
| 76 project='luci-logdog', | |
| 77 topic='logs', | |
| 78 ), | |
| 79 }, | |
| 41 | 80 |
| 42 # Linux | 81 # Linux |
| 43 ('Linux',): { | 82 ('Linux',): { |
| 44 'run_cmd': ['/opt/infra-python/run.py'], | 83 'run_cmd': ['/opt/infra-python/run.py'], |
| 84 'logdog_platform': LogDogPlatform( | |
| 85 butler=CipdBinary('infra/tools/luci/logdog/butler/linux-amd64', | |
| 86 'latest', 'logdog_butler'), | |
| 87 annotee=CipdBinary('infra/tools/luci/logdog/annotee/linux-amd64', | |
| 88 'latest', 'logdog_annotee'), | |
| 89 credential_path=( | |
| 90 '/creds/service_accounts/service-account-luci-logdog-pubsub.json'), | |
| 91 streamserver='unix', | |
| 92 ), | |
| 45 }, | 93 }, |
| 46 | 94 |
| 47 # Mac OSX | 95 # Mac OSX |
| 48 ('Darwin',): { | 96 ('Darwin',): { |
| 49 'run_cmd': ['/opt/infra-python/run.py'], | 97 'run_cmd': ['/opt/infra-python/run.py'], |
| 50 }, | 98 }, |
| 51 | 99 |
| 52 # Windows | 100 # Windows |
| 53 ('Windows',): { | 101 ('Windows',): { |
| 54 'run_cmd': ['C:\\infra-python\\ENV\\Scripts\\python.exe', | 102 'run_cmd': ['C:\\infra-python\\ENV\\Scripts\\python.exe', |
| 55 'C:\\infra-python\\run.py'], | 103 'C:\\infra-python\\run.py'], |
| 56 }, | 104 }, |
| 57 } | 105 } |
| 58 | 106 |
| 59 | 107 |
| 60 # Config is the runtime configuration used by `annotated_run.py` to bootstrap | 108 # Config is the runtime configuration used by `annotated_run.py` to bootstrap |
| 61 # the recipe engine. | 109 # the recipe engine. |
| 62 Config = collections.namedtuple('Config', ( | 110 Config = collections.namedtuple('Config', ( |
| 63 'run_cmd', | 111 'run_cmd', |
| 112 'logdog_pubsub', | |
| 113 'logdog_platform', | |
| 64 )) | 114 )) |
| 65 | 115 |
| 66 | 116 |
| 67 def get_config(): | 117 def get_config(): |
| 68 """Returns (Config): The constructed Config object. | 118 """Returns (Config): The constructed Config object. |
| 69 | 119 |
| 70 The Config object is constructed from: | 120 The Config object is constructed from: |
| 71 - Cascading the PLATFORM_CONFIG fields together based on current | 121 - Cascading the PLATFORM_CONFIG fields together based on current |
| 72 OS/Architecture. | 122 OS/Architecture. |
| 73 | 123 |
| 74 Raises: | 124 Raises: |
| 75 KeyError: if a required configuration key/parameter is not available. | 125 KeyError: if a required configuration key/parameter is not available. |
| 76 """ | 126 """ |
| 77 # Cascade the platform configuration. | 127 # Cascade the platform configuration. |
| 78 p = (platform.system(), platform.processor()) | 128 p = (platform.system(), platform.processor()) |
| 79 platform_config = {} | 129 platform_config = {} |
| 80 for i in xrange(len(p)+1): | 130 for i in xrange(len(p)+1): |
| 81 platform_config.update(PLATFORM_CONFIG.get(p[:i], {})) | 131 platform_config.update(PLATFORM_CONFIG.get(p[:i], {})) |
| 82 | 132 |
| 83 # Construct runtime configuration. | 133 # Construct runtime configuration. |
| 84 return Config( | 134 return Config( |
| 85 run_cmd=platform_config.get('run_cmd'), | 135 run_cmd=platform_config.get('run_cmd'), |
| 136 logdog_pubsub=platform_config.get('logdog_pubsub'), | |
| 137 logdog_platform=platform_config.get('logdog_platform'), | |
| 86 ) | 138 ) |
| 87 | 139 |
| 88 | 140 |
| 89 def ensure_directory(*path): | 141 def ensure_directory(*path): |
| 90 path = os.path.join(*path) | 142 path = os.path.join(*path) |
| 91 if not os.path.isdir(path): | 143 if not os.path.isdir(path): |
| 92 os.makedirs(path) | 144 os.makedirs(path) |
| 93 return path | 145 return path |
| 94 | 146 |
| 95 | 147 |
| 148 def _logdog_get_streamserver_uri(typ, d): | |
| 149 """Returns (str): The Butler StreamServer URI. | |
| 150 | |
| 151 Args: | |
| 152 typ (str): The type of URI to generate. One of: ['unix']. | |
| 153 d (str): The working directory path. | |
| 154 Raises: | |
| 155 LogDogBootstrapError: if |typ| is not a known type. | |
| 156 """ | |
| 157 if typ == 'unix': | |
| 158 return 'unix:%s' % (os.path.join(d, 'butler.sock'),) | |
| 159 raise LogDogBootstrapError('No streamserver URI generator.') | |
| 160 | |
| 161 | |
| 96 def _run_command(cmd, **kwargs): | 162 def _run_command(cmd, **kwargs): |
| 97 if kwargs.pop('dry_run', False): | 163 if kwargs.pop('dry_run', False): |
| 98 LOGGER.info('(Dry Run) Would have executed command: %s', cmd) | 164 LOGGER.info('(Dry Run) Would have executed command: %s', cmd) |
| 99 return 0, '' | 165 return 0, '' |
| 100 | 166 |
| 101 LOGGER.debug('Executing command: %s', cmd) | 167 LOGGER.debug('Executing command: %s', cmd) |
| 102 kwargs.setdefault('stderr', subprocess.STDOUT) | 168 kwargs.setdefault('stderr', subprocess.STDOUT) |
| 103 proc = subprocess.Popen(cmd, **kwargs) | 169 proc = subprocess.Popen(cmd, **kwargs) |
| 104 stdout, _ = proc.communicate() | 170 stdout, _ = proc.communicate() |
| 105 | 171 |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 137 LOGGER.debug('Cleaning up temporary directory [%s].', basedir) | 203 LOGGER.debug('Cleaning up temporary directory [%s].', basedir) |
| 138 try: | 204 try: |
| 139 chromium_utils.RemoveDirectory(basedir) | 205 chromium_utils.RemoveDirectory(basedir) |
| 140 except Exception: | 206 except Exception: |
| 141 LOGGER.exception('Failed to clean up temporary directory [%s].', | 207 LOGGER.exception('Failed to clean up temporary directory [%s].', |
| 142 basedir) | 208 basedir) |
| 143 else: | 209 else: |
| 144 LOGGER.warning('(--leak) Leaking temporary directory [%s].', basedir) | 210 LOGGER.warning('(--leak) Leaking temporary directory [%s].', basedir) |
| 145 | 211 |
| 146 | 212 |
| 213 class LogDogNotBootstrapped(Exception): | |
| 214 pass | |
| 215 | |
| 216 | |
| 217 class LogDogBootstrapError(Exception): | |
| 218 pass | |
| 219 | |
| 220 | |
| 221 def is_executable(path): | |
| 222 return os.path.isfile(path) and os.access(path, os.X_OK) | |
| 223 | |
| 224 | |
| 225 def ensure_directory(*path): | |
| 226 path = os.path.join(*path) | |
| 227 if not os.path.isdir(path): | |
| 228 os.makedirs(path) | |
| 229 return path | |
| 230 | |
| 231 | |
| 232 def _get_service_account_json(opts, credential_path): | |
| 233 """Returns (str/None): If specified, the path to the service account JSON. | |
| 234 | |
| 235 This method probes the local environment and returns a (possibly empty) list | |
| 236 of arguments to add to the Butler command line for authentication. | |
| 237 | |
| 238 If we're running on a GCE instance, no arguments will be returned, as GCE | |
| 239 service account is implicitly authenticated. If we're running on Baremetal, | |
| 240 a path to those credentials will be returned. | |
| 241 | |
| 242 Args: | |
| 243 rt (RecipeRuntime): The runtime environment. | |
| 244 Raises: | |
| 245 |LogDogBootstrapError| if no credentials could be found. | |
| 246 """ | |
| 247 path = opts.logdog_service_account_json | |
| 248 if path: | |
| 249 return path | |
| 250 | |
| 251 if gce.Authenticator.is_gce(): | |
| 252 LOGGER.info('Running on GCE. No credentials necessary.') | |
| 253 return None | |
| 254 | |
| 255 if os.path.isfile(credential_path): | |
| 256 return credential_path | |
| 257 | |
| 258 raise LogDogBootstrapError('Could not find service account credentials. ' | |
| 259 'Tried: %s' % (credential_path,)) | |
| 260 | |
| 261 | |
| 262 def _logdog_install_cipd(path, *packages): | |
| 263 """Returns (list): The paths to the binaries in each of the packages. | |
| 264 | |
| 265 This method bootstraps CIPD in "path", installing the packages specified | |
| 266 by "packages" and returning the paths to their binaries. | |
| 267 | |
| 268 Args: | |
| 269 path (str): The CIPD installation root. | |
| 270 packages (CipdBinary): The set of CIPD binary packages to install. | |
| 271 """ | |
| 272 verbosity = 0 | |
| 273 level = logging.getLogger().level | |
| 274 if level <= logging.INFO: | |
| 275 verbosity += 1 | |
| 276 if level <= logging.DEBUG: | |
| 277 verbosity += 1 | |
| 278 | |
| 279 packages_path = os.path.join(path, 'packages.json') | |
| 280 pmap = {} | |
| 281 cmd = [ | |
| 282 sys.executable, | |
| 283 os.path.join(env.Build, 'scripts', 'slave', 'cipd.py'), | |
| 284 '--dest-directory', path, | |
| 285 '--json-output', packages_path, | |
| 286 ] + (['--verbose'] * verbosity) | |
| 287 for p in packages: | |
| 288 cmd += ['-P', '%s@%s' % (p.package, p.version)] | |
| 289 pmap[p.package] = os.path.join(path, p.relpath) | |
| 290 | |
| 291 try: | |
| 292 _check_command(cmd) | |
| 293 except subprocess.CalledProcessError: | |
| 294 LOGGER.exception('Failed to install LogDog CIPD packages.') | |
| 295 raise LogDogBootstrapError() | |
| 296 | |
| 297 # Resolve installed packages. | |
| 298 return tuple(pmap[p.package] for p in packages) | |
| 299 | |
| 300 | |
| 301 def _logdog_bootstrap(tempdir, config, opts, cmd): | |
| 302 """Executes the recipe engine, bootstrapping it through LogDog/Annotee. | |
| 303 | |
| 304 This method executes the recipe engine, bootstrapping it through | |
| 305 LogDog/Annotee so its output and annotations are streamed to LogDog. The | |
| 306 bootstrap is configured to tee the annotations through STDOUT/STDERR so they | |
| 307 will still be sent to BuildBot. | |
| 308 | |
| 309 The overall setup here is: | |
| 310 [annotated_run.py] => [logdog_butler] => [logdog_annotee] => [recipes.py] | |
| 311 | |
| 312 Args: | |
| 313 config (Config): Recipe runtime configuration. | |
| 314 opts (argparse.Namespace): Command-line options. | |
| 315 cmd (list): The recipe runner command list to bootstrap. | |
| 316 | |
| 317 Returns (int): The return code of the recipe runner process. | |
| 318 | |
| 319 Raises: | |
| 320 LogDogNotBootstrapped: if the recipe engine was not executed because the | |
| 321 LogDog bootstrap requirements are not available. | |
| 322 LogDogBootstrapError: if there was an error bootstrapping the recipe runner | |
| 323 through LogDog. | |
| 324 """ | |
| 325 bootstrap_dir = ensure_directory(tempdir, 'logdog_bootstrap') | |
|
Vadim Sh.
2016/01/09 01:25:13
it will run bootstrap from scratch each time? :( T
dnj (Google)
2016/01/09 02:50:29
No specific reason other than this is nice and her
| |
| 326 | |
| 327 plat = config.logdog_platform | |
| 328 if not plat: | |
| 329 raise LogDogNotBootstrapped('LogDog platform is not configured.') | |
| 330 | |
| 331 cipd_path = os.path.join(bootstrap_dir, 'cipd') | |
| 332 butler, annotee = _logdog_install_cipd(cipd_path, plat.butler, plat.annotee) | |
| 333 if opts.logdog_butler_path: | |
| 334 butler = opts.logdog_butler_path | |
| 335 if opts.logdog_annotee_path: | |
| 336 annotee = opts.logdog_annotee_path | |
| 337 | |
| 338 if not config.logdog_pubsub: | |
| 339 raise LogDogNotBootstrapped('No Pub/Sub configured.') | |
| 340 if not config.logdog_pubsub.project: | |
| 341 raise LogDogNotBootstrapped('No Pub/Sub project configured.') | |
| 342 if not config.logdog_pubsub.topic: | |
| 343 raise LogDogNotBootstrapped('No Pub/Sub topic configured.') | |
| 344 | |
| 345 # Determine LogDog verbosity. | |
| 346 logdog_verbose = [] | |
| 347 if opts.logdog_verbose == 0: | |
| 348 pass | |
| 349 elif opts.logdog_verbose == 1: | |
| 350 logdog_verbose.append('-log_level=info') | |
| 351 else: | |
| 352 logdog_verbose.append('-log_level=debug') | |
| 353 | |
| 354 service_account_args = [] | |
| 355 service_account_json = _get_service_account_json(opts, plat.credential_path) | |
| 356 if service_account_json: | |
| 357 service_account_args += ['-service-account-json', service_account_json] | |
| 358 | |
| 359 # Generate our Butler stream server URI. | |
| 360 streamserver_uri = _logdog_get_streamserver_uri(plat.streamserver, tempdir) | |
| 361 | |
| 362 # Dump the bootstrapped Annotee command to JSON for Annotee to load. | |
| 363 # | |
| 364 # Annotee can run accept bootstrap parameters through either JSON or | |
| 365 # command-line, but using JSON effectively steps around any sort of command- | |
| 366 # line length limits such as those experienced on Windows. | |
| 367 cmd_json = os.path.join(bootstrap_dir, 'annotee_cmd.json') | |
| 368 with open(cmd_json, 'w') as fd: | |
| 369 json.dump(cmd, fd) | |
| 370 | |
| 371 # Butler Command. | |
| 372 cmd = [ | |
| 373 butler, | |
| 374 '-output', 'pubsub,project="%(project)s",topic="%(topic)s"' % ( | |
| 375 config.logdog_pubsub._asdict()), | |
| 376 ] | |
| 377 cmd += logdog_verbose | |
| 378 cmd += service_account_args | |
| 379 cmd += [ | |
| 380 'run', | |
| 381 '-streamserver-uri', streamserver_uri, | |
| 382 '--', | |
| 383 ] | |
| 384 | |
| 385 # Annotee Command. | |
| 386 cmd += [ | |
| 387 annotee, | |
| 388 '-butler-stream-server', streamserver_uri, | |
| 389 '-json-args-path', cmd_json, | |
| 390 ] | |
| 391 cmd += logdog_verbose | |
| 392 | |
| 393 rv, _ = _run_command(cmd, dry_run=opts.dry_run) | |
| 394 if rv in LOGDOG_ERROR_RETURNCODES: | |
| 395 raise LogDogBootstrapError('LogDog Error (%d)' % (rv,)) | |
| 396 return rv | |
| 397 | |
| 398 | |
| 399 def _assert_logdog_whitelisted(mastername, buildername): | |
| 400 """Asserts that the runtime environment is whitelisted for LogDog bootstrap. | |
| 401 | |
| 402 Args: | |
| 403 mastername (str): The master name string. | |
| 404 buildername (str): The builder name. | |
| 405 Raises: | |
| 406 LogDogNotBootstrapped: if the runtime is not whitelisted. | |
| 407 """ | |
| 408 if not all((mastername, buildername)): | |
| 409 raise LogDogNotBootstrapped('Required mastername/buildername is not set.') | |
| 410 | |
| 411 # Key on mastername. | |
| 412 bdict = LOGDOG_WHITELIST_MASTER_BUILDERS.get(mastername) | |
| 413 if bdict is not None: | |
| 414 # Key on buildername. | |
| 415 if WHITELIST_ALL in bdict or buildername in bdict: | |
| 416 LOGGER.info('Whitelisted master %s, builder %s.', | |
| 417 mastername, buildername) | |
| 418 return | |
| 419 raise LogDogNotBootstrapped('Master %s, builder %s is not whitelisted.' % ( | |
| 420 mastername, buildername)) | |
| 421 | |
| 422 | |
| 147 def get_recipe_properties(workdir, build_properties, | 423 def get_recipe_properties(workdir, build_properties, |
| 148 use_factory_properties_from_disk): | 424 use_factory_properties_from_disk): |
| 149 """Constructs the recipe's properties from buildbot's properties. | 425 """Constructs the recipe's properties from buildbot's properties. |
| 150 | 426 |
| 151 This retrieves the current factory properties from the master_config | 427 This retrieves the current factory properties from the master_config |
| 152 in the slave's checkout (no factory properties are handed to us from the | 428 in the slave's checkout (no factory properties are handed to us from the |
| 153 master), and merges in the build properties. | 429 master), and merges in the build properties. |
| 154 | 430 |
| 155 Using the values from the checkout allows us to do things like change | 431 Using the values from the checkout allows us to do things like change |
| 156 the recipe and other factory properties for a builder without needing | 432 the recipe and other factory properties for a builder without needing |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 help='factory properties in b64 gz JSON format') | 566 help='factory properties in b64 gz JSON format') |
| 291 parser.add_argument('--keep-stdin', action='store_true', default=False, | 567 parser.add_argument('--keep-stdin', action='store_true', default=False, |
| 292 help='don\'t close stdin when running recipe steps') | 568 help='don\'t close stdin when running recipe steps') |
| 293 parser.add_argument('--master-overrides-slave', action='store_true', | 569 parser.add_argument('--master-overrides-slave', action='store_true', |
| 294 help='use the property values given on the command line from the master, ' | 570 help='use the property values given on the command line from the master, ' |
| 295 'not the ones looked up on the slave') | 571 'not the ones looked up on the slave') |
| 296 parser.add_argument('--use-factory-properties-from-disk', | 572 parser.add_argument('--use-factory-properties-from-disk', |
| 297 action='store_true', default=False, | 573 action='store_true', default=False, |
| 298 help='use factory properties loaded from disk on the slave') | 574 help='use factory properties loaded from disk on the slave') |
| 299 | 575 |
| 576 group = parser.add_argument_group('LogDog Bootstrap') | |
| 577 group.add_argument('--logdog-verbose', | |
| 578 action='count', default=0, | |
| 579 help='Increase LogDog verbosity. This can be specified multiple times.') | |
| 580 group.add_argument('--logdog-force', action='store_true', | |
| 581 help='Force LogDog bootstrapping, even if the system is not configured.') | |
| 582 group.add_argument('--logdog-butler-path', | |
| 583 help='Path to the LogDog Butler. If empty, one will be probed/downloaded ' | |
| 584 'from CIPD.') | |
| 585 group.add_argument('--logdog-annotee-path', | |
| 586 help='Path to the LogDog Annotee. If empty, one will be ' | |
| 587 'probed/downloaded from CIPD.') | |
| 588 group.add_argument('--logdog-service-account-json', | |
| 589 help='Path to the service account JSON. If one is not provided, the ' | |
| 590 'local system credentials will be used.') | |
| 591 | |
| 300 return parser.parse_args(argv) | 592 return parser.parse_args(argv) |
| 301 | 593 |
| 302 | 594 |
| 303 def update_scripts(): | 595 def update_scripts(): |
| 304 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): | 596 if os.environ.get('RUN_SLAVE_UPDATED_SCRIPTS'): |
| 305 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') | 597 os.environ.pop('RUN_SLAVE_UPDATED_SCRIPTS') |
| 306 return False | 598 return False |
| 307 | 599 |
| 308 stream = annotator.StructuredAnnotationStream() | 600 stream = annotator.StructuredAnnotationStream() |
| 309 | 601 |
| 310 with stream.step('update_scripts') as s: | 602 with stream.step('update_scripts') as s: |
| 311 gclient_name = 'gclient' | 603 gclient_name = 'gclient' |
| 312 if sys.platform.startswith('win'): | 604 if sys.platform.startswith('win'): |
| 313 gclient_name += '.bat' | 605 gclient_name += '.bat' |
| 314 gclient_path = os.path.join(env.Build, '..', 'depot_tools', | 606 gclient_path = os.path.join(env.Build, os.pardir, 'depot_tools', |
| 315 gclient_name) | 607 gclient_name) |
| 316 gclient_cmd = [gclient_path, 'sync', '--force', '--verbose', '--jobs=2'] | 608 gclient_cmd = [gclient_path, 'sync', '--force', '--verbose', '--jobs=2'] |
| 317 try: | 609 try: |
| 318 fd, output_json = tempfile.mkstemp() | 610 fd, output_json = tempfile.mkstemp() |
| 319 os.close(fd) | 611 os.close(fd) |
| 320 gclient_cmd += ['--output-json', output_json] | 612 gclient_cmd += ['--output-json', output_json] |
| 321 except Exception: | 613 except Exception: |
| 322 # Super paranoia try block. | 614 # Super paranoia try block. |
| 323 output_json = None | 615 output_json = None |
| 324 cmd_dict = { | 616 cmd_dict = { |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 417 ] | 709 ] |
| 418 # Add this conditionally so that we get an error in | 710 # Add this conditionally so that we get an error in |
| 419 # send_monitoring_event log files in case it isn't present. | 711 # send_monitoring_event log files in case it isn't present. |
| 420 if hostname: | 712 if hostname: |
| 421 cmd += ['--build-event-hostname', hostname] | 713 cmd += ['--build-event-hostname', hostname] |
| 422 _check_command(cmd) | 714 _check_command(cmd) |
| 423 except Exception: | 715 except Exception: |
| 424 LOGGER.warning("Failed to send monitoring event.", exc_info=True) | 716 LOGGER.warning("Failed to send monitoring event.", exc_info=True) |
| 425 | 717 |
| 426 | 718 |
| 719 def _exec_recipe(opts, tdir, config, properties): | |
| 720 # Find out if the recipe we intend to run is in build_internal's recipes. If | |
| 721 # so, use recipes.py from there, otherwise use the one from build. | |
| 722 recipe_file = properties['recipe'].replace('/', os.path.sep) + '.py' | |
| 723 | |
| 724 # Use the standard recipe runner unless the recipes are explicitly in the | |
| 725 # "build_limited" repository. | |
| 726 recipe_runner = os.path.join(env.Build, | |
| 727 'scripts', 'slave', 'recipes.py') | |
| 728 if env.BuildInternal: | |
| 729 build_limited = os.path.join(env.BuildInternal, 'scripts', 'slave') | |
| 730 if os.path.exists(os.path.join(build_limited, 'recipes', recipe_file)): | |
| 731 recipe_runner = os.path.join(build_limited, 'recipes.py') | |
| 732 | |
| 733 # Dump properties to JSON and build recipe command. | |
| 734 props_file = os.path.join(tdir, 'recipe_properties.json') | |
| 735 with open(props_file, 'w') as fh: | |
| 736 json.dump(properties, fh) | |
| 737 | |
| 738 cmd = [ | |
| 739 sys.executable, '-u', recipe_runner, | |
| 740 'run', | |
| 741 '--workdir=%s' % os.getcwd(), | |
| 742 '--properties-file=%s' % props_file, | |
| 743 properties['recipe'], | |
| 744 ] | |
| 745 | |
| 746 status = None | |
| 747 try: | |
| 748 if not opts.logdog_force: | |
| 749 _assert_logdog_whitelisted(properties.get('mastername'), | |
|
Vadim Sh.
2016/01/09 01:25:13
I was confused by this... Do not use exception as
dnj (Google)
2016/01/09 02:50:28
Done.
| |
| 750 properties.get('buildername')) | |
| 751 status = _logdog_bootstrap(tdir, config, opts, cmd) | |
| 752 except LogDogNotBootstrapped as e: | |
| 753 LOGGER.info('Not bootstrapped: %s', e.message) | |
| 754 except LogDogBootstrapError as e: | |
| 755 LOGGER.warning('Could not bootstrap LogDog: %s', e.message) | |
| 756 except Exception as e: | |
| 757 LOGGER.exception('Exception while bootstrapping LogDog.') | |
| 758 finally: | |
| 759 if status is None: | |
| 760 LOGGER.info('Not using LogDog. Invoking `recipes.py` directly.') | |
| 761 status, _ = _run_command(cmd, dry_run=opts.dry_run) | |
| 762 | |
| 763 return status | |
| 764 | |
| 765 | |
| 427 def main(argv): | 766 def main(argv): |
| 428 opts = get_args(argv) | 767 opts = get_args(argv) |
| 429 | 768 |
| 430 if opts.verbose == 0: | 769 if opts.verbose == 0: |
| 431 level = logging.INFO | 770 level = logging.INFO |
| 432 else: | 771 else: |
| 433 level = logging.DEBUG | 772 level = logging.DEBUG |
| 434 logging.getLogger().setLevel(level) | 773 logging.getLogger().setLevel(level) |
| 435 | 774 |
| 436 clean_old_recipe_engine() | 775 clean_old_recipe_engine() |
| 437 | 776 |
| 438 # Enter our runtime environment. | 777 # Enter our runtime environment. |
| 439 with recipe_tempdir(leak=opts.leak) as tdir: | 778 with recipe_tempdir(leak=opts.leak) as tdir: |
| 440 LOGGER.debug('Using temporary directory: [%s].', tdir) | 779 LOGGER.debug('Using temporary directory: [%s].', tdir) |
| 441 | 780 |
| 442 # Load factory properties and configuration. | 781 # Load factory properties and configuration. |
| 443 # TODO(crbug.com/551165): remove flag "factory_properties". | 782 # TODO(crbug.com/551165): remove flag "factory_properties". |
| 444 use_factory_properties_from_disk = (opts.use_factory_properties_from_disk or | 783 use_factory_properties_from_disk = (opts.use_factory_properties_from_disk or |
| 445 bool(opts.factory_properties)) | 784 bool(opts.factory_properties)) |
| 446 properties = get_recipe_properties( | 785 properties = get_recipe_properties( |
| 447 tdir, opts.build_properties, use_factory_properties_from_disk) | 786 tdir, opts.build_properties, use_factory_properties_from_disk) |
| 448 LOGGER.debug('Loaded properties: %s', properties) | 787 LOGGER.debug('Loaded properties: %s', properties) |
| 449 | 788 |
| 450 config = get_config() | 789 config = get_config() |
| 451 LOGGER.debug('Loaded runtime configuration: %s', config) | 790 LOGGER.debug('Loaded runtime configuration: %s', config) |
| 452 | 791 |
| 453 # Find out if the recipe we intend to run is in build_internal's recipes. If | |
| 454 # so, use recipes.py from there, otherwise use the one from build. | |
| 455 recipe_file = properties['recipe'].replace('/', os.path.sep) + '.py' | |
| 456 | |
| 457 # Use the standard recipe runner unless the recipes are explicitly in the | |
| 458 # "build_limited" repository. | |
| 459 recipe_runner = os.path.join(env.Build, | |
| 460 'scripts', 'slave', 'recipes.py') | |
| 461 if env.BuildInternal: | |
| 462 build_limited = os.path.join(env.BuildInternal, 'scripts', 'slave') | |
| 463 if os.path.exists(os.path.join(build_limited, 'recipes', recipe_file)): | |
| 464 recipe_runner = os.path.join(build_limited, 'recipes.py') | |
| 465 | |
| 466 # Setup monitoring directory and send a monitoring event. | 792 # Setup monitoring directory and send a monitoring event. |
| 467 build_data_dir = ensure_directory(tdir, 'build_data') | 793 build_data_dir = ensure_directory(tdir, 'build_data') |
| 468 properties['build_data_dir'] = build_data_dir | 794 properties['build_data_dir'] = build_data_dir |
| 469 | 795 |
| 470 # Write our annotated_run.py monitoring event. | 796 # Write our annotated_run.py monitoring event. |
| 471 write_monitoring_event(config, build_data_dir, properties) | 797 write_monitoring_event(config, build_data_dir, properties) |
| 472 | 798 |
| 473 # Dump properties to JSON and build recipe command. | 799 # Execute our recipe. |
| 474 props_file = os.path.join(tdir, 'recipe_properties.json') | 800 return _exec_recipe(opts, tdir, config, properties) |
| 475 with open(props_file, 'w') as fh: | |
| 476 json.dump(properties, fh) | |
| 477 cmd = [ | |
| 478 sys.executable, '-u', recipe_runner, | |
| 479 'run', | |
| 480 '--workdir=%s' % os.getcwd(), | |
| 481 '--properties-file=%s' % props_file, | |
| 482 properties['recipe'], | |
| 483 ] | |
| 484 | |
| 485 status, _ = _run_command(cmd, dry_run=opts.dry_run) | |
| 486 | |
| 487 return status | |
| 488 | 801 |
| 489 | 802 |
| 490 def shell_main(argv): | 803 def shell_main(argv): |
| 491 if update_scripts(): | 804 if update_scripts(): |
| 492 # Re-execute with the updated annotated_run.py. | 805 # Re-execute with the updated annotated_run.py. |
| 493 rv, _ = _run_command([sys.executable] + argv) | 806 rv, _ = _run_command([sys.executable] + argv) |
| 494 return rv | 807 return rv |
| 495 else: | 808 else: |
| 496 return main(argv[1:]) | 809 return main(argv[1:]) |
| 497 | 810 |
| 498 | 811 |
| 499 if __name__ == '__main__': | 812 if __name__ == '__main__': |
| 500 logging.basicConfig(level=logging.INFO) | 813 logging.basicConfig(level=logging.INFO) |
| 501 sys.exit(shell_main(sys.argv)) | 814 sys.exit(shell_main(sys.argv)) |
| OLD | NEW |