Skip to content

Commit 004ee34

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

18 files changed

+916
-10
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 {
@@ -298,35 +314,37 @@ func (c *ClusterProvider) CreateCluster(ctx *commentity.DbsContext, request *cor
298314
//
299315
// 返回值:
300316
//
317+
// uint64 - 匹配到的 addon ID
301318
// error - 检查过程中遇到的错误,如果检查通过则为nil
302-
// bool - 是否发生了错误,true表示有错误发生
303-
func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error {
319+
func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) (uint64, error) {
304320
addonQueryParams := &metaentity.AddonQueryParams{
305321
AddonType: request.StorageAddonType,
306322
AddonVersion: request.StorageAddonVersion,
307323
}
308324
storageAddon, err := c.addonMetaProvider.FindStorageAddonByParams(addonQueryParams)
309325
if err != nil {
310-
return dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError,
326+
return 0, dbserrors.NewK8sDbsError(dbserrors.GetMetaDataError,
311327
fmt.Errorf("查询存储插件元数据失败: %w", err))
312328
}
313329
if len(storageAddon) == 0 {
314-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
330+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
315331
fmt.Errorf("插件类型 '%s' 版本 '%s' 不存在或未配置,请检查插件配置", request.StorageAddonType, request.StorageAddonVersion))
316332
}
317333

334+
addonID := storageAddon[0].ID
335+
318336
// 反序列化支持的版本列表
319337
var supportedVersions []string
320338
if err := json.Unmarshal([]byte(storageAddon[0].SupportedVersions), &supportedVersions); err != nil {
321339
slog.Error("failed to unmarshal supported versions", "error", err)
322-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
340+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
323341
fmt.Errorf("supported versions 反序列化失败"))
324342
}
325343

326344
// 检查组件版本是否在支持的版本列表中
327345
for _, component := range request.ComponentList {
328346
if !lo.Contains(supportedVersions, component.Version) {
329-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
347+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
330348
fmt.Errorf("组件 %s 的版本 %s 不在支持的版本列表中,支持的版本: %v",
331349
component.ComponentName, component.Version, supportedVersions))
332350
}
@@ -336,16 +354,16 @@ func (c *ClusterProvider) checkClusterVersion(request *coreentity.Request) error
336354
var supportedAcVersions []string
337355
if err := json.Unmarshal([]byte(storageAddon[0].SupportedAcVersions), &supportedAcVersions); err != nil {
338356
slog.Error("failed to unmarshal supported ac versions", "error", err)
339-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
357+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
340358
fmt.Errorf("supported ac versions 反序列化失败"))
341359
}
342360

343361
if !lo.Contains(supportedAcVersions, request.AddonClusterVersion) {
344-
return dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
362+
return 0, dbserrors.NewK8sDbsError(dbserrors.CreateClusterError,
345363
fmt.Errorf("addonClusterVersion 版本 %s 不在支持的版本列表中,支持的版本: %v",
346364
request.AddonClusterVersion, supportedAcVersions))
347365
}
348-
return nil
366+
return addonID, nil
349367
}
350368

