From cd364330915b4cf0c2da3c2b2c7051c8a8241a0f Mon Sep 17 00:00:00 2001 From: sophon Date: Mon, 18 Nov 2024 15:46:22 +0800 Subject: [PATCH] chore: update ut --- .../parameters/combine_upgrade_policy_test.go | 176 +-- controllers/parameters/configuration_test.go | 2 +- .../parallel_upgrade_policy_test.go | 314 ++--- docs/developer_docs/api-reference/cluster.md | 164 +-- .../config_manager/builder_test.go | 15 - .../config_manager/config_handler.go | 2 +- .../config_manager/handler_util_test.go | 1118 ++++++++--------- pkg/configuration/core/config_util.go | 3 +- pkg/configuration/validate/config_validate.go | 36 - .../validate/config_validate_test.go | 102 +- .../configuration/builtin_env_test.go | 516 ++++---- .../configuration/parameter_utils_test.go | 1 + .../configuration/resource_wrapper_test.go | 10 +- pkg/controllerutil/parameter_schema.go | 2 +- 14 files changed, 1135 insertions(+), 1326 deletions(-) diff --git a/controllers/parameters/combine_upgrade_policy_test.go b/controllers/parameters/combine_upgrade_policy_test.go index c9facd67ee2..daf6559c522 100644 --- a/controllers/parameters/combine_upgrade_policy_test.go +++ b/controllers/parameters/combine_upgrade_policy_test.go @@ -19,91 +19,91 @@ along with this program. If not, see . package parameters -// import ( -// "fmt" -// -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" -// -// parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" -// testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" -// ) -// -// var _ = Describe("Reconfigure CombineSyncPolicy", func() { -// -// var ( -// k8sMockClient *testutil.K8sClientMockHelper -// ) -// -// BeforeEach(func() { -// k8sMockClient = testutil.NewK8sMockClient() -// }) -// -// AfterEach(func() { -// k8sMockClient.Finish() -// }) -// -// Context("combine reconfigure policy test", func() { -// It("Should success without error", func() { -// By("check normal policy name") -// testPolicyExecs := &combineUpgradePolicy{ -// policyExecutors: []reconfigurePolicy{&testPolicy{}}, -// } -// -// Expect(upgradePolicyMap[parametersv1alpha1.DynamicReloadAndRestartPolicy]).ShouldNot(BeNil()) -// -// mockParam := newMockReconfigureParams("simplePolicy", k8sMockClient.Client(), -// withMockInstanceSet(2, nil), -// withConfigSpec("for_test", map[string]string{ -// "key": "value", -// }), -// withClusterComponent(2)) -// -// Expect(testPolicyExecs.GetPolicyName()).Should(BeEquivalentTo(parametersv1alpha1.DynamicReloadAndRestartPolicy)) -// status, err := testPolicyExecs.Upgrade(mockParam) -// Expect(err).Should(Succeed()) -// Expect(status.Status).Should(BeEquivalentTo(ESNone)) -// }) -// -// It("Should success without error", func() { -// By("check failed policy name") -// testPolicyExecs := &combineUpgradePolicy{ -// policyExecutors: []reconfigurePolicy{&testErrorPolicy{}}, -// } -// -// mockParam := newMockReconfigureParams("simplePolicy", k8sMockClient.Client(), -// withMockInstanceSet(2, nil), -// withConfigSpec("for_test", map[string]string{ -// "key": "value", -// }), -// withClusterComponent(2)) -// -// Expect(testPolicyExecs.GetPolicyName()).Should(BeEquivalentTo(parametersv1alpha1.DynamicReloadAndRestartPolicy)) -// status, err := testPolicyExecs.Upgrade(mockParam) -// Expect(err).ShouldNot(Succeed()) -// Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) -// }) -// }) -// }) -// -// type testPolicy struct { -// } -// -// type testErrorPolicy struct { -// } -// -// func (t testErrorPolicy) Upgrade(params reconfigureContext) (ReturnedStatus, error) { -// return makeReturnedStatus(ESFailedAndRetry), fmt.Errorf("testErrorPolicy failed") -// } -// -// func (t testErrorPolicy) GetPolicyName() string { -// return "testErrorPolicy" -// } -// -// func (t testPolicy) Upgrade(params reconfigureContext) (ReturnedStatus, error) { -// return makeReturnedStatus(ESNone), nil -// } -// -// func (t testPolicy) GetPolicyName() string { -// return "testPolicy" -// } +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" +) + +var _ = Describe("Reconfigure CombineSyncPolicy", func() { + + var ( + k8sMockClient *testutil.K8sClientMockHelper + ) + + BeforeEach(func() { + k8sMockClient = testutil.NewK8sMockClient() + }) + + AfterEach(func() { + k8sMockClient.Finish() + }) + + Context("combine reconfigure policy test", func() { + It("Should success without error", func() { + By("check normal policy name") + testPolicyExecs := &combineUpgradePolicy{ + policyExecutors: []reconfigurePolicy{&testPolicy{}}, + } + + Expect(upgradePolicyMap[parametersv1alpha1.DynamicReloadAndRestartPolicy]).ShouldNot(BeNil()) + + mockParam := newMockReconfigureParams("simplePolicy", k8sMockClient.Client(), + withMockInstanceSet(2, nil), + withConfigSpec("for_test", map[string]string{ + "key": "value", + }), + withClusterComponent(2)) + + Expect(testPolicyExecs.GetPolicyName()).Should(BeEquivalentTo(parametersv1alpha1.DynamicReloadAndRestartPolicy)) + status, err := testPolicyExecs.Upgrade(mockParam) + Expect(err).Should(Succeed()) + Expect(status.Status).Should(BeEquivalentTo(ESNone)) + }) + + It("Should success without error", func() { + By("check failed policy name") + testPolicyExecs := &combineUpgradePolicy{ + policyExecutors: []reconfigurePolicy{&testErrorPolicy{}}, + } + + mockParam := newMockReconfigureParams("simplePolicy", k8sMockClient.Client(), + withMockInstanceSet(2, nil), + withConfigSpec("for_test", map[string]string{ + "key": "value", + }), + withClusterComponent(2)) + + Expect(testPolicyExecs.GetPolicyName()).Should(BeEquivalentTo(parametersv1alpha1.DynamicReloadAndRestartPolicy)) + status, err := testPolicyExecs.Upgrade(mockParam) + Expect(err).ShouldNot(Succeed()) + Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) + }) + }) +}) + +type testPolicy struct { +} + +type testErrorPolicy struct { +} + +func (t testErrorPolicy) Upgrade(params reconfigureContext) (ReturnedStatus, error) { + return makeReturnedStatus(ESFailedAndRetry), fmt.Errorf("testErrorPolicy failed") +} + +func (t testErrorPolicy) GetPolicyName() string { + return "testErrorPolicy" +} + +func (t testPolicy) Upgrade(params reconfigureContext) (ReturnedStatus, error) { + return makeReturnedStatus(ESNone), nil +} + +func (t testPolicy) GetPolicyName() string { + return "testPolicy" +} diff --git a/controllers/parameters/configuration_test.go b/controllers/parameters/configuration_test.go index a8986f0da0c..200349506fb 100644 --- a/controllers/parameters/configuration_test.go +++ b/controllers/parameters/configuration_test.go @@ -183,6 +183,6 @@ func cleanEnv() { testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.SecretSignature, true, inNS) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.InstanceSetSignature, true, inNS, ml) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ConfigurationSignature, false, inNS, ml) - testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ComponentParameterSignature, true, inNS, ml) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ComponentParameterSignature, true, inNS) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ParameterSignature, true, inNS, ml) } diff --git a/controllers/parameters/parallel_upgrade_policy_test.go b/controllers/parameters/parallel_upgrade_policy_test.go index 925cb5248f5..da86abdffa6 100644 --- a/controllers/parameters/parallel_upgrade_policy_test.go +++ b/controllers/parameters/parallel_upgrade_policy_test.go @@ -19,160 +19,160 @@ along with this program. If not, see . package parameters -// import ( -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" -// -// "github.com/golang/mock/gomock" -// -// cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" -// cfgproto "github.com/apecloud/kubeblocks/pkg/configuration/proto" -// mock_proto "github.com/apecloud/kubeblocks/pkg/configuration/proto/mocks" -// testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" -// ) -// -// var parallelPolicy = parallelUpgradePolicy{} -// -// var _ = Describe("Reconfigure ParallelPolicy", func() { -// -// var ( -// k8sMockClient *testutil.K8sClientMockHelper -// reconfigureClient *mock_proto.MockReconfigureClient -// ) -// -// BeforeEach(func() { -// k8sMockClient = testutil.NewK8sMockClient() -// reconfigureClient = mock_proto.NewMockReconfigureClient(k8sMockClient.Controller()) -// }) -// -// AfterEach(func() { -// k8sMockClient.Finish() -// }) -// -// Context("parallel reconfigure policy test", func() { -// It("Should success without error", func() { -// Expect(parallelPolicy.GetPolicyName()).Should(BeEquivalentTo("parallel")) -// -// // mock client update caller -// k8sMockClient.MockPatchMethod(testutil.WithSucceed(testutil.WithTimes(3))) -// -// reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( -// &cfgproto.StopContainerResponse{}, nil). -// Times(3) -// -// mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), -// withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { -// return reconfigureClient, nil -// }), -// withMockInstanceSet(3, nil), -// withClusterComponent(3), -// withConfigSpec("for_test", map[string]string{ -// "a": "b", -// })) -// -// k8sMockClient.MockListMethod(testutil.WithListReturned( -// testutil.WithConstructListReturnedResult(fromPodObjectList( -// newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 3), -// )))) -// -// status, err := parallelPolicy.Upgrade(mockParam) -// Expect(err).Should(Succeed()) -// Expect(status.Status).Should(BeEquivalentTo(ESNone)) -// }) -// }) -// -// Context("parallel reconfigure policy test with List pods failed", func() { -// It("Should failed", func() { -// mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), -// withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { -// return reconfigureClient, nil -// }), -// withMockInstanceSet(3, nil), -// withClusterComponent(3), -// withConfigSpec("for_test", map[string]string{ -// "a": "b", -// })) -// -// // first failed -// getPodsError := cfgcore.MakeError("for grpc failed.") -// k8sMockClient.MockListMethod(testutil.WithFailed(getPodsError)) -// -// status, err := parallelPolicy.Upgrade(mockParam) -// // first failed -// Expect(err).Should(BeEquivalentTo(getPodsError)) -// Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) -// }) -// }) -// -// Context("parallel reconfigure policy test with stop container failed", func() { -// It("Should failed", func() { -// stopError := cfgcore.MakeError("failed to stop!") -// reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( -// &cfgproto.StopContainerResponse{}, stopError). -// Times(1) -// -// reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( -// &cfgproto.StopContainerResponse{ -// ErrMessage: "failed to stop container.", -// }, nil). -// Times(1) -// -// mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), -// withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { -// return reconfigureClient, nil -// }), -// withMockInstanceSet(3, nil), -// withClusterComponent(3), -// withConfigSpec("for_test", map[string]string{ -// "a": "b", -// })) -// -// k8sMockClient.MockListMethod(testutil.WithListReturned( -// testutil.WithConstructListReturnedResult( -// fromPodObjectList(newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 3))), testutil.WithTimes(2), -// )) -// -// status, err := parallelPolicy.Upgrade(mockParam) -// // first failed -// Expect(err).Should(BeEquivalentTo(stopError)) -// Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) -// -// status, err = parallelPolicy.Upgrade(mockParam) -// Expect(err).ShouldNot(Succeed()) -// Expect(err.Error()).Should(ContainSubstring("failed to stop container")) -// Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) -// }) -// }) -// -// Context("parallel reconfigure policy test with patch failed", func() { -// It("Should failed", func() { -// // mock client update caller -// patchError := cfgcore.MakeError("update failed!") -// k8sMockClient.MockPatchMethod(testutil.WithFailed(patchError, testutil.WithTimes(1))) -// -// reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( -// &cfgproto.StopContainerResponse{}, nil). -// Times(1) -// -// mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), -// withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { -// return reconfigureClient, nil -// }), -// withMockInstanceSet(3, nil), -// withClusterComponent(3), -// withConfigSpec("for_test", map[string]string{ -// "a": "b", -// })) -// -// setPods := newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 5) -// k8sMockClient.MockListMethod(testutil.WithListReturned( -// testutil.WithConstructListReturnedResult(fromPodObjectList(setPods)), testutil.WithAnyTimes(), -// )) -// -// status, err := parallelPolicy.Upgrade(mockParam) -// // first failed -// Expect(err).Should(BeEquivalentTo(patchError)) -// Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) -// }) -// }) -// }) +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/golang/mock/gomock" + + cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + cfgproto "github.com/apecloud/kubeblocks/pkg/configuration/proto" + mockproto "github.com/apecloud/kubeblocks/pkg/configuration/proto/mocks" + testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" +) + +var parallelPolicy = parallelUpgradePolicy{} + +var _ = Describe("Reconfigure ParallelPolicy", func() { + + var ( + k8sMockClient *testutil.K8sClientMockHelper + reconfigureClient *mockproto.MockReconfigureClient + ) + + BeforeEach(func() { + k8sMockClient = testutil.NewK8sMockClient() + reconfigureClient = mockproto.NewMockReconfigureClient(k8sMockClient.Controller()) + }) + + AfterEach(func() { + k8sMockClient.Finish() + }) + + Context("parallel reconfigure policy test", func() { + It("Should success without error", func() { + Expect(parallelPolicy.GetPolicyName()).Should(BeEquivalentTo("parallel")) + + // mock client update caller + k8sMockClient.MockPatchMethod(testutil.WithSucceed(testutil.WithTimes(3))) + + reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( + &cfgproto.StopContainerResponse{}, nil). + Times(3) + + mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), + withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { + return reconfigureClient, nil + }), + withMockInstanceSet(3, nil), + withClusterComponent(3), + withConfigSpec("for_test", map[string]string{ + "a": "b", + })) + + k8sMockClient.MockListMethod(testutil.WithListReturned( + testutil.WithConstructListReturnedResult(fromPodObjectList( + newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 3), + )))) + + status, err := parallelPolicy.Upgrade(mockParam) + Expect(err).Should(Succeed()) + Expect(status.Status).Should(BeEquivalentTo(ESNone)) + }) + }) + + Context("parallel reconfigure policy test with List pods failed", func() { + It("Should failed", func() { + mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), + withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { + return reconfigureClient, nil + }), + withMockInstanceSet(3, nil), + withClusterComponent(3), + withConfigSpec("for_test", map[string]string{ + "a": "b", + })) + + // first failed + getPodsError := cfgcore.MakeError("for grpc failed.") + k8sMockClient.MockListMethod(testutil.WithFailed(getPodsError)) + + status, err := parallelPolicy.Upgrade(mockParam) + // first failed + Expect(err).Should(BeEquivalentTo(getPodsError)) + Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) + }) + }) + + Context("parallel reconfigure policy test with stop container failed", func() { + It("Should failed", func() { + stopError := cfgcore.MakeError("failed to stop!") + reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( + &cfgproto.StopContainerResponse{}, stopError). + Times(1) + + reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( + &cfgproto.StopContainerResponse{ + ErrMessage: "failed to stop container.", + }, nil). + Times(1) + + mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), + withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { + return reconfigureClient, nil + }), + withMockInstanceSet(3, nil), + withClusterComponent(3), + withConfigSpec("for_test", map[string]string{ + "a": "b", + })) + + k8sMockClient.MockListMethod(testutil.WithListReturned( + testutil.WithConstructListReturnedResult( + fromPodObjectList(newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 3))), testutil.WithTimes(2), + )) + + status, err := parallelPolicy.Upgrade(mockParam) + // first failed + Expect(err).Should(BeEquivalentTo(stopError)) + Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) + + status, err = parallelPolicy.Upgrade(mockParam) + Expect(err).ShouldNot(Succeed()) + Expect(err.Error()).Should(ContainSubstring("failed to stop container")) + Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) + }) + }) + + Context("parallel reconfigure policy test with patch failed", func() { + It("Should failed", func() { + // mock client update caller + patchError := cfgcore.MakeError("update failed!") + k8sMockClient.MockPatchMethod(testutil.WithFailed(patchError, testutil.WithTimes(1))) + + reconfigureClient.EXPECT().StopContainer(gomock.Any(), gomock.Any()).Return( + &cfgproto.StopContainerResponse{}, nil). + Times(1) + + mockParam := newMockReconfigureParams("parallelPolicy", k8sMockClient.Client(), + withGRPCClient(func(addr string) (cfgproto.ReconfigureClient, error) { + return reconfigureClient, nil + }), + withMockInstanceSet(3, nil), + withClusterComponent(3), + withConfigSpec("for_test", map[string]string{ + "a": "b", + })) + + setPods := newMockPodsWithInstanceSet(&mockParam.InstanceSetUnits[0], 5) + k8sMockClient.MockListMethod(testutil.WithListReturned( + testutil.WithConstructListReturnedResult(fromPodObjectList(setPods)), testutil.WithAnyTimes(), + )) + + status, err := parallelPolicy.Upgrade(mockParam) + // first failed + Expect(err).Should(BeEquivalentTo(patchError)) + Expect(status.Status).Should(BeEquivalentTo(ESFailedAndRetry)) + }) + }) +}) diff --git a/docs/developer_docs/api-reference/cluster.md b/docs/developer_docs/api-reference/cluster.md index fcdbe5b4b5e..6274e7a5cdb 100644 --- a/docs/developer_docs/api-reference/cluster.md +++ b/docs/developer_docs/api-reference/cluster.md @@ -837,6 +837,20 @@ bool If set, all the computing resources will be released.

