From 747f95afa25871bf8d934234ba01f959c579be5a Mon Sep 17 00:00:00 2001 From: guohelu <19503896967@163.com> Date: Wed, 4 Dec 2024 20:01:42 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B5=81=E7=A8=8B=E5=B8=82=E5=9C=BA?= =?UTF-8?q?=E5=85=B1=E4=BA=AB=E6=A8=A1=E6=9D=BF=E6=9F=A5=E7=9C=8B=20#7626?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/default.py | 7 ++ config/urls_custom.py | 10 +- env.py | 5 + gcloud/contrib/templatemaker/__init__.py | 0 gcloud/contrib/templatemaker/admin.py | 23 +++++ gcloud/contrib/templatemaker/apis/__init__.py | 0 .../templatemaker/apis/django/__init__.py | 0 .../contrib/templatemaker/apis/django/api.py | 98 +++++++++++++++++++ .../templatemaker/apis/django/validators.py | 33 +++++++ .../templatemaker/apis/drf/__init__.py | 0 .../apis/drf/viewsets/__init__.py | 14 +++ .../apis/drf/viewsets/template_marker.py | 57 +++++++++++ gcloud/contrib/templatemaker/apps.py | 6 ++ .../templatemaker/migrations/0001_initial.py | 37 +++++++ .../templatemaker/migrations/__init__.py | 0 gcloud/contrib/templatemaker/models.py | 31 ++++++ gcloud/contrib/templatemaker/urls.py | 26 +++++ 17 files changed, 342 insertions(+), 5 deletions(-) create mode 100644 gcloud/contrib/templatemaker/__init__.py create mode 100644 gcloud/contrib/templatemaker/admin.py create mode 100644 gcloud/contrib/templatemaker/apis/__init__.py create mode 100644 gcloud/contrib/templatemaker/apis/django/__init__.py create mode 100644 gcloud/contrib/templatemaker/apis/django/api.py create mode 100644 gcloud/contrib/templatemaker/apis/django/validators.py create mode 100644 gcloud/contrib/templatemaker/apis/drf/__init__.py create mode 100644 gcloud/contrib/templatemaker/apis/drf/viewsets/__init__.py create mode 100644 gcloud/contrib/templatemaker/apis/drf/viewsets/template_marker.py create mode 100644 gcloud/contrib/templatemaker/apps.py create mode 100644 gcloud/contrib/templatemaker/migrations/0001_initial.py create mode 100644 gcloud/contrib/templatemaker/migrations/__init__.py create mode 100644 gcloud/contrib/templatemaker/models.py create mode 100644 gcloud/contrib/templatemaker/urls.py diff --git a/config/default.py b/config/default.py index ba5c58e90a..ad04ebe8dc 100644 --- a/config/default.py +++ b/config/default.py @@ -67,6 +67,7 @@ "gcloud.contrib.develop", "gcloud.contrib.collection", "gcloud.contrib.operate_record", + "gcloud.contrib.templatemaker", "gcloud.apigw", "gcloud.common_template", "gcloud.label", @@ -883,3 +884,9 @@ def check_engine_admin_permission(request, *args, **kwargs): if "BKAPP_SOPS_BROKER_URL" in os.environ: BROKER_URL = os.getenv("BKAPP_SOPS_BROKER_URL") print(f"BROKER_URL: {BROKER_URL}") + + +# 共享模板开关 +ENABLE_FLOW_MARKET = env.ENABLE_FLOW_MARKET +# SRE 商店路由 +FLOW_MARKET_API_URL = env.FLOW_MARKET_API_URL diff --git a/config/urls_custom.py b/config/urls_custom.py index 6ed3c5ecef..8efd65a41f 100644 --- a/config/urls_custom.py +++ b/config/urls_custom.py @@ -10,12 +10,11 @@ an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ -from rest_framework import permissions -from drf_yasg.views import get_schema_view -from drf_yasg import openapi -from django.conf.urls import include, url from django.conf import settings - +from django.conf.urls import include, url +from drf_yasg import openapi +from drf_yasg.views import get_schema_view +from rest_framework import permissions # 用户自定义 urlconf urlpatterns_custom = [ @@ -41,6 +40,7 @@ url(r"^plugin_service/", include("plugin_service.urls")), url(r"^mako_operations/", include("gcloud.mako_template_helper.urls")), url(r"^engine_admin/", include("pipeline.contrib.engine_admin.urls")), + url(r"^template_maker/", include("gcloud.contrib.templatemaker.urls")), ] schema_view = get_schema_view( diff --git a/env.py b/env.py index 59c9045d14..2cd154eeae 100644 --- a/env.py +++ b/env.py @@ -153,3 +153,8 @@ # bk_audit BK_AUDIT_ENDPOINT = os.getenv("BK_AUDIT_ENDPOINT", None) BK_AUDIT_DATA_TOKEN = os.getenv("BK_AUDIT_DATA_TOKEN", None) + +# 共享模板开关 +ENABLE_FLOW_MARKET = False if os.getenv("BKAPP_ENABLE_FLOW_MARKET") is None else True +# SRE 商店路由 +FLOW_MARKET_API_URL = os.getenv("FLOW_MARKET_API_URL", "") diff --git a/gcloud/contrib/templatemaker/__init__.py b/gcloud/contrib/templatemaker/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gcloud/contrib/templatemaker/admin.py b/gcloud/contrib/templatemaker/admin.py new file mode 100644 index 0000000000..d9c3dc45a2 --- /dev/null +++ b/gcloud/contrib/templatemaker/admin.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +from django.contrib import admin + +from gcloud.contrib.templatemaker import models + + +@admin.register(models.TemplateSharedRecord) +class AppMakerAdmin(admin.ModelAdmin): + list_display = ["project_id", "template_id", "template_source", "create", "create_time"] + list_filter = ["project_id", "template_source", "create", "create_time"] + search_fields = ["project_id", "create"] diff --git a/gcloud/contrib/templatemaker/apis/__init__.py b/gcloud/contrib/templatemaker/apis/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gcloud/contrib/templatemaker/apis/django/__init__.py b/gcloud/contrib/templatemaker/apis/django/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gcloud/contrib/templatemaker/apis/django/api.py b/gcloud/contrib/templatemaker/apis/django/api.py new file mode 100644 index 0000000000..0f6f687d6a --- /dev/null +++ b/gcloud/contrib/templatemaker/apis/django/api.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" +import json +import requests +import logging + +from rest_framework.response import Response +from django.views.decorators.http import require_POST, require_GET +from django.http import JsonResponse + +from gcloud.conf import settings +from gcloud import err_code +from gcloud.contrib.templatemaker.models import TemplateSharedRecord +from gcloud.tasktmpl3.models import TaskTemplate +from gcloud.utils.decorators import request_validate +from gcloud.contrib.templatemaker.apis.django.validators import JsonValidator + + +def _get_market_routing(market_url): + return "{}/{}".format(settings.FLOW_MARKET_API_URL, market_url) + + +@require_GET +def get_template_market_details(request, template_id): + project_id = json.loads(request.body).get("project_id") + + url = _get_market_routing("market/details/") + + kwargs = { + "project_id": project_id, + } + + # 根据业务id和模板id从第三方接口获取模板详情 + result = requests.post(url, data=kwargs) + + if not result: + logging.exception("get market template details from third party fails") + return Response( + { + "result": False, + "message": "get market template details from third party fails", + "code": err_code.OPERATION_FAIL.code, + } + ) + + return True + + +@require_POST +@request_validate(JsonValidator) +def maker_template(request, template_id): + if not settings.ENABLE_FLOW_MARKET: + return False + + params = json.loads(request.body) + project_id = params.get("project_id") + try: + TaskTemplate.objects.filter(id=template_id, project__id=project_id).first() + except Exception as e: + logging.exception(e) + return Response( + {"result": False, "message": "template_id does not exist", "code": err_code.OPERATION_FAIL.code} + ) + + url = _get_market_routing("prod/api/") + + kwargs = None + + headers = None + + # 调用第三方接口 + result = requests.post(url, headers=headers, data=kwargs) + + if not result: + logging.exception("Sharing template to SRE store fails") + return JsonResponse( + {"result": False, "message": "Sharing template to SRE store fails", "code": err_code.OPERATION_FAIL.code} + ) + + record, created = TemplateSharedRecord.objects.get_or_create( + project_id=params["project_id"], + template_id=template_id, + defaults={"template_source": params["template_source"], "create": request.user.username}, + ) + if not created: + return JsonResponse({"result": False, "message": "Record already exists", "code": err_code.OPERATION_FAIL.code}) + + return JsonResponse({"result": True, "message": "shared success", "code": err_code.SUCCESS.code}) diff --git a/gcloud/contrib/templatemaker/apis/django/validators.py b/gcloud/contrib/templatemaker/apis/django/validators.py new file mode 100644 index 0000000000..af43e70e54 --- /dev/null +++ b/gcloud/contrib/templatemaker/apis/django/validators.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +import logging +import ujson as json + +from django.utils.translation import ugettext_lazy as _ + +from gcloud.utils.validate import RequestValidator + +logger = logging.getLogger("root") + + +class JsonValidator(RequestValidator): + def validate(self, request, *args, **kwargs): + try: + json.loads(request.body) + except Exception: + message = _("非法请求: 数据错误, 请求不是合法的Json格式 | validate") + logger.error(message) + return False, message + + return True, "" diff --git a/gcloud/contrib/templatemaker/apis/drf/__init__.py b/gcloud/contrib/templatemaker/apis/drf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gcloud/contrib/templatemaker/apis/drf/viewsets/__init__.py b/gcloud/contrib/templatemaker/apis/drf/viewsets/__init__.py new file mode 100644 index 0000000000..ea4177dd98 --- /dev/null +++ b/gcloud/contrib/templatemaker/apis/drf/viewsets/__init__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +from gcloud.contrib.templatemaker.apis.drf.viewsets.template_marker import TemplateMakerViewSet # noqa diff --git a/gcloud/contrib/templatemaker/apis/drf/viewsets/template_marker.py b/gcloud/contrib/templatemaker/apis/drf/viewsets/template_marker.py new file mode 100644 index 0000000000..6570b03130 --- /dev/null +++ b/gcloud/contrib/templatemaker/apis/drf/viewsets/template_marker.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +import logging + +from rest_framework.response import Response +from rest_framework import permissions + +from gcloud.contrib.templatemaker.models import TemplateSharedRecord +from gcloud.core.apis.drf.serilaziers.task_template import TaskTemplateSerializer, TaskTemplateListSerializer +from gcloud.taskflow3.models import TaskTemplate +from gcloud.label.models import TemplateLabelRelation +from gcloud.core.apis.drf.viewsets.base import GcloudModelViewSet + + +class HasValidTemplateID(permissions.BasePermission): + def has_permission(self, request, view): + template_id = view.kwargs.get("pk") + + if not template_id: + logging.warning("template_id is required.") + return False + try: + TemplateSharedRecord.objects.get(template_id=template_id) + except Exception: + logging.warning("template_id {} does not exist.".format(template_id)) + return False + return True + + +class TemplateMakerViewSet(GcloudModelViewSet): + queryset = TaskTemplate.objects.filter(pipeline_template__isnull=False, is_deleted=False) + permission_classes = [permissions.IsAuthenticated, HasValidTemplateID] + + def get_serializer_class(self): + if self.action == "list": + return TaskTemplateListSerializer + return TaskTemplateSerializer + + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + data = serializer.data + labels = TemplateLabelRelation.objects.fetch_templates_labels([instance.id]).get(instance.id, []) + data["template_labels"] = [label["label_id"] for label in labels] + + return Response(data) diff --git a/gcloud/contrib/templatemaker/apps.py b/gcloud/contrib/templatemaker/apps.py new file mode 100644 index 0000000000..a5fcdbd722 --- /dev/null +++ b/gcloud/contrib/templatemaker/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TemplatemakerConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "gcloud.contrib.templatemaker" diff --git a/gcloud/contrib/templatemaker/migrations/0001_initial.py b/gcloud/contrib/templatemaker/migrations/0001_initial.py new file mode 100644 index 0000000000..9643d92d63 --- /dev/null +++ b/gcloud/contrib/templatemaker/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 3.2.15 on 2024-12-04 10:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="TemplateSharedRecord", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("project_id", models.IntegerField(default=-1, help_text="项目 ID", verbose_name="项目 ID")), + ("template_id", models.IntegerField(db_index=True, verbose_name="模版ID")), + ( + "template_source", + models.CharField( + choices=[("project", "项目流程"), ("common", "公共流程"), ("onetime", "一次性任务")], + default="project", + max_length=32, + verbose_name="流程模板来源", + ), + ), + ("create", models.CharField(help_text="执行者", max_length=128, verbose_name="执行者")), + ("create_time", models.DateTimeField(auto_now_add=True, help_text="执行时间", verbose_name="执行时间")), + ], + options={ + "verbose_name": "模板共享记录 TemplateSharedRecord", + "verbose_name_plural": "模板共享记录 TemplateSharedRecord", + "unique_together": {("project_id", "template_id")}, + }, + ), + ] diff --git a/gcloud/contrib/templatemaker/migrations/__init__.py b/gcloud/contrib/templatemaker/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gcloud/contrib/templatemaker/models.py b/gcloud/contrib/templatemaker/models.py new file mode 100644 index 0000000000..f32dd63fc2 --- /dev/null +++ b/gcloud/contrib/templatemaker/models.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from gcloud.constants import TEMPLATE_SOURCE, PROJECT + + +class TemplateSharedRecord(models.Model): + project_id = models.IntegerField(_("项目 ID"), default=-1, help_text="项目 ID") + template_id = models.IntegerField(_("模版ID"), db_index=True) + template_source = models.CharField(_("流程模板来源"), max_length=32, choices=TEMPLATE_SOURCE, default=PROJECT) + create = models.CharField(_("执行者"), max_length=128, help_text="执行者") + create_time = models.DateTimeField(_("执行时间"), auto_now_add=True, help_text="执行时间") + + class Meta: + verbose_name = _("模板共享记录 TemplateSharedRecord") + verbose_name_plural = _("模板共享记录 TemplateSharedRecord") + unique_together = ("project_id", "template_id") diff --git a/gcloud/contrib/templatemaker/urls.py b/gcloud/contrib/templatemaker/urls.py new file mode 100644 index 0000000000..28bcb0d984 --- /dev/null +++ b/gcloud/contrib/templatemaker/urls.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +""" +Tencent is pleased to support the open source community by making 蓝鲸智云PaaS平台社区版 (BlueKing PaaS Community +Edition) available. +Copyright (C) 2017 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at +http://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. +""" + +from django.conf.urls import include, url +from rest_framework.routers import DefaultRouter +from gcloud.contrib.templatemaker.apis.drf.viewsets import TemplateMakerViewSet +from gcloud.contrib.templatemaker.apis.django import api + +drf_api = DefaultRouter() +drf_api.register(r"template", TemplateMakerViewSet) + +urlpatterns = [ + url(r"^api/maker/", include(drf_api.urls)), + url(r"^api/process_maker/(?P\d+)/", api.maker_template), + url(r"^api/template_detail/(?P\d+)/", api.get_template_market_details), +]