351369
// saveClusterReleaseMeta 记录集群 release 元数据
@@ -452,6 +470,10 @@ func (c *ClusterProvider) UpdateClusterRelease(
452470
if err := c.validateAddonClusterVersion(request, clusterEntity); err != nil {
453471
return err
454472
}
473+
// 验证环境变量参数
474+
if err := c.validateComponentEnv(clusterEntity.AddonID, request); err != nil {
475+
return dbserrors.NewK8sDbsError(dbserrors.ParameterInvalidError, err)
476+
}
455477
// 更新 cluster release
456478
values, err := c.updateClusterRelease(ctx, request, k8sClient, isPartial)
457479
if err != nil {
@@ -1129,3 +1151,30 @@ func (c *ClusterProvider) validateAddonClusterVersion(
11291151
}
11301152
return nil
11311153
}
1154+
1155+
// validateComponentEnv 验证组件环境变量参数
1156+
func (c *ClusterProvider) validateComponentEnv(addonID uint64, request *coreentity.Request) error {
1157+
if c.envValidator == nil {
1158+
// 如果没有配置验证器,跳过验证
1159+
return nil
1160+
}
1161+
if request.ComponentList == nil {
1162+
return nil
1163+
}
1164+
1165+
for _, component := range request.ComponentList {
1166+
if component.Env == nil {
1167+
continue
1168+
}
1169+
// 使用组件的服务版本进行参数验证
1170+
if err := c.envValidator.ValidateVMEnv(
1171+
addonID,
1172+
component.Version,
1173+
component.ComponentName,
1174+
component.Env,
1175+
); err != nil {
1176+
return fmt.Errorf("组件 '%s' 环境变量验证失败: %w", component.ComponentName, err)
1177+
}
1178+
}
1179+
return nil
1180+
}
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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.AddonParamConfigProvider
34+
addonProvider metaprovider.K8sCrdStorageAddonProvider
35+
}
36+
37+
// NewEnvValidator 创建验证器实例
38+
func NewEnvValidator(
39+
paramConfigProvider metaprovider.AddonParamConfigProvider,
40+
addonProvider metaprovider.K8sCrdStorageAddonProvider,
41+
) *EnvValidator {
42+
return &EnvValidator{
43+
paramConfigProvider: paramConfigProvider,
44+
addonProvider: addonProvider,
45+
}
46+
}
47+
48+
// ValidateVMEnv 验证 VictoriaMetrics 组件环境变量
49+
// 参数:addonID(存储addon的ID), serviceVersion(服务版本), componentName(组件名), env(环境变量)
50+
// 目前只验证 EXTRA_ARGS 模式
51+
func (v *EnvValidator) ValidateVMEnv(
52+
addonID uint64,
53+
serviceVersion string,
54+
componentName string,
55+
env map[string]interface{},
56+
) error {
57+
// 检查 addon 是否启用参数校验
58+
addon, err := v.addonProvider.FindStorageAddonByID(addonID)
59+
if err != nil {
60+
// 找不到 addon 或查询出错,跳过验证
61+
return nil
62+
}
63+
if !addon.EnableEnvValidation {
64+
// 未启用参数校验,跳过验证
65+
return nil
66+
}
67+
68+
if env == nil {
69+
return nil
70+
}
71+
72+
// 检查是否有 EXTRA_ARGS
73+
extraArgs, ok := env["EXTRA_ARGS"]
74+
if !ok {
75+
return nil
76+
}
77+
78+
extraArgsMap, ok := extraArgs.(map[string]interface{})
79+
if !ok {
80+
return fmt.Errorf("EXTRA_ARGS must be a map")
81+
}
82+
83+
// 如果 EXTRA_ARGS 为空,跳过验证
84+
if len(extraArgsMap) == 0 {
85+
return nil
86+
}
87+
88+
// 从数据库获取该组件支持的参数配置
89+
supportedParams, err := v.paramConfigProvider.FindByVersionAndComponent(addonID, serviceVersion, componentName)
90+
if err != nil {
91+
return err
92+
}
93+
94+
// 如果没有配置任何参数规则,跳过验证
95+
if len(supportedParams) == 0 {
96+
return nil
97+
}
98+
99+
// 构建支持的参数 map
100+
supportedParamsMap := make(map[string]*metaentity.AddonParamConfigEntity)
101+
for _, param := range supportedParams {
102+
supportedParamsMap[param.ParamName] = param
103+
}
104+
105+
// 验证每个 EXTRA_ARGS 参数
106+
for key, value := range extraArgsMap {
107+
paramConfig, exists := supportedParamsMap[key]
108+
if !exists {
109+
return fmt.Errorf("parameter '%s' is not supported for component '%s'", key, componentName)
110+
}
111+
112+
if err := v.validateParamType(key, value, paramConfig.ParamType); err != nil {
113+
return err
114+
}
115+
}
116+
117+
return nil
118+
}
119+
120+
// validateParamType 验证参数类型
121+
func (v *EnvValidator) validateParamType(
122+
paramName string,
123+
value interface{},
124+
paramType metaentity.ParamType,
125+
) error {
126+
switch paramType {
127+
case metaentity.ParamTypeString:
128+
// string 类型不需要额外验证
129+
return nil
130+
case metaentity.ParamTypeInt:
131+
return v.validateInt(paramName, value)
132+
case metaentity.ParamTypeBool:
133+
return v.validateBool(paramName, value)
134+
default:
135+
// 未知类型当作 string 处理
136+
return nil
137+
}
138+
}
139+
140+
// validateInt 验证整数类型
141+
func (v *EnvValidator) validateInt(paramName string, value interface{}) error {
142+
switch val := value.(type) {
143+
case int, int32, int64, float64:
144+
return nil
145+
case string:
146+
if _, err := strconv.ParseInt(val, 10, 64); err != nil {
147+
return fmt.Errorf("parameter '%s' must be an integer, got '%s'", paramName, val)
148+
}
149+
return nil
150+
default:
151+
return fmt.Errorf("parameter '%s' must be an integer, got type %T", paramName, value)
152+
}
153+
}
154+
155+
// validateBool 验证布尔类型
156+
func (v *EnvValidator) validateBool(paramName string, value interface{}) error {
157+
switch val := value.(type) {
158+
case bool:
159+
return nil
160+
case string:
161+
if _, err := strconv.ParseBool(val); err != nil {
162+
return fmt.Errorf("parameter '%s' must be a boolean (true/false), got '%s'", paramName, val)
163+
}
164+
return nil
165+
default:
166+
return fmt.Errorf("parameter '%s' must be a boolean, got type %T", paramName, value)
167+
}
168+
}

0 commit comments

Comments
 (0)