Skip to content

Commit c92ed90

Browse files
committed
feat(k8s-dbs): 新增vm参数校验功能 #15016
1 parent e64498b commit c92ed90

File tree

13 files changed

+917
-30
lines changed

13 files changed

+917
-30
lines changed

dbm-services/k8s-dbs/core/provider/cluster_provider.go

Lines changed: 59 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
coreconst "k8s-dbs/core/constant"
3030
coreentity "k8s-dbs/core/entity"
3131
coreutil "k8s-dbs/core/util"
32+
corevalidator "k8s-dbs/core/validator"
3233
infrautil "k8s-dbs/infrastructure/util"
3334
metaentity "k8s-dbs/metadata/entity"
3435
metaprovider "k8s-dbs/metadata/provider"
@@ -68,6 +69,7 @@ type ClusterProvider struct {
6869
clusterHelmRepoProvider metaprovider.AddonClusterHelmRepoProvider
6970
ClusterTagProvider metaprovider.K8sCrdClusterTagProvider
7071
dbmAPIService *thirdapi.DbmAPIService
72+
envValidator *corevalidator.EnvValidator
7173
}
7274

7375
// ClusterProviderOptions ClusterProvider 的函数选项
@@ -157,6 +159,15 @@ func (c *ClusterProviderBuilder) WithDbmAPIService(
157159
}
158160
}
159161

