OLD | NEW |
1 # Copyright 2014 The Chromium Authors. All rights reserved. | 1 # Copyright 2014 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 from slave import recipe_api | 5 from slave import recipe_api |
6 | 6 |
7 | 7 |
8 # Minimally supported version of swarming.py script (reported by --version). | 8 # Minimally supported version of swarming.py script (reported by --version). |
9 MINIMAL_SWARMING_VERSION = (0, 4, 8) | 9 MINIMAL_SWARMING_VERSION = (0, 4, 8) |
10 | 10 |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 x.startswith('--test-launcher-summary-output=') for x in extra_args) | 201 x.startswith('--test-launcher-summary-output=') for x in extra_args) |
202 if bad_args: # pragma: no cover | 202 if bad_args: # pragma: no cover |
203 raise ValueError('--test-launcher-summary-output should not be used.') | 203 raise ValueError('--test-launcher-summary-output should not be used.') |
204 | 204 |
205 # Append it. output.json name is expected by collect_gtest_task.py. | 205 # Append it. output.json name is expected by collect_gtest_task.py. |
206 extra_args.append( | 206 extra_args.append( |
207 '--test-launcher-summary-output=${ISOLATED_OUTDIR}/output.json') | 207 '--test-launcher-summary-output=${ISOLATED_OUTDIR}/output.json') |
208 | 208 |
209 # Make a task, configure it to be collected through shim script. | 209 # Make a task, configure it to be collected through shim script. |
210 task = self.task(title, isolated_hash, make_unique, shards, extra_args) | 210 task = self.task(title, isolated_hash, make_unique, shards, extra_args) |
211 task.collect_step_builder = lambda *args: ( | 211 task.collect_step_builder = lambda *args, **kwargs: ( |
212 self._gtest_collect_step(test_launcher_summary_output, *args)) | 212 self._gtest_collect_step(test_launcher_summary_output, *args, **kwargs)) |
213 return task | 213 return task |
214 | 214 |
215 def check_client_version(self): | 215 def check_client_version(self): |
216 """Yields steps to verify compatibility with swarming_client version.""" | 216 """Yields steps to verify compatibility with swarming_client version.""" |
217 yield self.m.swarming_client.ensure_script_version( | 217 yield self.m.swarming_client.ensure_script_version( |
218 'swarming.py', MINIMAL_SWARMING_VERSION) | 218 'swarming.py', MINIMAL_SWARMING_VERSION) |
219 | 219 |
220 def trigger(self, tasks): | 220 def trigger(self, tasks, **kwargs): |
221 """Asynchronously launches a set of tasks on Swarming. | 221 """Asynchronously launches a set of tasks on Swarming. |
222 | 222 |
223 This steps justs posts the tasks and immediately returns. Use 'collect' to | 223 This steps justs posts the tasks and immediately returns. Use 'collect' to |
224 wait for a task to finish and grab its result. | 224 wait for a task to finish and grab its result. |
225 | 225 |
226 Args: | 226 Args: |
227 tasks: an enumerable of SwarmingTask instances. | 227 tasks: an enumerable of SwarmingTask instances. |
| 228 kwargs: passed to recipe step constructor as-is, may contain |
| 229 always_run, can_fail_build, etc. |
228 """ | 230 """ |
229 # TODO(vadimsh): Trigger multiple tasks as a single step. | 231 # TODO(vadimsh): Trigger multiple tasks as a single step. |
230 assert all(isinstance(t, SwarmingTask) for t in tasks) | 232 assert all(isinstance(t, SwarmingTask) for t in tasks) |
231 steps = [] | 233 steps = [] |
232 for task in tasks: | 234 for task in tasks: |
233 assert task.task_id not in self._pending_tasks, ( | 235 assert task.task_id not in self._pending_tasks, ( |
234 'Triggered same task twice: %s' % task.task_id) | 236 'Triggered same task twice: %s' % task.task_id) |
235 self._pending_tasks.add(task.task_id) | 237 self._pending_tasks.add(task.task_id) |
236 | 238 |
237 # Trigger parameters. | 239 # Trigger parameters. |
(...skipping 21 matching lines...) Expand all Loading... |
259 | 261 |
260 # Additional command line args for isolated command. | 262 # Additional command line args for isolated command. |
261 if task.extra_args: | 263 if task.extra_args: |
262 args.append('--') | 264 args.append('--') |
263 args.extend(task.extra_args) | 265 args.extend(task.extra_args) |
264 | 266 |
265 # Build corresponding step. | 267 # Build corresponding step. |
266 steps.append(self.m.python( | 268 steps.append(self.m.python( |
267 name=self._get_step_name('trigger', task), | 269 name=self._get_step_name('trigger', task), |
268 script=self.m.swarming_client.path.join('swarming.py'), | 270 script=self.m.swarming_client.path.join('swarming.py'), |
269 args=args)) | 271 args=args, |
| 272 **kwargs)) |
270 | 273 |
271 return steps | 274 return steps |
272 | 275 |
273 def collect(self, tasks, followup_fn=None): | 276 def collect(self, tasks, **kwargs): |
274 """Waits for a set of Swarming tasks to finish. | 277 """Waits for a set of Swarming tasks to finish. |
275 | 278 |
276 Always waits for all task results. Failed tasks will be marked as such | 279 Always waits for all task results. Failed tasks will be marked as such |
277 but would not abort the build (corresponds to always_run=True step | 280 but would not abort the build (corresponds to always_run=True step |
278 property). | 281 property). |
279 | 282 |
280 Args: | 283 Args: |
281 tasks: an enumerable of SwarmingTask instances. All of them should have | 284 tasks: an enumerable of SwarmingTask instances. All of them should have |
282 been triggered previously with 'trigger' method. | 285 been triggered previously with 'trigger' method. |
283 followup_fn: if given, will be called with each step result before step is | 286 kwargs: passed to recipe step constructor as-is, may contain |
284 finalized. Can be used to customize step appearance. | 287 always_run, can_fail_build, followup_fn, etc. |
285 """ | 288 """ |
286 # TODO(vadimsh): Implement "wait for any" to wait for first finished task. | 289 # TODO(vadimsh): Implement "wait for any" to wait for first finished task. |
287 # TODO(vadimsh): Update |tasks| in-place with results of task execution. | 290 # TODO(vadimsh): Update |tasks| in-place with results of task execution. |
288 # TODO(vadimsh): Add timeouts. | 291 # TODO(vadimsh): Add timeouts. |
289 assert all(isinstance(t, SwarmingTask) for t in tasks) | 292 assert all(isinstance(t, SwarmingTask) for t in tasks) |
290 steps = [] | 293 steps = [] |
291 for task in tasks: | 294 for task in tasks: |
292 assert task.task_id in self._pending_tasks, ( | 295 assert task.task_id in self._pending_tasks, ( |
293 'Trying to collect a task that was not triggered: %s' % task.task_id) | 296 'Trying to collect a task that was not triggered: %s' % task.task_id) |
294 self._pending_tasks.remove(task.task_id) | 297 self._pending_tasks.remove(task.task_id) |
295 steps.append(task.collect_step_builder(task, followup_fn)) | 298 steps.append(task.collect_step_builder(task, **kwargs)) |
296 return steps | 299 return steps |
297 | 300 |
298 def _default_collect_step(self, task, followup_fn): | 301 def _default_collect_step(self, task, **kwargs): |
299 """Produces a step that collects a result of an arbitrary task.""" | 302 """Produces a step that collects a result of an arbitrary task.""" |
300 # Always wait for all tasks to finish even if some of them failed. | 303 # By default wait for all tasks to finish even if some of them failed. |
| 304 kwargs.setdefault('always_run', True) |
301 return self.m.python( | 305 return self.m.python( |
302 name=self._get_step_name('swarming', task), | 306 name=self._get_step_name('swarming', task), |
303 script=self.m.swarming_client.path.join('swarming.py'), | 307 script=self.m.swarming_client.path.join('swarming.py'), |
304 args=self._get_collect_cmd_args(task), | 308 args=self._get_collect_cmd_args(task), |
305 always_run=True, | 309 **kwargs) |
306 followup_fn=followup_fn) | |
307 | 310 |
308 def _gtest_collect_step(self, merged_test_output, task, followup_fn): | 311 def _gtest_collect_step(self, merged_test_output, task, **kwargs): |
309 """Produces a step that collects and processes a result of gtest task.""" | 312 """Produces a step that collects and processes a result of gtest task.""" |
| 313 # By default wait for all tasks to finish even if some of them failed. |
| 314 kwargs.setdefault('always_run', True) |
| 315 |
310 # Shim script's own arguments. | 316 # Shim script's own arguments. |
311 args = [ | 317 args = [ |
312 '--swarming-client-dir', self.m.swarming_client.path, | 318 '--swarming-client-dir', self.m.swarming_client.path, |
313 '--temp-root-dir', self.m.path['tmp_base'], | 319 '--temp-root-dir', self.m.path['tmp_base'], |
314 ] | 320 ] |
315 | 321 |
316 # Where to put combined summary to, consumed by recipes. Also emit | 322 # Where to put combined summary to, consumed by recipes. Also emit |
317 # test expectation only if |merged_test_output| is really used. | 323 # test expectation only if |merged_test_output| is really used. |
318 step_test_data = None | 324 step_test_data = None |
319 if merged_test_output: | 325 if merged_test_output: |
320 args.extend(['--merged-test-output', merged_test_output]) | 326 args.extend(['--merged-test-output', merged_test_output]) |
321 step_test_data = lambda: self.m.json.test_api.canned_gtest_output(True) | 327 step_test_data = lambda: self.m.json.test_api.canned_gtest_output(True) |
322 | 328 |
323 # Arguments for actual 'collect' command. | 329 # Arguments for actual 'collect' command. |
324 args.append('--') | 330 args.append('--') |
325 args.extend(self._get_collect_cmd_args(task)) | 331 args.extend(self._get_collect_cmd_args(task)) |
326 | 332 |
327 # Always wait for all tasks to finish even if some of them failed. Allow | 333 # Always wait for all tasks to finish even if some of them failed. Allow |
328 # collect_gtest_task.py to emit all necessary annotations itself. | 334 # collect_gtest_task.py to emit all necessary annotations itself. |
329 return self.m.python( | 335 return self.m.python( |
330 name=self._get_step_name('swarming', task), | 336 name=self._get_step_name('swarming', task), |
331 script=self.resource('collect_gtest_task.py'), | 337 script=self.resource('collect_gtest_task.py'), |
332 args=args, | 338 args=args, |
333 always_run=True, | |
334 allow_subannotations=True, | 339 allow_subannotations=True, |
335 followup_fn=followup_fn, | 340 step_test_data=step_test_data, |
336 step_test_data=step_test_data) | 341 **kwargs) |
337 | 342 |
338 def _get_step_name(self, prefix, task): | 343 def _get_step_name(self, prefix, task): |
339 """SwarmingTask -> name of a step of a waterfall. | 344 """SwarmingTask -> name of a step of a waterfall. |
340 | 345 |
341 Will take a task title (+ step name prefix) and optionally append | 346 Will take a task title (+ step name prefix) and optionally append |
342 OS dimension to it in case the task is triggered on OS that is different | 347 OS dimension to it in case the task is triggered on OS that is different |
343 from OS this recipe is running on. It shortens step names for the most | 348 from OS this recipe is running on. It shortens step names for the most |
344 common case of triggering a task on the same OS as one that recipe | 349 common case of triggering a task on the same OS as one that recipe |
345 is running on. | 350 is running on. |
346 | 351 |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
397 isolated tests based on gtest. Swarming uses GTEST_SHARD_INDEX | 402 isolated tests based on gtest. Swarming uses GTEST_SHARD_INDEX |
398 and GTEST_TOTAL_SHARDS environment variables to tell the executable | 403 and GTEST_TOTAL_SHARDS environment variables to tell the executable |
399 what shard to run. | 404 what shard to run. |
400 builder: buildbot builder this task was triggered from. | 405 builder: buildbot builder this task was triggered from. |
401 build_number: build number of a build this task was triggered from. | 406 build_number: build number of a build this task was triggered from. |
402 profile: True to enable swarming profiling. | 407 profile: True to enable swarming profiling. |
403 suffix: string suffix to append to task ID. | 408 suffix: string suffix to append to task ID. |
404 extra_args: list of command line arguments to pass to isolated tasks. | 409 extra_args: list of command line arguments to pass to isolated tasks. |
405 collect_step_builder: callback that will be called to generate recipe step | 410 collect_step_builder: callback that will be called to generate recipe step |
406 that collects and processes results of task execution, signature is | 411 that collects and processes results of task execution, signature is |
407 collect_step_builder(task, followup_fn). | 412 collect_step_builder(task, **step_kwargs). |
408 """ | 413 """ |
409 assert 'os' in dimensions | 414 assert 'os' in dimensions |
410 self.title = title | 415 self.title = title |
411 self.isolated_hash = isolated_hash | 416 self.isolated_hash = isolated_hash |
412 self.dimensions = dimensions.copy() | 417 self.dimensions = dimensions.copy() |
413 self.env = env.copy() | 418 self.env = env.copy() |
414 self.priority = priority | 419 self.priority = priority |
415 self.shards = shards | 420 self.shards = shards |
416 self.builder = builder | 421 self.builder = builder |
417 self.build_number = build_number | 422 self.build_number = build_number |
418 self.profile = profile | 423 self.profile = profile |
419 self.suffix = suffix | 424 self.suffix = suffix |
420 self.extra_args = tuple(extra_args or []) | 425 self.extra_args = tuple(extra_args or []) |
421 self.collect_step_builder = collect_step_builder | 426 self.collect_step_builder = collect_step_builder |
422 | 427 |
423 @property | 428 @property |
424 def task_id(self): | 429 def task_id(self): |
425 """ID of this task, derived from its other properties. | 430 """ID of this task, derived from its other properties. |
426 | 431 |
427 Task ID identifies what task is doing and what machine configuration it | 432 Task ID identifies what task is doing and what machine configuration it |
428 expects. It is used as a key in table of a cached results. If Swarming | 433 expects. It is used as a key in table of a cached results. If Swarming |
429 service figures out that a task with given ID has successfully finished | 434 service figures out that a task with given ID has successfully finished |
430 before, it will reuse the result right away, without even running | 435 before, it will reuse the result right away, without even running |
431 the task again. | 436 the task again. |
432 """ | 437 """ |
433 return '%s/%s/%s/%s/%d%s' % ( | 438 return '%s/%s/%s/%s/%d%s' % ( |
434 self.title, self.dimensions['os'], self.isolated_hash, | 439 self.title, self.dimensions['os'], self.isolated_hash, |
435 self.builder, self.build_number, self.suffix) | 440 self.builder, self.build_number, self.suffix) |
OLD | NEW |