| #!/usr/bin/env python | |
| # Copyright 2019 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. | |
| """Custom swarming trigger script for ChromeOS device tests. | |
| CrOS device tests are unique in that the device OS they prefer to run on is | |
| continuously changing. The LKGM file, checked into src at | |
| //chromeos/CHROMEOS_LKGM, represents the ChromeOS version Chrome's ToT aims | |
| to be compatible with. Therefore, a CrOS test for Chrome ideally targets a | |
| device running the LKGM. | |
| Since the LKGM file gets updated frequently (~daily), we can't reasonably | |
| hardcode the LKGM in the test specs. So this special trigger script will read | |
| the current LKGM (at the time of trigger) and append that to the task's | |
| dimensions. If such a device isn't available in time, the task will fallback | |
| to one running any OS. | |
| """ | |
| import argparse | |
| import os | |
| import re | |
| import sys | |
| import base_test_triggerer | |
| SRC_DIR = os.path.dirname( | |
| os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| LKGM_FILE_PATH = os.path.join(SRC_DIR, 'chromeos', 'CHROMEOS_LKGM') | |
| # Should match something that looks like "12345.0.0". | |
| LKGM_RE = re.compile(r'\d+\.\d+\.\d+') | |
| PRIMARY_SLICE_EXPIRATION_S = 300 | |
| def read_current_lkgm(): | |
| if not os.path.exists(LKGM_FILE_PATH): | |
| sys.stderr.write('LKGM file not present at %s\n' % LKGM_FILE_PATH) | |
| return None | |
| with open(LKGM_FILE_PATH) as f: | |
| lkgm = f.read().strip() | |
| if not LKGM_RE.match(lkgm): | |
| sys.stderr.write('Unknown format of LKGM: %s\n' % lkgm) | |
| return None | |
| # Just the major version should be sufficient. | |
| return lkgm.split('.')[0] | |
| def parse_args(triggerer): | |
| # This script will do nothing but inspect and tweak the dimension args to | |
| # `swarming.py trigger`. So let's pull just those out. | |
| parser = argparse.ArgumentParser(description=__doc__) | |
| parser.add_argument( | |
| '-d', | |
| '--dimension', | |
| default=[], | |
| action='append', | |
| nargs=2, | |
| dest='dimensions', | |
| help= | |
| 'Dimensions to filter on. Duplicated from the `swarming.py trigger` ' | |
| 'command. Parsed here to ensure `device_os` is not added.') | |
| parser.add_argument( | |
| '--optional-dimension', | |
| default=[], | |
| action='append', | |
| nargs=3, | |
| dest='optional_dimensions', | |
| help='Optional dimensions which will result in additional task slices. ' | |
| 'Duplicated from the `swarming.py trigger` command.') | |
| base_test_triggerer.BaseTestTriggerer.setup_parser_contract(parser) | |
| args, additional_args = parser.parse_known_args() | |
| additional_args = triggerer.modify_args(additional_args, 0, | |
| args.shard_index, args.shards, | |
| args.dump_json) | |
| if additional_args[0] != 'trigger': | |
| parser.error( | |
| 'This script is only supported for `swarming.py trigger`' | |
| ' invocations.' | |
| ) | |
| for k, _ in args.dimensions: | |
| if k == 'device_os': | |
| parser.error( | |
| 'Must not specify the device_os dimension when using this' | |
| ' script. (It will be added automatically.)') | |
| # It might be a valid use-case to include optional-dimensions in the initial | |
| # invocation. But it'd be difficult to integrate them into what we're doing | |
| # here. So let's just ensure there aren't any. | |
| if args.optional_dimensions: | |
| parser.error( | |
| 'Must not specify optional dimensions when using this script.') | |
| return args, additional_args | |
| def main(): | |
| triggerer = base_test_triggerer.BaseTestTriggerer() | |
| args, additional_args = parse_args(triggerer) | |
| current_lkgm = read_current_lkgm() | |
| if not current_lkgm: | |
| return 1 | |
| new_args = additional_args[:1] | |
| # Insert our modified dimension args in between the 1st and 2nd args of the | |
| # initial `swarming.py` invocation. This avoids the presence of the special | |
| # `--` arg from causing swarming.py to ignore them. | |
| needs_device_status = True | |
| for k, v in args.dimensions: | |
| new_args.extend(['--dimension', k, v]) | |
| if k == 'device_status': | |
| needs_device_status = False | |
| # Only CrOS device bots with a device_status dimension of "available" should | |
| # run tests. So target those explicitly if we aren't already. | |
| if needs_device_status: | |
| new_args.extend(['--dimension', 'device_status', 'available']) | |
| new_args.extend([ | |
| '-optional-dimension', | |
| 'device_os=%s:%d' % (current_lkgm, PRIMARY_SLICE_EXPIRATION_S), | |
| ]) | |
| new_args += additional_args[1:] | |
| return triggerer.run_swarming_go(new_args, args.dump_json, | |
| args.shard_index or 0, args.shards) | |
| if __name__ == '__main__': | |
| sys.exit(main()) |