blob: c434f8d793e04a4ff871ecfa4f3f092558963e3c [file] [log] [blame]
xixuand7b7b7b2017-08-15 15:07:35 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for CrOS ToT manager."""
6
7import re
8
9import build_lib
10import constants
11import rest_client
12
13
14def check_branch_specs(branch_specs, tot_manager):
15 """Make sure entries in the branch_specs list are correctly formed.
16
17 It accepts any of BARE_BRANCHES in |branch_specs|, one numeric string of
18 the form '>=RXX' or '==RXX', where 'RXX' is a CrOS milestone number, or
19 one numeric string of the form '**tot**', like '>=tot-2'.
20
21 Args:
22 branch_specs: an iterable of branch specifiers, like ['>=tot-1'].
23 tot_manager: a TotMilestoneManager to convert tot string.
24
25 Returns:
26 True if branch_specs check is passed.
27
28 Raises:
29 ValueError: when parsing non-valid branch_spec or there're more than one
30 numeric string in branch_specs.
31 """
32 only_one_numeric_constraint = False
33 for branch in branch_specs:
34 if branch in build_lib.BARE_BRANCHES:
35 continue
36
37 if not only_one_numeric_constraint:
38 # '<=' is dropped
39 if branch.startswith('>=R') or branch.startswith('==R'):
40 only_one_numeric_constraint = True
41 elif 'tot' in branch:
42 tot_manager.convert_tot_spec(branch[branch.index('tot'):])
43 only_one_numeric_constraint = True
44
45 if only_one_numeric_constraint:
46 continue
47
48 raise ValueError("%s isn't a valid branch spec." % branch)
49
50 return True
51
52
53class TotMilestoneManager(object):
54 """A class capable of converting tot string to milestone numbers.
55
56 This class is used as a cache for the tot milestone, so we don't
57 repeatedly hit google storage for all O(100) tasks in suite
58 scheduler's ini file.
59 """
60
61 def __init__(self, is_sanity=False):
62 """Initialize a manager for getting/calculating the Tot milestone.
63
64 Args:
65 is_sanity: True if suite_scheduler is running for sanity check.
66 When it's set to True, the code won't make gsutil call to
67 get the actual tot milestone to avoid dependency on the
68 installation of gsutil to run sanity check. By default it's
69 False.
70 """
71 self.is_sanity = is_sanity
Xinan Lin33937d62020-04-14 14:41:23 -070072 if not self.is_sanity:
73 self.storage_client = rest_client.StorageRestClient(
74 rest_client.BaseRestClient(
75 constants.RestClient.STORAGE_CLIENT.scopes,
76 constants.RestClient.STORAGE_CLIENT.service_name,
77 constants.RestClient.STORAGE_CLIENT.service_version))
xixuand7b7b7b2017-08-15 15:07:35 -070078 self.tot = self._tot_milestone()
79
80 def convert_tot_spec(self, tot_spec):
81 """Convert a tot spec to the appropriate milestone.
82
Xinan Lined57b182020-04-14 14:00:17 -070083 The valid lag number scope is [1,9].
xixuand7b7b7b2017-08-15 15:07:35 -070084 Assume tot is R40:
85 tot -> R40
86 tot-1 -> R39
87 tot-2 -> R38
Xinan Lined57b182020-04-14 14:00:17 -070088 ...
89 tot-9 -> R31
90 tot-(any other numbers) -> Raise a ValueError.
xixuand7b7b7b2017-08-15 15:07:35 -070091
92 With the last option one assumes that a malformed configuration
93 that has 'tot' in it, wants at least tot.
94
95 Args:
96 tot_spec: A string representing the tot spec.
97
98 Returns:
99 A milestone string, like 'R40'.
100
101 Raises:
102 ValueError: If the tot_spec doesn't match the expected format.
103 """
104 tot_spec = tot_spec.lower()
Xinan Lined57b182020-04-14 14:00:17 -0700105 match = re.match('(tot)[-]?([1-9]?)$', tot_spec)
xixuand7b7b7b2017-08-15 15:07:35 -0700106 if not match:
107 raise ValueError('%s is not a valid branch spec.' % tot_spec)
108
109 tot_mstone = self.tot
110 num_back = match.groups()[1]
111 if num_back:
112 tot_mstone_num = tot_mstone.lstrip('R')
113 tot_mstone = tot_mstone.replace(
114 tot_mstone_num, str(int(tot_mstone_num) - int(num_back)))
115
116 return tot_mstone
117
118 def _tot_milestone(self):
119 """Get the tot milestone, eg: R40.
120
121 Returns:
122 A string representing the Tot milestone.
123
124 Raises:
125 build_lib.NoBuildError: if no milestone is found.
126 """
127 if self.is_sanity:
128 return 'R40'
129
130 response = build_lib.get_latest_cros_build_from_gs(
131 self.storage_client)
132 return response.split('-')[0]