blob: 102ccfffd314a36197b69b6e87be5561b328d1ac [file] [log] [blame]
Xixuan Wu835dee22017-09-07 10:47:29 -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 executing tasks queued by suite scheduler."""
Xixuan Wu0a8d3ee2017-10-19 11:33:26 -07006# pylint: disable=g-bad-import-order
Xixuan Wu835dee22017-09-07 10:47:29 -07007
Prathmesh Prabhue8182312020-03-06 23:33:07 -08008import collections
Xixuan Wu835dee22017-09-07 10:47:29 -07009import logging
10
Xinan Lin3ba18a02019-08-13 15:44:55 -070011import buildbucket
12import constants
Xixuan Wu835dee22017-09-07 10:47:29 -070013
Xixuan Wu0a8d3ee2017-10-19 11:33:26 -070014import apiclient
Xixuan Wu835dee22017-09-07 10:47:29 -070015from google.appengine.api import taskqueue
16from google.appengine.runtime import apiproxy_errors
17
18
19SUITES_QUEUE = 'suitesQueue'
Xixuan Wu835dee22017-09-07 10:47:29 -070020
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080021Options = collections.namedtuple(
22 'Options',
23 ['batch_size', 'multirequest_size', 'per_suite_multirequest_size'])
Prathmesh Prabhue8182312020-03-06 23:33:07 -080024
25_DEFAULT_OPTIONS = Options(
Prathmesh Prabhuc23748e2020-03-07 00:00:51 -080026 batch_size=100,
Prathmesh Prabhue8182312020-03-06 23:33:07 -080027 multirequest_size=constants.Buildbucket.MULTIREQUEST_SIZE,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080028 # A limit on the number of tasks included per request for specific suites.
29 per_suite_multirequest_size={
30 # crbug.com/1028732: Generates too many results to store in BuildBucket
31 # output properties.
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070032 'arc-cts': 15,
33 'arc-cts-qual': 15,
34 'arc-cts-unibuild': 15,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080035 'arc-gts': 10,
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070036 'crosbolt_perf_perbuild': 15,
37 'ent-nightly': 15,
38 'faft_bios': 15,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080039 'graphics_per-day': 10,
Prathmesh Prabhu3e0bfbb2020-03-11 12:28:17 -070040 'graphics_per-week': 15,
41 'wifi_matfunc': 10,
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080042 })
Prathmesh Prabhue8182312020-03-06 23:33:07 -080043
Xixuan Wu835dee22017-09-07 10:47:29 -070044
Prathmesh Prabhu46331482020-03-07 00:18:10 -080045def new_task_processor():
46 """Factory function to create a task processor appropriate to environment."""
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080047 # Schedule tests to PROD_BUILDER if in production project, otherwise use
48 # STAGING_BUILDER.
49 builder = constants.Buildbucket.STAGING_BUILDER
50 if (constants.environment() == constants.RunningEnv.ENV_PROD
51 and constants.application_id() == constants.AppID.PROD_APP):
52 builder = constants.Buildbucket.PROD_BUILDER
53 test_platform_client = buildbucket.TestPlatformClient(
54 constants.Buildbucket.HOST, constants.Buildbucket.PROJECT,
55 constants.Buildbucket.BUCKET, builder)
56 return TaskProcessor(
57 queue_name=SUITES_QUEUE,
58 test_platform_client=test_platform_client,
59 options=_DEFAULT_OPTIONS)
Prathmesh Prabhu46331482020-03-07 00:18:10 -080060
61
Xixuan Wu835dee22017-09-07 10:47:29 -070062class TaskProcessor(object):
63 """A class capable of executing tasks by kicking off suites.
64
Prathmesh Prabhu46331482020-03-07 00:18:10 -080065 This class fetches tasks from pullqueue, and kicks off suites
66 represented by tasks' params.
Xixuan Wu835dee22017-09-07 10:47:29 -070067 """
68
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080069 def __init__(self, queue_name, test_platform_client, options):
Xixuan Wu835dee22017-09-07 10:47:29 -070070 """Initialize a task executor for further pulling & execution.
71
72 Args:
Xinan Lin3ba18a02019-08-13 15:44:55 -070073 queue_name: The name of a pull queue.
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080074 test_platform_client: A buildbucket.TestPlatformClient object.
Prathmesh Prabhu7b961d52020-03-06 23:57:42 -080075 options: Options to configure the task processor.
Xixuan Wu835dee22017-09-07 10:47:29 -070076 """
Xixuan Wua5a29442017-10-11 11:03:02 -070077 self.queue = taskqueue.Queue(queue_name)
Prathmesh Prabhue8182312020-03-06 23:33:07 -080078 self._options = options
Prathmesh Prabhu8f43d312020-03-07 00:25:06 -080079 self.test_platform_client = test_platform_client
Xixuan Wu835dee22017-09-07 10:47:29 -070080
81 def batch_execute(self):
Craig Bergstrom58263d32018-04-26 14:11:35 -060082 """Execute tasks."""
Xinan Lin9e4917d2019-11-04 10:58:47 -080083 executed_tasks_count = 0
Prathmesh Prabhue8182312020-03-06 23:33:07 -080084 while (executed_tasks_count + self._options.multirequest_size <=
85 self._options.batch_size):
Xixuan Wu835dee22017-09-07 10:47:29 -070086 try:
Prathmesh Prabhue8182312020-03-06 23:33:07 -080087 tasks = self.queue.lease_tasks_by_tag(
88 3600, self._options.multirequest_size, deadline=60)
Xinan Lin9e4917d2019-11-04 10:58:47 -080089 except (taskqueue.UnknownQueueError,
90 taskqueue.TransientError,
91 apiproxy_errors.DeadlineExceededError) as e:
92 logging.exception(e)
93 raise
Xixuan Wua5a29442017-10-11 11:03:02 -070094
Xinan Lin9e4917d2019-11-04 10:58:47 -080095 if not tasks:
96 return
97
Prathmesh Prabhu55c40032020-03-07 00:45:15 -080098 tasks = self._limit_heavy_tasks(tasks)
99 executed_tasks = []
Xinan Lin9e4917d2019-11-04 10:58:47 -0800100 try:
101 executed_tasks.extend(
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800102 self.test_platform_client.multirequest_run(tasks, _suite(tasks)))
Xinan Lin9e4917d2019-11-04 10:58:47 -0800103 except (ValueError,
104 buildbucket.BuildbucketRunError,
105 apiclient.errors.HttpError) as e:
106 logging.exception('Failed to kick off %d tasks for suite %s',
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800107 len(tasks), _suite(tasks))
Xixuan Wu835dee22017-09-07 10:47:29 -0700108 finally:
109 if executed_tasks:
Xinan Lin9e4917d2019-11-04 10:58:47 -0800110 executed_tasks_count += len(executed_tasks)
111 logging.info('Successfully kicking %d tasks for suite %s',
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800112 len(executed_tasks), _suite(tasks))
Xixuan Wua5a29442017-10-11 11:03:02 -0700113 self.queue.delete_tasks(executed_tasks)
114
115 def purge(self):
116 """Purge the entire tasks in the task queue."""
117 self.queue.purge()
Xixuan Wu835dee22017-09-07 10:47:29 -0700118
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800119 def _limit_heavy_tasks(self, tasks):
120 """Further limits tasks known to cause large load on cros_test_platform."""
121 if not tasks:
122 return tasks
123 limit = self._options.per_suite_multirequest_size.get(
124 _suite(tasks),
125 self._options.multirequest_size,
126 )
127 keep = tasks[:limit]
128 forget = tasks[limit:]
129 for task in forget:
130 self.queue.modify_task_lease(task, 0)
131 return keep
132
Xixuan Wu835dee22017-09-07 10:47:29 -0700133
Xinan Lin9e4917d2019-11-04 10:58:47 -0800134def push(queue_name, tag=None, **suite_kwargs):
Xixuan Wu835dee22017-09-07 10:47:29 -0700135 """Push suites to suite queue for later kickoff.
136
137 Args:
138 queue_name: the name of a pull queue.
Xinan Lin9e4917d2019-11-04 10:58:47 -0800139 tag: tag of a pull queue task.
Xixuan Wu835dee22017-09-07 10:47:29 -0700140 **suite_kwargs: the args for a suite to kick off.
141 """
142 queue = taskqueue.Queue(queue_name)
Xinan Lin9e4917d2019-11-04 10:58:47 -0800143 queue.add(taskqueue.Task(method='PULL', tag=tag, params=suite_kwargs))
Prathmesh Prabhu55c40032020-03-07 00:45:15 -0800144
145
146def _suite(tasks):
147 return tasks[0].tag