+ + +initParameters
+ + +ComponentParameters + + + + +(Optional) +

Specifies the initialization parameters.

+ + @@ -4435,135 +4449,6 @@ string -

ComponentConfigSpec -

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FieldDescription
-ComponentTemplateSpec
- - -ComponentTemplateSpec - - -
-

-(Members of ComponentTemplateSpec are embedded into this type.) -

-
-keys
- -[]string - -
-(Optional) -

Specifies the configuration files within the ConfigMap that support dynamic updates.

-

A configuration template (provided in the form of a ConfigMap) may contain templates for multiple -configuration files. -Each configuration file corresponds to a key in the ConfigMap. -Some of these configuration files may support dynamic modification and reloading without requiring -a pod restart.

-

If empty or omitted, all configuration files in the ConfigMap are assumed to support dynamic updates, -and ConfigConstraint applies to all keys.

-
-constraintRef
- -string - -
-(Optional) -

Specifies the name of the referenced configuration constraints object.

-
-asEnvFrom
- -[]string - -
-(Optional) -

Specifies the containers to inject the ConfigMap parameters as environment variables.

-

This is useful when application images accept parameters through environment variables and -generate the final configuration file in the startup script based on these variables.

-

This field allows users to specify a list of container names, and KubeBlocks will inject the environment -variables converted from the ConfigMap into these designated containers. This provides a flexible way to -pass the configuration items from the ConfigMap to the container without modifying the image.

