From 3a231efd07a9cba2ae0ca41cbee834506e95e942 Mon Sep 17 00:00:00 2001 From: jamesge Date: Mon, 21 Oct 2024 19:42:36 +0800 Subject: [PATCH] fix: missing update deployment processes fields in ImageReleaseMgr (#1665) --- .../paasng/platform/bkapp_model/manager.py | 10 -- .../paasng/platform/bkapp_model/manifest.py | 26 ++- .../migrations/0016_processservicesflag.py | 39 +++++ .../paasng/platform/bkapp_model/models.py | 10 +- .../declarative/deployment/controller.py | 15 +- .../paasng/platform/declarative/entities.py | 8 +- .../paasng/platform/engine/deploy/building.py | 17 +- .../platform/engine/deploy/image_release.py | 165 ++++++++++-------- .../platform/bkapp_model/test_manifest.py | 13 ++ .../handlers/v3/test_deployment.py | 6 - .../engine/deploy/test_image_release.py | 94 ++++++++++ 11 files changed, 290 insertions(+), 113 deletions(-) create mode 100644 apiserver/paasng/paasng/platform/bkapp_model/migrations/0016_processservicesflag.py create mode 100644 apiserver/paasng/tests/paasng/platform/engine/deploy/test_image_release.py diff --git a/apiserver/paasng/paasng/platform/bkapp_model/manager.py b/apiserver/paasng/paasng/platform/bkapp_model/manager.py index 5b6f213dad..b0757abc52 100644 --- a/apiserver/paasng/paasng/platform/bkapp_model/manager.py +++ b/apiserver/paasng/paasng/platform/bkapp_model/manager.py @@ -199,13 +199,3 @@ def sync_hooks(module: Module, hooks: HookList): # Remove existing data that is not touched. module.deploy_hooks.filter(id__in=existing_index.values()).delete() - - -def sync_to_bkapp_model(module, processes: List[ProcessTmpl], hooks: Optional[HookList] = None): - """保存应用描述文件记录的信息到 bkapp_models - - Processes - - Hooks - """ - ModuleProcessSpecManager(module).sync_from_desc(processes=processes) - if hooks is not None: - sync_hooks(module, hooks) diff --git a/apiserver/paasng/paasng/platform/bkapp_model/manifest.py b/apiserver/paasng/paasng/platform/bkapp_model/manifest.py index 865ebb8011..db8c05bd9a 100644 --- a/apiserver/paasng/paasng/platform/bkapp_model/manifest.py +++ b/apiserver/paasng/paasng/platform/bkapp_model/manifest.py @@ -36,7 +36,6 @@ LOG_COLLECTOR_TYPE_ANNO_KEY, MODULE_NAME_ANNO_KEY, PA_SITE_ID_ANNO_KEY, - PROC_SERVICES_ENABLED_ANNOTATION_KEY, USE_CNB_ANNO_KEY, WLAPP_NAME_ANNO_KEY, ApiVersion, @@ -58,6 +57,7 @@ DomainResolution, ModuleProcessSpec, ObservabilityConfig, + ProcessServicesFlag, ProcessSpecEnvOverlay, SvcDiscConfig, ) @@ -67,7 +67,6 @@ merge_env_vars_overlay, override_env_vars_overlay, ) -from paasng.platform.declarative.models import DeploymentDescription from paasng.platform.engine.configurations.config_var import get_env_variables from paasng.platform.engine.constants import AppEnvName, ConfigVarEnvName, RuntimeType from paasng.platform.engine.models import Deployment @@ -500,11 +499,8 @@ def get_bkapp_resource_for_deploy( # such as: if log collector type is set to "ELK", the operator should mount app logs to host path model_res.metadata.annotations[LOG_COLLECTOR_TYPE_ANNO_KEY] = get_log_collector_type(env) - # 设置 bkapp.paas.bk.tencent.com/proc-services-feature-enabled 注解值 - proc_svc_enabled = "true" - if deployment and (desc_obj := DeploymentDescription.objects.filter(deployment=deployment).first()): - proc_svc_enabled = desc_obj.runtime.get(PROC_SERVICES_ENABLED_ANNOTATION_KEY, "true") - model_res.set_proc_services_annotation(proc_svc_enabled) + # 由于 bkapp 新增了 process services 配置特性,部分旧模块需要平台创建 process services 并注入到 model_res 中 + apply_proc_svc_if_implicit_needed(model_res, env) # Apply other changes to the resource apply_env_annots(model_res, env, deploy_id=deploy_id) @@ -563,3 +559,19 @@ def apply_builtin_env_vars(model_res: crd.BkAppResource, env: ModuleEnvironment) if not overlay: overlay = model_res.spec.envOverlay = crd.EnvOverlay(envVariables=[]) overlay.envVariables = override_env_vars_overlay(overlay.envVariables or [], builtin_env_vars_overlay) + + +def apply_proc_svc_if_implicit_needed(model_res: crd.BkAppResource, env: ModuleEnvironment): + """如果 implicit_needed flag 为 True, 则创建 process services, 并注入到 model_res 中; 否则不做任何操作. + + :param model_res: The bkapp model resource object. + :param env: The environment object. + + # 说明: 现阶段通过设置注解 bkapp.paas.bk.tencent.com/proc-services-feature-enabled, 在 operator 侧完成对应功能. + # TODO 后续处理: 当 implicit_needed 为 True 时, 创建并注入 process services 配置, 并且统一设置注解值为 true + """ + flag, _ = ProcessServicesFlag.objects.get_or_create(app_environment=env, defaults={"implicit_needed": False}) + if not flag.implicit_needed: + model_res.set_proc_services_annotation("true") + else: + model_res.set_proc_services_annotation("false") diff --git a/apiserver/paasng/paasng/platform/bkapp_model/migrations/0016_processservicesflag.py b/apiserver/paasng/paasng/platform/bkapp_model/migrations/0016_processservicesflag.py new file mode 100644 index 0000000000..20a82a1b62 --- /dev/null +++ b/apiserver/paasng/paasng/platform/bkapp_model/migrations/0016_processservicesflag.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.25 on 2024-10-18 13:31 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('applications', '0012_application_is_ai_agent_app'), + ('bkapp_model', '0015_auto_20240913_1509'), + ] + + operations = [ + migrations.CreateModel( + name='ProcessServicesFlag', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('region', models.CharField(help_text='部署区域', max_length=32)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ( + 'implicit_needed', + models.BooleanField(default=False, verbose_name='是否隐式需要 process services 配置'), + ), + ( + 'app_environment', + models.OneToOneField( + db_constraint=False, + on_delete=django.db.models.deletion.CASCADE, + to='applications.applicationenvironment', + ), + ), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apiserver/paasng/paasng/platform/bkapp_model/models.py b/apiserver/paasng/paasng/platform/bkapp_model/models.py index ee89c814a0..21c87f224b 100644 --- a/apiserver/paasng/paasng/platform/bkapp_model/models.py +++ b/apiserver/paasng/paasng/platform/bkapp_model/models.py @@ -23,7 +23,7 @@ from django.utils.translation import gettext_lazy as _ from paas_wl.utils.models import AuditedModel, TimestampedModel -from paasng.platform.applications.models import Application, ModuleEnvironment +from paasng.platform.applications.models import Application, ApplicationEnvironment, ModuleEnvironment from paasng.platform.bkapp_model.entities import ( AutoscalingConfig, HostAlias, @@ -187,6 +187,14 @@ class Meta: unique_together = ("proc_spec", "environment_name") +class ProcessServicesFlag(TimestampedModel): + """ProcessServicesFlag 主要用途是标记是否隐式需要 process services 配置""" + + app_environment = models.OneToOneField(ApplicationEnvironment, on_delete=models.CASCADE, db_constraint=False) + # 非 3 版本的 app_desc.yaml/Procfile, 由于不支持用户显式配置 process services, 因此设计 implicit_needed 字段来标记是否需要平台隐式创建 + implicit_needed = models.BooleanField("是否隐式需要 process services 配置", default=False) + + class ModuleDeployHookManager(models.Manager): """ModuleDeployHook RelatedManager, should be used by `module.deploy_hooks`""" diff --git a/apiserver/paasng/paasng/platform/declarative/deployment/controller.py b/apiserver/paasng/paasng/platform/declarative/deployment/controller.py index 67d348b91b..4c6df5ec32 100644 --- a/apiserver/paasng/paasng/platform/declarative/deployment/controller.py +++ b/apiserver/paasng/paasng/platform/declarative/deployment/controller.py @@ -20,7 +20,6 @@ from django.db.transaction import atomic -from paas_wl.bk_app.cnative.specs.constants import PROC_SERVICES_ENABLED_ANNOTATION_KEY from paas_wl.bk_app.monitoring.app_monitor.shim import upsert_app_monitor from paas_wl.bk_app.processes.constants import ProbeType from paasng.platform.applications.constants import ApplicationType @@ -69,7 +68,7 @@ def perform_action( self.handle_desc(desc) - return DeployHandleResult(use_cnb=desc.spec_version == AppSpecVersion.VER_3) + return DeployHandleResult(desc.spec_version) def handle_desc(self, desc: DeploymentDesc): """Handle the description object, which was read from the app description file.""" @@ -150,18 +149,10 @@ def _handle_desc_normal_style(self, desc: DeploymentDesc, desc_obj: DeploymentDe def _save_desc_obj(self, desc: DeploymentDesc) -> DeploymentDescription: """Save the raw description data, return the object created.""" - runtime = {"source_dir": desc.source_dir} - - # specVersion: 3 ,默认开启 proc services 特性; 旧版本不启用 - if desc.spec_version == AppSpecVersion.VER_3: - runtime[PROC_SERVICES_ENABLED_ANNOTATION_KEY] = "true" - else: - runtime[PROC_SERVICES_ENABLED_ANNOTATION_KEY] = "false" - deploy_desc, _ = DeploymentDescription.objects.update_or_create( deployment=self.deployment, defaults={ - "runtime": runtime, + "runtime": {"source_dir": desc.source_dir}, "spec": desc.spec, # TODO: store desc.bk_monitor to DeploymentDescription }, @@ -220,7 +211,7 @@ def handle_procfile_procs(deployment: Deployment, procfile_procs: List[ProcfileP # Save the process configs to both the module's spec and current deployment object. ModuleProcessSpecManager(module).sync_from_desc(processes=list(proc_tmpls.values())) deployment.update_fields(processes=proc_tmpls) - return DeployHandleResult(use_cnb=False) + return DeployHandleResult() def sanitize_bkapp_spec_to_dict(spec: v1alpha2.BkAppSpec) -> Dict: diff --git a/apiserver/paasng/paasng/platform/declarative/entities.py b/apiserver/paasng/paasng/platform/declarative/entities.py index b0cdd4c054..c8be3d4f5c 100644 --- a/apiserver/paasng/paasng/platform/declarative/entities.py +++ b/apiserver/paasng/paasng/platform/declarative/entities.py @@ -13,14 +13,18 @@ # # We undertake not to change the open source license (MIT license) applicable # to the current version of the project delivered to anyone in the future. +from typing import Optional + from attrs import define +from paasng.platform.declarative.constants import AppSpecVersion + @define class DeployHandleResult: """部署阶段处理应用描述文件的结果类。 - :param use_cnb: 是否使用了 v3 版本 CNB 模式,对 S-Mart 应用有意义 + :param: spec_version 应用描述文件的版本. None 表示没有有效的spec version, 如仅提供了 Procfile 的应用 """ - use_cnb: bool + spec_version: Optional[AppSpecVersion] = None diff --git a/apiserver/paasng/paasng/platform/engine/deploy/building.py b/apiserver/paasng/paasng/platform/engine/deploy/building.py index 47ba45ea08..7c1e5d6312 100644 --- a/apiserver/paasng/paasng/platform/engine/deploy/building.py +++ b/apiserver/paasng/paasng/platform/engine/deploy/building.py @@ -32,6 +32,8 @@ from paasng.platform.applications.constants import ApplicationType from paasng.platform.bkapp_model.exceptions import ManifestImportError from paasng.platform.bkapp_model.manifest import get_bkapp_resource +from paasng.platform.bkapp_model.models import ProcessServicesFlag +from paasng.platform.declarative.constants import AppSpecVersion from paasng.platform.declarative.deployment.controller import DeployHandleResult from paasng.platform.declarative.exceptions import DescriptionValidationError from paasng.platform.engine.configurations.building import ( @@ -148,17 +150,26 @@ def compress_and_upload( def handle_app_description(self) -> DeployHandleResult: """Handle the description files for deployment. It try to parse the app description file and store the related configurations, e.g. processes. + Set the implicit_needed flag for process services at the end. :raises HandleAppDescriptionError: When failed to handle the app description. """ try: + app_environment = self.deployment.app_environment handler = get_deploy_desc_handler_by_version( - self.deployment.app_environment.module, + app_environment.module, self.deployment.operator, self.deployment.version_info, self.deployment.get_source_dir(), ) - return handler.handle(self.deployment) + result = handler.handle(self.deployment) + + # 非 3 版本的 app_desc.yaml/Procfile, 由于不支持用户显式配置 process services, 因此设置隐式标记, 由平台负责创建 + implicit_needed = result.spec_version != AppSpecVersion.VER_3 + ProcessServicesFlag.objects.update_or_create( + app_environment=app_environment, defaults={"implicit_needed": implicit_needed} + ) + except InitDeployDescHandlerError as e: raise HandleAppDescriptionError(reason=_("处理应用描述文件失败:{}".format(e))) except (DescriptionValidationError, ManifestImportError) as e: @@ -166,6 +177,8 @@ def handle_app_description(self) -> DeployHandleResult: except Exception as e: logger.exception("Error while handling app description file, deployment: %s.", self.deployment) raise HandleAppDescriptionError(reason=_("处理应用描述文件时出现异常, 请检查应用描述文件")) from e + else: + return result def create_bkapp_revision(self) -> int: """generate bkapp model and store it into AppModelResource for querying the deployed bkapp model""" diff --git a/apiserver/paasng/paasng/platform/engine/deploy/image_release.py b/apiserver/paasng/paasng/platform/engine/deploy/image_release.py index cd6009e8e0..9d62ce53a3 100644 --- a/apiserver/paasng/paasng/platform/engine/deploy/image_release.py +++ b/apiserver/paasng/paasng/platform/engine/deploy/image_release.py @@ -26,11 +26,9 @@ from paas_wl.workloads.images.models import AppImageCredential from paasng.accessories.servicehub.manager import mixed_service_mgr from paasng.platform.applications.constants import ApplicationType -from paasng.platform.bkapp_model.manager import sync_to_bkapp_model -from paasng.platform.bkapp_model.models import ModuleProcessSpec -from paasng.platform.declarative.handlers import ( - DeployHandleResult, -) +from paasng.platform.bkapp_model.models import ModuleProcessSpec, ProcessServicesFlag +from paasng.platform.declarative.constants import AppSpecVersion +from paasng.platform.declarative.handlers import DeployHandleResult from paasng.platform.engine.configurations.image import ImageCredentialManager, RuntimeImageInfo, get_credential_refs from paasng.platform.engine.constants import JobStatus from paasng.platform.engine.deploy.release import start_release_step @@ -75,61 +73,9 @@ def start(self): pre_phase_start.send(self, phase=DeployPhaseTypes.PREPARATION) preparation_phase = self.deployment.deployphase_set.get(type=DeployPhaseTypes.PREPARATION) - module = self.module_environment.module - is_smart_app = module.application.is_smart_app # DB 中存储的步骤名为中文,所以 procedure_force_phase 必须传中文,不能做国际化处理 with self.procedure("解析应用进程信息", phase=preparation_phase): - build_id = self.deployment.advanced_options.build_id - if build_id: - # 托管源码的应用在发布历史镜像时, advanced_options.build_id 不为空 - deployment: Deployment = ( - Deployment.objects.filter(build_id=build_id).exclude(processes={}).order_by("-created").first() - ) - if not deployment: - raise DeployShouldAbortError("failed to get processes") - processes = deployment.get_processes() - # 保存上一次部署的 Processes/Hooks 到 bkapp models - # FIXME: sync_hooks 会重置 proc_command, 导致 proc_command 再次优先于 command/args 解析 - sync_to_bkapp_model(module=module, processes=processes, hooks=deployment.get_deploy_hooks()) - else: - # advanced_options.build_id 为空有 2 种可能情况 - # 1. s-mart 应用 - # 2. 仅托管镜像的应用(包含云原生应用和旧镜像应用) - use_cnb = False - if is_smart_app: - # S-Mart 应用使用 S-Mart 包的元信息记录启动进程 - handle_result = self._handle_smart_app_description() - use_cnb = handle_result.use_cnb - else: - env_name = self.module_environment.environment - processes_dict = { - proc_spec.name: ProcessTmpl( - name=proc_spec.name, - command=proc_spec.get_proc_command(), - replicas=proc_spec.get_target_replicas(env_name), - plan=proc_spec.get_plan_name(env_name), - autoscaling=bool(proc_spec.get_autoscaling(env_name)), - scaling_config=proc_spec.get_scaling_config(env_name), - probes=proc_spec.probes, - ) - for proc_spec in ModuleProcessSpec.objects.filter(module=module) - } - # 手动将进程配置数据保存到本次的 Deployment 对象中 - self.deployment.update_fields(processes=processes_dict) - - runtime_info = RuntimeImageInfo(engine_app=self.engine_app) - # 目前构建流程必须 build_id, 因此需要构造 Build 对象 - build_id = self.engine_client.create_build( - image=runtime_info.generate_image(self.version_info), - extra_envs={"BKPAAS_IMAGE_APPLICATION_FLAG": "1"}, - # 需要兼容 s-mart 应用 - artifact_type=ArtifactType.SLUG if is_smart_app else ArtifactType.NONE, - artifact_metadata={ - "use_cnb": use_cnb, - }, - ) - - self.deployment.update_fields(build_status=JobStatus.SUCCESSFUL, build_id=build_id) + self._handle_app_processes_and_dummy_build() with self.procedure_force_phase("配置镜像访问凭证", phase=preparation_phase): self._setup_image_credentials() @@ -141,6 +87,94 @@ def start(self): post_phase_end.send(self, status=JobStatus.SUCCESSFUL, phase=DeployPhaseTypes.PREPARATION) start_release_step(deployment_id=self.deployment.id) + def _handle_app_processes_and_dummy_build(self): + """处理应用进程信息并且完成 dummy build""" + # build_id 值有效, 表示源码应用选择已构建的镜像进行部署操作 + if self.deployment.advanced_options and (build_id := self.deployment.advanced_options.build_id): + self._handle_processes_by_build(build_id) + self.deployment.update_fields(build_status=JobStatus.SUCCESSFUL, build_id=build_id) + return + + # smart 应用部署操作 + if is_smart_app := self.module_environment.module.application.is_smart_app: + parse_result = self._handle_smart_app_description() + use_cnb = parse_result.spec_version == AppSpecVersion.VER_3 + # 仅托管镜像的应用(包含云原生应用和旧镜像应用)部署操作 + else: + self._handle_image_app_processes() + use_cnb = False + + # 目前构建流程必须有有效的 build, 因此需要 dummy build 过程 + build_id = self._create_build(is_smart_app=is_smart_app, use_cnb=use_cnb) + + # dummy build 完成,更新 deployment 的 build_id + self.deployment.update_fields(build_status=JobStatus.SUCCESSFUL, build_id=build_id) + + def _handle_processes_by_build(self, build_id: str): + """根据 build_id, 处理关联 deployment 的应用进程信息""" + last_deployment: Deployment = ( + Deployment.objects.filter(build_id=build_id).exclude(processes={}).order_by("-created").first() + ) + if not last_deployment: + raise DeployShouldAbortError("failed to get processes") + + self.deployment.update_fields(processes=last_deployment.processes) + + def _handle_smart_app_description(self) -> DeployHandleResult: + """Handle the description files for Smart app. Set the implicit_needed flag for process services at the end.""" + try: + app_environment = self.deployment.app_environment + handler = get_deploy_desc_handler_by_version( + app_environment.module, + self.deployment.operator, + self.deployment.version_info, + ) + + result = handler.handle(self.deployment) + + # 非 3 版本的 app_desc.yaml/Procfile, 由于不支持用户显式配置 process services, 因此设置隐式标记, 由平台负责创建 + implicit_needed = result.spec_version != AppSpecVersion.VER_3 + ProcessServicesFlag.objects.update_or_create( + app_environment=app_environment, defaults={"implicit_needed": implicit_needed} + ) + + except InitDeployDescHandlerError as e: + raise HandleAppDescriptionError(reason=_("处理应用描述文件失败:{}".format(e))) + except Exception as e: + logger.exception("Error while handling s-mart app description file, deployment: %s.", self.deployment) + raise HandleAppDescriptionError(reason=_("处理应用描述文件时出现异常, 请检查应用描述文件")) from e + else: + return result + + def _handle_image_app_processes(self): + """处理镜像应用的进程信息""" + env_name = self.module_environment.environment + processes_dict = { + proc_spec.name: ProcessTmpl( + name=proc_spec.name, + command=proc_spec.get_proc_command(), + replicas=proc_spec.get_target_replicas(env_name), + plan=proc_spec.get_plan_name(env_name), + autoscaling=bool(proc_spec.get_autoscaling(env_name)), + scaling_config=proc_spec.get_scaling_config(env_name), + probes=proc_spec.probes, + ) + for proc_spec in ModuleProcessSpec.objects.filter(module=self.module_environment.module) + } + self.deployment.update_fields(processes=processes_dict) + + def _create_build(self, is_smart_app: bool, use_cnb: bool = False) -> str: + runtime_info = RuntimeImageInfo(engine_app=self.engine_app) + return self.engine_client.create_build( + image=runtime_info.generate_image(self.version_info), + extra_envs={"BKPAAS_IMAGE_APPLICATION_FLAG": "1"}, + # 需要兼容 s-mart 应用 + artifact_type=ArtifactType.SLUG if is_smart_app else ArtifactType.NONE, + artifact_metadata={ + "use_cnb": use_cnb, + }, + ) + def _provision_services(self, p: DeployProcedure): """Provision all preset services @@ -177,18 +211,3 @@ def _setup_image_credentials(self): raise AppImageCredential.objects.flush_from_refs(application, self.engine_app.to_wl_obj(), credential_refs) - - def _handle_smart_app_description(self) -> DeployHandleResult: - """Handle the description files for S-Mart app.""" - try: - handler = get_deploy_desc_handler_by_version( - self.deployment.app_environment.module, - self.deployment.operator, - self.deployment.version_info, - ) - return handler.handle(self.deployment) - except InitDeployDescHandlerError as e: - raise HandleAppDescriptionError(reason=_("处理应用描述文件失败:{}".format(e))) - except Exception as e: - logger.exception("Error while handling s-mart app description file, deployment: %s.", self.deployment) - raise HandleAppDescriptionError(reason=_("处理应用描述文件时出现异常, 请检查应用描述文件")) from e diff --git a/apiserver/paasng/tests/paasng/platform/bkapp_model/test_manifest.py b/apiserver/paasng/tests/paasng/platform/bkapp_model/test_manifest.py index 6ad157f20c..9b9a457b4d 100644 --- a/apiserver/paasng/tests/paasng/platform/bkapp_model/test_manifest.py +++ b/apiserver/paasng/tests/paasng/platform/bkapp_model/test_manifest.py @@ -44,12 +44,14 @@ SvcDiscoveryManifestConstructor, apply_builtin_env_vars, apply_env_annots, + apply_proc_svc_if_implicit_needed, get_manifest, ) from paasng.platform.bkapp_model.models import ( DomainResolution, ModuleProcessSpec, ObservabilityConfig, + ProcessServicesFlag, ProcessSpecEnvOverlay, SvcDiscConfig, ) @@ -538,3 +540,14 @@ def test_builtin_env_has_high_priority(blank_resource, bk_stag_env): assert vars_overlay[("BK_LOGIN_URL", "stag")] != custom_login_url assert vars["BK_LOGIN_URL"] == vars_overlay[("BK_LOGIN_URL", "stag")] + + +def test_apply_proc_svc_if_implicit_needed_is_false(blank_resource, bk_stag_env): + apply_proc_svc_if_implicit_needed(blank_resource, bk_stag_env) + assert blank_resource.get_proc_services_annotation() == "true" + + +def test_apply_proc_svc_if_implicit_needed_is_true(blank_resource, bk_stag_env): + ProcessServicesFlag.objects.create(app_environment=bk_stag_env, implicit_needed=True) + apply_proc_svc_if_implicit_needed(blank_resource, bk_stag_env) + assert blank_resource.get_proc_services_annotation() == "false" diff --git a/apiserver/paasng/tests/paasng/platform/declarative/handlers/v3/test_deployment.py b/apiserver/paasng/tests/paasng/platform/declarative/handlers/v3/test_deployment.py index 30b52481c5..f848bda04f 100644 --- a/apiserver/paasng/tests/paasng/platform/declarative/handlers/v3/test_deployment.py +++ b/apiserver/paasng/tests/paasng/platform/declarative/handlers/v3/test_deployment.py @@ -24,12 +24,10 @@ import pytest import yaml -from paas_wl.bk_app.cnative.specs.constants import PROC_SERVICES_ENABLED_ANNOTATION_KEY from paasng.platform.bkapp_model.entities import ProcService from paasng.platform.bkapp_model.models import ModuleProcessSpec, get_svc_disc_as_env_variables from paasng.platform.declarative.exceptions import DescriptionValidationError from paasng.platform.declarative.handlers import get_deploy_desc_handler -from paasng.platform.declarative.models import DeploymentDescription from paasng.platform.engine.configurations.config_var import get_preset_env_variables from paasng.platform.modules.models.module import Module @@ -86,10 +84,6 @@ def test_handle_normal(self, bk_module, bk_deployment, yaml_v3_example): assert bk_deployment.hooks[0].command == [] assert bk_deployment.hooks[0].args == ["python", "manage.py", "migrate"] - # 测试 specVersion: 3, 正确记录 bkapp.paas.bk.tencent.com/proc-services-feature-enabled: true - desc_obj = DeploymentDescription.objects.get(deployment=bk_deployment) - assert desc_obj.runtime[PROC_SERVICES_ENABLED_ANNOTATION_KEY] == "true" - assert get_preset_env_variables(bk_deployment.app_environment) == {"FOO": "1"} assert get_svc_disc_as_env_variables(bk_deployment.app_environment) == { "BKPAAS_SERVICE_ADDRESSES_BKSAAS": base64.b64encode( diff --git a/apiserver/paasng/tests/paasng/platform/engine/deploy/test_image_release.py b/apiserver/paasng/tests/paasng/platform/engine/deploy/test_image_release.py new file mode 100644 index 0000000000..71d7fc2d93 --- /dev/null +++ b/apiserver/paasng/tests/paasng/platform/engine/deploy/test_image_release.py @@ -0,0 +1,94 @@ +# -*- coding: utf-8 -*- +# TencentBlueKing is pleased to support the open source community by making +# 蓝鲸智云 - PaaS 平台 (BlueKing - PaaS System) 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. +# +# We undertake not to change the open source license (MIT license) applicable +# to the current version of the project delivered to anyone in the future. +import uuid +from unittest import mock + +import pytest +from django_dynamic_fixture import G + +from paas_wl.bk_app.applications.models import Build +from paasng.platform.applications.models import Application +from paasng.platform.bkapp_model.models import ModuleProcessSpec +from paasng.platform.declarative.constants import AppSpecVersion +from paasng.platform.declarative.entities import DeployHandleResult +from paasng.platform.engine.deploy.image_release import ImageReleaseMgr +from paasng.platform.engine.handlers import attach_all_phases +from paasng.platform.engine.models.deployment import Deployment, ProcessTmpl + +pytestmark = pytest.mark.django_db(databases=["default", "workloads"]) + + +class TestHandleProcessesAndDummyBuild: + @pytest.fixture() + def deployment_with_build_options(self, bk_stag_env): + fake_build_id = uuid.uuid4() + # 创建上一次 deployment + G( + Deployment, + build_id=fake_build_id, + app_environment=bk_stag_env, + processes={"web": ProcessTmpl(name="web", command="run server")}, + ) + deployment = G( + Deployment, + app_environment=bk_stag_env, + build_id=fake_build_id, + advanced_options={"build_id": fake_build_id}, + ) + attach_all_phases(sender=deployment.app_environment, deployment=deployment) + return deployment + + @pytest.fixture() + def simple_deployment(self, bk_stag_env): + deployment = G(Deployment, app_environment=bk_stag_env) + attach_all_phases(sender=deployment.app_environment, deployment=deployment) + return deployment + + def test_for_last_build(self, deployment_with_build_options): + """test ImageReleaseMgr._handle_processes_by_build""" + deployment = deployment_with_build_options + + ImageReleaseMgr.from_deployment_id(deployment.id)._handle_app_processes_and_dummy_build() + deployment = Deployment.objects.get(id=deployment.id) + assert deployment.processes == {"web": ProcessTmpl(name="web", command="run server")} + assert str(deployment.build_id) == deployment.advanced_options.build_id + + @pytest.mark.usefixtures("_with_wl_apps") + def test_for_smart_app(self, bk_app, simple_deployment): + """test ImageReleaseMgr._handle_smart_app_description""" + app = Application.objects.get(id=bk_app.id) + app.is_smart_app = True + app.save() + + with mock.patch( + "paasng.platform.engine.deploy.image_release.ImageReleaseMgr._handle_smart_app_description", + return_value=DeployHandleResult(spec_version=AppSpecVersion.VER_3), + ): + ImageReleaseMgr.from_deployment_id(simple_deployment.id)._handle_app_processes_and_dummy_build() + deployment = Deployment.objects.get(id=simple_deployment.id) + build = Build.objects.get(uuid=deployment.build_id) + assert build.artifact_metadata.get("use_cnb") is True + + @pytest.mark.usefixtures("_with_wl_apps") + def test_for_image_app(self, bk_module, simple_deployment): + """test ImageReleaseMgr._handle_image_app_processes""" + G(ModuleProcessSpec, module=bk_module, name="web", command=["npm"], args=["run", "server"]) + + ImageReleaseMgr.from_deployment_id(simple_deployment.id)._handle_app_processes_and_dummy_build() + deployment = Deployment.objects.get(id=simple_deployment.id) + assert deployment.processes["web"].command == "npm run server" + assert Build.objects.filter(uuid=deployment.build_id).exists()