162+
// WithEnvValidator 设置 EnvValidator
163+
func (c *ClusterProviderBuilder) WithEnvValidator(
164+
validator *corevalidator.EnvValidator,
165+
) ClusterProviderOptions {
166+
return func(c *ClusterProvider) {
167+
c.envValidator = validator
168+
}
169+
}
170+
160171
// validateProvider 验证 ClusterProvider 必要字段
161172
func (c *ClusterProvider) validateProvider() error {
162173
if c.clusterMetaProvider == nil {
@@ -216,9 +227,14 @@ func InstanceSetGVR() schema.GroupVersionResource {
216227
// CreateCluster 创建集群
217228
func (c *ClusterProvider) CreateCluster(ctx *commentity.DbsContext, request *coreentity.Request) error {
218229
// 检查集群版本
219-
if err := c.checkClusterVersion(request); err != nil {
230+
addonID, err := c.checkClusterVersion(request)
231+
if err != nil {
220232
return err
221233
}
234+
// 验证环境变量参数
235+
if err := c.validateComponentEnv(addonID, request); err != nil {
236+
return dbserrors.NewK8sDbsError(dbserrors.ParameterInvalidError, err)
237+
}
222238
// 检查是否重复创建
223239
k8sClusterConfig, err := c.clusterConfigProvider.FindConfigByName(request.K8sClusterName)
224240
if err != nil {
@@ -496,35 +512,37 @@ func (c *ClusterProvider) syncClusterUpdatedWithContext(
496512
//
497513
// 返回值:
498514
//
515+
// uint64 - 匹配到的 addon ID
499516
// error - 检查过程中遇到的错误,如果检查通过则为nil
500-
// bool - 是否发生了错误,true表示有错误发生
501-
func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error {
517+
func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) (uint64, error) {
502518
addonQueryParams := &metaentity.AddonQueryParams{
503519
AddonType: request.StorageAddonType,
504520
AddonVersion: request.StorageAddonVersion,
505521
}
506522
storageAddon, err := c.addonMetaProvider.FindStorageAddonByParams(addonQueryParams)
507523
if err != nil {
508-
return dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError,
524+
return 0, dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError,
509525
fmt.Errorf("查询存储插件元数据失败: %w", err))
510526
}
511527
if len(storageAddon) == 0 {
512-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
528+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
513529
fmt.Errorf("插件类型 '%s' 版本 '%s' 不存在或未配置,请检查插件配置", request.StorageAddonType, request.StorageAddonVersion))
514530
}
515531

532+
addonID := storageAddon[0].ID
533+
516534
// 反序列化支持的版本列表
517535
var supportedVersions []string
518536
if err := json.Unmarshal([]byte(storageAddon[0].SupportedVersions), &supportedVersions); err != nil {
519537
slog.Error("failed to unmarshal supported versions", "error", err)
520-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
538+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
521539
fmt.Errorf("supported versions 反序列化失败"))
522540
}
523541

524542
// 检查组件版本是否在支持的版本列表中
525543
for _, component := range request.ComponentList {
526544
if !lo.Contains(supportedVersions, component.Version) {
527-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
545+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
528546
fmt.Errorf("组件 %s 的版本 %s 不在支持的版本列表中,支持的版本: %v",
529547
component.ComponentName, component.Version, supportedVersions))
530548
}
@@ -534,16 +552,16 @@ func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error
534552
var supportedAcVersions []string
535553
if err := json.Unmarshal([]byte(storageAddon[0].SupportedAcVersions), &supportedAcVersions); err != nil {
536554
slog.Error("failed to unmarshal supported ac versions", "error", err)
537-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
555+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
538556
fmt.Errorf("supported ac versions 反序列化失败"))
539557
}
540558

541559
if !lo.Contains(supportedAcVersions, request.AddonClusterVersion) {
542-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
560+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
543561
fmt.Errorf("addonClusterVersion 版本 %s 不在支持的版本列表中,支持的版本: %v",
544562
request.AddonClusterVersion, supportedAcVersions))
545563
}
546-
return nil
564+
return addonID, nil
547565
}
548566

549567
// saveClusterReleaseMeta 记录集群 release 元数据
@@ -650,6 +668,10 @@ func (c *ClusterProvider) UpdateClusterRelease(
650668
if err := c.validateAddonClusterVersion(request, clusterEntity); err != nil {
651669
return err
652670
}
671+
// 验证环境变量参数
672+
if err := c.validateComponentEnv(clusterEntity.AddonID, request); err != nil {
673+
return dbserrors.NewK8sDbsError(dbserrors.ParameterInvalidError, err)
674+
}
653675
// 更新 cluster release
654676
values, err := c.updateClusterRelease(ctx, request, k8sClient, isPartial)
655677
if err != nil {
@@ -1327,3 +1349,30 @@ func (c *ClusterProvider) validateAddonClusterVersion(
13271349
}
13281350
return nil
13291351
}
1352+
1353+
// validateComponentEnv 验证组件环境变量参数
1354+
func (c *ClusterProvider) validateComponentEnv(addonID uint64, request *coreentity.Request) error {
1355+
if c.envValidator == nil {
1356+
// 如果没有配置验证器,跳过验证
1357+
return nil
1358+
}
1359+
if request.ComponentList == nil {
1360+
return nil
1361+
}
1362+
1363+
for _, component := range request.ComponentList {
1364+
if component.Env == nil {
1365+
continue
1366+
}
1367+
// 使用组件的服务版本进行参数验证
1368+
if err := c.envValidator.ValidateVMComponentEnv(
1369+
addonID,
1370+
component.Version,
1371+
component.ComponentName,
1372+
component.Env,
1373+
); err != nil {
1374+
return fmt.Errorf("组件 '%s' 环境变量验证失败: %w", component.ComponentName, err)
1375+
}
1376+
}
1377+
return nil
1378+
}
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
TencentBlueKing is pleased to support the open source community by making
3+
蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available.
4+
5+
Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved.
6+
7+
Licensed under the MIT License (the "License");
8+
you may not use this file except in compliance with the License.
9+
10+
You may obtain a copy of the License at
11+
https://opensource.org/licenses/MIT
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
// Package validator 提供环境变量参数验证功能
21+
package validator
22+
23+
import (
24+
"fmt"
25+
"strconv"
26+
27+
metaentity "k8s-dbs/metadata/entity"
28+
metaprovider "k8s-dbs/metadata/provider"
29+
)
30+
31+
// EnvValidator 环境变量验证器
32+
type EnvValidator struct {
33+
paramConfigProvider metaprovider.K8sComponentParamConfigProvider
34+
}
35+
36+
// NewEnvValidator 创建验证器实例
37+
func NewEnvValidator(provider metaprovider.K8sComponentParamConfigProvider) *EnvValidator {
38+
return &EnvValidator{paramConfigProvider: provider}
39+
}
40+
41+
// ValidateComponentEnv 验证组件环境变量
42+
// 参数:addonID(存储addon的ID), serviceVersion(服务版本), componentName(组件名), env(环境变量)
43+
// 目前只验证 EXTRA_ARGS 模式
44+
func (v *EnvValidator) ValidateVMComponentEnv(
45+
addonID uint64,
46+
serviceVersion string,
47+
componentName string,
48+
env map[string]interface{},
49+
) error {
50+
if env == nil {
51+
return nil
52+
}
53+
54+
// 检查是否有 EXTRA_ARGS
55+
extraArgs, ok := env["EXTRA_ARGS"]
56+
if !ok {
57+
return nil
58+
}
59+
60+
extraArgsMap, ok := extraArgs.(map[string]interface{})
61+
if !ok {
62+
return fmt.Errorf("EXTRA_ARGS must be a map")
63+
}
64+
65+
// 如果 EXTRA_ARGS 为空,跳过验证
66+
if len(extraArgsMap) == 0 {
67+
return nil
68+
}
69+
70+
// 从数据库获取该组件支持的参数配置(精确匹配版本)
71+
supportedParams, err := v.paramConfigProvider.FindByVersionAndComponent(addonID, serviceVersion, componentName)
72+
if err != nil {
73+
return err
74+
}
75+
76+
// 如果没有配置任何参数规则,跳过验证
77+
if len(supportedParams) == 0 {
78+
return nil
79+
}
80+
81+
// 构建支持的参数 map
82+
supportedParamsMap := make(map[string]*metaentity.K8sComponentParamConfigEntity)
83+
for _, param := range supportedParams {
84+
if param.EnvSource == metaentity.EnvSourceExtraArgs {
85+
supportedParamsMap[param.ParamName] = param
86+
}
87+
}
88+
89+
// 验证每个 EXTRA_ARGS 参数
90+
for key, value := range extraArgsMap {
91+
paramConfig, exists := supportedParamsMap[key]
92+
if !exists {
93+
return fmt.Errorf("parameter '%s' is not supported for component '%s'", key, componentName)
94+
}
95+
96+
if err := v.validateParamType(key, value, paramConfig.ParamType); err != nil {
97+
return err
98+
}
99+
}
100+
101+
return nil
102+
}
103+
104+
// validateParamType 验证参数类型
105+
func (v *EnvValidator) validateParamType(
106+
paramName string,
107+
value interface{},
108+
paramType metaentity.ParamType,
109+
) error {
110+
switch paramType {
111+
case metaentity.ParamTypeString:
112+
// string 类型不需要额外验证
113+
return nil
114+
case metaentity.ParamTypeInt:
115+
return v.validateInt(paramName, value)
116+
case metaentity.ParamTypeBool:
117+
return v.validateBool(paramName, value)
118+
default:
119+
// 未知类型当作 string 处理
120+
return nil
121+
}
122+
}
123+
124+
// validateInt 验证整数类型
125+
func (v *EnvValidator) validateInt(paramName string, value interface{}) error {
126+
switch val := value.(type) {
127+
case int, int32, int64, float64:
128+
return nil
129+
case string:
130+
if _, err := strconv.ParseInt(val, 10, 64); err != nil {
131+
return fmt.Errorf("parameter '%s' must be an integer, got '%s'", paramName, val)
132+
}
133+
return nil
134+
default:
135+
return fmt.Errorf("parameter '%s' must be an integer, got type %T", paramName, value)
136+
}
137+
}
138+
139+
// validateBool 验证布尔类型
140+
func (v *EnvValidator) validateBool(paramName string, value interface{}) error {
141+
switch val := value.(type) {
142+
case bool:
143+
return nil
144+
case string:
145+
if _, err := strconv.ParseBool(val); err != nil {
146+
return fmt.Errorf("parameter '%s' must be a boolean (true/false), got '%s'", paramName, val)
147+
}
148+
return nil
149+
default:
150+
return fmt.Errorf("parameter '%s' must be a boolean, got type %T", paramName, value)
151+
}
152+
}

0 commit comments

Comments
 (0)