-

Deprecated: asEnvFrom has been deprecated since 0.9.0 and will be removed in 0.10.0. -Use injectEnvTo instead.

-
-injectEnvTo
- -[]string - -
-(Optional) -

Specifies the containers to inject the ConfigMap parameters as environment variables.

-

This is useful when application images accept parameters through environment variables and -generate the final configuration file in the startup script based on these variables.

-

This field allows users to specify a list of container names, and KubeBlocks will inject the environment -variables converted from the ConfigMap into these designated containers. This provides a flexible way to -pass the configuration items from the ConfigMap to the container without modifying the image.

-
-reRenderResourceTypes
- - -[]RerenderResourceType - - -
-(Optional) -

Specifies whether the configuration needs to be re-rendered after v-scale or h-scale operations to reflect changes.

-

In some scenarios, the configuration may need to be updated to reflect the changes in resource allocation -or cluster topology. Examples:

-
    -
  • Redis: adjust maxmemory after v-scale operation.
  • -
  • MySQL: increase max connections after v-scale operation.
  • -
  • Zookeeper: update zoo.cfg with new node addresses after h-scale operation.
  • -
-
-asSecret
- -bool - -
-(Optional) -

Whether to store the final rendered parameters as a secret.

-

ComponentDefinitionSpec

@@ -5555,7 +5440,7 @@ and other administrative tasks.

ComponentParameters (map[string]*string alias)

-(Appears on:ClusterComponentSpec) +(Appears on:ClusterComponentSpec, ComponentSpec)

@@ -6083,6 +5968,20 @@ bool If set, all the computing resources will be released.

+ + +initParameters
+ + +ComponentParameters + + + + +(Optional) +

Specifies the initialization parameters.

+ +

ComponentStatus @@ -6231,7 +6130,7 @@ ProvisionSecretRef

ComponentTemplateSpec

-(Appears on:ComponentConfigSpec, ComponentDefinitionSpec) +(Appears on:ComponentDefinitionSpec)

@@ -8576,9 +8475,6 @@ int32

RerenderResourceType (string alias)

-

-(Appears on:ComponentConfigSpec) -

RerenderResourceType defines the resource requirements for a component.

