Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(679)

Side by Side Diff: scripts/slave/gatekeeper_ng_config.py

Issue 1789693003: Actually handle booleans properly in gatekeeper. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Update documentation. Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2014 The Chromium Authors. All rights reserved. 2 # Copyright 2014 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Loads gatekeeper configuration files for use with gatekeeper_ng.py. 6 """Loads gatekeeper configuration files for use with gatekeeper_ng.py.
7 7
8 The gatekeeper json configuration file has two main sections: 'masters' 8 The gatekeeper json configuration file has two main sections: 'masters'
9 and 'categories.' The following shows the breakdown of a possible config, 9 and 'categories.' The following shows the breakdown of a possible config,
10 but note that all nodes are optional (including the root 'masters' and 10 but note that all nodes are optional (including the root 'masters' and
11 'categories' nodes). 11 'categories' nodes).
12 12
13 A builder ultimately needs 4 lists (sets): 13 A builder ultimately needs 4 lists (sets):
14 closing_steps: steps which close the tree on failure or omission 14 closing_steps: steps which close the tree on failure or omission
15 forgiving_steps: steps which close the tree but don't email committers 15 forgiving_steps: steps which close the tree but don't email committers
16 tree_notify: any additional emails to notify on tree failure 16 tree_notify: any additional emails to notify on tree failure
17 sheriff_classes: classes of sheriffs to notify on build failure 17 sheriff_classes: classes of sheriffs to notify on build failure
18 18
19 Builders can inherit these properties from categories, they can inherit 19 Builders can inherit these properties from categories, they can inherit
20 tree_notify and sheriff_classes from their master, and they can have these 20 tree_notify and sheriff_classes from their master, and they can have these
21 properties assigned in the builder itself. Any property not specified 21 properties assigned in the builder itself. Any property not specified
22 is considered blank (empty set), and inheritance is always constructive (you 22 is considered blank (empty set), and inheritance is always constructive (you
23 can't remove a property by inheriting or overwriting it). Builders can inherit 23 can't remove a property by inheriting or overwriting it). Builders can inherit
24 categories from their master. 24 categories from their master.
25 25
26 A master consists of zero or more sections, which specify which builders are 26 A master consists of zero or more sections, which specify which builders are
27 watched by the section and what action should be taken. A section can specify 27 watched by the section and what action should be taken.
28 tree_closing to be false, which causes the section to only send out emails
29 instead of closing the tree. A section or builder can also specify to respect
30 a build's failure status with respect_build_status.
31 28
32 The 'excluded_builders' key is a list of builder names that will not be 29 The 'excluded_builders' key is a list of builder names that will not be
33 processed even if they match a configuration. This is useful when the builder 30 processed even if they match a configuration. This is useful when the builder
34 set is specified using the wildcard ('*'). Entries in this list may use 31 set is specified using the wildcard ('*'). Entries in this list may use
35 filename-style globbing (e.g., *mybuilder*) to specify builder name patterns. 32 filename-style globbing (e.g., *mybuilder*) to specify builder name patterns.
36 33
37 The 'subject_template' key is the template used for the email subjects. Its 34 The 'subject_template' key is the template used for the email subjects. Its
38 formatting arguments are found at https://chromium.googlesource.com/chromium/ 35 formatting arguments are found at https://chromium.googlesource.com/chromium/
39 tools/chromium-build/+/master/gatekeeper_mailer.py, but the list is 36 tools/chromium-build/+/master/gatekeeper_mailer.py, but the list is
40 reproduced here: 37 reproduced here:
(...skipping 12 matching lines...) Expand all
53 'forgive_all' converts all closing_steps to be forgiving_steps. Since 50 'forgive_all' converts all closing_steps to be forgiving_steps. Since
54 forgiving_steps only email sheriffs + watchlist (not the committer), this is a 51 forgiving_steps only email sheriffs + watchlist (not the committer), this is a
55 great way to set up experimental or informational builders without spamming 52 great way to set up experimental or informational builders without spamming
56 people. It is enabled by providing the string 'true'. 53 people. It is enabled by providing the string 'true'.
57 54
58 'forgiving_optional' and 'closing_optional' work just like 'forgiving_steps' 55 'forgiving_optional' and 'closing_optional' work just like 'forgiving_steps'
59 and 'closing_steps', but they won't close if the step is missing. This is like 56 and 'closing_steps', but they won't close if the step is missing. This is like
60 previous gatekeeper behavior. They can be set to '*', which will match all 57 previous gatekeeper behavior. They can be set to '*', which will match all
61 steps in the builder. 58 steps in the builder.
62 59
60 'respect_build_status' means to use the buildbot result of the entire build
61 as an additional way to close the tree. As an example, if a build's closing
62 steps succeeded but the overall build result was FAILURE, the tree would
63 close if respect_build_status is set to True. respect_build_status only checks
64 for FAILURE, not any of the other statuses (including EXCEPTION). A build
65 status of SUCCESS will not override failing closing or forgiving steps.
66 respect_build_status is a boolean (true or false in JSON) and defaults to
67 False.
Dirk Pranke 2016/03/16 22:29:52 This seems kinda confusing (the concept, not the d
ghost stip (do not use) 2016/03/16 22:56:16 it's the return code of the build. think of what y
68
69 'close_tree' allows masters or builders to disable the --set-status option
70 set in gatekeeper_trees.json. In particular, this would be useful for a specific
71 builder on a tree-closing master which should notify the blamelist about
72 failures but should not close the tree. close_tree is a boolean (true or false
73 in JSON) and defaults to True.
74
63 Note that if a builder sets something as forgiving_optional which is set as 75 Note that if a builder sets something as forgiving_optional which is set as
64 closing_optional in the master config, this value will be removed from 76 closing_optional in the master config, this value will be removed from
65 closing_optional. This allows builders to override master configuration values. 77 closing_optional. This allows builders to override master configuration values.
66 78
67 The 'comment' key can be put anywhere and is ignored by the parser. 79 The 'comment' key can be put anywhere and is ignored by the parser.
68 80
69 # Python, not JSON. 81 # Python, not JSON.
70 { 82 {
71 'masters': { 83 'masters': {
72 'http://build.chromium.org/p/chromium.win': [ 84 'http://build.chromium.org/p/chromium.win': [
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 135
124 DATA_DIR = os.path.dirname(os.path.abspath(__file__)) 136 DATA_DIR = os.path.dirname(os.path.abspath(__file__))
125 137
126 138
127 # Keys which have defaults besides None or set([]). 139 # Keys which have defaults besides None or set([]).
128 DEFAULTS = { 140 DEFAULTS = {
129 'status_template': ('Tree is closed (Automatic: "%(unsatisfied)s" on ' 141 'status_template': ('Tree is closed (Automatic: "%(unsatisfied)s" on '
130 '"%(builder_name)s" %(blamelist)s)'), 142 '"%(builder_name)s" %(blamelist)s)'),
131 'subject_template': ('buildbot %(result)s in %(project_name)s on ' 143 'subject_template': ('buildbot %(result)s in %(project_name)s on '
132 '%(builder_name)s, revision %(revision)s'), 144 '%(builder_name)s, revision %(revision)s'),
145 'respect_build_status': False,
133 } 146 }
134 147
135 148
136 def allowed_keys(test_dict, *keys): 149 def allowed_keys(test_dict, *keys):
137 keys = keys + ('comment',) 150 keys = keys + ('comment',)
138 assert all(k in keys for k in test_dict), ( 151 assert all(k in keys for k in test_dict), (
139 'not valid: %s; allowed: %s' % ( 152 'not valid: %s; allowed: %s' % (
140 ', '.join(set(test_dict.keys()) - set(keys)), 153 ', '.join(set(test_dict.keys()) - set(keys)),
141 ', '.join(keys))) 154 ', '.join(keys)))
142 155
(...skipping 22 matching lines...) Expand all
165 if union: 178 if union:
166 raise ValueError( 179 raise ValueError(
167 "The builder categories have conflicting entries %s for keys %s " 180 "The builder categories have conflicting entries %s for keys %s "
168 "and %s." % (union, k, v)) 181 "and %s." % (union, k, v))
169 182
170 183
171 def load_gatekeeper_config(filename): 184 def load_gatekeeper_config(filename):
172 """Loads and verifies config json, constructs builder config dict.""" 185 """Loads and verifies config json, constructs builder config dict."""
173 186
174 # Keys which are allowed in a master or builder section. 187 # Keys which are allowed in a master or builder section.
175 master_keys = ['excluded_builders', 188 master_keys = ['close_tree',
189 'excluded_builders',
176 'excluded_steps', 190 'excluded_steps',
177 'forgive_all', 191 'forgive_all',
192 'respect_build_status',
178 'sheriff_classes', 193 'sheriff_classes',
179 'status_template', 194 'status_template',
180 'subject_template', 195 'subject_template',
181 'tree_notify', 196 'tree_notify',
182 ] 197 ]
183 198
184 builder_keys = ['closing_optional', 199 builder_keys = ['close_tree',
200 'closing_optional',
185 'closing_steps', 201 'closing_steps',
186 'excluded_builders', 202 'excluded_builders',
187 'excluded_steps', 203 'excluded_steps',
188 'forgive_all', 204 'forgive_all',
189 'forgiving_optional', 205 'forgiving_optional',
190 'forgiving_steps', 206 'forgiving_steps',
207 'respect_build_status',
191 'sheriff_classes', 208 'sheriff_classes',
192 'status_template', 209 'status_template',
193 'subject_template', 210 'subject_template',
194 'tree_notify', 211 'tree_notify',
195 ] 212 ]
196 213
197 # These keys are strings instead of sets. Strings can't be merged, 214 # These keys are strings instead of sets. Strings can't be merged,
198 # so more specific (master -> category -> builder) strings clobber 215 # so more specific (master -> category -> builder) strings clobber
199 # more generic ones. 216 # more generic ones.
200 strings = ['forgive_all', 'status_template', 'subject_template'] 217 strings = ['forgive_all', 'status_template', 'subject_template']
201 218
219 # Bools also share the 'strings' clobbering logic.
220 bools = ['close_tree', 'respect_build_status']
221
202 with open(filename) as f: 222 with open(filename) as f:
203 raw_gatekeeper_config = json.load(f) 223 raw_gatekeeper_config = json.load(f)
204 224
205 allowed_keys(raw_gatekeeper_config, 'categories', 'masters') 225 allowed_keys(raw_gatekeeper_config, 'categories', 'masters')
206 226
207 categories = raw_gatekeeper_config.get('categories', {}) 227 categories = raw_gatekeeper_config.get('categories', {})
208 masters = raw_gatekeeper_config.get('masters', {}) 228 masters = raw_gatekeeper_config.get('masters', {})
209 229
210 for category in categories.values(): 230 for category in categories.values():
211 allowed_keys(category, *builder_keys) 231 allowed_keys(category, *builder_keys)
212 232
213 gatekeeper_config = {} 233 gatekeeper_config = {}
214 for master_url, master_sections in masters.iteritems(): 234 for master_url, master_sections in masters.iteritems():
215 for master_section in master_sections: 235 for master_section in master_sections:
216 gatekeeper_config.setdefault(master_url, []).append({}) 236 gatekeeper_config.setdefault(master_url, []).append({})
217 allowed_keys(master_section, 'builders', 'categories', 'close_tree', 237 allowed_keys(master_section, 'builders', 'categories', *master_keys)
218 'respect_build_status', *master_keys)
219 238
220 builders = master_section.get('builders', {}) 239 builders = master_section.get('builders', {})
221 for buildername, builder in builders.iteritems(): 240 for buildername, builder in builders.iteritems():
222 allowed_keys(builder, 'categories', *builder_keys) 241 allowed_keys(builder, 'categories', *builder_keys)
223 for key, item in builder.iteritems(): 242 for key, item in builder.iteritems():
224 if key in strings: 243 if key in strings:
225 assert isinstance(item, basestring) 244 assert isinstance(item, basestring)
245 elif key in bools:
246 assert isinstance(item, bool)
226 else: 247 else:
227 assert isinstance(item, list) 248 assert isinstance(item, list)
228 assert all(isinstance(elem, basestring) for elem in item) 249 assert all(isinstance(elem, basestring) for elem in item)
229 250
230 gatekeeper_config[master_url][-1].setdefault(buildername, {}) 251 gatekeeper_config[master_url][-1].setdefault(buildername, {})
231 gatekeeper_builder = gatekeeper_config[master_url][-1][buildername] 252 gatekeeper_builder = gatekeeper_config[master_url][-1][buildername]
232 253
233 # Populate with specified defaults. 254 # Populate with specified defaults.
234 for k in builder_keys: 255 for k in builder_keys:
235 if k in DEFAULTS: 256 if k in DEFAULTS:
236 gatekeeper_builder.setdefault(k, DEFAULTS[k]) 257 gatekeeper_builder.setdefault(k, DEFAULTS[k])
237 elif k in strings: 258 elif k in strings:
238 gatekeeper_builder.setdefault(k, '') 259 gatekeeper_builder.setdefault(k, '')
260 elif k in bools:
261 gatekeeper_builder.setdefault(k, True)
239 else: 262 else:
240 gatekeeper_builder.setdefault(k, set()) 263 gatekeeper_builder.setdefault(k, set())
241 264
242 # Inherit any values from the master. 265 # Inherit any values from the master.
243 for k in master_keys: 266 for k in master_keys:
244 if k in strings: 267 if k in strings or k in bools:
245 if k in master_section: 268 if k in master_section:
246 gatekeeper_builder[k] = master_section[k] 269 gatekeeper_builder[k] = master_section[k]
247 else: 270 else:
248 gatekeeper_builder[k] |= set(master_section.get(k, [])) 271 gatekeeper_builder[k] |= set(master_section.get(k, []))
249 272
250 gatekeeper_builder['close_tree'] = master_section.get('close_tree',
251 True)
252 gatekeeper_builder['respect_build_status'] = master_section.get(
253 'respect_build_status', False)
254
255 # Inherit any values from the categories. 273 # Inherit any values from the categories.
256 for c in master_section.get('categories', []): 274 for c in master_section.get('categories', []):
257 for k in builder_keys: 275 for k in builder_keys:
258 if k in strings: 276 if k in strings or k in bools:
259 if k in categories[c]: 277 if k in categories[c]:
260 gatekeeper_builder[k] = categories[c][k] 278 gatekeeper_builder[k] = categories[c][k]
261 else: 279 else:
262 gatekeeper_builder[k] |= set(categories[c].get(k, [])) 280 gatekeeper_builder[k] |= set(categories[c].get(k, []))
263 281
264 special_keys = { 282 special_keys = {
265 'forgiving': 'closing', 283 'forgiving': 'closing',
266 'forgiving_optional': 'closing_optional', 284 'forgiving_optional': 'closing_optional',
267 } 285 }
268 286
269 check_builder_conflicts( 287 check_builder_conflicts(
270 special_keys, builder.get('categories', []), categories) 288 special_keys, builder.get('categories', []), categories)
271 289
272 for c in builder.get('categories', []): 290 for c in builder.get('categories', []):
273 for k in builder_keys: 291 for k in builder_keys:
274 if k in strings: 292 if k in strings or k in bools:
275 if k in categories[c]: 293 if k in categories[c]:
276 gatekeeper_builder[k] = categories[c][k] 294 gatekeeper_builder[k] = categories[c][k]
277 else: 295 else:
278 gatekeeper_builder[k] |= set(categories[c].get(k, [])) 296 gatekeeper_builder[k] |= set(categories[c].get(k, []))
279 297
280 # If we're forgiving something in the builder that we set as 298 # If we're forgiving something in the builder that we set as
281 # closing in the master config, then don't close on it. Builders 299 # closing in the master config, then don't close on it. Builders
282 # can override master configurations. 300 # can override master configurations.
283 for key, key_to_modify in special_keys.items(): 301 for key, key_to_modify in special_keys.items():
284 if key_to_modify in gatekeeper_builder: 302 if key_to_modify in gatekeeper_builder:
285 gatekeeper_builder[key_to_modify] -= set( 303 gatekeeper_builder[key_to_modify] -= set(
286 gatekeeper_builder.get(key, [])) 304 gatekeeper_builder.get(key, []))
287 305
288 # Add in any builder-specific values. 306 # Add in any builder-specific values.
289 for k in builder_keys: 307 for k in builder_keys:
290 if k in strings: 308 if k in strings or k in bools:
291 if k in builder: 309 if k in builder:
292 gatekeeper_builder[k] = builder[k] 310 gatekeeper_builder[k] = builder[k]
293 else: 311 else:
294 gatekeeper_builder[k] |= set(builder.get(k, [])) 312 gatekeeper_builder[k] |= set(builder.get(k, []))
295 313
296 # Builder postprocessing. 314 # Builder postprocessing.
297 if gatekeeper_builder['forgive_all'] == 'true': 315 if gatekeeper_builder['forgive_all'] == 'true':
298 gatekeeper_builder['forgiving_steps'] |= gatekeeper_builder[ 316 gatekeeper_builder['forgiving_steps'] |= gatekeeper_builder[
299 'closing_steps'] 317 'closing_steps']
300 gatekeeper_builder['forgiving_optional'] |= gatekeeper_builder[ 318 gatekeeper_builder['forgiving_optional'] |= gatekeeper_builder[
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after
395 gatekeeper_config = inject_hashes(gatekeeper_config) 413 gatekeeper_config = inject_hashes(gatekeeper_config)
396 414
397 flatten_to_json(gatekeeper_config, sys.stdout) 415 flatten_to_json(gatekeeper_config, sys.stdout)
398 print 416 print
399 417
400 return 0 418 return 0
401 419
402 420
403 if __name__ == '__main__': 421 if __name__ == '__main__':
404 sys.exit(main()) 422 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698