chromeos_device_trigger.py 4.7 KB

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