diff --git a/pkg/configuration/config_manager/builder_test.go b/pkg/configuration/config_manager/builder_test.go index b5ced7dfb3a..756b505d7bf 100644 --- a/pkg/configuration/config_manager/builder_test.go +++ b/pkg/configuration/config_manager/builder_test.go @@ -266,21 +266,6 @@ formatterConfig: } }) - It("builds secondary render correctly", func() { - mockTplScriptCM() - param := newCMBuildParams(false) - reloadOptions := newReloadOptions(parametersv1alpha1.TPLScriptType, syncFn(false)) - for i := range param.ConfigSpecsBuildParams { - buildParam := ¶m.ConfigSpecsBuildParams[i] - buildParam.ReloadAction = reloadOptions - buildParam.ReloadType = parametersv1alpha1.TPLScriptType - } - Expect(BuildConfigManagerContainerParams(mockK8sCli.Client(), context.TODO(), param, newVolumeMounts())).Should(Succeed()) - for _, buildParam := range param.ConfigSpecsBuildParams { - Expect(FindVolumeMount(param.Volumes, GetConfigVolumeName(buildParam.ConfigSpec))).ShouldNot(BeNil()) - } - }) - It("builds downwardAPI correctly", func() { mockTplScriptCM() param := newCMBuildParams(false) diff --git a/pkg/configuration/config_manager/config_handler.go b/pkg/configuration/config_manager/config_handler.go index 39c808684a3..07456085899 100644 --- a/pkg/configuration/config_manager/config_handler.go +++ b/pkg/configuration/config_manager/config_handler.go @@ -596,7 +596,7 @@ func CreateCombinedHandler(config string, backupPath string) (ConfigHandler, err return nil, err } hkey := configMeta.ConfigSpec.Name - if configMeta.ConfigFile == "" { + if configMeta.ConfigFile != "" { hkey = hkey + "/" + configMeta.ConfigFile } mHandler.handlers[hkey] = h diff --git a/pkg/configuration/config_manager/handler_util_test.go b/pkg/configuration/config_manager/handler_util_test.go index 677854e1671..9db9a416187 100644 --- a/pkg/configuration/config_manager/handler_util_test.go +++ b/pkg/configuration/config_manager/handler_util_test.go @@ -19,568 +19,556 @@ along with this program. If not, see . package configmanager -// import ( -// "context" -// "strings" -// "testing" -// -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" -// -// "github.com/stretchr/testify/assert" -// corev1 "k8s.io/api/core/v1" -// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -// "sigs.k8s.io/controller-runtime/pkg/client" -// -// appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" -// appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" -// cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" -// cfgutil "github.com/apecloud/kubeblocks/pkg/configuration/util" -// testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" -// ) -// -// func TestIsSupportReload(t *testing.T) { -// type args struct { -// reload *appsv1beta1.ReloadAction -// } -// tests := []struct { -// name string -// args args -// want bool -// }{{ -// name: "reload_test_with_nil_reload_options", -// args: args{ -// reload: nil, -// }, -// want: false, -// }, { -// name: "reload_test_with_empty_reload_options", -// args: args{ -// reload: &appsv1beta1.ReloadAction{}, -// }, -// want: false, -// }, { -// name: "reload_test_with_unix_signal", -// args: args{ -// reload: &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// ProcessName: "test", -// Signal: appsv1beta1.SIGHUP, -// }, -// }, -// }, -// want: true, -// }, { -// name: "reload_test_with_shell", -// args: args{ -// reload: &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Command: strings.Fields("pg_ctl reload"), -// }, -// }, -// }, -// want: true, -// }, { -// name: "reload_test_with_tpl_script", -// args: args{ -// reload: &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: "cm", -// Namespace: "default", -// }, -// }, -// }, -// }, -// want: true, -// }, { -// name: "auto_trigger_reload_test_with_process_name", -// args: args{ -// reload: &appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{ -// ProcessName: "test", -// }, -// }, -// }, -// want: true, -// }, { -// name: "auto_trigger_reload_test", -// args: args{ -// reload: &appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{}, -// }, -// }, -// want: true, -// }} -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// if got := IsSupportReload(tt.args.reload); got != tt.want { -// t.Errorf("IsSupportReload() = %v, want %v", got, tt.want) -// } -// }) -// } -// } -// -// var _ = Describe("Handler Util Test", func() { -// -// var mockK8sCli *testutil.K8sClientMockHelper -// -// BeforeEach(func() { -// // Add any setup steps that needs to be executed before each test -// mockK8sCli = testutil.NewK8sMockClient() -// }) -// -// AfterEach(func() { -// DeferCleanup(mockK8sCli.Finish) -// }) -// -// mockConfigConstraint := func(ccName string, reloadOptions *appsv1beta1.ReloadAction) *appsv1beta1.ConfigConstraint { -// return &appsv1beta1.ConfigConstraint{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: ccName, -// }, -// Spec: appsv1beta1.ConfigConstraintSpec{ -// ReloadAction: reloadOptions, -// FileFormatConfig: &appsv1beta1.FileFormatConfig{ -// Format: appsv1beta1.Properties, -// }, -// }} -// } -// -// mockConfigSpec := func(ccName string) appsv1.ComponentTemplateSpec { -// return appsv1.ComponentTemplateSpec{ -// Name: "test", -// TemplateRef: "config_template", -// Namespace: "default", -// VolumeName: "for_test", -// } -// } -// -// Context("TestValidateReloadOptions", func() { -// It("Should succeed with no error", func() { -// mockK8sCli.MockGetMethod( -// testutil.WithFailed(cfgcore.MakeError("failed to get resource."), testutil.WithTimes(1)), -// testutil.WithSucceed(testutil.WithTimes(1)), -// ) -// -// type args struct { -// reloadAction *appsv1beta1.ReloadAction -// cli client.Client -// ctx context.Context -// } -// tests := []struct { -// name string -// args args -// wantErr bool -// }{{ -// name: "unixSignalTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// Signal: appsv1beta1.SIGHUP, -// }}, -// }, -// wantErr: false, -// }, { -// name: "unixSignalTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// Signal: "SIGNOEXIST", -// }}, -// }, -// wantErr: true, -// }, { -// name: "shellTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Command: nil, -// }}, -// }, -// wantErr: true, -// }, { -// name: "shellTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Command: strings.Fields("go"), -// }}, -// }, -// wantErr: false, -// }, { -// name: "TPLScriptTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: "test", -// }, -// }}, -// cli: mockK8sCli.Client(), -// ctx: context.TODO(), -// }, -// wantErr: true, -// }, { -// name: "TPLScriptTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: "test", -// }, -// }}, -// cli: mockK8sCli.Client(), -// ctx: context.TODO(), -// }, -// wantErr: false, -// }, { -// name: "autoTriggerTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{ -// ProcessName: "test", -// }}, -// }, -// wantErr: false, -// }, { -// name: "autoTriggerTest", -// args: args{ -// reloadAction: &appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{}}, -// }, -// wantErr: false, -// }} -// for _, tt := range tests { -// By(tt.name) -// err := ValidateReloadOptions(tt.args.reloadAction, tt.args.cli, tt.args.ctx) -// Expect(err != nil).Should(BeEquivalentTo(tt.wantErr)) -// } -// }) -// }) -// -// Context("TestGetSupportReloadConfigSpecs", func() { -// It("not support reload", func() { -// configSpecs, err := GetSupportReloadConfigSpecs([]appsv1.ComponentTemplateSpec{{ -// Name: "test", -// }}, nil, nil) -// Expect(err).Should(Succeed()) -// Expect(len(configSpecs)).Should(BeEquivalentTo(0)) -// }) -// -// It("not ConfigConstraint ", func() { -// configSpecs, err := GetSupportReloadConfigSpecs([]appsv1.ComponentTemplateSpec{{ -// Name: "test", -// TemplateRef: "config_template", -// Namespace: "default", -// }}, nil, nil) -// Expect(err).Should(Succeed()) -// Expect(len(configSpecs)).Should(BeEquivalentTo(0)) -// }) -// -// It("not support reload", func() { -// ccName := "config_constraint" -// mockK8sCli.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]client.Object{ -// mockConfigConstraint(ccName, nil), -// }), testutil.WithTimes(1))) -// -// configSpecs, err := GetSupportReloadConfigSpecs( -// []appsv1.ComponentTemplateSpec{mockConfigSpec(ccName)}, -// mockK8sCli.Client(), ctx) -// -// Expect(err).Should(Succeed()) -// Expect(len(configSpecs)).Should(BeEquivalentTo(0)) -// }) -// -// It("normal test", func() { -// ccName := "config_constraint" -// cc := mockConfigConstraint(ccName, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// ProcessName: "test", -// Signal: appsv1beta1.SIGHUP, -// }, -// }) -// mockK8sCli.MockGetMethod(testutil.WithGetReturned( -// testutil.WithConstructSimpleGetResult([]client.Object{cc}), -// testutil.WithTimes(1))) -// -// configSpecs, err := GetSupportReloadConfigSpecs( -// []appsv1.ComponentConfigSpec{mockConfigSpec(ccName)}, -// mockK8sCli.Client(), ctx) -// -// Expect(err).Should(Succeed()) -// Expect(len(configSpecs)).Should(BeEquivalentTo(1)) -// Expect(configSpecs[0].ConfigSpec).Should(BeEquivalentTo(mockConfigSpec(ccName))) -// Expect(configSpecs[0].ReloadType).Should(BeEquivalentTo(appsv1beta1.UnixSignalType)) -// Expect(configSpecs[0].FormatterConfig).Should(BeEquivalentTo(*cc.Spec.FileFormatConfig)) -// }) -// -// It("auto trigger test", func() { -// ccName := "auto_trigger_config_constraint" -// cc := mockConfigConstraint(ccName, &appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{ -// ProcessName: "test", -// }, -// }) -// mockK8sCli.MockGetMethod(testutil.WithGetReturned( -// testutil.WithConstructSimpleGetResult([]client.Object{cc}), -// testutil.WithTimes(1))) -// -// configSpecs, err := GetSupportReloadConfigSpecs( -// []appsv1.ComponentConfigSpec{mockConfigSpec(ccName)}, -// mockK8sCli.Client(), ctx) -// -// Expect(err).Should(Succeed()) -// Expect(len(configSpecs)).Should(BeEquivalentTo(0)) -// }) -// }) -// -// Context("TestFromReloadTypeConfig", func() { -// It("TestSignalTrigger", func() { -// Expect(appsv1beta1.UnixSignalType).Should(BeEquivalentTo(FromReloadTypeConfig(&appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// ProcessName: "test", -// Signal: appsv1beta1.SIGHUP, -// }}))) -// }) -// -// It("TestAutoTrigger", func() { -// Expect(appsv1beta1.AutoType).Should(BeEquivalentTo(FromReloadTypeConfig(&appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{ -// ProcessName: "test", -// }}))) -// }) -// -// It("TestShellTrigger", func() { -// Expect(appsv1beta1.ShellType).Should(BeEquivalentTo(FromReloadTypeConfig(&appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Command: []string{"/bin/true"}, -// }}))) -// }) -// -// It("TestTplScriptsTrigger", func() { -// Expect(appsv1beta1.TPLScriptType).Should(BeEquivalentTo(FromReloadTypeConfig(&appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: "test", -// Namespace: "default", -// }, -// }}))) -// }) -// -// It("TestInvalidTrigger", func() { -// Expect("").Should(BeEquivalentTo(FromReloadTypeConfig(&appsv1beta1.ReloadAction{}))) -// }) -// }) -// -// Context("TestValidateReloadOptions", func() { -// It("TestSignalTrigger", func() { -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{ -// ProcessName: "test", -// Signal: appsv1beta1.SIGHUP, -// }}, nil, nil), -// ).Should(Succeed()) -// }) -// -// It("TestSignalTrigger", func() { -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{ -// AutoTrigger: &appsv1beta1.AutoTrigger{ -// ProcessName: "test", -// }}, nil, nil), -// ).Should(Succeed()) -// }) -// -// It("TestShellTrigger", func() { -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Command: []string{"/bin/true"}, -// }}, nil, nil), -// ).Should(Succeed()) -// }) -// -// It("TestTplScriptsTrigger", func() { -// ns := "default" -// testName1 := "test1" -// testName2 := "not_test1" -// mockK8sCli.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]client.Object{ -// &corev1.ConfigMap{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: testName1, -// Namespace: ns, -// }, -// }, -// }), testutil.WithTimes(2))) -// -// By("Test valid") -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: testName1, -// Namespace: ns, -// }, -// }}, mockK8sCli.Client(), ctx), -// ).Should(Succeed()) -// -// By("Test invalid") -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// ScriptConfig: appsv1beta1.ScriptConfig{ -// ScriptConfigMapRef: testName2, -// Namespace: ns, -// }, -// }}, mockK8sCli.Client(), ctx), -// ).ShouldNot(Succeed()) -// }) -// -// It("TestInvalidTrigger", func() { -// Expect(ValidateReloadOptions(&appsv1beta1.ReloadAction{}, nil, nil)).ShouldNot(Succeed()) -// }) -// }) -// }) -// -// func TestFilterSubPathVolumeMount(t *testing.T) { -// createConfigMeta := func(volumeName string, reloadType appsv1beta1.DynamicReloadType, reloadAction *appsv1beta1.ReloadAction) ConfigSpecMeta { -// return ConfigSpecMeta{ConfigSpecInfo: ConfigSpecInfo{ -// ReloadAction: reloadAction, -// ReloadType: reloadType, -// ConfigSpec: appsv1.ComponentConfigSpec{ -// ComponentTemplateSpec: appsv1.ComponentTemplateSpec{ -// VolumeName: volumeName, -// }}}} -// } -// -// type args struct { -// metas []ConfigSpecMeta -// volumes []corev1.VolumeMount -// } -// tests := []struct { -// name string -// args args -// want []ConfigSpecMeta -// }{{ -// name: "test1", -// args: args{ -// metas: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Sync: cfgutil.ToPointer(true), -// }, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// Sync: cfgutil.ToPointer(true), -// }, -// }), -// }, -// volumes: []corev1.VolumeMount{ -// {Name: "test1", SubPath: "test1"}, -// {Name: "test2", SubPath: "test2"}, -// {Name: "test3", SubPath: "test3"}, -// }, -// }, -// want: []ConfigSpecMeta{ -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Sync: cfgutil.ToPointer(true), -// }, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// Sync: cfgutil.ToPointer(true), -// }, -// }), -// }, -// }, { -// name: "test2", -// args: args{ -// metas: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{}, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{}, -// }), -// }, -// volumes: []corev1.VolumeMount{ -// {Name: "test1"}, -// {Name: "test2"}, -// {Name: "test3"}, -// }, -// }, -// want: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{}, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{}, -// }), -// }, -// }, { -// name: "test3", -// args: args{ -// metas: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{}, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{}, -// }), -// }, -// volumes: []corev1.VolumeMount{}, -// }, -// want: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{}, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{}, -// }), -// }, -// }, { -// name: "test4", -// args: args{ -// metas: []ConfigSpecMeta{ -// createConfigMeta("test1", appsv1beta1.UnixSignalType, &appsv1beta1.ReloadAction{ -// UnixSignalTrigger: &appsv1beta1.UnixSignalTrigger{}, -// }), -// createConfigMeta("test2", appsv1beta1.ShellType, &appsv1beta1.ReloadAction{ -// ShellTrigger: &appsv1beta1.ShellTrigger{ -// Sync: cfgutil.ToPointer(false), -// }, -// }), -// createConfigMeta("test3", appsv1beta1.TPLScriptType, &appsv1beta1.ReloadAction{ -// TPLScriptTrigger: &appsv1beta1.TPLScriptTrigger{ -// Sync: cfgutil.ToPointer(false), -// }, -// }), -// }, -// volumes: []corev1.VolumeMount{ -// {Name: "test1", SubPath: "test1"}, -// {Name: "test2", SubPath: "test2"}, -// {Name: "test3", SubPath: "test3"}, -// }, -// }, -// want: nil, -// }} -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// assert.Equalf(t, tt.want, FilterSupportReloadActionConfigSpecs(tt.args.metas, tt.args.volumes), "FilterSupportReloadActionConfigSpecs(%v, %v)", tt.args.metas, tt.args.volumes) -// }) -// } -// } +import ( + "context" + "strings" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + cfgutil "github.com/apecloud/kubeblocks/pkg/configuration/util" + testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" +) + +func TestIsSupportReload(t *testing.T) { + type args struct { + reload *parametersv1alpha1.ReloadAction + } + tests := []struct { + name string + args args + want bool + }{{ + name: "reload_test_with_nil_reload_options", + args: args{ + reload: nil, + }, + want: false, + }, { + name: "reload_test_with_empty_reload_options", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{}, + }, + want: false, + }, { + name: "reload_test_with_unix_signal", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + ProcessName: "test", + Signal: parametersv1alpha1.SIGHUP, + }, + }, + }, + want: true, + }, { + name: "reload_test_with_shell", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Command: strings.Fields("pg_ctl reload"), + }, + }, + }, + want: true, + }, { + name: "reload_test_with_tpl_script", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: "cm", + Namespace: "default", + }, + }, + }, + }, + want: true, + }, { + name: "auto_trigger_reload_test_with_process_name", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{ + ProcessName: "test", + }, + }, + }, + want: true, + }, { + name: "auto_trigger_reload_test", + args: args{ + reload: ¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{}, + }, + }, + want: true, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsSupportReload(tt.args.reload); got != tt.want { + t.Errorf("IsSupportReload() = %v, want %v", got, tt.want) + } + }) + } +} + +var _ = Describe("Handler Util Test", func() { + + var mockK8sCli *testutil.K8sClientMockHelper + + BeforeEach(func() { + // Add any setup steps that needs to be executed before each test + mockK8sCli = testutil.NewK8sMockClient() + }) + + AfterEach(func() { + DeferCleanup(mockK8sCli.Finish) + }) + + mockParametersDef := func(ccName string, reloadOptions *parametersv1alpha1.ReloadAction) *parametersv1alpha1.ParametersDefinition { + return ¶metersv1alpha1.ParametersDefinition{ + ObjectMeta: metav1.ObjectMeta{ + Name: ccName, + }, + Spec: parametersv1alpha1.ParametersDefinitionSpec{ + FileName: "test", + ReloadAction: reloadOptions, + }} + } + mockConfigDescription := func(tplName string, format parametersv1alpha1.CfgFileFormat) []parametersv1alpha1.ComponentConfigDescription { + return []parametersv1alpha1.ComponentConfigDescription{ + { + Name: "test", + TemplateName: tplName, + FileFormatConfig: ¶metersv1alpha1.FileFormatConfig{ + Format: format, + }, + }, + } + } + + mockConfigSpec := func(tplName string) appsv1.ComponentTemplateSpec { + return appsv1.ComponentTemplateSpec{ + Name: tplName, + TemplateRef: "config_template", + Namespace: "default", + VolumeName: "for_test", + } + } + + Context("TestValidateReloadOptions", func() { + It("Should succeed with no error", func() { + mockK8sCli.MockGetMethod( + testutil.WithFailed(cfgcore.MakeError("failed to get resource."), testutil.WithTimes(1)), + testutil.WithSucceed(testutil.WithTimes(1)), + ) + + type args struct { + reloadAction *parametersv1alpha1.ReloadAction + cli client.Client + ctx context.Context + } + tests := []struct { + name string + args args + wantErr bool + }{{ + name: "unixSignalTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + Signal: parametersv1alpha1.SIGHUP, + }}, + }, + wantErr: false, + }, { + name: "unixSignalTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + Signal: "SIGNOEXIST", + }}, + }, + wantErr: true, + }, { + name: "shellTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Command: nil, + }}, + }, + wantErr: true, + }, { + name: "shellTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Command: strings.Fields("go"), + }}, + }, + wantErr: false, + }, { + name: "TPLScriptTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: "test", + }, + }}, + cli: mockK8sCli.Client(), + ctx: context.TODO(), + }, + wantErr: true, + }, { + name: "TPLScriptTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: "test", + }, + }}, + cli: mockK8sCli.Client(), + ctx: context.TODO(), + }, + wantErr: false, + }, { + name: "autoTriggerTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{ + ProcessName: "test", + }}, + }, + wantErr: false, + }, { + name: "autoTriggerTest", + args: args{ + reloadAction: ¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{}}, + }, + wantErr: false, + }} + for _, tt := range tests { + By(tt.name) + err := ValidateReloadOptions(tt.args.reloadAction, tt.args.cli, tt.args.ctx) + Expect(err != nil).Should(BeEquivalentTo(tt.wantErr)) + } + }) + }) + + Context("TestGetSupportReloadConfigSpecs", func() { + It("not support reload", func() { + configSpecs, err := GetSupportReloadConfigSpecs([]appsv1.ComponentTemplateSpec{{ + Name: "test", + }}, nil, nil) + Expect(err).Should(Succeed()) + Expect(len(configSpecs)).Should(BeEquivalentTo(0)) + }) + + It("not ComponentConfigDescription", func() { + configSpecs, err := GetSupportReloadConfigSpecs([]appsv1.ComponentTemplateSpec{{ + Name: "test", + TemplateRef: "config_template", + Namespace: "default", + }}, nil, []*parametersv1alpha1.ParametersDefinition{mockParametersDef("test", nil)}) + Expect(err).Should(Succeed()) + Expect(len(configSpecs)).Should(BeEquivalentTo(0)) + }) + + It("not support reload for paramsDef", func() { + ccName := "config_constraint" + configtpl := mockConfigSpec(ccName) + configSpecs, err := GetSupportReloadConfigSpecs( + []appsv1.ComponentTemplateSpec{configtpl}, + mockConfigDescription(configtpl.Name, parametersv1alpha1.Ini), + nil, + ) + + Expect(err).Should(Succeed()) + Expect(len(configSpecs)).Should(BeEquivalentTo(0)) + }) + + It("normal test", func() { + ccName := "config_constraint" + pd := mockParametersDef(ccName, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + ProcessName: "test", + Signal: parametersv1alpha1.SIGHUP, + }, + }) + + cd := mockConfigDescription(ccName, parametersv1alpha1.Ini) + configSpecs, err := GetSupportReloadConfigSpecs( + []appsv1.ComponentTemplateSpec{mockConfigSpec(ccName)}, + cd, + []*parametersv1alpha1.ParametersDefinition{pd}, + ) + + Expect(err).Should(Succeed()) + Expect(len(configSpecs)).Should(BeEquivalentTo(1)) + Expect(configSpecs[0].ConfigSpec).Should(BeEquivalentTo(mockConfigSpec(ccName))) + Expect(configSpecs[0].ReloadType).Should(BeEquivalentTo(parametersv1alpha1.UnixSignalType)) + Expect(&configSpecs[0].FormatterConfig).Should(BeEquivalentTo(cd[0].FileFormatConfig)) + }) + }) + + Context("TestFromReloadTypeConfig", func() { + It("TestSignalTrigger", func() { + Expect(parametersv1alpha1.UnixSignalType).Should(BeEquivalentTo(FromReloadTypeConfig(¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + ProcessName: "test", + Signal: parametersv1alpha1.SIGHUP, + }}))) + }) + + It("TestAutoTrigger", func() { + Expect(parametersv1alpha1.AutoType).Should(BeEquivalentTo(FromReloadTypeConfig(¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{ + ProcessName: "test", + }}))) + }) + + It("TestShellTrigger", func() { + Expect(parametersv1alpha1.ShellType).Should(BeEquivalentTo(FromReloadTypeConfig(¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Command: []string{"/bin/true"}, + }}))) + }) + + It("TestTplScriptsTrigger", func() { + Expect(parametersv1alpha1.TPLScriptType).Should(BeEquivalentTo(FromReloadTypeConfig(¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: "test", + Namespace: "default", + }, + }}))) + }) + + It("TestInvalidTrigger", func() { + Expect("").Should(BeEquivalentTo(FromReloadTypeConfig(¶metersv1alpha1.ReloadAction{}))) + }) + }) + + Context("TestValidateReloadOptions", func() { + It("TestSignalTrigger", func() { + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{ + ProcessName: "test", + Signal: parametersv1alpha1.SIGHUP, + }}, nil, nil), + ).Should(Succeed()) + }) + + It("TestSignalTrigger", func() { + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{ + AutoTrigger: ¶metersv1alpha1.AutoTrigger{ + ProcessName: "test", + }}, nil, nil), + ).Should(Succeed()) + }) + + It("TestShellTrigger", func() { + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Command: []string{"/bin/true"}, + }}, nil, nil), + ).Should(Succeed()) + }) + + It("TestTplScriptsTrigger", func() { + ns := "default" + testName1 := "test1" + testName2 := "not_test1" + mockK8sCli.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]client.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: testName1, + Namespace: ns, + }, + }, + }), testutil.WithTimes(2))) + + By("Test valid") + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: testName1, + Namespace: ns, + }, + }}, mockK8sCli.Client(), ctx), + ).Should(Succeed()) + + By("Test invalid") + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + ScriptConfig: parametersv1alpha1.ScriptConfig{ + ScriptConfigMapRef: testName2, + Namespace: ns, + }, + }}, mockK8sCli.Client(), ctx), + ).ShouldNot(Succeed()) + }) + + It("TestInvalidTrigger", func() { + Expect(ValidateReloadOptions(¶metersv1alpha1.ReloadAction{}, nil, nil)).ShouldNot(Succeed()) + }) + }) +}) + +func TestFilterSubPathVolumeMount(t *testing.T) { + createConfigMeta := func(volumeName string, reloadType parametersv1alpha1.DynamicReloadType, reloadAction *parametersv1alpha1.ReloadAction) ConfigSpecMeta { + return ConfigSpecMeta{ConfigSpecInfo: ConfigSpecInfo{ + ReloadAction: reloadAction, + ReloadType: reloadType, + ConfigSpec: appsv1.ComponentTemplateSpec{ + VolumeName: volumeName, + }}} + } + + type args struct { + metas []ConfigSpecMeta + volumes []corev1.VolumeMount + } + tests := []struct { + name string + args args + want []ConfigSpecMeta + }{{ + name: "test1", + args: args{ + metas: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Sync: cfgutil.ToPointer(true), + }, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + Sync: cfgutil.ToPointer(true), + }, + }), + }, + volumes: []corev1.VolumeMount{ + {Name: "test1", SubPath: "test1"}, + {Name: "test2", SubPath: "test2"}, + {Name: "test3", SubPath: "test3"}, + }, + }, + want: []ConfigSpecMeta{ + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Sync: cfgutil.ToPointer(true), + }, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + Sync: cfgutil.ToPointer(true), + }, + }), + }, + }, { + name: "test2", + args: args{ + metas: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{}, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{}, + }), + }, + volumes: []corev1.VolumeMount{ + {Name: "test1"}, + {Name: "test2"}, + {Name: "test3"}, + }, + }, + want: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{}, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{}, + }), + }, + }, { + name: "test3", + args: args{ + metas: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{}, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{}, + }), + }, + volumes: []corev1.VolumeMount{}, + }, + want: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{}, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{}, + }), + }, + }, { + name: "test4", + args: args{ + metas: []ConfigSpecMeta{ + createConfigMeta("test1", parametersv1alpha1.UnixSignalType, ¶metersv1alpha1.ReloadAction{ + UnixSignalTrigger: ¶metersv1alpha1.UnixSignalTrigger{}, + }), + createConfigMeta("test2", parametersv1alpha1.ShellType, ¶metersv1alpha1.ReloadAction{ + ShellTrigger: ¶metersv1alpha1.ShellTrigger{ + Sync: cfgutil.ToPointer(false), + }, + }), + createConfigMeta("test3", parametersv1alpha1.TPLScriptType, ¶metersv1alpha1.ReloadAction{ + TPLScriptTrigger: ¶metersv1alpha1.TPLScriptTrigger{ + Sync: cfgutil.ToPointer(false), + }, + }), + }, + volumes: []corev1.VolumeMount{ + {Name: "test1", SubPath: "test1"}, + {Name: "test2", SubPath: "test2"}, + {Name: "test3", SubPath: "test3"}, + }, + }, + want: nil, + }} + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, FilterSupportReloadActionConfigSpecs(tt.args.metas, tt.args.volumes), "FilterSupportReloadActionConfigSpecs(%v, %v)", tt.args.metas, tt.args.volumes) + }) + } +} diff --git a/pkg/configuration/core/config_util.go b/pkg/configuration/core/config_util.go index d4c740e0144..00b8d3c66ff 100644 --- a/pkg/configuration/core/config_util.go +++ b/pkg/configuration/core/config_util.go @@ -21,6 +21,7 @@ package core import ( "context" + "path/filepath" "regexp" "strings" @@ -133,7 +134,7 @@ func ToV1ConfigDescription(keys []string, format *parametersv1alpha1.FileFormatC var configs []parametersv1alpha1.ComponentConfigDescription for _, key := range keys { configs = append(configs, parametersv1alpha1.ComponentConfigDescription{ - Name: key, + Name: filepath.Base(key), FileFormatConfig: format, }) } diff --git a/pkg/configuration/validate/config_validate.go b/pkg/configuration/validate/config_validate.go index 6f20aa9a603..6d43cfb800f 100644 --- a/pkg/configuration/validate/config_validate.go +++ b/pkg/configuration/validate/config_validate.go @@ -20,7 +20,6 @@ along with this program. If not, see . package validate import ( - "github.com/StudioSol/set" apiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/kube-openapi/pkg/validation/errors" kubeopenapispec "k8s.io/kube-openapi/pkg/validation/spec" @@ -37,35 +36,12 @@ type ConfigValidator interface { Validate(data string) error } -type cmKeySelector struct { - // A ConfigMap object may contain multiple configuration files and only some of them can be parsed and verified by kubeblocks, - // such as postgresql, there are two files pg_hba.conf & postgresql.conf in the ConfigMap, and we can only validate postgresql.conf, - // so pg_hba.conf file needs to be ignored during when doing verification. - // keySelector filters the keys in the configmap. - keySelector []ValidatorOptions -} - type configCueValidator struct { - cmKeySelector - // cue describes configuration template cueScript string cfgType parametersv1alpha1.CfgFileFormat } -func (s *cmKeySelector) filter(key string) bool { - if len(s.keySelector) == 0 { - return false - } - - for _, option := range s.keySelector { - if !option(key) { - return true - } - } - return false -} - func (c *configCueValidator) Validate(content string) error { if c.cueScript == "" { return nil @@ -74,8 +50,6 @@ func (c *configCueValidator) Validate(content string) error { } type schemaValidator struct { - cmKeySelector - typeName string schema *apiext.JSONSchemaProps cfgType parametersv1alpha1.CfgFileFormat @@ -103,16 +77,6 @@ func (e emptyValidator) Validate(_ string) error { return nil } -func WithKeySelector(keys []string) ValidatorOptions { - var sets *set.LinkedHashSetString - if len(keys) > 0 { - sets = core.FromCMKeysSelector(keys) - } - return func(key string) bool { - return sets == nil || sets.InArray(key) - } -} - func NewConfigValidator(paramsSchema *parametersv1alpha1.ParametersSchema, fileFormat *parametersv1alpha1.FileFormatConfig) ConfigValidator { if fileFormat == nil { return &emptyValidator{} diff --git a/pkg/configuration/validate/config_validate_test.go b/pkg/configuration/validate/config_validate_test.go index 6058d747d89..81f8ab013a7 100644 --- a/pkg/configuration/validate/config_validate_test.go +++ b/pkg/configuration/validate/config_validate_test.go @@ -48,7 +48,6 @@ func TestSchemaValidatorWithCue(t *testing.T) { cueFile string configFile string format parametersv1alpha1.CfgFileFormat - options []ValidatorOptions } tests := []struct { name string @@ -112,9 +111,8 @@ mysqld.innodb_autoinc_lock_mode: conflicting values 2 and 100: name: "configmap_key_filter", args: args{ cueFile: "cue_testdata/mysql.cue", - configFile: "cue_testdata/mysql_err.cnf", + configFile: "cue_testdata/mysql.cnf", format: parametersv1alpha1.Ini, - options: []ValidatorOptions{WithKeySelector([]string{"key2", "key3"})}, }, }} @@ -127,62 +125,44 @@ mysqld.innodb_autoinc_lock_mode: conflicting values 2 and 100: } } -// func TestSchemaValidatorWithSelector(t *testing.T) { -// validator := NewConfigValidator(newFakeConfigSchema("cue_testdata/mysql.cue", appsv1beta1.Ini)) -// require.NotNil(t, validator) -// require.ErrorContains(t, validator.Validate( -// map[string]string{ -// "normal_key": fromTestData("cue_testdata/mysql.cnf"), -// "abnormal_key": fromTestData("cue_testdata/mysql_err.cnf"), -// }), "[mysqld.innodb_autoinc_lock_mode: 3 errors in empty disjunction") -// -// validator = NewConfigValidator(newFakeConfigSchema("cue_testdata/mysql.cue", appsv1beta1.Ini), WithKeySelector([]string{})) -// require.NotNil(t, validator) -// require.ErrorContains(t, validator.Validate( -// map[string]string{ -// "normal_key": fromTestData("cue_testdata/mysql.cnf"), -// "abnormal_key": fromTestData("cue_testdata/mysql_err.cnf"), -// }), "[mysqld.innodb_autoinc_lock_mode: 3 errors in empty disjunction") -// -// validator = NewConfigValidator(newFakeConfigSchema("cue_testdata/mysql.cue", appsv1beta1.Ini), WithKeySelector([]string{"normal_key"})) -// require.NotNil(t, validator) -// require.Nil(t, validator.Validate( -// map[string]string{ -// "normal_key": fromTestData("cue_testdata/mysql.cnf"), -// "abnormal_key": fromTestData("cue_testdata/mysql_err.cnf"), -// })) -// } +func TestSchemaValidatorWithSelector(t *testing.T) { + validator := NewConfigValidator(newFakeConfigSchema("cue_testdata/mysql.cue"), ¶metersv1alpha1.FileFormatConfig{Format: parametersv1alpha1.Ini}) + require.NotNil(t, validator) + require.ErrorContains(t, validator.Validate( + fromTestData("cue_testdata/mysql_err.cnf"), + ), "[mysqld.innodb_autoinc_lock_mode: 3 errors in empty disjunction") +} -// func TestSchemaValidatorWithOpenSchema(t *testing.T) { -// type args struct { -// cueFile string -// configFile string -// format parametersv1alpha1.CfgFileFormat -// SchemaTypeName string -// } -// tests := []struct { -// name string -// args args -// err error -// }{{ -// name: "test_wesql", -// args: args{ -// cueFile: "cue_testdata/mysql.cue", -// configFile: "cue_testdata/mysql.cnf", -// format: parametersv1alpha1.Ini, -// }, -// err: nil, -// }} -// -// for _, tt := range tests { -// t.Run(tt.name, func(t *testing.T) { -// tplConstraint := newFakeConfigSchema(tt.args.cueFile) -// validator := &schemaValidator{ -// typeName: tt.args.SchemaTypeName, -// cfgType: tt.args.format, -// schema: tplConstraint.ParametersSchema.SchemaInJSON, -// } -// require.Equal(t, tt.err, validator.Validate(fromTestData(tt.args.configFile))) -// }) -// } -// } +func TestSchemaValidatorWithOpenSchema(t *testing.T) { + type args struct { + cueFile string + configFile string + format parametersv1alpha1.CfgFileFormat + SchemaTypeName string + } + tests := []struct { + name string + args args + err error + }{{ + name: "test_wesql", + args: args{ + cueFile: "cue_testdata/mysql.cue", + configFile: "cue_testdata/mysql.cnf", + format: parametersv1alpha1.Ini, + }, + err: nil, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + configSchema := newFakeConfigSchema(tt.args.cueFile) + validator := &schemaValidator{ + typeName: tt.args.SchemaTypeName, + cfgType: tt.args.format, + schema: configSchema.SchemaInJSON, + } + require.Equal(t, tt.err, validator.Validate(fromTestData(tt.args.configFile))) + }) + } +} diff --git a/pkg/controller/configuration/builtin_env_test.go b/pkg/controller/configuration/builtin_env_test.go index b1715c0e661..5a29cf3ded4 100644 --- a/pkg/controller/configuration/builtin_env_test.go +++ b/pkg/controller/configuration/builtin_env_test.go @@ -19,261 +19,261 @@ along with this program. If not, see . package configuration -// import ( -// "fmt" -// "strconv" -// -// . "github.com/onsi/ginkgo/v2" -// . "github.com/onsi/gomega" -// -// corev1 "k8s.io/api/core/v1" -// "k8s.io/apimachinery/pkg/api/resource" -// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -// coreclient "sigs.k8s.io/controller-runtime/pkg/client" -// -// appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" -// "github.com/apecloud/kubeblocks/pkg/constant" -// ctrlcomp "github.com/apecloud/kubeblocks/pkg/controller/component" -// testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" -// ) -// -// var _ = Describe("tpl env template", func() { -// -// patroniTemplate := ` -// bootstrap: -// initdb: -// - auth-host: md5 -// - auth-local: trust -// ` -// const ( -// cmTemplateName = "patroni-template-config" -// cmConfigFileName = "postgresql.yaml" -// ) -// -// var ( -// podSpec *corev1.PodSpec -// component *ctrlcomp.SynthesizedComponent -// cluster *appsv1.Cluster -// -// mockClient *testutil.K8sClientMockHelper -// ) -// -// BeforeEach(func() { -// mockClient = testutil.NewK8sMockClient() -// -// mockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]coreclient.Object{ -// &corev1.ConfigMap{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: cmTemplateName, -// Namespace: "default", -// }, -// Data: map[string]string{ -// cmConfigFileName: patroniTemplate, -// }}, -// &corev1.ConfigMap{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "my-config-env", -// Namespace: "default", -// }, -// Data: map[string]string{ -// "KB_0_HOSTNAME": "my-mysql-0.my-mysql-headless", -// "KB_FOLLOWERS": "", -// "KB_LEADER": "my-mysql-0", -// "KB_REPLICA_COUNT": "1", -// "LOOP_REFERENCE_A": "$(LOOP_REFERENCE_B)", -// "LOOP_REFERENCE_B": "$(LOOP_REFERENCE_C)", -// "LOOP_REFERENCE_C": "$(LOOP_REFERENCE_A)", -// }}, -// &corev1.Secret{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "my-conn-credential", -// Namespace: "default", -// }, -// Data: map[string][]byte{ -// "password": []byte("4zrqfl2r"), -// "username": []byte("root"), -// }}, -// }), testutil.WithAnyTimes())) -// -// // 2 configmap and 2 secret -// // Add any setup steps that needs to be executed before each test -// podSpec = &corev1.PodSpec{ -// Containers: []corev1.Container{ -// { -// Name: "mytest", -// Env: []corev1.EnvVar{ -// { -// Name: constant.KBEnvClusterName, -// Value: "my", -// }, -// { -// Name: constant.KBEnvCompName, -// Value: "mysql", -// }, -// { -// Name: "MEMORY_SIZE", -// ValueFrom: &corev1.EnvVarSource{ -// ResourceFieldRef: &corev1.ResourceFieldSelector{ -// ContainerName: "mytest", -// Resource: "limits.memory", -// }, -// }, -// }, -// { -// Name: "CPU", -// ValueFrom: &corev1.EnvVarSource{ -// ResourceFieldRef: &corev1.ResourceFieldSelector{ -// Resource: "limits.cpu", -// }, -// }, -// }, -// { -// Name: "CPU2", -// ValueFrom: &corev1.EnvVarSource{ -// ResourceFieldRef: &corev1.ResourceFieldSelector{ -// ContainerName: "not_exist_container", -// Resource: "limits.memory", -// }, -// }, -// }, -// { -// Name: "MYSQL_USER", -// ValueFrom: &corev1.EnvVarSource{ -// SecretKeyRef: &corev1.SecretKeySelector{ -// LocalObjectReference: corev1.LocalObjectReference{ -// Name: "my-conn-credential", -// }, -// Key: "username", -// }, -// }, -// }, -// { -// Name: "MYSQL_PASSWORD", -// ValueFrom: &corev1.EnvVarSource{ -// SecretKeyRef: &corev1.SecretKeySelector{ -// LocalObjectReference: corev1.LocalObjectReference{ -// Name: "my-conn-credential", -// }, -// Key: "password", -// }, -// }, -// }, -// { -// Name: "SPILO_CONFIGURATION", -// ValueFrom: &corev1.EnvVarSource{ -// ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ -// LocalObjectReference: corev1.LocalObjectReference{ -// Name: cmTemplateName, -// }, -// Key: cmConfigFileName, -// }, -// }, -// }, -// }, -// EnvFrom: []corev1.EnvFromSource{ -// { -// ConfigMapRef: &corev1.ConfigMapEnvSource{ -// LocalObjectReference: corev1.LocalObjectReference{ -// Name: "my-config-env", -// }, -// }, -// }, -// { -// SecretRef: &corev1.SecretEnvSource{ -// LocalObjectReference: corev1.LocalObjectReference{ -// Name: "my-secret-env", -// }, -// }, -// }, -// }, -// Resources: corev1.ResourceRequirements{ -// Limits: map[corev1.ResourceName]resource.Quantity{ -// corev1.ResourceMemory: resource.MustParse("8Gi"), -// corev1.ResourceCPU: resource.MustParse("4"), -// }, -// }, -// }, -// { -// Name: "invalid_container", -// }, -// }, -// } -// cluster = &appsv1.Cluster{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "my", -// UID: "b006a20c-fb03-441c-bffa-2605cad7e297", -// }, -// } -// component = &ctrlcomp.SynthesizedComponent{ -// Name: "mysql", -// ClusterName: cluster.Name, -// } -// }) -// -// AfterEach(func() { -// mockClient.Finish() -// }) -// -// // for test GetContainerWithVolumeMount -// Context("ConfigTemplateBuilder built-in env test", func() { -// It("test built-in function", func() { -// cfgBuilder := newTemplateBuilder( -// "my_test", -// "default", -// ctx, mockClient.Client(), -// ) -// -// localObjs := []coreclient.Object{ -// &corev1.ConfigMap{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: cmTemplateName, -// Namespace: "default", -// }, -// Data: map[string]string{ -// cmConfigFileName: patroniTemplate, -// }}, -// } -// cfgBuilder.injectBuiltInObjectsAndFunctions(podSpec, component, localObjs, -// &appsv1.Cluster{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "my_test", -// Namespace: "default", -// }, -// }) -// -// rendered, err := cfgBuilder.render(map[string]string{ -// // KB_CLUSTER_NAME, KB_COMP_NAME from env -// // MYSQL_USER,MYSQL_PASSWORD from valueFrom secret key -// // SPILO_CONFIGURATION from valueFrom configmap key -// // KB_LEADER from envFrom configmap -// // MEMORY_SIZE, CPU from resourceFieldRef -// "my": fmt.Sprintf("{{ getEnvByName ( index $.podSpec.containers 0 ) \"%s\" }}", constant.KBEnvClusterName), -// "mysql": fmt.Sprintf("{{ getEnvByName ( index $.podSpec.containers 0 ) \"%s\" }}", constant.KBEnvCompName), -// "root": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_USER\" }}", -// "4zrqfl2r": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_PASSWORD\" }}", -// patroniTemplate: "{{ getEnvByName ( index $.podSpec.containers 0 ) \"SPILO_CONFIGURATION\" }}", -// "my-mysql-0": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"KB_LEADER\" }}", -// -// strconv.Itoa(4): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU\" }}", -// strconv.Itoa(8 * 1024 * 1024 * 1024): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MEMORY_SIZE\" }}", -// }) -// -// Expect(err).Should(Succeed()) -// for key, value := range rendered { -// Expect(key).Should(BeEquivalentTo(value)) -// } -// -// _, err = cfgBuilder.render(map[string]string{ -// "error": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU2\" }}", -// }) -// Expect(err).ShouldNot(Succeed()) -// Expect(err.Error()).Should(ContainSubstring("not found named[not_exist_container] container")) -// -// _, err = cfgBuilder.render(map[string]string{ -// "error_loop_reference": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"LOOP_REFERENCE_A\" }}", -// }) -// Expect(err).ShouldNot(Succeed()) -// Expect(err.Error()).Should(ContainSubstring("too many reference count, maybe there is a cycled reference")) -// }) -// }) -// }) +import ( + "fmt" + "strconv" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + coreclient "sigs.k8s.io/controller-runtime/pkg/client" + + appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + "github.com/apecloud/kubeblocks/pkg/constant" + ctrlcomp "github.com/apecloud/kubeblocks/pkg/controller/component" + testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" + testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" +) + +var _ = Describe("tpl env template", func() { + + patroniTemplate := ` +bootstrap: + initdb: + - auth-host: md5 + - auth-local: trust +` + const ( + cmTemplateName = "patroni-template-config" + cmConfigFileName = "postgresql.yaml" + ) + + var ( + podSpec *corev1.PodSpec + component *ctrlcomp.SynthesizedComponent + cluster *appsv1.Cluster + + mockClient *testutil.K8sClientMockHelper + ) + + BeforeEach(func() { + mockClient = testutil.NewK8sMockClient() + + mockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]coreclient.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cmTemplateName, + Namespace: "default", + }, + Data: map[string]string{ + cmConfigFileName: patroniTemplate, + }}, + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-config-env", + Namespace: "default", + }, + Data: map[string]string{ + "KB_0_HOSTNAME": "my-mysql-0.my-mysql-headless", + "KB_FOLLOWERS": "", + "KB_LEADER": "my-mysql-0", + "KB_REPLICA_COUNT": "1", + "LOOP_REFERENCE_A": "$(LOOP_REFERENCE_B)", + "LOOP_REFERENCE_B": "$(LOOP_REFERENCE_C)", + "LOOP_REFERENCE_C": "$(LOOP_REFERENCE_A)", + }}, + &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-conn-credential", + Namespace: "default", + }, + Data: map[string][]byte{ + "password": []byte("4zrqfl2r"), + "username": []byte("root"), + }}, + }), testutil.WithAnyTimes())) + + // 2 configmap and 2 secret + // Add any setup steps that needs to be executed before each test + podSpec = &corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "mytest", + Env: []corev1.EnvVar{ + { + Name: constant.KBEnvClusterName, + Value: "my", + }, + { + Name: constant.KBEnvCompName, + Value: "mysql", + }, + { + Name: "MEMORY_SIZE", + ValueFrom: &corev1.EnvVarSource{ + ResourceFieldRef: &corev1.ResourceFieldSelector{ + ContainerName: "mytest", + Resource: "limits.memory", + }, + }, + }, + { + Name: "CPU", + ValueFrom: &corev1.EnvVarSource{ + ResourceFieldRef: &corev1.ResourceFieldSelector{ + Resource: "limits.cpu", + }, + }, + }, + { + Name: "CPU2", + ValueFrom: &corev1.EnvVarSource{ + ResourceFieldRef: &corev1.ResourceFieldSelector{ + ContainerName: "not_exist_container", + Resource: "limits.memory", + }, + }, + }, + { + Name: "MYSQL_USER", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "my-conn-credential", + }, + Key: "username", + }, + }, + }, + { + Name: "MYSQL_PASSWORD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "my-conn-credential", + }, + Key: "password", + }, + }, + }, + { + Name: "SPILO_CONFIGURATION", + ValueFrom: &corev1.EnvVarSource{ + ConfigMapKeyRef: &corev1.ConfigMapKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: cmTemplateName, + }, + Key: cmConfigFileName, + }, + }, + }, + }, + EnvFrom: []corev1.EnvFromSource{ + { + ConfigMapRef: &corev1.ConfigMapEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "my-config-env", + }, + }, + }, + { + SecretRef: &corev1.SecretEnvSource{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: "my-secret-env", + }, + }, + }, + }, + Resources: corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]resource.Quantity{ + corev1.ResourceMemory: resource.MustParse("8Gi"), + corev1.ResourceCPU: resource.MustParse("4"), + }, + }, + }, + { + Name: "invalid_container", + }, + }, + } + cluster = &appsv1.Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my", + UID: "b006a20c-fb03-441c-bffa-2605cad7e297", + }, + } + component = &ctrlcomp.SynthesizedComponent{ + Name: "mysql", + ClusterName: cluster.Name, + } + }) + + AfterEach(func() { + mockClient.Finish() + }) + + // for test GetContainerWithVolumeMount + Context("ConfigTemplateBuilder built-in env test", func() { + It("test built-in function", func() { + cfgBuilder := NewTemplateBuilder( + &ReconcileCtx{ + ResourceCtx: &ResourceCtx{ + Context: ctx, + Client: mockClient.Client(), + Namespace: testCtx.DefaultNamespace, + }, + Cluster: testapps.NewClusterFactory("my_test", "default", "cmpd").GetObject(), + SynthesizedComponent: component, + Cache: []coreclient.Object{ + &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: cmTemplateName, + Namespace: "default", + }, + Data: map[string]string{ + cmConfigFileName: patroniTemplate, + }}, + }, + PodSpec: podSpec, + }, + ) + + rendered, err := cfgBuilder.render(map[string]string{ + // KB_CLUSTER_NAME, KB_COMP_NAME from env + // MYSQL_USER,MYSQL_PASSWORD from valueFrom secret key + // SPILO_CONFIGURATION from valueFrom configmap key + // KB_LEADER from envFrom configmap + // MEMORY_SIZE, CPU from resourceFieldRef + "my": fmt.Sprintf("{{ getEnvByName ( index $.podSpec.containers 0 ) \"%s\" }}", constant.KBEnvClusterName), + "mysql": fmt.Sprintf("{{ getEnvByName ( index $.podSpec.containers 0 ) \"%s\" }}", constant.KBEnvCompName), + "root": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_USER\" }}", + "4zrqfl2r": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MYSQL_PASSWORD\" }}", + patroniTemplate: "{{ getEnvByName ( index $.podSpec.containers 0 ) \"SPILO_CONFIGURATION\" }}", + "my-mysql-0": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"KB_LEADER\" }}", + + strconv.Itoa(4): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU\" }}", + strconv.Itoa(8 * 1024 * 1024 * 1024): "{{ getEnvByName ( index $.podSpec.containers 0 ) \"MEMORY_SIZE\" }}", + }) + + Expect(err).Should(Succeed()) + for key, value := range rendered { + Expect(key).Should(BeEquivalentTo(value)) + } + + _, err = cfgBuilder.render(map[string]string{ + "error": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"CPU2\" }}", + }) + Expect(err).ShouldNot(Succeed()) + Expect(err.Error()).Should(ContainSubstring("not found named[not_exist_container] container")) + + _, err = cfgBuilder.render(map[string]string{ + "error_loop_reference": "{{ getEnvByName ( index $.podSpec.containers 0 ) \"LOOP_REFERENCE_A\" }}", + }) + Expect(err).ShouldNot(Succeed()) + Expect(err.Error()).Should(ContainSubstring("too many reference count, maybe there is a cycled reference")) + }) + }) +}) diff --git a/pkg/controller/configuration/parameter_utils_test.go b/pkg/controller/configuration/parameter_utils_test.go index 76223688cd3..e445f2b163e 100644 --- a/pkg/controller/configuration/parameter_utils_test.go +++ b/pkg/controller/configuration/parameter_utils_test.go @@ -19,6 +19,7 @@ package configuration import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" "k8s.io/utils/pointer" diff --git a/pkg/controller/configuration/resource_wrapper_test.go b/pkg/controller/configuration/resource_wrapper_test.go index 85093e343be..a85e6e25158 100644 --- a/pkg/controller/configuration/resource_wrapper_test.go +++ b/pkg/controller/configuration/resource_wrapper_test.go @@ -25,12 +25,11 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/controller/builder" testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" ) @@ -70,18 +69,13 @@ var _ = Describe("resource Fetcher", func() { []client.Object{ cluster, testapps.NewConfigMap("default", cfgcore.GetComponentCfgName(clusterName, mysqlCompName, mysqlConfigName)), - &appsv1beta1.ConfigConstraint{ - ObjectMeta: metav1.ObjectMeta{ - Name: mysqlConfigName, - }, - }, + builder.NewComponentParameterBuilder(testCtx.DefaultNamespace, cfgcore.GenerateComponentParameterName(clusterName, mysqlCompName)).GetObject(), }, ), testutil.WithAnyTimes())) err := NewTest(k8sMockClient.Client(), ctx). Cluster(). ComponentSpec(). ConfigMap(mysqlConfigName). - // ConfigConstraints(mysqlConfigName). ComponentParameter(). Complete() Expect(err).Should(Succeed()) diff --git a/pkg/controllerutil/parameter_schema.go b/pkg/controllerutil/parameter_schema.go index 6c7437e4dd6..93694890a9e 100644 --- a/pkg/controllerutil/parameter_schema.go +++ b/pkg/controllerutil/parameter_schema.go @@ -43,7 +43,7 @@ func ResolveConfigParameterSchema(paramDef *parametersv1alpha1.ParametersDefinit } paramMeta := &ParameterMeta{ - FileName: paramDef.Name, + FileName: paramDef.Spec.FileName, ConfigTemplateName: configTemplate.Name, } props := openapi.FlattenSchema(schema.Properties[openapi.DefaultSchemaName]).Properties