|
16 | 16 | import collections |
17 | 17 | import ipaddress |
18 | 18 | import os |
19 | | -import typing |
20 | 19 | import uuid |
21 | 20 |
|
22 | 21 | import google.auth |
|
26 | 25 | from kubernetes import client as k8s_client |
27 | 26 | import yaml |
28 | 27 |
|
| 28 | +from clusterfuzz._internal.base import feature_flags |
29 | 29 | from clusterfuzz._internal.base import tasks |
30 | 30 | from clusterfuzz._internal.base import utils |
31 | 31 | from clusterfuzz._internal.base.tasks import task_utils |
|
39 | 39 | CLUSTER_NAME = project_config = local_config.ProjectConfig().get( |
40 | 40 | 'cluster_name', 'clusterfuzz-cronjobs-gke') |
41 | 41 |
|
| 42 | +K8S_JOBS_PENDING_LIMIT_DEFAULT = 1000 |
| 43 | + |
42 | 44 | KubernetesJobConfig = collections.namedtuple('KubernetesJobConfig', [ |
43 | 45 | 'job_type', |
44 | 46 | 'docker_image', |
|
50 | 52 | ]) |
51 | 53 |
|
52 | 54 |
|
53 | | -def _get_config_names(remote_tasks: typing.List[remote_task_types.RemoteTask]): |
| 55 | +def _get_config_names(remote_tasks: list[remote_task_types.RemoteTask]): |
54 | 56 | """Gets the name of the configs for each batch_task. Returns a dict |
55 | 57 |
|
56 | 58 | that is indexed by command and job_type for efficient lookup.""" |
@@ -87,9 +89,8 @@ def _get_config_names(remote_tasks: typing.List[remote_task_types.RemoteTask]): |
87 | 89 | return config_map |
88 | 90 |
|
89 | 91 |
|
90 | | -def _get_k8s_job_configs( |
91 | | - remote_tasks: typing.List[remote_task_types.RemoteTask] |
92 | | -) -> typing.Dict[typing.Tuple[str, str], KubernetesJobConfig]: |
| 92 | +def _get_k8s_job_configs(remote_tasks: list[remote_task_types.RemoteTask] |
| 93 | + ) -> dict[tuple[str, str], KubernetesJobConfig]: |
93 | 94 | """Gets the configured specifications for a batch workload.""" |
94 | 95 |
|
95 | 96 | if not remote_tasks: |
@@ -283,37 +284,46 @@ def _get_pending_jobs_count(self) -> int: |
283 | 284 |
|
284 | 285 | def create_utask_main_job(self, module: str, job_type: str, |
285 | 286 | input_download_url: str): |
286 | | - """Creates a single batch job for a uworker main task.""" |
| 287 | + """Creates a single Kubernetes job for a uworker main task.""" |
287 | 288 |
|
288 | 289 | command = task_utils.get_command_from_module(module) |
289 | 290 | batch_tasks = [ |
290 | 291 | remote_task_types.RemoteTask(command, job_type, input_download_url) |
291 | 292 | ] |
292 | | - result = self.create_utask_main_jobs(batch_tasks) |
293 | | - |
294 | | - if result is None: |
295 | | - return result |
296 | | - return result[0] |
| 293 | + uncreated_tasks = self.create_utask_main_jobs(batch_tasks) |
| 294 | + return uncreated_tasks |
297 | 295 |
|
298 | | - def create_utask_main_jobs( |
299 | | - self, remote_tasks: typing.List[remote_task_types.RemoteTask]): |
300 | | - """Creates a batch job for a list of uworker main tasks. |
| 296 | + def create_utask_main_jobs(self, |
| 297 | + remote_tasks: list[remote_task_types.RemoteTask]): |
| 298 | + """Creates Kubernetes jobs for a list of uworker main tasks. |
301 | 299 |
|
302 | | - This method groups the tasks by their workload specification and creates a |
303 | | - separate batch job for each group. This allows tasks with similar |
304 | | - requirements to be processed together, which can improve efficiency. |
| 300 | + This method groups the tasks by their workload specification to efficiently |
| 301 | + schedule them. It then iterates through the groups and creates a separate |
| 302 | + Kubernetes job for each task. |
305 | 303 | """ |
| 304 | + |
| 305 | + k8s_limit_flag = feature_flags.FeatureFlags.K8S_JOBS_PENDING_LIMIT |
| 306 | + if k8s_limit_flag.content and k8s_limit_flag.enabled: |
| 307 | + limit = int(k8s_limit_flag.content) |
| 308 | + else: |
| 309 | + limit = K8S_JOBS_PENDING_LIMIT_DEFAULT |
| 310 | + |
| 311 | + pending_jobs_count = self._get_pending_jobs_count() |
| 312 | + if pending_jobs_count >= limit: |
| 313 | + logs.warning( |
| 314 | + f'Pending jobs count {pending_jobs_count} reached limit {limit} ' |
| 315 | + f'for k8s.') |
| 316 | + return remote_tasks |
| 317 | + |
306 | 318 | job_specs = collections.defaultdict(list) |
307 | 319 | configs = _get_k8s_job_configs(remote_tasks) |
308 | 320 | for remote_task in remote_tasks: |
309 | | - logs.info(f'Scheduling {remote_task.command}, {remote_task.job_type}.') |
| 321 | + logs.info( |
| 322 | + f'Scheduling {remote_task.command}, {remote_task.job_type} in K8s.') |
310 | 323 | config = configs[(remote_task.command, remote_task.job_type)] |
311 | 324 | job_specs[config].append(remote_task.input_download_url) |
312 | | - logs.info('Creating batch jobs.') |
313 | | - jobs = [] |
314 | | - logs.info('Batching utask_mains.') |
| 325 | + logs.info('Creating Kubernetes jobs.') |
315 | 326 | for config, input_urls in job_specs.items(): |
316 | 327 | for input_url in input_urls: |
317 | | - jobs.append(self.create_job(config, input_url)) |
318 | | - |
319 | | - return jobs |
| 328 | + self.create_job(config, input_url) |
| 329 | + return [] |
0 commit comments