From 6fe22bfe240f4f8758ea585bf66244a3c09765d2 Mon Sep 17 00:00:00 2001 From: guohelu <19503896967@163.com> Date: Thu, 21 Nov 2024 15:54:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=B8=8A=E4=BA=91=E5=91=A8=E6=9C=9F?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E6=B7=BB=E5=8A=A0=E6=9C=80=E4=BD=8E=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E9=97=B4=E9=9A=94=E6=97=B6=E9=97=B4=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=20--story=3D120737215?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/default.py | 3 +++ env.py | 3 +++ .../core/apis/drf/viewsets/periodic_task.py | 15 ++++++++++++ gcloud/periodictask/models.py | 24 +++++++++++++++++++ requirements.txt | 1 + 5 files changed, 46 insertions(+) diff --git a/config/default.py b/config/default.py index cfde7dba3e..03774c646b 100644 --- a/config/default.py +++ b/config/default.py @@ -868,6 +868,9 @@ def check_engine_admin_permission(request, *args, **kwargs): # 周期任务消息通知类型 PERIODIC_TASK_REMINDER_NOTIFY_TYPE = env.PERIODIC_TASK_REMINDER_NOTIFY_TYPE +# 周期任务最短时间间隔 +PERIODIC_TASK_SHORTEST_TIME = env.PERIODIC_TASK_SHORTEST_TIME + # bk_audit ENABLE_BK_AUDIT = True if env.BK_AUDIT_DATA_TOKEN else False BK_AUDIT_SETTINGS = { diff --git a/env.py b/env.py index 30a10047df..5cf00a6601 100644 --- a/env.py +++ b/env.py @@ -150,6 +150,9 @@ # 周期任务消息通知类型 PERIODIC_TASK_REMINDER_NOTIFY_TYPE = json.loads(os.getenv("PERIODIC_TASK_REMINDER_NOTIFY_TYPE", '["email"]')) +# 周期任务最短时间间隔 +PERIODIC_TASK_SHORTEST_TIME = os.getenv("PERIODIC_TASK_SHORTEST_TIME", "") + # bk_audit BK_AUDIT_ENDPOINT = os.getenv("BK_AUDIT_ENDPOINT", None) BK_AUDIT_DATA_TOKEN = os.getenv("BK_AUDIT_DATA_TOKEN", None) diff --git a/gcloud/core/apis/drf/viewsets/periodic_task.py b/gcloud/core/apis/drf/viewsets/periodic_task.py index 658331065b..c842ebec08 100644 --- a/gcloud/core/apis/drf/viewsets/periodic_task.py +++ b/gcloud/core/apis/drf/viewsets/periodic_task.py @@ -22,6 +22,7 @@ from rest_framework.pagination import LimitOffsetPagination from rest_framework.response import Response +from gcloud.conf import settings from gcloud import err_code from gcloud.common_template.models import CommonTemplate from gcloud.constants import COMMON, PERIOD_TASK_NAME_MAX_LENGTH, PROJECT @@ -253,6 +254,11 @@ def destroy(self, request, *args, **kwargs): def create(self, request, *args, **kwargs): serializer = CreatePeriodicTaskSerializer(data=request.data) serializer.is_valid(raise_exception=True) + project = Project.objects.filter(id=serializer.validated_data["project"].id).first() + if settings.PERIODIC_TASK_SHORTEST_TIME: + result = PeriodicTask().inspect_time(request, serializer.validated_data["cron"], project.time_zone) + if not result: + raise ValidationException("The interval between tasks should be at least 30 minutes") try: self._handle_serializer(request, serializer) instance = serializer.save() @@ -272,6 +278,11 @@ def update(self, request, *args, **kwargs): instance = self.get_object() serializer = CreatePeriodicTaskSerializer(instance, data=request.data) serializer.is_valid(raise_exception=True) + project = Project.objects.filter(id=serializer.validated_data["project"].id).first() + if settings.PERIODIC_TASK_SHORTEST_TIME: + result = PeriodicTask().inspect_time(request, serializer.validated_data["cron"], project.time_zone) + if not result: + raise ValidationException("The interval between tasks should be at least 30 minutes") try: self._handle_serializer(request, serializer) instance = PeriodicTask.objects.update(instance, **serializer.validated_data) @@ -293,6 +304,10 @@ def partial_update(self, request, *args, **kwargs): with transaction.atomic(): if "cron" in serializer.validated_data: project = Project.objects.filter(id=serializer.validated_data["project"]).first() + if settings.PERIODIC_TASK_SHORTEST_TIME: + result = instance.inspect_time(request, serializer.validated_data["cron"], project.time_zone) + if not result: + raise ValidationException("The interval between tasks should be at least 30 minutes") instance.modify_cron(serializer.validated_data["cron"], project.time_zone) if "constants" in serializer.validated_data: instance.modify_constants(serializer.validated_data["constants"]) diff --git a/gcloud/periodictask/models.py b/gcloud/periodictask/models.py index 9b42d975c4..71064fa158 100644 --- a/gcloud/periodictask/models.py +++ b/gcloud/periodictask/models.py @@ -14,6 +14,9 @@ import logging import ujson as json +import pytz +from croniter import croniter +from datetime import datetime, timedelta from django.conf import settings from django.db import models, transaction from django.utils.translation import ugettext_lazy as _ @@ -254,6 +257,27 @@ def delete(self, using=None): super(PeriodicTask, self).delete(using) PeriodicTaskHistory.objects.filter(task=self).delete() + def inspect_time(self, request, cron, timezone=None): + try: + tz = pytz.timezone(timezone or "UTC") + except pytz.UnknownTimeZoneError: + return {"result": False, "data": None, "message": f"未知时区: {timezone}"} + result = True + if not request.user.is_superuser: + now_time = datetime.now(tz) + cron_expression = " ".join(list(cron.values())) + schedule_iter = croniter(cron_expression, now_time) + + next_time_1 = schedule_iter.get_next(datetime) + next_time_2 = schedule_iter.get_next(datetime) + + interval_difference = next_time_2 - next_time_1 + shortest_time = int(settings.PERIODIC_TASK_SHORTEST_TIME) + if interval_difference < timedelta(minutes=shortest_time): + result = False + + return result + def modify_cron(self, cron, timezone): if self.task.enabled is False: self.task.modify_cron(cron, timezone) diff --git a/requirements.txt b/requirements.txt index acd5115175..81c27a2681 100644 --- a/requirements.txt +++ b/requirements.txt @@ -102,3 +102,4 @@ opentelemetry-instrumentation-logging==0.30b1 opentelemetry-instrumentation-requests==0.30b1 bk-notice-sdk==1.3.0 +croniter==0.3.29