| #!/usr/bin/env python3 |
| # Copyright 2019 The Chromium Authors |
| # 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()) |