Wenbin Zhang | a3801ed | 2022-05-04 21:49:15 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
Avi Drissman | dfd88085 | 2022-09-15 20:11:09 | [diff] [blame] | 2 | # Copyright 2019 The Chromium Authors |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 5 | """Custom swarming trigger script for ChromeOS device tests. |
| 6 | |
| 7 | CrOS device tests are unique in that the device OS they prefer to run on is |
| 8 | continuously changing. The LKGM file, checked into src at |
| 9 | //chromeos/CHROMEOS_LKGM, represents the ChromeOS version Chrome's ToT aims |
| 10 | to be compatible with. Therefore, a CrOS test for Chrome ideally targets a |
| 11 | device running the LKGM. |
| 12 | |
| 13 | Since the LKGM file gets updated frequently (~daily), we can't reasonably |
| 14 | hardcode the LKGM in the test specs. So this special trigger script will read |
| 15 | the current LKGM (at the time of trigger) and append that to the task's |
| 16 | dimensions. If such a device isn't available in time, the task will fallback |
| 17 | to one running any OS. |
| 18 | """ |
| 19 | |
| 20 | import argparse |
| 21 | import os |
| 22 | import re |
| 23 | import sys |
| 24 | |
| 25 | import base_test_triggerer |
| 26 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 27 | SRC_DIR = os.path.dirname( |
| 28 | os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 29 | LKGM_FILE_PATH = os.path.join(SRC_DIR, 'chromeos', 'CHROMEOS_LKGM') |
| 30 | # Should match something that looks like "12345.0.0". |
| 31 | LKGM_RE = re.compile(r'\d+\.\d+\.\d+') |
Ben Pastene | fdd4a77 | 2020-07-20 22:38:20 | [diff] [blame] | 32 | PRIMARY_SLICE_EXPIRATION_S = 300 |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 33 | |
| 34 | |
| 35 | def read_current_lkgm(): |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 36 | if not os.path.exists(LKGM_FILE_PATH): |
| 37 | sys.stderr.write('LKGM file not present at %s\n' % LKGM_FILE_PATH) |
| 38 | return None |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 39 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 40 | with open(LKGM_FILE_PATH) as f: |
| 41 | lkgm = f.read().strip() |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 42 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 43 | if not LKGM_RE.match(lkgm): |
| 44 | sys.stderr.write('Unknown format of LKGM: %s\n' % lkgm) |
| 45 | return None |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 46 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 47 | # Just the major version should be sufficient. |
| 48 | return lkgm.split('.')[0] |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 49 | |
| 50 | |
Ben Pastene | 789bdca | 2019-02-26 18:22:01 | [diff] [blame] | 51 | def parse_args(triggerer): |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 52 | # This script will do nothing but inspect and tweak the dimension args to |
| 53 | # `swarming.py trigger`. So let's pull just those out. |
| 54 | parser = argparse.ArgumentParser(description=__doc__) |
| 55 | parser.add_argument( |
| 56 | '-d', |
| 57 | '--dimension', |
| 58 | default=[], |
| 59 | action='append', |
| 60 | nargs=2, |
| 61 | dest='dimensions', |
Ben Pastene | b5c6726 | 2024-05-15 21:24:01 | [diff] [blame] | 62 | help='Dimensions to filter on. Duplicated from the `swarming.py ' |
| 63 | 'trigger` command. Parsed here to ensure `device_os` is not added.') |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 64 | parser.add_argument( |
| 65 | '--optional-dimension', |
| 66 | default=[], |
| 67 | action='append', |
| 68 | nargs=3, |
| 69 | dest='optional_dimensions', |
| 70 | help='Optional dimensions which will result in additional task slices. ' |
| 71 | 'Duplicated from the `swarming.py trigger` command.') |
| 72 | base_test_triggerer.BaseTestTriggerer.setup_parser_contract(parser) |
| 73 | args, additional_args = parser.parse_known_args() |
| 74 | additional_args = triggerer.modify_args(additional_args, 0, |
| 75 | args.shard_index, args.shards, |
| 76 | args.dump_json) |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 77 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 78 | if additional_args[0] != 'trigger': |
Ben Pastene | b5c6726 | 2024-05-15 21:24:01 | [diff] [blame] | 79 | parser.error('This script is only supported for `swarming.py trigger`' |
| 80 | ' invocations.') |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 81 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 82 | for k, _ in args.dimensions: |
| 83 | if k == 'device_os': |
| 84 | parser.error( |
| 85 | 'Must not specify the device_os dimension when using this' |
| 86 | ' script. (It will be added automatically.)') |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 87 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 88 | # It might be a valid use-case to include optional-dimensions in the initial |
| 89 | # invocation. But it'd be difficult to integrate them into what we're doing |
| 90 | # here. So let's just ensure there aren't any. |
| 91 | if args.optional_dimensions: |
| 92 | parser.error( |
| 93 | 'Must not specify optional dimensions when using this script.') |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 94 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 95 | return args, additional_args |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 96 | |
| 97 | |
| 98 | def main(): |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 99 | triggerer = base_test_triggerer.BaseTestTriggerer() |
| 100 | args, additional_args = parse_args(triggerer) |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 101 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 102 | current_lkgm = read_current_lkgm() |
| 103 | if not current_lkgm: |
| 104 | return 1 |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 105 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 106 | new_args = additional_args[:1] |
| 107 | # Insert our modified dimension args in between the 1st and 2nd args of the |
| 108 | # initial `swarming.py` invocation. This avoids the presence of the special |
| 109 | # `--` arg from causing swarming.py to ignore them. |
| 110 | needs_device_status = True |
| 111 | for k, v in args.dimensions: |
| 112 | new_args.extend(['--dimension', k, v]) |
| 113 | if k == 'device_status': |
| 114 | needs_device_status = False |
Ben Pastene | 4104178 | 2019-02-16 04:21:58 | [diff] [blame] | 115 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 116 | # Only CrOS device bots with a device_status dimension of "available" should |
| 117 | # run tests. So target those explicitly if we aren't already. |
| 118 | if needs_device_status: |
| 119 | new_args.extend(['--dimension', 'device_status', 'available']) |
Ben Pastene | 4104178 | 2019-02-16 04:21:58 | [diff] [blame] | 120 | |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 121 | new_args.extend([ |
| 122 | '-optional-dimension', |
| 123 | 'device_os=%s:%d' % (current_lkgm, PRIMARY_SLICE_EXPIRATION_S), |
| 124 | ]) |
| 125 | new_args += additional_args[1:] |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 126 | |
Ben Pastene | b5c6726 | 2024-05-15 21:24:01 | [diff] [blame] | 127 | return triggerer.run_swarming_go(new_args, args.dump_json, args.shard_index |
| 128 | or 0, args.shards) |
Ben Pastene | a9e583b | 2019-01-16 02:57:26 | [diff] [blame] | 129 | |
| 130 | |
| 131 | if __name__ == '__main__': |
Takuto Ikuta | c8ebda3 | 2021-06-28 15:28:17 | [diff] [blame] | 132 | sys.exit(main()) |