Skip to content

Commit 81d4d86

Browse files
committed
fix: resolve v1.2 parameter metadata
1 parent 87ca92d commit 81d4d86

2 files changed

Lines changed: 252 additions & 13 deletions

File tree

pkg/cmd/cluster/config_ops_test.go

Lines changed: 133 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package cluster
2121

2222
import (
2323
"bytes"
24+
"context"
2425
"io"
2526

2627
. "github.com/onsi/ginkgo/v2"
@@ -83,6 +84,8 @@ var _ = Describe("reconfigure test", func() {
8384
configmap := testapps.NewCustomizedObj("resources/mysql-config-template.yaml", &corev1.ConfigMap{}, testapps.WithNamespace(ns), testapps.WithName(testing.FakeMysqlTemplateName))
8485
componentConfig := testapps.NewConfigMap(ns, cfgcore.GetComponentCfgName(clusterName, statefulCompName, configSpecName), setConfigMapData("my.cnf", ""))
8586
objs := []runtime.Object{configmap, componentConfig}
87+
pd := testing.FakeParameterDefinition()
88+
pd.Status = parametersv1alpha1.ParametersDefinitionStatus{Phase: parametersv1alpha1.PDAvailablePhase}
8689
ttf, ops := NewFakeOperationsOptions(ns, clusterName, objs...)
8790
o := &configOpsOptions{
8891
// nil cannot be set to a map struct in CueLang, so init the map of KeyValues.
@@ -95,7 +98,7 @@ var _ = Describe("reconfigure test", func() {
9598
o.clientSet = kbfakeclient.NewSimpleClientset(
9699
testing.FakeCluster(clusterName, ns),
97100
testing.FakeCompDef(),
98-
testing.FakeParameterDefinition(),
101+
pd,
99102
testing.FakeParameterConfigRenderer(),
100103
)
101104
defer ttf.Cleanup()
@@ -141,6 +144,135 @@ var _ = Describe("reconfigure test", func() {
141144
Expect(supportsDynamicReload(&parametersv1alpha1.ParametersDefinitionSpec{}, nil)).Should(BeFalse())
142145
})
143146

147+
It("discovers direct ParametersDefinitions without legacy ParamConfigRenderer", func() {
148+
pd := testing.FakeParameterDefinition()
149+
pd.Name = "direct-pd"
150+
pd.Spec.ComponentDef = testing.CompDefName
151+
pd.Spec.TemplateName = "mysql-config"
152+
pd.Spec.FileName = "my.cnf"
153+
pd.Spec.FileFormatConfig = &parametersv1alpha1.FileFormatConfig{Format: parametersv1alpha1.Ini}
154+
pd.Status = parametersv1alpha1.ParametersDefinitionStatus{Phase: parametersv1alpha1.PDAvailablePhase}
155+
156+
rctx, err := generateReconfigureContext(
157+
context.TODO(),
158+
kbfakeclient.NewSimpleClientset(
159+
testing.FakeCluster(clusterName, testing.Namespace),
160+
testing.FakeCompDef(),
161+
pd,
162+
),
163+
clusterName,
164+
testing.ComponentName,
165+
testing.Namespace,
166+
)
167+
168+
Expect(err).Should(Succeed())
169+
Expect(rctx.ParametersDefs).Should(HaveLen(1))
170+
Expect(rctx.ParametersDefs[0].Name).Should(Equal("direct-pd"))
171+
Expect(configDescriptions(rctx)).Should(Equal([]parametersv1alpha1.ComponentConfigDescription{{
172+
Name: "my.cnf",
173+
TemplateName: "mysql-config",
174+
FileFormatConfig: &parametersv1alpha1.FileFormatConfig{
175+
Format: parametersv1alpha1.Ini,
176+
},
177+
}}))
178+
Expect(rctx.ConfigRender).Should(BeNil())
179+
})
180+
181+
It("keeps legacy ParamConfigRenderer discovery with component definition pattern matching", func() {
182+
pd := testing.FakeParameterDefinition()
183+
pd.Status = parametersv1alpha1.ParametersDefinitionStatus{Phase: parametersv1alpha1.PDAvailablePhase}
184+
pcr := testing.FakeParameterConfigRenderer()
185+
pcr.Spec.ComponentDef = "fake-component"
186+
187+
rctx, err := generateReconfigureContext(
188+
context.TODO(),
189+
kbfakeclient.NewSimpleClientset(
190+
testing.FakeCluster(clusterName, testing.Namespace),
191+
testing.FakeCompDef(),
192+
pd,
193+
pcr,
194+
),
195+
clusterName,
196+
testing.ComponentName,
197+
testing.Namespace,
198+
)
199+
200+
Expect(err).Should(Succeed())
201+
Expect(rctx.ParametersDefs).Should(HaveLen(1))
202+
Expect(rctx.ParametersDefs[0].Name).Should(Equal("test-pd"))
203+
Expect(configDescriptions(rctx)).Should(Equal(pcr.Spec.Configs))
204+
Expect(rctx.ConfigRender).ShouldNot(BeNil())
205+
})
206+
207+
It("merges direct ParametersDefinitions with legacy ParamConfigRenderer for uncovered files", func() {
208+
directPD := testing.FakeParameterDefinition()
209+
directPD.Name = "direct-pd"
210+
directPD.Spec.ComponentDef = testing.CompDefName
211+
directPD.Spec.TemplateName = "mysql-config"
212+
directPD.Spec.FileName = "my.cnf"
213+
directPD.Spec.FileFormatConfig = &parametersv1alpha1.FileFormatConfig{Format: parametersv1alpha1.Ini}
214+
directPD.Status = parametersv1alpha1.ParametersDefinitionStatus{Phase: parametersv1alpha1.PDAvailablePhase}
215+
216+
legacyPD := testing.FakeParameterDefinition()
217+
legacyPD.Name = "legacy-pd"
218+
legacyPD.Spec.FileName = "log.conf"
219+
legacyPD.Status = parametersv1alpha1.ParametersDefinitionStatus{Phase: parametersv1alpha1.PDAvailablePhase}
220+
221+
pcr := testing.FakeParameterConfigRenderer()
222+
pcr.Spec.ParametersDefs = []string{"direct-pd", "legacy-pd"}
223+
pcr.Spec.Configs = []parametersv1alpha1.ComponentConfigDescription{
224+
{
225+
Name: "my.cnf",
226+
TemplateName: "legacy-mysql-config",
227+
FileFormatConfig: &parametersv1alpha1.FileFormatConfig{
228+
Format: parametersv1alpha1.Properties,
229+
},
230+
},
231+
{
232+
Name: "log.conf",
233+
TemplateName: "log-config",
234+
FileFormatConfig: &parametersv1alpha1.FileFormatConfig{
235+
Format: parametersv1alpha1.Properties,
236+
},
237+
},
238+
}
239+
240+
rctx, err := generateReconfigureContext(
241+
context.TODO(),
242+
kbfakeclient.NewSimpleClientset(
243+
testing.FakeCluster(clusterName, testing.Namespace),
244+
testing.FakeCompDef(),
245+
directPD,
246+
legacyPD,
247+
pcr,
248+
),
249+
clusterName,
250+
testing.ComponentName,
251+
testing.Namespace,
252+
)
253+
254+
Expect(err).Should(Succeed())
255+
Expect(rctx.ParametersDefs).Should(HaveLen(2))
256+
Expect(rctx.ParametersDefs[0].Name).Should(Equal("direct-pd"))
257+
Expect(rctx.ParametersDefs[1].Name).Should(Equal("legacy-pd"))
258+
Expect(configDescriptions(rctx)).Should(Equal([]parametersv1alpha1.ComponentConfigDescription{
259+
{
260+
Name: "my.cnf",
261+
TemplateName: "mysql-config",
262+
FileFormatConfig: &parametersv1alpha1.FileFormatConfig{
263+
Format: parametersv1alpha1.Ini,
264+
},
265+
},
266+
{
267+
Name: "log.conf",
268+
TemplateName: "log-config",
269+
FileFormatConfig: &parametersv1alpha1.FileFormatConfig{
270+
Format: parametersv1alpha1.Properties,
271+
},
272+
},
273+
}))
274+
})
275+
144276
It("uses config template name to transform typed parameter values", func() {
145277
value := "true"
146278
params := map[string]*parametersv1alpha1.ParametersInFile{

pkg/cmd/cluster/config_wrapper.go

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,13 @@ import (
2323
"context"
2424
"errors"
2525
"fmt"
26+
"slices"
27+
"strings"
2628

2729
kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
2830
parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1"
2931
"github.com/apecloud/kubeblocks/pkg/client/clientset/versioned"
32+
"github.com/apecloud/kubeblocks/pkg/controller/component"
3033
"github.com/apecloud/kubeblocks/pkg/generics"
3134
configctrl "github.com/apecloud/kubeblocks/pkg/parameters"
3235
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -40,6 +43,7 @@ type ReconfigureContext struct {
4043
Cluster *kbappsv1.Cluster
4144
Cmpd *kbappsv1.ComponentDefinition
4245
ConfigRender *parametersv1alpha1.ParamConfigRenderer
46+
ConfigDescs []parametersv1alpha1.ComponentConfigDescription
4347
ParametersDefs []*parametersv1alpha1.ParametersDefinition
4448

4549
CompName string
@@ -60,7 +64,7 @@ func (w *ReconfigureWrapper) ConfigSpecName() string {
6064
return w.configFileKey
6165
}
6266
file := w.ConfigFile()
63-
if file != "" && w.rctx.ConfigRender != nil {
67+
if file != "" {
6468
config := configctrl.GetComponentConfigDescription(configDescriptions(w.rctx), file)
6569
if config != nil {
6670
return config.TemplateName
@@ -77,14 +81,20 @@ func (w *ReconfigureWrapper) ConfigFile() string {
7781
if w.configFileKey != "" {
7882
return w.configFileKey
7983
}
80-
if w.rctx.ConfigRender != nil && len(w.rctx.ConfigRender.Spec.Configs) > 0 {
81-
return w.rctx.ConfigRender.Spec.Configs[0].Name
84+
if configDescs := configDescriptions(w.rctx); len(configDescs) > 0 {
85+
return configDescs[0].Name
8286
}
8387
return ""
8488
}
8589

8690
func configDescriptions(rctx *ReconfigureContext) []parametersv1alpha1.ComponentConfigDescription {
87-
if rctx == nil || rctx.ConfigRender == nil {
91+
if rctx == nil {
92+
return nil
93+
}
94+
if len(rctx.ConfigDescs) > 0 {
95+
return rctx.ConfigDescs
96+
}
97+
if rctx.ConfigRender == nil {
8898
return nil
8999
}
90100
return rctx.ConfigRender.Spec.Configs
@@ -191,20 +201,117 @@ func resolveComponentDefObj(ctx context.Context, client versioned.Interface, clu
191201
}
192202

193203
func resolveCmpdParametersDefs(rctx *ReconfigureContext) error {
194-
configRender, err := resolveComponentConfigRender(rctx, rctx.Cmpd)
204+
configDescs, paramsDefs, coveredFiles, err := resolveDirectParametersDefs(rctx)
195205
if err != nil {
196206
return err
197207
}
198-
if configRender == nil || len(configRender.Spec.ParametersDefs) == 0 {
199-
return nil
208+
209+
configRender, err := resolveComponentConfigRender(rctx, rctx.Cmpd)
210+
if err != nil {
211+
return err
200212
}
201213
rctx.ConfigRender = configRender
202-
for _, defName := range configRender.Spec.ParametersDefs {
203-
pd, err := rctx.Client.ParametersV1alpha1().ParametersDefinitions().Get(rctx.Context, defName, metav1.GetOptions{})
214+
if configRender != nil {
215+
legacyConfigFiles := make(map[string]struct{}, len(configRender.Spec.Configs))
216+
for _, configDesc := range configRender.Spec.Configs {
217+
if _, ok := coveredFiles[configDesc.Name]; ok {
218+
continue
219+
}
220+
configDescs = append(configDescs, configDesc)
221+
legacyConfigFiles[configDesc.Name] = struct{}{}
222+
}
223+
for _, defName := range configRender.Spec.ParametersDefs {
224+
pd, err := rctx.Client.ParametersV1alpha1().ParametersDefinitions().Get(rctx.Context, defName, metav1.GetOptions{})
225+
if err != nil {
226+
return err
227+
}
228+
if pd.Status.Phase != parametersv1alpha1.PDAvailablePhase {
229+
return fmt.Errorf("the referenced ParametersDefinition is unavailable: %s", pd.Name)
230+
}
231+
if _, ok := legacyConfigFiles[pd.Spec.FileName]; ok {
232+
paramsDefs = append(paramsDefs, pd)
233+
}
234+
}
235+
}
236+
237+
rctx.ConfigDescs = configDescs
238+
rctx.ParametersDefs = paramsDefs
239+
return nil
240+
}
241+
242+
func resolveDirectParametersDefs(rctx *ReconfigureContext) ([]parametersv1alpha1.ComponentConfigDescription, []*parametersv1alpha1.ParametersDefinition, map[string]struct{}, error) {
243+
paramsDefList, err := rctx.Client.ParametersV1alpha1().ParametersDefinitions().List(rctx.Context, metav1.ListOptions{})
244+
if err != nil {
245+
return nil, nil, nil, err
246+
}
247+
slices.SortFunc(paramsDefList.Items, func(a, b parametersv1alpha1.ParametersDefinition) int {
248+
if cmp := strings.Compare(b.Spec.ComponentDef, a.Spec.ComponentDef); cmp != 0 {
249+
return cmp
250+
}
251+
if cmp := strings.Compare(a.Spec.TemplateName, b.Spec.TemplateName); cmp != 0 {
252+
return cmp
253+
}
254+
if cmp := strings.Compare(a.Spec.FileName, b.Spec.FileName); cmp != 0 {
255+
return cmp
256+
}
257+
return strings.Compare(a.Name, b.Name)
258+
})
259+
260+
var paramsDefs []*parametersv1alpha1.ParametersDefinition
261+
configDescs := make([]parametersv1alpha1.ComponentConfigDescription, 0, len(paramsDefList.Items))
262+
coveredFiles := make(map[string]struct{})
263+
for i := range paramsDefList.Items {
264+
paramsDef := &paramsDefList.Items[i]
265+
matched, err := matchParametersDefinition(rctx.Cmpd, paramsDef)
204266
if err != nil {
205-
return err
267+
return nil, nil, nil, err
268+
}
269+
if !matched {
270+
continue
206271
}
207-
rctx.ParametersDefs = append(rctx.ParametersDefs, pd)
272+
if paramsDef.Status.Phase != parametersv1alpha1.PDAvailablePhase {
273+
return nil, nil, nil, fmt.Errorf("the referenced ParametersDefinition is unavailable: %s", paramsDef.Name)
274+
}
275+
if err := validateMatchedParametersDefinition(paramsDef); err != nil {
276+
return nil, nil, nil, err
277+
}
278+
if _, ok := coveredFiles[paramsDef.Spec.FileName]; ok {
279+
return nil, nil, nil, fmt.Errorf("config file[%s] has been defined in other parametersdefinition", paramsDef.Spec.FileName)
280+
}
281+
coveredFiles[paramsDef.Spec.FileName] = struct{}{}
282+
paramsDefs = append(paramsDefs, paramsDef)
283+
configDescs = append(configDescs, parametersv1alpha1.ComponentConfigDescription{
284+
Name: paramsDef.Spec.FileName,
285+
TemplateName: paramsDef.Spec.TemplateName,
286+
FileFormatConfig: paramsDef.Spec.FileFormatConfig.DeepCopy(),
287+
})
288+
}
289+
return configDescs, paramsDefs, coveredFiles, nil
290+
}
291+
292+
func matchParametersDefinition(cmpd *kbappsv1.ComponentDefinition, paramsDef *parametersv1alpha1.ParametersDefinition) (bool, error) {
293+
if cmpd == nil || paramsDef == nil {
294+
return false, nil
295+
}
296+
pattern := paramsDef.Spec.ComponentDef
297+
if pattern == "" {
298+
return false, nil
299+
}
300+
if !component.PrefixOrRegexMatched(cmpd.Name, pattern) {
301+
return false, nil
302+
}
303+
return paramsDef.Spec.ServiceVersion == "" || paramsDef.Spec.ServiceVersion == cmpd.Spec.ServiceVersion, nil
304+
}
305+
306+
func validateMatchedParametersDefinition(paramsDef *parametersv1alpha1.ParametersDefinition) error {
307+
if paramsDef.Spec.TemplateName == "" {
308+
return fmt.Errorf("ParametersDefinition[%s] misses templateName", paramsDef.Name)
309+
}
310+
if paramsDef.Spec.FileName == "" {
311+
return fmt.Errorf("ParametersDefinition[%s] misses fileName", paramsDef.Name)
312+
}
313+
if paramsDef.Spec.FileFormatConfig == nil {
314+
return fmt.Errorf("ParametersDefinition[%s] misses fileFormatConfig", paramsDef.Name)
208315
}
209316
return nil
210317
}
@@ -217,7 +324,7 @@ func resolveComponentConfigRender(rctx *ReconfigureContext, cmpd *kbappsv1.Compo
217324

218325
var prcs []parametersv1alpha1.ParamConfigRenderer
219326
for i, item := range pcrList.Items {
220-
if item.Spec.ComponentDef != cmpd.Name {
327+
if !component.PrefixOrRegexMatched(cmpd.Name, item.Spec.ComponentDef) {
221328
continue
222329
}
223330
if item.Spec.ServiceVersion == "" || item.Spec.ServiceVersion == cmpd.Spec.ServiceVersion {

0 commit comments

Comments
 (0)