diff --git a/dbm-services/k8s-dbs/core/provider/cluster_provider.go b/dbm-services/k8s-dbs/core/provider/cluster_provider.go index a3c00e9596..3561a77850 100644 --- a/dbm-services/k8s-dbs/core/provider/cluster_provider.go +++ b/dbm-services/k8s-dbs/core/provider/cluster_provider.go @@ -29,6 +29,7 @@ import ( coreconst "k8s-dbs/core/constant" coreentity "k8s-dbs/core/entity" coreutil "k8s-dbs/core/util" + corevalidator "k8s-dbs/core/validator" infrautil "k8s-dbs/infrastructure/util" metaentity "k8s-dbs/metadata/entity" metaprovider "k8s-dbs/metadata/provider" @@ -68,6 +69,7 @@ type ClusterProvider struct { clusterHelmRepoProvider metaprovider.AddonClusterHelmRepoProvider ClusterTagProvider metaprovider.K8sCrdClusterTagProvider dbmAPIService *thirdapi.DbmAPIService + envValidator *corevalidator.EnvValidator } // ClusterProviderOptions ClusterProvider 的函数选项 @@ -157,6 +159,15 @@ func (c *ClusterProviderBuilder) WithDbmAPIService( } } +// WithEnvValidator 设置 EnvValidator +func (c *ClusterProviderBuilder) WithEnvValidator( + validator *corevalidator.EnvValidator, +) ClusterProviderOptions { + return func(c *ClusterProvider) { + c.envValidator = validator + } +} + // validateProvider 验证 ClusterProvider 必要字段 func (c *ClusterProvider) validateProvider() error { if c.clusterMetaProvider == nil { @@ -216,9 +227,14 @@ func InstanceSetGVR() schema.GroupVersionResource { // CreateCluster 创建集群 func (c *ClusterProvider) CreateCluster(ctx *commentity.DbsContext, request *coreentity.Request) error { // 检查集群版本 - if err := c.checkClusterVersion(request); err != nil { + addonID, err := c.checkClusterVersion(request) + if err != nil { return err } + // 验证环境变量参数 + if err := c.validateComponentEnv(addonID, request); err != nil { + return dbserrors.NewK8sDbsError(dbserrors.ParameterInvalidError, err) + } // 检查是否重复创建 k8sClusterConfig, err := c.clusterConfigProvider.FindConfigByName(request.K8sClusterName) if err != nil { @@ -298,35 +314,37 @@ func (c *ClusterProvider) CreateCluster(ctx *commentity.DbsContext, request *cor // // 返回值: // +// uint64 - 匹配到的 addon ID // error - 检查过程中遇到的错误,如果检查通过则为nil -// bool - 是否发生了错误,true表示有错误发生 -func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error { +func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) (uint64, error) { addonQueryParams := &metaentity.AddonQueryParams{ AddonType: request.StorageAddonType, AddonVersion: request.StorageAddonVersion, } storageAddon, err := c.addonMetaProvider.FindStorageAddonByParams(addonQueryParams) if err != nil { - return dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError, + return 0, dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError, fmt.Errorf("查询存储插件元数据失败: %w", err)) } if len(storageAddon) == 0 { - return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, + return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, fmt.Errorf("插件类型 '%s' 版本 '%s' 不存在或未配置,请检查插件配置", request.StorageAddonType, request.StorageAddonVersion)) } + addonID := storageAddon[0].ID + // 反序列化支持的版本列表 var supportedVersions []string if err := json.Unmarshal([]byte(storageAddon[0].SupportedVersions), &supportedVersions); err != nil { slog.Error("failed to unmarshal supported versions", "error", err) - return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, + return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, fmt.Errorf("supported versions 反序列化失败")) } // 检查组件版本是否在支持的版本列表中 for _, component := range request.ComponentList { if !lo.Contains(supportedVersions, component.Version) { - return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, + return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, fmt.Errorf("组件 %s 的版本 %s 不在支持的版本列表中,支持的版本: %v", component.ComponentName, component.Version, supportedVersions)) } @@ -336,16 +354,16 @@ func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error var supportedAcVersions []string if err := json.Unmarshal([]byte(storageAddon[0].SupportedAcVersions), &supportedAcVersions); err != nil { slog.Error("failed to unmarshal supported ac versions", "error", err) - return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, + return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, fmt.Errorf("supported ac versions 反序列化失败")) } if !lo.Contains(supportedAcVersions, request.AddonClusterVersion) { - return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, + return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError, fmt.Errorf("addonClusterVersion 版本 %s 不在支持的版本列表中,支持的版本: %v", request.AddonClusterVersion, supportedAcVersions)) } - return nil + return addonID, nil } // saveClusterReleaseMeta 记录集群 release 元数据 @@ -452,6 +470,10 @@ func (c *ClusterProvider) UpdateClusterRelease( if err := c.validateAddonClusterVersion(request, clusterEntity); err != nil { return err } + // 验证环境变量参数 + if err := c.validateComponentEnv(clusterEntity.AddonID, request); err != nil { + return dbserrors.NewK8sDbsError(dbserrors.ParameterInvalidError, err) + } // 更新 cluster release values, err := c.updateClusterRelease(ctx, request, k8sClient, isPartial) if err != nil { @@ -1129,3 +1151,30 @@ func (c *ClusterProvider) validateAddonClusterVersion( } return nil } + +// validateComponentEnv 验证组件环境变量参数 +func (c *ClusterProvider) validateComponentEnv(addonID uint64, request *coreentity.Request) error { + if c.envValidator == nil { + // 如果没有配置验证器,跳过验证 + return nil + } + if request.ComponentList == nil { + return nil + } + + for _, component := range request.ComponentList { + if component.Env == nil { + continue + } + // 使用组件的服务版本进行参数验证 + if err := c.envValidator.ValidateVMEnv( + addonID, + component.Version, + component.ComponentName, + component.Env, + ); err != nil { + return fmt.Errorf("组件 '%s' 环境变量验证失败: %w", component.ComponentName, err) + } + } + return nil +} diff --git a/dbm-services/k8s-dbs/core/validator/env_validator.go b/dbm-services/k8s-dbs/core/validator/env_validator.go new file mode 100644 index 0000000000..a21a4960b2 --- /dev/null +++ b/dbm-services/k8s-dbs/core/validator/env_validator.go @@ -0,0 +1,168 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +// Package validator 提供环境变量参数验证功能 +package validator + +import ( + "fmt" + "strconv" + + metaentity "k8s-dbs/metadata/entity" + metaprovider "k8s-dbs/metadata/provider" +) + +// EnvValidator 环境变量验证器 +type EnvValidator struct { + paramConfigProvider metaprovider.AddonParamConfigProvider + addonProvider metaprovider.K8sCrdStorageAddonProvider +} + +// NewEnvValidator 创建验证器实例 +func NewEnvValidator( + paramConfigProvider metaprovider.AddonParamConfigProvider, + addonProvider metaprovider.K8sCrdStorageAddonProvider, +) *EnvValidator { + return &EnvValidator{ + paramConfigProvider: paramConfigProvider, + addonProvider: addonProvider, + } +} + +// ValidateVMEnv 验证 VictoriaMetrics 组件环境变量 +// 参数:addonID(存储addon的ID), serviceVersion(服务版本), componentName(组件名), env(环境变量) +// 目前只验证 EXTRA_ARGS 模式 +func (v *EnvValidator) ValidateVMEnv( + addonID uint64, + serviceVersion string, + componentName string, + env map[string]interface{}, +) error { + // 检查 addon 是否启用参数校验 + addon, err := v.addonProvider.FindStorageAddonByID(addonID) + if err != nil { + // 找不到 addon 或查询出错,跳过验证 + return nil + } + if !addon.EnableEnvValidation { + // 未启用参数校验,跳过验证 + return nil + } + + if env == nil { + return nil + } + + // 检查是否有 EXTRA_ARGS + extraArgs, ok := env["EXTRA_ARGS"] + if !ok { + return nil + } + + extraArgsMap, ok := extraArgs.(map[string]interface{}) + if !ok { + return fmt.Errorf("EXTRA_ARGS must be a map") + } + + // 如果 EXTRA_ARGS 为空,跳过验证 + if len(extraArgsMap) == 0 { + return nil + } + + // 从数据库获取该组件支持的参数配置 + supportedParams, err := v.paramConfigProvider.FindByVersionAndComponent(addonID, serviceVersion, componentName) + if err != nil { + return err + } + + // 如果没有配置任何参数规则,跳过验证 + if len(supportedParams) == 0 { + return nil + } + + // 构建支持的参数 map + supportedParamsMap := make(map[string]*metaentity.AddonParamConfigEntity) + for _, param := range supportedParams { + supportedParamsMap[param.ParamName] = param + } + + // 验证每个 EXTRA_ARGS 参数 + for key, value := range extraArgsMap { + paramConfig, exists := supportedParamsMap[key] + if !exists { + return fmt.Errorf("parameter '%s' is not supported for component '%s'", key, componentName) + } + + if err := v.validateParamType(key, value, paramConfig.ParamType); err != nil { + return err + } + } + + return nil +} + +// validateParamType 验证参数类型 +func (v *EnvValidator) validateParamType( + paramName string, + value interface{}, + paramType metaentity.ParamType, +) error { + switch paramType { + case metaentity.ParamTypeString: + // string 类型不需要额外验证 + return nil + case metaentity.ParamTypeInt: + return v.validateInt(paramName, value) + case metaentity.ParamTypeBool: + return v.validateBool(paramName, value) + default: + // 未知类型当作 string 处理 + return nil + } +} + +// validateInt 验证整数类型 +func (v *EnvValidator) validateInt(paramName string, value interface{}) error { + switch val := value.(type) { + case int, int32, int64, float64: + return nil + case string: + if _, err := strconv.ParseInt(val, 10, 64); err != nil { + return fmt.Errorf("parameter '%s' must be an integer, got '%s'", paramName, val) + } + return nil + default: + return fmt.Errorf("parameter '%s' must be an integer, got type %T", paramName, value) + } +} + +// validateBool 验证布尔类型 +func (v *EnvValidator) validateBool(paramName string, value interface{}) error { + switch val := value.(type) { + case bool: + return nil + case string: + if _, err := strconv.ParseBool(val); err != nil { + return fmt.Errorf("parameter '%s' must be a boolean (true/false), got '%s'", paramName, val) + } + return nil + default: + return fmt.Errorf("parameter '%s' must be a boolean, got type %T", paramName, value) + } +} diff --git a/dbm-services/k8s-dbs/metadata/api/controller/component_param_config_controller.go b/dbm-services/k8s-dbs/metadata/api/controller/component_param_config_controller.go new file mode 100644 index 0000000000..5807e03471 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/api/controller/component_param_config_controller.go @@ -0,0 +1,215 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package controller + +import ( + "k8s-dbs/common/api" + commconst "k8s-dbs/common/constant" + "k8s-dbs/errors" + metaentity "k8s-dbs/metadata/entity" + metamodel "k8s-dbs/metadata/model" + "k8s-dbs/metadata/provider" + "k8s-dbs/metadata/vo/request" + "k8s-dbs/metadata/vo/response" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/jinzhu/copier" +) + +// AddonParamConfigDbAccess 数据库访问接口(用于 Controller) +type AddonParamConfigDbAccess interface { + Create(model *metamodel.AddonParamConfigModel) (*metamodel.AddonParamConfigModel, error) + BatchCreate(models []*metamodel.AddonParamConfigModel) error + DeleteByID(id uint64) (uint64, error) + Update(model *metamodel.AddonParamConfigModel) (uint64, error) +} + +// AddonParamConfigController 组件参数配置控制器 +type AddonParamConfigController struct { + provider provider.AddonParamConfigProvider + dbAccess AddonParamConfigDbAccess +} + +// NewAddonParamConfigController 创建控制器实例 +func NewAddonParamConfigController( + p provider.AddonParamConfigProvider, + dbAccess AddonParamConfigDbAccess, +) *AddonParamConfigController { + return &AddonParamConfigController{provider: p, dbAccess: dbAccess} +} + +// List 查询参数配置列表 +func (c *AddonParamConfigController) List(ctx *gin.Context) { + ctx.Set(commconst.APIName, "component_param_config_list") + + var params metaentity.AddonParamConfigQueryParams + + // 从查询参数获取过滤条件 + if addonIDStr := ctx.Query("addonId"); addonIDStr != "" { + if addonID, err := strconv.ParseUint(addonIDStr, 10, 64); err == nil { + params.AddonID = addonID + } + } + params.ServiceVersion = ctx.Query("serviceVersion") + params.ComponentName = ctx.Query("componentName") + + configs, err := c.provider.FindByParams(¶ms) + if err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.GetMetaDataError, err)) + return + } + + var respList []response.AddonParamConfigResponse + if err = copier.Copy(&respList, configs); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.GetMetaDataError, err)) + return + } + + api.SuccessResponse(ctx, respList, commconst.Success) +} + +// Create 创建单条参数配置 +func (c *AddonParamConfigController) Create(ctx *gin.Context) { + ctx.Set(commconst.APIName, "component_param_config_create") + + var reqVo request.AddonParamConfigRequest + if err := ctx.ShouldBindJSON(&reqVo); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.ParameterInvalidError, err)) + return + } + + model := &metamodel.AddonParamConfigModel{ + AddonID: reqVo.AddonID, + ServiceVersion: reqVo.ServiceVersion, + ComponentName: reqVo.ComponentName, + ParamName: reqVo.ParamName, + ParamType: reqVo.ParamType, + DefaultValue: reqVo.DefaultValue, + Active: true, + } + + added, err := c.dbAccess.Create(model) + if err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.CreateMetaDataError, err)) + return + } + + var resp response.AddonParamConfigResponse + if err = copier.Copy(&resp, added); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.CreateMetaDataError, err)) + return + } + + api.SuccessResponse(ctx, resp, commconst.Success) +} + +// BatchCreate 批量创建参数配置 +func (c *AddonParamConfigController) BatchCreate(ctx *gin.Context) { + ctx.Set(commconst.APIName, "component_param_config_batch_create") + + var reqList []request.AddonParamConfigRequest + if err := ctx.ShouldBindJSON(&reqList); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.ParameterInvalidError, err)) + return + } + + var models []*metamodel.AddonParamConfigModel + for _, req := range reqList { + model := &metamodel.AddonParamConfigModel{ + AddonID: req.AddonID, + ServiceVersion: req.ServiceVersion, + ComponentName: req.ComponentName, + ParamName: req.ParamName, + ParamType: req.ParamType, + DefaultValue: req.DefaultValue, + Active: true, + } + models = append(models, model) + } + + if err := c.dbAccess.BatchCreate(models); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.CreateMetaDataError, err)) + return + } + + api.SuccessResponse(ctx, gin.H{"count": len(models)}, commconst.Success) +} + +// Delete 删除参数配置 +func (c *AddonParamConfigController) Delete(ctx *gin.Context) { + ctx.Set(commconst.APIName, "component_param_config_delete") + + idParam := ctx.Param("id") + id, err := strconv.ParseUint(idParam, 10, 64) + if err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.ParameterInvalidError, err)) + return + } + + if _, err := c.dbAccess.DeleteByID(id); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.DeleteMetaDataError, err)) + return + } + + api.SuccessResponse(ctx, nil, commconst.Success) +} + +// Update 更新参数配置 +func (c *AddonParamConfigController) Update(ctx *gin.Context) { + ctx.Set(commconst.APIName, "component_param_config_update") + + idParam := ctx.Param("id") + id, err := strconv.ParseUint(idParam, 10, 64) + if err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.ParameterInvalidError, err)) + return + } + + var reqVo request.AddonParamConfigRequest + if err := ctx.ShouldBindJSON(&reqVo); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.ParameterInvalidError, err)) + return + } + + model := &metamodel.AddonParamConfigModel{ + ID: id, + AddonID: reqVo.AddonID, + ServiceVersion: reqVo.ServiceVersion, + ComponentName: reqVo.ComponentName, + ParamName: reqVo.ParamName, + ParamType: reqVo.ParamType, + DefaultValue: reqVo.DefaultValue, + Active: true, + } + + if _, err := c.dbAccess.Update(model); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.UpdateMetaDataError, err)) + return + } + + var resp response.AddonParamConfigResponse + if err = copier.Copy(&resp, model); err != nil { + api.ErrorResponse(ctx, errors.NewK8sDbsError(errors.UpdateMetaDataError, err)) + return + } + + api.SuccessResponse(ctx, resp, commconst.Success) +} diff --git a/dbm-services/k8s-dbs/metadata/constant/db_const.go b/dbm-services/k8s-dbs/metadata/constant/db_const.go index a3c21fcfbd..58b175b5bc 100644 --- a/dbm-services/k8s-dbs/metadata/constant/db_const.go +++ b/dbm-services/k8s-dbs/metadata/constant/db_const.go @@ -42,4 +42,5 @@ const ( TbAddonType = "tb_addon_type" TbAddonTopology = "tb_addon_topology" TbAuthUserRole = "bkdata_basic.auth_user_role" + TbAddonParamsConfig = "tb_addon_params_config" ) diff --git a/dbm-services/k8s-dbs/metadata/dbaccess/component_param_config_dbaccess.go b/dbm-services/k8s-dbs/metadata/dbaccess/component_param_config_dbaccess.go new file mode 100644 index 0000000000..c4f94700a3 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/dbaccess/component_param_config_dbaccess.go @@ -0,0 +1,114 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package dbaccess + +import ( + commconst "k8s-dbs/common/constant" + metaentity "k8s-dbs/metadata/entity" + metamodel "k8s-dbs/metadata/model" + + "github.com/pkg/errors" + "gorm.io/gorm" +) + +// AddonParamConfigDbAccess 定义组件参数配置的数据库访问接口 +type AddonParamConfigDbAccess interface { + // FindByParams 根据条件查询参数配置 + FindByParams(params *metaentity.AddonParamConfigQueryParams) ([]*metamodel.AddonParamConfigModel, error) + // Create 创建单条记录 + Create(model *metamodel.AddonParamConfigModel) (*metamodel.AddonParamConfigModel, error) + // BatchCreate 批量创建 + BatchCreate(models []*metamodel.AddonParamConfigModel) error + // DeleteByID 根据 ID 删除 + DeleteByID(id uint64) (uint64, error) + // Update 更新记录 + Update(model *metamodel.AddonParamConfigModel) (uint64, error) +} + +// AddonParamConfigDbAccessImpl AddonParamConfigDbAccess 的具体实现 +type AddonParamConfigDbAccessImpl struct { + db *gorm.DB +} + +// FindByParams 根据条件查询参数配置 +func (k *AddonParamConfigDbAccessImpl) FindByParams( + params *metaentity.AddonParamConfigQueryParams, +) ([]*metamodel.AddonParamConfigModel, error) { + var models []*metamodel.AddonParamConfigModel + err := k.db. + Where(params). + Limit(commconst.MaxFetchSize). + Find(&models).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + if err != nil { + return nil, errors.Wrapf(err, "failed to find component param config with params %+v", params) + } + return models, nil +} + +// Create 创建单条记录 +func (k *AddonParamConfigDbAccessImpl) Create( + model *metamodel.AddonParamConfigModel, +) (*metamodel.AddonParamConfigModel, error) { + if err := k.db.Create(model).Error; err != nil { + return nil, errors.Wrapf(err, "failed to create component param config with model %+v", model) + } + return model, nil +} + +// BatchCreate 批量创建 +func (k *AddonParamConfigDbAccessImpl) BatchCreate( + models []*metamodel.AddonParamConfigModel, +) error { + if len(models) == 0 { + return nil + } + if err := k.db.Create(models).Error; err != nil { + return errors.Wrapf(err, "failed to batch create component param configs") + } + return nil +} + +// DeleteByID 根据 ID 删除 +func (k *AddonParamConfigDbAccessImpl) DeleteByID(id uint64) (uint64, error) { + result := k.db.Delete(&metamodel.AddonParamConfigModel{}, id) + if result.Error != nil { + return 0, errors.Wrapf(result.Error, "failed to delete component param config with id %d", id) + } + return uint64(result.RowsAffected), nil +} + +// Update 更新记录 +func (k *AddonParamConfigDbAccessImpl) Update( + model *metamodel.AddonParamConfigModel, +) (uint64, error) { + result := k.db.Omit("CreatedAt", "CreatedBy").Save(model) + if result.Error != nil { + return 0, errors.Wrapf(result.Error, "failed to update component param config with model %+v", model) + } + return uint64(result.RowsAffected), nil +} + +// NewAddonParamConfigDbAccess 创建 AddonParamConfigDbAccess 接口实现实例 +func NewAddonParamConfigDbAccess(db *gorm.DB) AddonParamConfigDbAccess { + return &AddonParamConfigDbAccessImpl{db: db} +} diff --git a/dbm-services/k8s-dbs/metadata/entity/addon_entity.go b/dbm-services/k8s-dbs/metadata/entity/addon_entity.go index 063fb47ee7..fc7f74edb0 100644 --- a/dbm-services/k8s-dbs/metadata/entity/addon_entity.go +++ b/dbm-services/k8s-dbs/metadata/entity/addon_entity.go @@ -36,6 +36,7 @@ type K8sCrdStorageAddonEntity struct { Releases string `json:"releases"` Active bool `json:"active"` Description string `json:"description"` + EnableEnvValidation bool `json:"enableEnvValidation"` CreatedBy string `json:"createdBy"` CreatedAt commtypes.JSONDatetime `json:"createdAt"` UpdatedBy string `json:"updatedBy"` diff --git a/dbm-services/k8s-dbs/metadata/entity/component_param_config_entity.go b/dbm-services/k8s-dbs/metadata/entity/component_param_config_entity.go new file mode 100644 index 0000000000..d86f28ff36 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/entity/component_param_config_entity.go @@ -0,0 +1,68 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package entity + +import commtypes "k8s-dbs/common/types" + +// EnvSourceType 环境变量来源类型 +type EnvSourceType string + +const ( + // EnvSourceExtraArgs EXTRA_ARGS 模式:参数以 map 形式传入,转换为 --key=value 格式 + EnvSourceExtraArgs EnvSourceType = "EXTRA_ARGS" +) + +// ParamType 参数类型 +type ParamType string + +const ( + // ParamTypeString 字符串类型 + ParamTypeString ParamType = "STRING" + + // ParamTypeInt 整数类型 + ParamTypeInt ParamType = "INTEGER" + + // ParamTypeBool 布尔类型 + ParamTypeBool ParamType = "BOOLEAN" +) + +// AddonParamConfigEntity 组件参数配置实体 +type AddonParamConfigEntity struct { + ID uint64 `json:"id"` + AddonID uint64 `json:"addonId"` + ServiceVersion string `json:"serviceVersion"` + ComponentName string `json:"componentName"` + ParamName string `json:"paramName"` + ParamType ParamType `json:"paramType"` + DefaultValue *string `json:"defaultValue"` + Active bool `json:"active"` + CreatedBy string `json:"createdBy"` + CreatedAt commtypes.JSONDatetime `json:"createdAt"` + UpdatedBy string `json:"updatedBy"` + UpdatedAt commtypes.JSONDatetime `json:"updatedAt"` +} + +// AddonParamConfigQueryParams 组件参数配置查询参数 +type AddonParamConfigQueryParams struct { + AddonID uint64 `gorm:"column:addon_id" json:"addonId"` + ServiceVersion string `gorm:"column:service_version" json:"serviceVersion"` + ComponentName string `gorm:"column:component_name" json:"componentName"` + Active *bool `gorm:"column:active" json:"active"` +} diff --git a/dbm-services/k8s-dbs/metadata/model/addon_model.go b/dbm-services/k8s-dbs/metadata/model/addon_model.go index 3b8c1c5ab2..8be780c367 100644 --- a/dbm-services/k8s-dbs/metadata/model/addon_model.go +++ b/dbm-services/k8s-dbs/metadata/model/addon_model.go @@ -39,6 +39,7 @@ type K8sCrdStorageAddonModel struct { Releases string `gorm:"type:text;column:releases" json:"releases"` Active bool `gorm:"type:tinyint(1);not null;default:1;column:active" json:"active"` Description string `gorm:"size:100;column:description" json:"description"` + EnableEnvValidation bool `gorm:"type:tinyint(1);not null;default:0;column:enable_env_validation" json:"enableEnvValidation"` //nolint:lll CreatedBy string `gorm:"size:50;not null;column:created_by" json:"createdBy"` CreatedAt commtypes.JSONDatetime `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP;column:created_at" json:"createdAt"` //nolint:lll UpdatedBy string `gorm:"size:50;not null;column:updated_by" json:"updatedBy"` diff --git a/dbm-services/k8s-dbs/metadata/model/component_param_config_model.go b/dbm-services/k8s-dbs/metadata/model/component_param_config_model.go new file mode 100644 index 0000000000..8c99a4a5c9 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/model/component_param_config_model.go @@ -0,0 +1,47 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package model + +import ( + commtypes "k8s-dbs/common/types" + "k8s-dbs/metadata/constant" +) + +// AddonParamConfigModel 组件参数配置模型 +// 用于存储各存储类型组件支持的参数配置 +type AddonParamConfigModel struct { + ID uint64 `gorm:"primaryKey;autoIncrement;column:id" json:"id"` + AddonID uint64 `gorm:"not null;column:addon_id" json:"addonId"` + ServiceVersion string `gorm:"size:32;not null;column:service_version" json:"serviceVersion"` + ComponentName string `gorm:"size:32;not null;column:component_name" json:"componentName"` + ParamName string `gorm:"size:64;not null;column:param_name" json:"paramName"` + ParamType string `gorm:"size:32;not null;column:param_type" json:"paramType"` + DefaultValue *string `gorm:"size:64;column:default_value" json:"defaultValue"` + Active bool `gorm:"type:tinyint(1);not null;default:1;column:active" json:"active"` + CreatedBy string `gorm:"size:50;not null;column:created_by" json:"createdBy"` + CreatedAt commtypes.JSONDatetime `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP;column:created_at" json:"createdAt"` //nolint:lll + UpdatedBy string `gorm:"size:50;not null;column:updated_by" json:"updatedBy"` + UpdatedAt commtypes.JSONDatetime `gorm:"type:timestamp;not null;default:CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP;column:updated_at" json:"updatedAt"` //nolint:lll +} + +// TableName 获取 model 对应的数据库表名 +func (AddonParamConfigModel) TableName() string { + return constant.TbAddonParamsConfig +} diff --git a/dbm-services/k8s-dbs/metadata/provider/component_param_config_provider.go b/dbm-services/k8s-dbs/metadata/provider/component_param_config_provider.go new file mode 100644 index 0000000000..a17d73b6ae --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/provider/component_param_config_provider.go @@ -0,0 +1,90 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package provider + +import ( + "k8s-dbs/metadata/dbaccess" + metaentity "k8s-dbs/metadata/entity" + + "github.com/jinzhu/copier" + "github.com/pkg/errors" +) + +// AddonParamConfigProvider 定义组件参数配置业务逻辑层访问接口 +type AddonParamConfigProvider interface { + // FindByVersionAndComponent 根据版本和组件查询参数配置 + // 实现版本匹配规则:精确匹配 > 空(跳过验证) + FindByVersionAndComponent( + addonID uint64, serviceVersion, componentName string, + ) ([]*metaentity.AddonParamConfigEntity, error) + + // FindByParams 根据参数查询 + FindByParams( + params *metaentity.AddonParamConfigQueryParams, + ) ([]*metaentity.AddonParamConfigEntity, error) +} + +// AddonParamConfigProviderImpl AddonParamConfigProvider 具体实现 +type AddonParamConfigProviderImpl struct { + dbAccess dbaccess.AddonParamConfigDbAccess +} + +// FindByVersionAndComponent 根据版本和组件查询参数配置 +func (k *AddonParamConfigProviderImpl) FindByVersionAndComponent( + addonID uint64, serviceVersion, componentName string, +) ([]*metaentity.AddonParamConfigEntity, error) { + active := true + params := &metaentity.AddonParamConfigQueryParams{ + AddonID: addonID, + ServiceVersion: serviceVersion, + ComponentName: componentName, + Active: &active, + } + return k.FindByParams(params) +} + +// FindByParams 根据参数查询 +func (k *AddonParamConfigProviderImpl) FindByParams( + params *metaentity.AddonParamConfigQueryParams, +) ([]*metaentity.AddonParamConfigEntity, error) { + models, err := k.dbAccess.FindByParams(params) + if err != nil { + return nil, errors.Wrapf(err, "failed to find component param config with params %+v", params) + } + + var entities []*metaentity.AddonParamConfigEntity + if err = copier.Copy(&entities, models); err != nil { + return nil, errors.Wrap(err, "failed to copy models to entities") + } + + // 转换 ParamType 类型 + for i, model := range models { + entities[i].ParamType = metaentity.ParamType(model.ParamType) + } + + return entities, nil +} + +// NewAddonParamConfigProvider 创建 AddonParamConfigProvider 接口实现实例 +func NewAddonParamConfigProvider( + dbAccess dbaccess.AddonParamConfigDbAccess, +) AddonParamConfigProvider { + return &AddonParamConfigProviderImpl{dbAccess: dbAccess} +} diff --git a/dbm-services/k8s-dbs/metadata/vo/request/addon_request.go b/dbm-services/k8s-dbs/metadata/vo/request/addon_request.go index d8bd0b7561..82ebe18e5f 100644 --- a/dbm-services/k8s-dbs/metadata/vo/request/addon_request.go +++ b/dbm-services/k8s-dbs/metadata/vo/request/addon_request.go @@ -34,6 +34,7 @@ type AddonRequest struct { Topologies string `json:"topologies" binding:"required"` Releases string `json:"releases" binding:"required"` Description string `json:"description" binding:"required"` + EnableEnvValidation bool `json:"enableEnvValidation"` commentity.BKAuth `json:",inline"` } diff --git a/dbm-services/k8s-dbs/metadata/vo/request/component_param_config_request.go b/dbm-services/k8s-dbs/metadata/vo/request/component_param_config_request.go new file mode 100644 index 0000000000..2fab7e7803 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/vo/request/component_param_config_request.go @@ -0,0 +1,30 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package request + +// AddonParamConfigRequest 组件参数配置请求 +type AddonParamConfigRequest struct { + AddonID uint64 `json:"addonId" binding:"required"` + ServiceVersion string `json:"serviceVersion" binding:"required"` + ComponentName string `json:"componentName" binding:"required"` + ParamName string `json:"paramName" binding:"required"` + ParamType string `json:"paramType"` + DefaultValue *string `json:"defaultValue"` +} diff --git a/dbm-services/k8s-dbs/metadata/vo/response/addon_response.go b/dbm-services/k8s-dbs/metadata/vo/response/addon_response.go index fa1be14a44..48bdd19081 100644 --- a/dbm-services/k8s-dbs/metadata/vo/response/addon_response.go +++ b/dbm-services/k8s-dbs/metadata/vo/response/addon_response.go @@ -40,6 +40,7 @@ type AddonResponse struct { Releases string `json:"releases"` Active bool `json:"active"` Description string `json:"description"` + EnableEnvValidation bool `json:"enableEnvValidation"` CreatedBy string `json:"createdBy"` CreatedAt commtypes.JSONDatetime `json:"createdAt"` UpdatedBy string `json:"updatedBy"` @@ -82,6 +83,7 @@ func (k AddonResponse) MarshalJSON() ([]byte, error) { "releases": releasesArray, "active": k.Active, "description": k.Description, + "enableEnvValidation": k.EnableEnvValidation, "createdBy": k.CreatedBy, "createdAt": k.CreatedAt, "updatedBy": k.UpdatedBy, diff --git a/dbm-services/k8s-dbs/metadata/vo/response/component_param_config_response.go b/dbm-services/k8s-dbs/metadata/vo/response/component_param_config_response.go new file mode 100644 index 0000000000..b41d4da0d7 --- /dev/null +++ b/dbm-services/k8s-dbs/metadata/vo/response/component_param_config_response.go @@ -0,0 +1,38 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package response + +import commtypes "k8s-dbs/common/types" + +// AddonParamConfigResponse 组件参数配置响应 +type AddonParamConfigResponse struct { + ID uint64 `json:"id"` + AddonID uint64 `json:"addonId"` + ServiceVersion string `json:"serviceVersion"` + ComponentName string `json:"componentName"` + ParamName string `json:"paramName"` + ParamType string `json:"paramType"` + DefaultValue *string `json:"defaultValue"` + Active bool `json:"active"` + CreatedBy string `json:"createdBy"` + CreatedAt commtypes.JSONDatetime `json:"createdAt"` + UpdatedBy string `json:"updatedBy"` + UpdatedAt commtypes.JSONDatetime `json:"updatedAt"` +} diff --git a/dbm-services/k8s-dbs/router/metadata/addon_param_config.go b/dbm-services/k8s-dbs/router/metadata/addon_param_config.go new file mode 100644 index 0000000000..316e4efd62 --- /dev/null +++ b/dbm-services/k8s-dbs/router/metadata/addon_param_config.go @@ -0,0 +1,51 @@ +/* +TencentBlueKing is pleased to support the open source community by making +蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. + +Copyright (C) 2017-2023 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 +https://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. +*/ + +package metadata + +import ( + metacontroller "k8s-dbs/metadata/api/controller" + metadbaccess "k8s-dbs/metadata/dbaccess" + metaprovider "k8s-dbs/metadata/provider" + routerutil "k8s-dbs/router/util" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// BuildAddonParamConfigRouter Addon参数配置管理路由构建 +func BuildAddonParamConfigRouter(db *gorm.DB, baseRouter *gin.RouterGroup) { + metaRouter := baseRouter.Group(BasePath) + dbAccess := metadbaccess.NewAddonParamConfigDbAccess(db) + metaProvider := metaprovider.NewAddonParamConfigProvider(dbAccess) + metaController := metacontroller.NewAddonParamConfigController(metaProvider, dbAccess) + + paramConfigGroup := metaRouter.Group("/addon_param_config") + { + paramConfigGroup.GET("", metaController.List) + paramConfigGroup.POST("", metaController.Create) + paramConfigGroup.POST("/batch", metaController.BatchCreate) + paramConfigGroup.PUT("/:id", metaController.Update) + paramConfigGroup.DELETE("/:id", metaController.Delete) + } +} + +func init() { + routerutil.RegisterAPIRouterBuilder(BuildAddonParamConfigRouter) +} diff --git a/dbm-services/k8s-dbs/router/util/router_util.go b/dbm-services/k8s-dbs/router/util/router_util.go index 8b07d3cf94..ce6dd18f53 100644 --- a/dbm-services/k8s-dbs/router/util/router_util.go +++ b/dbm-services/k8s-dbs/router/util/router_util.go @@ -22,6 +22,7 @@ package util import ( "k8s-dbs/common/api" coreprovider "k8s-dbs/core/provider" + corevalidator "k8s-dbs/core/validator" "k8s-dbs/infrastructure/thirdapi" metadbaccess "k8s-dbs/metadata/dbaccess" metaprovider "k8s-dbs/metadata/provider" @@ -98,6 +99,7 @@ func BuildClusterProvider(db *gorm.DB) *coreprovider.ClusterProvider { clusterProviderBuilder.WithAddonMeta(coreAPIProviders.AddonMetaProvider), clusterProviderBuilder.WithClusterTagsMeta(coreAPIProviders.ClusterTagProvider), clusterProviderBuilder.WithDbmAPIService(coreAPIProviders.DbmAPIService), + clusterProviderBuilder.WithEnvValidator(coreAPIProviders.EnvValidator), ) if err != nil { slog.Error("failed to build cluster provider", "error", err) @@ -135,6 +137,11 @@ func BuildCoreAPIProviders(db *gorm.DB) (*CoreAPIProviders, error) { clusterTagProvider := metaprovider.NewK8sCrdClusterTagProvider(metadbaccess.NewK8sCrdClusterTagDbAccess(db)) dbmAPIService := thirdapi.NewDbmAPIService() + + addonParamConfigDbAccess := metadbaccess.NewAddonParamConfigDbAccess(db) + addonParamConfigProvider := metaprovider.NewAddonParamConfigProvider(addonParamConfigDbAccess) + envValidator := corevalidator.NewEnvValidator(addonParamConfigProvider, addonMetaProvider) + return &CoreAPIProviders{ ClusterMetaProvider: clusterMetaProvider, ComponentMetaProvider: componentMetaProvider, @@ -145,6 +152,7 @@ func BuildCoreAPIProviders(db *gorm.DB) (*CoreAPIProviders, error) { AddonMetaProvider: addonMetaProvider, ClusterTagProvider: clusterTagProvider, DbmAPIService: dbmAPIService, + EnvValidator: envValidator, }, nil } @@ -159,6 +167,7 @@ type CoreAPIProviders struct { AddonMetaProvider metaprovider.K8sCrdStorageAddonProvider ClusterTagProvider metaprovider.K8sCrdClusterTagProvider DbmAPIService *thirdapi.DbmAPIService + EnvValidator *corevalidator.EnvValidator } // CustomRouterBuilder 自定义 Router 构建函数 diff --git a/dbm-services/k8s-dbs/scrips/sql/0001_20250630_dbs_mysql.sql b/dbm-services/k8s-dbs/scrips/sql/0001_20250630_dbs_mysql.sql index 30b3d8ee41..95dc0ee6dd 100644 --- a/dbm-services/k8s-dbs/scrips/sql/0001_20250630_dbs_mysql.sql +++ b/dbm-services/k8s-dbs/scrips/sql/0001_20250630_dbs_mysql.sql @@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS tb_k8s_crd_storageaddon ( releases text NOT NULL COMMENT '集群组件版本定义', active tinyint(1) NOT NULL DEFAULT 1 COMMENT '0:无效,1:有效', description varchar(100) Null COMMENT '存储插件描述', + enable_env_validation tinyint(1) NOT NULL DEFAULT 0 COMMENT '是否启用环境变量校验', created_by varchar(50) NOT NULL COMMENT '创建者', created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', updated_by varchar(50) NOT NULL COMMENT '更新者', diff --git a/dbm-services/k8s-dbs/scrips/sql/0015_20251216_dbs_mysql.sql b/dbm-services/k8s-dbs/scrips/sql/0015_20251216_dbs_mysql.sql new file mode 100644 index 0000000000..5125692690 --- /dev/null +++ b/dbm-services/k8s-dbs/scrips/sql/0015_20251216_dbs_mysql.sql @@ -0,0 +1,20 @@ +-- Create a database and set character set and collation + +USE bkbase_dbs; +SET NAMES utf8; + +-- 创建组件参数配置表 +CREATE TABLE IF NOT EXISTS tb_addon_params_config ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY COMMENT '主键', + addon_id BIGINT UNSIGNED NOT NULL COMMENT '关联 tb_k8s_crd_storageaddon 表的 ID', + service_version VARCHAR(32) NOT NULL COMMENT '服务版本: 1.93.10-2.0.0', + component_name VARCHAR(32) NOT NULL COMMENT '组件名: vminsert', + param_name VARCHAR(64) NOT NULL COMMENT '参数名', + param_type ENUM('STRING', 'INTEGER', 'BOOLEAN') DEFAULT 'STRING' COMMENT '参数类型', + default_value VARCHAR(64) DEFAULT NULL COMMENT '参数默认值', + active TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用', + created_by VARCHAR(50) NOT NULL DEFAULT 'system' COMMENT '创建人', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + updated_by VARCHAR(50) NOT NULL DEFAULT 'system' COMMENT '更新人', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='组件参数配置表';