From b14e13580458a30a4a9e2ba17da832c8f2770b98 Mon Sep 17 00:00:00 2001 From: guohelu <19503896967@163.com> Date: Thu, 21 Nov 2024 18:59:45 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=8D=95=E4=BE=A7=20--story=3D12073?= =?UTF-8?q?7215?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env.py | 2 +- .../core/apis/drf/viewsets/periodic_task.py | 12 +++++-- gcloud/periodictask/models.py | 33 +++++++++++-------- .../periodictask/models/test_periodic_task.py | 11 ++++++- requirements.txt | 2 +- 5 files changed, 40 insertions(+), 20 deletions(-) diff --git a/env.py b/env.py index 5cf00a6601..e7a0df94dd 100644 --- a/env.py +++ b/env.py @@ -150,7 +150,7 @@ # 周期任务消息通知类型 PERIODIC_TASK_REMINDER_NOTIFY_TYPE = json.loads(os.getenv("PERIODIC_TASK_REMINDER_NOTIFY_TYPE", '["email"]')) -# 周期任务最短时间间隔 +# 周期任务最短时间间隔,以分钟为单位,例如:30 PERIODIC_TASK_SHORTEST_TIME = os.getenv("PERIODIC_TASK_SHORTEST_TIME", "") # bk_audit diff --git a/gcloud/core/apis/drf/viewsets/periodic_task.py b/gcloud/core/apis/drf/viewsets/periodic_task.py index c842ebec08..d7264e0073 100644 --- a/gcloud/core/apis/drf/viewsets/periodic_task.py +++ b/gcloud/core/apis/drf/viewsets/periodic_task.py @@ -256,7 +256,9 @@ def create(self, request, *args, **kwargs): 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) + result = PeriodicTask().inspect_time( + request.user.is_superuser, serializer.validated_data["cron"], project.time_zone + ) if not result: raise ValidationException("The interval between tasks should be at least 30 minutes") try: @@ -280,7 +282,9 @@ def update(self, request, *args, **kwargs): 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) + result = PeriodicTask().inspect_time( + request.user.is_superuser, serializer.validated_data["cron"], project.time_zone + ) if not result: raise ValidationException("The interval between tasks should be at least 30 minutes") try: @@ -305,7 +309,9 @@ def partial_update(self, request, *args, **kwargs): 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) + result = instance.inspect_time( + request.user.is_superuser, 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) diff --git a/gcloud/periodictask/models.py b/gcloud/periodictask/models.py index 71064fa158..f361243319 100644 --- a/gcloud/periodictask/models.py +++ b/gcloud/periodictask/models.py @@ -14,7 +14,7 @@ import logging import ujson as json -import pytz +from django_celery_beat.models import CrontabSchedule as DjangoCeleryBeatCrontabSchedule from croniter import croniter from datetime import datetime, timedelta from django.conf import settings @@ -257,23 +257,28 @@ 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}"} + def inspect_time(self, is_superuser, cron, timezone=None): + schedule, _ = DjangoCeleryBeatCrontabSchedule.objects.get_or_create( + minute=cron.get("minute", "*"), + hour=cron.get("hour", "*"), + day_of_week=cron.get("day_of_week", "*"), + day_of_month=cron.get("day_of_month", "*"), + month_of_year=cron.get("month_of_year", "*"), + timezone=timezone or "UTC", + ) 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) + if not is_superuser: + cron_expression = ( + f"{schedule.minute} {schedule.hour} {schedule.day_of_month} {schedule.month_of_year} " + f"{schedule.day_of_week}" + ) + schedule_iter = croniter(cron_expression) - next_time_1 = schedule_iter.get_next(datetime) - next_time_2 = schedule_iter.get_next(datetime) + next_times = [schedule_iter.get_next(datetime) for _ in range(10)] + min_interval = min((next_times[i] - next_times[i - 1] for i in range(1, len(next_times)))) - interval_difference = next_time_2 - next_time_1 shortest_time = int(settings.PERIODIC_TASK_SHORTEST_TIME) - if interval_difference < timedelta(minutes=shortest_time): + if min_interval < timedelta(minutes=shortest_time): result = False return result diff --git a/gcloud/tests/periodictask/models/test_periodic_task.py b/gcloud/tests/periodictask/models/test_periodic_task.py index f520772da2..1afd8c204e 100644 --- a/gcloud/tests/periodictask/models/test_periodic_task.py +++ b/gcloud/tests/periodictask/models/test_periodic_task.py @@ -78,7 +78,10 @@ def setUp(self): template_id=uniqid(), name=self.task_template_name, creator=self.creator, snapshot=self.snapshot ) self.template_version = "template_version" - task_template = TaskTemplate(project=self.project, pipeline_template=self.pipeline_template,) + task_template = TaskTemplate( + project=self.project, + pipeline_template=self.pipeline_template, + ) task_template.save() self.template = task_template self.task = self.create_a_task() @@ -234,6 +237,12 @@ def test_delete(self): PipelinePeriodicTask.DoesNotExist, PipelinePeriodicTask.objects.get, id=pipeline_periodic_task_id ) + def test_inspect_time(self): + self.cron = {"day_of_month": "*", "day_of_week": "*", "hour": "*", "minute": "*/30", "month_of_year": "*"} + self.timezone = "Asia/Shanghai" + self.periodic_task = self.task.inspect_time(is_superuser=0, cron=self.cron, timezone=self.timezone) + self.assertTrue(self.periodic_task) + def test_modify_constants(self): expect_constants = copy.deepcopy(self.task.task.execution_data["constants"]) expect_constants["key_1"]["value"] = "val_3" diff --git a/requirements.txt b/requirements.txt index 81c27a2681..4727dfc8a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -102,4 +102,4 @@ opentelemetry-instrumentation-logging==0.30b1 opentelemetry-instrumentation-requests==0.30b1 bk-notice-sdk==1.3.0 -croniter==0.3.29 +croniter==1.4.1