diff --git a/controllers/apps/clusterdefinition_controller_test.go b/controllers/apps/clusterdefinition_controller_test.go index 241bed304da..f5698ea48ef 100644 --- a/controllers/apps/clusterdefinition_controller_test.go +++ b/controllers/apps/clusterdefinition_controller_test.go @@ -58,7 +58,6 @@ var _ = Describe("ClusterDefinition Controller", func() { testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ClusterDefinitionSignature, true, ml) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ShardingDefinitionSignature, true, ml) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ComponentDefinitionSignature, true, ml) - testapps.ClearResources(&testCtx, intctrlutil.ConfigConstraintSignature, ml) // namespaced testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ConfigMapSignature, true, inNS, ml) diff --git a/controllers/apps/component_controller.go b/controllers/apps/component_controller.go index a2bd75a6ae2..f1a186541ba 100644 --- a/controllers/apps/component_controller.go +++ b/controllers/apps/component_controller.go @@ -37,7 +37,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" workloads "github.com/apecloud/kubeblocks/apis/workloads/v1" "github.com/apecloud/kubeblocks/pkg/constant" @@ -152,14 +151,12 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( &componentAccountTransformer{}, // handle the TLS configuration &componentTLSTransformer{Client: r.Client}, - // rerender parameters after v-scale and h-scale - &componentRelatedParametersTransformer{Client: r.Client}, // resolve and build vars for template and Env &componentVarsTransformer{}, // provision component system accounts, depend on vars &componentAccountProvisionTransformer{}, // render component configurations - &componentConfigurationTransformer{Client: r.Client}, + &componentReloadActionSidecarTransformer{Client: r.Client}, // handle restore before workloads transform &componentRestoreTransformer{Client: r.Client}, // handle the component workload @@ -207,8 +204,7 @@ func (r *ComponentReconciler) setupWithManager(mgr ctrl.Manager) error { Owns(&corev1.Secret{}). Owns(&corev1.ConfigMap{}). Watches(&dpv1alpha1.Restore{}, handler.EnqueueRequestsFromMapFunc(r.filterComponentResources)). - Watches(&corev1.PersistentVolumeClaim{}, handler.EnqueueRequestsFromMapFunc(r.filterComponentResources)). - Watches(&appsv1alpha1.Configuration{}, handler.EnqueueRequestsFromMapFunc(r.configurationEventHandler)) + Watches(&corev1.PersistentVolumeClaim{}, handler.EnqueueRequestsFromMapFunc(r.filterComponentResources)) if viper.GetBool(constant.EnableRBACManager) { b.Owns(&rbacv1.RoleBinding{}). @@ -228,8 +224,7 @@ func (r *ComponentReconciler) setupWithMultiClusterManager(mgr ctrl.Manager, mul MaxConcurrentReconciles: viper.GetInt(constant.CfgKBReconcileWorkers), }). Owns(&workloads.InstanceSet{}). - Watches(&dpv1alpha1.Restore{}, handler.EnqueueRequestsFromMapFunc(r.filterComponentResources)). - Watches(&appsv1alpha1.Configuration{}, handler.EnqueueRequestsFromMapFunc(r.configurationEventHandler)) + Watches(&dpv1alpha1.Restore{}, handler.EnqueueRequestsFromMapFunc(r.filterComponentResources)) eventHandler := handler.EnqueueRequestsFromMapFunc(r.filterComponentResources) multiClusterMgr.Watch(b, &corev1.Service{}, eventHandler). @@ -263,18 +258,3 @@ func (r *ComponentReconciler) filterComponentResources(ctx context.Context, obj }, } } - -func (r *ComponentReconciler) configurationEventHandler(_ context.Context, obj client.Object) []reconcile.Request { - cr, ok := obj.(*appsv1alpha1.Configuration) - if !ok { - return []reconcile.Request{} - } - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Namespace: obj.GetNamespace(), - Name: constant.GenerateClusterComponentName(cr.Spec.ClusterRef, cr.Spec.ComponentName), - }, - }, - } -} diff --git a/controllers/apps/componentdefinition_controller_test.go b/controllers/apps/componentdefinition_controller_test.go index 204a50169e0..d218dd60dd9 100644 --- a/controllers/apps/componentdefinition_controller_test.go +++ b/controllers/apps/componentdefinition_controller_test.go @@ -61,7 +61,6 @@ var _ = Describe("ComponentDefinition Controller", func() { // non-namespaced testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ComponentDefinitionSignature, true, ml) - testapps.ClearResources(&testCtx, intctrlutil.ConfigConstraintSignature, ml) // namespaced testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, intctrlutil.ConfigMapSignature, true, inNS, ml) diff --git a/controllers/apps/suite_test.go b/controllers/apps/suite_test.go index 21a4145781b..c42df5a778f 100644 --- a/controllers/apps/suite_test.go +++ b/controllers/apps/suite_test.go @@ -43,8 +43,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/metrics/server" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" @@ -123,18 +121,10 @@ var _ = BeforeSuite(func() { Expect(err).NotTo(HaveOccurred()) Expect(cfg).NotTo(BeNil()) - err = appsv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - model.AddScheme(appsv1alpha1.AddToScheme) - err = opsv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) model.AddScheme(opsv1alpha1.AddToScheme) - err = appsv1beta1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - model.AddScheme(appsv1beta1.AddToScheme) - err = appsv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) model.AddScheme(appsv1.AddToScheme) @@ -255,6 +245,13 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager, nil) Expect(err).ToNot(HaveOccurred()) + err = (¶meters.ComponentDrivenParameterReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("component-parameter-controller"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + err = (¶meters.ParametersDefinitionReconciler{ Client: k8sManager.GetClient(), Scheme: k8sManager.GetScheme(), diff --git a/controllers/apps/transformer_component_configuration.go b/controllers/apps/transformer_component_configuration.go deleted file mode 100644 index 0cc0a8afd52..00000000000 --- a/controllers/apps/transformer_component_configuration.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package apps - -import ( - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/apecloud/kubeblocks/pkg/common" - "github.com/apecloud/kubeblocks/pkg/controller/graph" - "github.com/apecloud/kubeblocks/pkg/controller/model" - "github.com/apecloud/kubeblocks/pkg/controller/plan" - "github.com/apecloud/kubeblocks/pkg/controller/render" -) - -// componentConfigurationTransformer handles component configuration render -type componentConfigurationTransformer struct { - client.Client -} - -var _ graph.Transformer = &componentConfigurationTransformer{} - -func (t *componentConfigurationTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { - transCtx, _ := ctx.(*componentTransformContext) - - comp := transCtx.Component - cluster := transCtx.Cluster - compOrig := transCtx.ComponentOrig - synthesizeComp := transCtx.SynthesizeComponent - - if model.IsObjectDeleting(compOrig) { - return nil - } - if common.IsCompactMode(compOrig.Annotations) { - transCtx.V(1).Info("Component is in compact mode, no need to create configuration related objects", - "component", client.ObjectKeyFromObject(transCtx.ComponentOrig)) - return nil - } - - // get dependOnObjs which will be used in configuration render - var dependOnObjs []client.Object - for _, vertex := range dag.Vertices() { - v, _ := vertex.(*model.ObjectVertex) - if cm, ok := v.Obj.(*corev1.ConfigMap); ok { - dependOnObjs = append(dependOnObjs, cm) - continue - } - if secret, ok := v.Obj.(*corev1.Secret); ok { - dependOnObjs = append(dependOnObjs, secret) - continue - } - } - - // configuration render - if err := plan.RenderConfigNScriptFiles( - &render.ResourceCtx{ - Context: transCtx.Context, - Client: t.Client, - Namespace: comp.GetNamespace(), - ClusterName: synthesizeComp.ClusterName, - ComponentName: synthesizeComp.Name, - }, - cluster, - comp, - synthesizeComp, - synthesizeComp.PodSpec, - dependOnObjs); err != nil { - return err - } - return nil -} diff --git a/controllers/apps/transformer_component_deletion.go b/controllers/apps/transformer_component_deletion.go index fe7e58beff3..5f7dbbcca7f 100644 --- a/controllers/apps/transformer_component_deletion.go +++ b/controllers/apps/transformer_component_deletion.go @@ -30,7 +30,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" workloads "github.com/apecloud/kubeblocks/apis/workloads/v1" "github.com/apecloud/kubeblocks/pkg/constant" "github.com/apecloud/kubeblocks/pkg/controller/component" @@ -169,7 +169,7 @@ func compOwnedKinds() []client.ObjectList { &corev1.SecretList{}, &corev1.ConfigMapList{}, &corev1.PersistentVolumeClaimList{}, - &appsv1alpha1.ConfigurationList{}, + ¶metersv1alpha1.ComponentParameterList{}, &corev1.ServiceAccountList{}, &rbacv1.RoleBindingList{}, } diff --git a/controllers/apps/transformer_component_parameters.go b/controllers/apps/transformer_component_parameters.go deleted file mode 100644 index d34ebbd7f8b..00000000000 --- a/controllers/apps/transformer_component_parameters.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package apps - -import ( - "sigs.k8s.io/controller-runtime/pkg/client" - - parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" - cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" - "github.com/apecloud/kubeblocks/pkg/controller/configuration" - "github.com/apecloud/kubeblocks/pkg/controller/graph" - intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" -) - -type componentRelatedParametersTransformer struct { - client.Client -} - -var _ = componentRelatedParametersTransformer{} - -func (c *componentRelatedParametersTransformer) Transform(ctx graph.TransformContext, _ *graph.DAG) error { - transCtx, _ := ctx.(*componentTransformContext) - synthesizedComp := transCtx.SynthesizeComponent - - componentParameter := ¶metersv1alpha1.ComponentParameter{} - configKey := client.ObjectKey{Namespace: synthesizedComp.Namespace, - Name: cfgcore.GenerateComponentConfigurationName(synthesizedComp.ClusterName, synthesizedComp.Name)} - if err := c.Get(ctx.GetContext(), configKey, componentParameter); err != nil { - return client.IgnoreNotFound(err) - } - - configRender, err := intctrlutil.ResolveComponentConfigRender(ctx.GetContext(), c, transCtx.CompDef) - if err != nil { - return client.IgnoreNotFound(err) - } - if configRender == nil { - return nil - } - - configNew := componentParameter.DeepCopy() - if err = configuration.UpdateConfigPayload(&configNew.Spec, &transCtx.Component.Spec, &configRender.Spec); err != nil { - return err - } - return c.Patch(ctx.GetContext(), configNew, client.MergeFrom(componentParameter.DeepCopy())) -} diff --git a/controllers/apps/transformer_component_reconfigure.go b/controllers/apps/transformer_component_reconfigure.go new file mode 100644 index 00000000000..1da13e500b6 --- /dev/null +++ b/controllers/apps/transformer_component_reconfigure.go @@ -0,0 +1,142 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +This file is part of KubeBlocks project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +package apps + +import ( + "context" + + corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "sigs.k8s.io/controller-runtime/pkg/client" + + appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + "github.com/apecloud/kubeblocks/pkg/common" + "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/controller/component" + configctrl "github.com/apecloud/kubeblocks/pkg/controller/configuration" + "github.com/apecloud/kubeblocks/pkg/controller/graph" + "github.com/apecloud/kubeblocks/pkg/controller/model" + "github.com/apecloud/kubeblocks/pkg/controller/render" + intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" +) + +// componentReloadActionSidecarTransformer handles component configuration render +type componentReloadActionSidecarTransformer struct { + client.Client +} + +var _ graph.Transformer = &componentReloadActionSidecarTransformer{} + +func (t *componentReloadActionSidecarTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { + transCtx, _ := ctx.(*componentTransformContext) + + comp := transCtx.Component + cluster := transCtx.Cluster + compOrig := transCtx.ComponentOrig + synthesizeComp := transCtx.SynthesizeComponent + + if model.IsObjectDeleting(compOrig) { + return nil + } + if common.IsCompactMode(compOrig.Annotations) { + transCtx.V(1).Info("Component is in compact mode, no need to create configuration related objects", + "component", client.ObjectKeyFromObject(transCtx.ComponentOrig)) + return nil + } + + reconcileCtx := &render.ResourceCtx{ + Context: transCtx.Context, + Client: t.Client, + Namespace: comp.GetNamespace(), + ClusterName: synthesizeComp.ClusterName, + ComponentName: synthesizeComp.Name, + } + + var configmaps []*corev1.ConfigMap + cachedObjs := resolveRerenderDependOnObjects(dag) + for _, tpls := range [][]appsv1.ComponentTemplateSpec{synthesizeComp.ScriptTemplates, synthesizeComp.ConfigTemplates} { + objects, err := render.RenderTemplate(reconcileCtx, cluster, synthesizeComp, comp, cachedObjs, tpls) + if err != nil { + return err + } + configmaps = append(configmaps, objects...) + } + + graphCli, _ := transCtx.Client.(model.GraphClient) + if err := ensureConfigMapsPresence(transCtx, graphCli, dag, configmaps...); err != nil { + return err + } + if err := updatePodVolumes(synthesizeComp.PodSpec, synthesizeComp); err != nil { + return err + } + if len(synthesizeComp.ConfigTemplates) == 0 { + return nil + } + + return configctrl.BuildReloadActionContainer(reconcileCtx, cluster, synthesizeComp, transCtx.CompDef, configmaps) +} + +func ensureConfigMapsPresence(ctx context.Context, cli model.GraphClient, dag *graph.DAG, configmaps ...*corev1.ConfigMap) error { + for _, configmap := range configmaps { + var cm = &corev1.ConfigMap{} + if err := cli.Get(ctx, client.ObjectKeyFromObject(configmap), cm); err != nil { + if !apierrors.IsNotFound(err) { + return err + } + cli.Create(dag, configmap, inDataContext4G()) + } + } + return nil +} + +func resolveRerenderDependOnObjects(dag *graph.DAG) []client.Object { + var dependOnObjs []client.Object + for _, vertex := range dag.Vertices() { + v, _ := vertex.(*model.ObjectVertex) + if cm, ok := v.Obj.(*corev1.ConfigMap); ok { + dependOnObjs = append(dependOnObjs, cm) + continue + } + if secret, ok := v.Obj.(*corev1.Secret); ok { + dependOnObjs = append(dependOnObjs, secret) + continue + } + } + return dependOnObjs +} + +func updatePodVolumes(podSpec *corev1.PodSpec, component *component.SynthesizedComponent) error { + volumes := make(map[string]appsv1.ComponentTemplateSpec, len(component.ConfigTemplates)) + for _, tpls := range [][]appsv1.ComponentTemplateSpec{component.ConfigTemplates, component.ScriptTemplates} { + for _, tpl := range tpls { + cmName := core.GetComponentCfgName(component.ClusterName, component.Name, tpl.Name) + volumes[cmName] = tpl + } + } + return intctrlutil.CreateOrUpdatePodVolumes(podSpec, volumes, configSetsFromComponent(component.ConfigTemplates)) +} + +func configSetsFromComponent(templates []appsv1.ComponentTemplateSpec) []string { + configSet := make([]string, 0, len(templates)) + for _, template := range templates { + configSet = append(configSet, template.Name) + } + return configSet +} diff --git a/controllers/apps/transformer_component_status.go b/controllers/apps/transformer_component_status.go index 5fc3e5c65b3..8d7b45e6da0 100644 --- a/controllers/apps/transformer_component_status.go +++ b/controllers/apps/transformer_component_status.go @@ -34,15 +34,12 @@ import ( appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" workloads "github.com/apecloud/kubeblocks/apis/workloads/v1" - cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" "github.com/apecloud/kubeblocks/pkg/constant" "github.com/apecloud/kubeblocks/pkg/controller/component" "github.com/apecloud/kubeblocks/pkg/controller/graph" "github.com/apecloud/kubeblocks/pkg/controller/instanceset" "github.com/apecloud/kubeblocks/pkg/controller/model" - intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" ) const ( @@ -133,12 +130,6 @@ func (t *componentStatusTransformer) reconcileStatus(transCtx *componentTransfor // check if the ITS is running isITSUpdatedNRunning := t.isInstanceSetRunning() - // check if all configTemplates are synced - isAllConfigSynced, err := t.isAllConfigSynced(transCtx) - if err != nil { - return err - } - // check if the component has failed pod hasFailedPod, messages := t.hasFailedPod() @@ -175,8 +166,8 @@ func (t *componentStatusTransformer) reconcileStatus(transCtx *componentTransfor }() transCtx.Logger.Info( - fmt.Sprintf("status conditions, creating: %v, its running: %v, has failure: %v, updating: %v, config synced: %v", - isInCreatingPhase, isITSUpdatedNRunning, hasFailure, hasRunningScaleOut || hasRunningVolumeExpansion, isAllConfigSynced)) + fmt.Sprintf("status conditions, creating: %v, its running: %v, has failure: %v, updating: %v", + isInCreatingPhase, isITSUpdatedNRunning, hasFailure, hasRunningScaleOut || hasRunningVolumeExpansion)) switch { case isDeleting: @@ -185,7 +176,7 @@ func (t *componentStatusTransformer) reconcileStatus(transCtx *componentTransfor t.setComponentStatusPhase(transCtx, appsv1.StoppingComponentPhase, nil, "component is Stopping") case stopped: t.setComponentStatusPhase(transCtx, appsv1.StoppedComponentPhase, nil, "component is Stopped") - case isITSUpdatedNRunning && isAllConfigSynced && !hasRunningScaleOut && !hasRunningVolumeExpansion: + case isITSUpdatedNRunning && !hasRunningScaleOut && !hasRunningVolumeExpansion: t.setComponentStatusPhase(transCtx, appsv1.RunningComponentPhase, nil, "component is Running") case !hasFailure && isInCreatingPhase: t.setComponentStatusPhase(transCtx, appsv1.CreatingComponentPhase, nil, "component is Creating") @@ -234,46 +225,6 @@ func (t *componentStatusTransformer) isInstanceSetRunning() bool { return instanceset.IsInstanceSetReady(t.runningITS) } -// isAllConfigSynced checks if all configTemplates are synced. -func (t *componentStatusTransformer) isAllConfigSynced(transCtx *componentTransformContext) (bool, error) { - var ( - cmKey client.ObjectKey - cmObj = &corev1.ConfigMap{} - ) - - if len(t.synthesizeComp.ConfigTemplates) == 0 { - return true, nil - } - - configurationKey := client.ObjectKey{ - Namespace: t.cluster.Namespace, - Name: cfgcore.GenerateComponentConfigurationName(t.cluster.Name, t.synthesizeComp.Name), - } - configuration := ¶metersv1alpha1.ComponentParameter{} - if err := t.Client.Get(transCtx.Context, configurationKey, configuration); err != nil { - return false, err - } - for _, configSpec := range t.synthesizeComp.ConfigTemplates { - item := intctrlutil.GetConfigTemplateItem(&configuration.Spec, configSpec.Name) - status := intctrlutil.GetItemStatus(&configuration.Status, configSpec.Name) - // for creating phase - if item == nil || status == nil { - return false, nil - } - cmKey = client.ObjectKey{ - Namespace: t.cluster.Namespace, - Name: cfgcore.GetComponentCfgName(t.cluster.Name, t.synthesizeComp.Name, configSpec.Name), - } - if err := t.Client.Get(transCtx.Context, cmKey, cmObj, inDataContext4C()); err != nil { - return false, err - } - if intctrlutil.GetConfigSpecReconcilePhase(cmObj, *item, status) != parametersv1alpha1.CFinishedPhase { - return false, nil - } - } - return true, nil -} - // hasScaleOutRunning checks if the scale out is running. func (t *componentStatusTransformer) hasScaleOutRunning(transCtx *componentTransformContext) (running bool, failed bool, err error) { if t.runningITS == nil || t.runningITS.Spec.Replicas == nil { diff --git a/controllers/parameters/componentdrivenparameter_controller.go b/controllers/parameters/componentdrivenparameter_controller.go new file mode 100644 index 00000000000..83209bf4213 --- /dev/null +++ b/controllers/parameters/componentdrivenparameter_controller.go @@ -0,0 +1,235 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +This file is part of KubeBlocks project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +package parameters + +import ( + "context" + "fmt" + "reflect" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + configcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/constant" + "github.com/apecloud/kubeblocks/pkg/controller/builder" + "github.com/apecloud/kubeblocks/pkg/controller/component" + configctrl "github.com/apecloud/kubeblocks/pkg/controller/configuration" + "github.com/apecloud/kubeblocks/pkg/controller/model" + intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" +) + +// ComponentDrivenParameterReconciler reconciles a Parameter object +type ComponentDrivenParameterReconciler struct { + client.Client + Scheme *runtime.Scheme + Recorder record.EventRecorder +} + +// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=components,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=components/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=components/finalizers,verbs=update + +// +kubebuilder:rbac:groups=parameters.kubeblocks.io,resources=componentparameters,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=parameters.kubeblocks.io,resources=componentparameters/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=parameters.kubeblocks.io,resources=componentparameters/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.4/pkg/reconcile +func (r *ComponentDrivenParameterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + reqCtx := intctrlutil.RequestCtx{ + Ctx: ctx, + Req: req, + Recorder: r.Recorder, + Log: log.FromContext(ctx). + WithName("ComponentParameterReconciler"). + WithValues("Namespace", req.Namespace, "Parameter", req.Name), + } + + comp := &appsv1.Component{} + if err := r.Client.Get(reqCtx.Ctx, reqCtx.Req.NamespacedName, comp); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + return r.reconcile(reqCtx, comp) +} + +// SetupWithManager sets up the controller with the Manager. +func (r *ComponentDrivenParameterReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&appsv1.Component{}). + Complete(r) +} + +func (r *ComponentDrivenParameterReconciler) reconcile(reqCtx intctrlutil.RequestCtx, component *appsv1.Component) (ctrl.Result, error) { + var err error + var existingObject *parametersv1alpha1.ComponentParameter + var expectedObject *parametersv1alpha1.ComponentParameter + + if existingObject, err = runningComponentParameter(reqCtx, r.Client, component); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + if model.IsObjectDeleting(component) { + return r.delete(reqCtx, existingObject) + } + if expectedObject, err = buildComponentParameter(reqCtx, r.Client, component); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + + switch { + case expectedObject == nil: + return r.delete(reqCtx, existingObject) + case existingObject == nil: + return r.create(reqCtx, expectedObject) + default: + return r.update(reqCtx, expectedObject, existingObject) + } +} + +func (r *ComponentDrivenParameterReconciler) create(reqCtx intctrlutil.RequestCtx, object *parametersv1alpha1.ComponentParameter) (ctrl.Result, error) { + if err := r.Client.Create(reqCtx.Ctx, object); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + reqCtx.Log.Info("ComponentParameter created") + intctrlutil.RecordCreatedEvent(r.Recorder, object) + return intctrlutil.Reconciled() +} + +func (r *ComponentDrivenParameterReconciler) delete(reqCtx intctrlutil.RequestCtx, object *parametersv1alpha1.ComponentParameter) (ctrl.Result, error) { + if object == nil { + return intctrlutil.Reconciled() + } + if err := r.Client.Delete(reqCtx.Ctx, object); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + reqCtx.Log.Info("ComponentParameter deleted") + return intctrlutil.Reconciled() +} + +func (r *ComponentDrivenParameterReconciler) update(reqCtx intctrlutil.RequestCtx, expected, existing *parametersv1alpha1.ComponentParameter) (ctrl.Result, error) { + mergedObject := r.mergeComponentParameter(expected, existing) + if reflect.DeepEqual(mergedObject, existing) { + return intctrlutil.Reconciled() + } + if err := r.Client.Patch(reqCtx.Ctx, mergedObject, client.MergeFrom(existing)); err != nil { + return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "") + } + return intctrlutil.Reconciled() +} + +func runningComponentParameter(reqCtx intctrlutil.RequestCtx, reader client.Reader, comp *appsv1.Component) (*parametersv1alpha1.ComponentParameter, error) { + var componentParameter = ¶metersv1alpha1.ComponentParameter{} + + clusterName, _ := component.GetClusterName(comp) + componentName, _ := component.ShortName(clusterName, comp.Name) + + parameterKey := types.NamespacedName{ + Name: configcore.GenerateComponentConfigurationName(clusterName, componentName), + Namespace: comp.Namespace, + } + if err := reader.Get(reqCtx.Ctx, parameterKey, componentParameter); err != nil { + return nil, client.IgnoreNotFound(err) + } + return componentParameter, nil +} + +func getCompDefinition(ctx context.Context, cli client.Reader, comp *appsv1.Component) (*appsv1.ComponentDefinition, error) { + compKey := types.NamespacedName{ + Name: comp.Spec.CompDef, + } + cmpd := &appsv1.ComponentDefinition{} + if err := cli.Get(ctx, compKey, cmpd); err != nil { + return nil, err + } + if cmpd.Status.Phase != appsv1.AvailablePhase { + return nil, fmt.Errorf("the referenced ComponentDefinition is unavailable: %s", cmpd.Name) + } + return cmpd, nil +} + +func buildComponentParameter(reqCtx intctrlutil.RequestCtx, reader client.Reader, comp *appsv1.Component) (*parametersv1alpha1.ComponentParameter, error) { + var err error + var cmpd *appsv1.ComponentDefinition + + if cmpd, err = getCompDefinition(reqCtx.Ctx, reader, comp); err != nil { + return nil, err + } + if len(cmpd.Spec.Configs) == 0 { + return nil, nil + } + + configRender, paramsDefs, err := intctrlutil.ResolveCmpdParametersDefs(reqCtx.Ctx, reader, cmpd) + if err != nil { + return nil, err + } + tpls, err := resolveComponentTemplate(reqCtx.Ctx, reader, cmpd) + if err != nil { + return nil, err + } + + clusterName, _ := component.GetClusterName(comp) + componentName, _ := component.ShortName(clusterName, comp.Name) + parameterObj := builder.NewComponentParameterBuilder(comp.Namespace, + configcore.GenerateComponentConfigurationName(clusterName, componentName)). + AddLabelsInMap(constant.GetCompLabelsWithDef(clusterName, componentName, cmpd.Name)). + ClusterRef(clusterName). + Component(componentName). + SetConfigurationItem(configctrl.ClassifyParamsFromConfigTemplate( + intctrlutil.TransformComponentParameters(comp.Spec.InitParameters), + cmpd, paramsDefs, tpls)). + GetObject() + if err = intctrlutil.SetOwnerReference(comp, parameterObj); err != nil { + return nil, err + } + if configRender != nil { + err = configctrl.UpdateConfigPayload(¶meterObj.Spec, &comp.Spec, &configRender.Spec) + } + return parameterObj, err +} + +func resolveComponentTemplate(ctx context.Context, reader client.Reader, cmpd *appsv1.ComponentDefinition) (map[string]*corev1.ConfigMap, error) { + tpls := make(map[string]*corev1.ConfigMap, len(cmpd.Spec.Configs)) + for _, config := range cmpd.Spec.Configs { + cm := &corev1.ConfigMap{} + if err := reader.Get(ctx, client.ObjectKey{Name: config.TemplateRef, Namespace: config.Namespace}, cm); err != nil { + return nil, err + } + tpls[config.Name] = cm + } + return tpls, nil +} + +func (r *ComponentDrivenParameterReconciler) mergeComponentParameter(expected *parametersv1alpha1.ComponentParameter, existing *parametersv1alpha1.ComponentParameter) *parametersv1alpha1.ComponentParameter { + return configctrl.MergeComponentParameter(expected, existing, func(dest, expected *parametersv1alpha1.ConfigTemplateItemDetail) { + if len(dest.ConfigFileParams) == 0 && len(expected.ConfigFileParams) != 0 { + dest.ConfigFileParams = expected.ConfigFileParams + } + dest.Payload = expected.Payload + }) +} diff --git a/controllers/parameters/componentdrivenparameter_controller_test.go b/controllers/parameters/componentdrivenparameter_controller_test.go new file mode 100644 index 00000000000..18de079ec8a --- /dev/null +++ b/controllers/parameters/componentdrivenparameter_controller_test.go @@ -0,0 +1,111 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package parameters + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + configcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/constant" + intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" + testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" + testparameters "github.com/apecloud/kubeblocks/pkg/testutil/parameters" +) + +var _ = Describe("ComponentParameterGenerator Controller", func() { + + BeforeEach(cleanEnv) + + AfterEach(cleanEnv) + + initTestResource := func() *appsv1.Component { + By("Create a config template obj") + configmap := testparameters.NewComponentTemplateFactory(configSpecName, testCtx.DefaultNamespace). + Create(&testCtx). + GetObject() + + By("Create a parameters definition obj") + paramsDef := testparameters.NewParametersDefinitionFactory(paramsDefName). + SetReloadAction(testparameters.WithNoneAction()). + Create(&testCtx). + GetObject() + Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(paramsDef), func(obj *parametersv1alpha1.ParametersDefinition) { + obj.Status.Phase = parametersv1alpha1.PDAvailablePhase + })()).Should(Succeed()) + + By("Create a component definition obj and mock to available") + compDefObj := testapps.NewComponentDefinitionFactory(compDefName). + WithRandomName(). + SetDefaultSpec(). + AddConfigTemplate(configSpecName, configmap.Name, testCtx.DefaultNamespace, configVolumeName). + Create(&testCtx). + GetObject() + Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(compDefObj), func(obj *appsv1.ComponentDefinition) { + obj.Status.Phase = appsv1.AvailablePhase + })()).Should(Succeed()) + + pdcr := testparameters.NewParametersDrivenConfigFactory(pdcrName). + SetParametersDefs(paramsDef.Name). + SetComponentDefinition(compDefObj.GetName()). + SetTemplateName(configSpecName). + HScaleEnabled(). + VScaleEnabled(). + TLSEnabled(). + Create(&testCtx). + GetObject() + Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(pdcr), func(obj *parametersv1alpha1.ParameterDrivenConfigRender) { + obj.Status.Phase = parametersv1alpha1.PDAvailablePhase + })()).Should(Succeed()) + + By("Create a component obj") + fullCompName := constant.GenerateClusterComponentName(clusterName, defaultCompName) + compObj := testapps.NewComponentFactory(testCtx.DefaultNamespace, fullCompName, compDefObj.Name). + AddLabels(constant.AppInstanceLabelKey, clusterName). + SetUID(types.UID("test-uid")). + SetReplicas(1). + SetResources(corev1.ResourceRequirements{Limits: corev1.ResourceList{"memory": resource.MustParse("2Gi")}}). + Create(&testCtx). + GetObject() + + return compObj + } + + Context("Generate ComponentParameter", func() { + It("Should reconcile success", func() { + component := initTestResource() + parameterKey := types.NamespacedName{ + Namespace: component.Namespace, + Name: configcore.GenerateComponentConfigurationName(clusterName, defaultCompName), + } + + Eventually(testapps.CheckObj(&testCtx, parameterKey, func(g Gomega, parameter *parametersv1alpha1.ComponentParameter) { + item := intctrlutil.GetConfigTemplateItem(¶meter.Spec, configSpecName) + g.Expect(item).ShouldNot(BeNil()) + g.Expect(item.Payload).Should(HaveKey(constant.ReplicasPayload)) + g.Expect(item.Payload).Should(HaveKey(constant.ComponentResourcePayload)) + })).Should(Succeed()) + }) + }) +}) diff --git a/controllers/parameters/configuration_test.go b/controllers/parameters/configuration_test.go index 9a0274466e7..06e0dc14bba 100644 --- a/controllers/parameters/configuration_test.go +++ b/controllers/parameters/configuration_test.go @@ -179,7 +179,6 @@ func cleanEnv() { inNS := client.InNamespace(testCtx.DefaultNamespace) ml := client.HasLabels{testCtx.TestObjLabelKey} // non-namespaced - testapps.ClearResources(&testCtx, generics.ConfigConstraintSignature, ml) testapps.ClearResources(&testCtx, generics.ParametersDefinitionSignature, ml) testapps.ClearResources(&testCtx, generics.ParameterDrivenConfigRenderSignature, ml) // namespaced @@ -187,7 +186,6 @@ func cleanEnv() { testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ConfigMapSignature, true, inNS) 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) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ParameterSignature, true, inNS, ml) } diff --git a/controllers/parameters/reconcile_task.go b/controllers/parameters/reconcile_task.go index 2b352c15197..161158e1dd4 100644 --- a/controllers/parameters/reconcile_task.go +++ b/controllers/parameters/reconcile_task.go @@ -21,16 +21,25 @@ package parameters import ( "context" + "encoding/json" + "errors" + "reflect" "strconv" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" "github.com/apecloud/kubeblocks/pkg/configuration/core" cfgutil "github.com/apecloud/kubeblocks/pkg/configuration/util" + "github.com/apecloud/kubeblocks/pkg/constant" "github.com/apecloud/kubeblocks/pkg/controller/component" configctrl "github.com/apecloud/kubeblocks/pkg/controller/configuration" + "github.com/apecloud/kubeblocks/pkg/controller/model" "github.com/apecloud/kubeblocks/pkg/controller/render" intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" ) @@ -132,32 +141,190 @@ func syncImpl(taskCtx *TaskContext, status *parametersv1alpha1.ConfigTemplateItemDetailStatus, revision string, configMap *corev1.ConfigMap) (err error) { - err = configctrl.NewReconcilePipeline(render.ReconcileCtx{ - ResourceCtx: fromResourceCtx(fetcher.ResourceCtx), + if intctrlutil.IsApplyConfigChanged(configMap, item) { + return nil + } + + failStatus := func(err error) error { + status.Message = pointer.String(err.Error()) + status.Phase = parametersv1alpha1.CMergeFailedPhase + return err + } + + reconcileCtx := &render.ReconcileCtx{ + ResourceCtx: fetcher.ResourceCtx, Cluster: fetcher.ClusterObj, Component: fetcher.ComponentObj, SynthesizedComponent: taskCtx.component, PodSpec: taskCtx.component.PodSpec, - }, item, status, configMap, taskCtx.componentParameter). - ComponentAndComponentDef(). - PrepareForTemplate(). - RerenderTemplate(). - ApplyParameters(). - UpdateConfigVersion(revision). - Sync(). - Complete() + } - if err != nil { - status.Message = cfgutil.ToPointer(err.Error()) - status.Phase = parametersv1alpha1.CMergeFailedPhase - } else { - status.Message = nil - status.Phase = parametersv1alpha1.CMergedPhase + var baseConfig = configMap + var updatedConfig *corev1.ConfigMap + if intctrlutil.IsRerender(configMap, item) { + if baseConfig, err = configctrl.RerenderParametersTemplate(reconcileCtx, item, taskCtx.configRender, taskCtx.paramsDefs); err != nil { + return failStatus(err) + } + updatedConfig = baseConfig + } + if len(item.ConfigFileParams) != 0 { + if updatedConfig, err = configctrl.ApplyParameters(item, baseConfig, taskCtx.configRender, taskCtx.paramsDefs); err != nil { + return failStatus(err) + } + } + if err = persistUpdatedParameters(fetcher.ResourceCtx, taskCtx.component, taskCtx.configRender, updatedConfig, configMap, fetcher.ComponentParameterObj, item, revision); err != nil { + return failStatus(err) } + + status.Message = nil + status.Phase = parametersv1alpha1.CMergedPhase status.UpdateRevision = revision + return nil +} + +// persistUpdatedParameters merges and updates parameter-related configmaps. +// It first calls mergeAndApplyConfig to merge and update the configmap. +// If the updatedConfig is nil, it returns nil. Otherwise, it calls updateInjectedEnvVars +// to check and update the injected environment variables. +func persistUpdatedParameters(rctx *render.ResourceCtx, + comp *component.SynthesizedComponent, + configRender *parametersv1alpha1.ParameterDrivenConfigRender, + updatedConfig *corev1.ConfigMap, + original *corev1.ConfigMap, + owner client.Object, + item parametersv1alpha1.ConfigTemplateItemDetail, + revision string) error { + if err := mergeAndApplyConfig(rctx, updatedConfig, original, owner, item, revision); err != nil { + return err + } + if updatedConfig == nil { + return nil + } + return updateInjectedEnvVars(rctx, comp, configRender, updatedConfig, owner, item, revision) +} + +func updateInjectedEnvVars(rctx *render.ResourceCtx, + comp *component.SynthesizedComponent, + configRender *parametersv1alpha1.ParameterDrivenConfigRender, + config *corev1.ConfigMap, + owner client.Object, + item parametersv1alpha1.ConfigTemplateItemDetail, + revision string) error { + getOriginal := func(key types.NamespacedName) (*corev1.ConfigMap, error) { + var err error + var cmObj = &corev1.ConfigMap{} + err = rctx.Client.Get(rctx.Context, key, cmObj) + if err != nil && apierrors.IsNotFound(err) { + return nil, nil + } + return cmObj, err + } + + envObjs, err := configctrl.InjectTemplateEnvFrom(comp, nil, configRender, []*corev1.ConfigMap{config}) + if err != nil { + return err + } + + var original *corev1.ConfigMap + for _, obj := range envObjs { + if original, err = getOriginal(client.ObjectKeyFromObject(obj)); err != nil { + return err + } + if original == nil { + err = errors.Join(err, create(rctx.Context, rctx.Client, obj, updateReconcileObject(item, owner, revision))) + } else { + err = errors.Join(err, update(rctx.Context, rctx.Client, original, original, mergedConfigmap(obj, updateReconcileObject(item, owner, revision)))) + } + } return err } +func mergeAndApplyConfig(resourceCtx *render.ResourceCtx, + expected *corev1.ConfigMap, + running *corev1.ConfigMap, + owner client.Object, + item parametersv1alpha1.ConfigTemplateItemDetail, + revision string) error { + switch { + case expected == nil: // not update + return update(resourceCtx.Context, resourceCtx.Client, running, running, updateReconcileObject(item, owner, revision)) + case running == nil: // cm been deleted + return create(resourceCtx.Context, resourceCtx.Client, expected, updateReconcileObject(item, owner, revision)) + default: + return update(resourceCtx.Context, resourceCtx.Client, running, running, mergedConfigmap(expected, updateReconcileObject(item, owner, revision))) + } +} + +func mergedConfigmap(expected *corev1.ConfigMap, setter func(*corev1.ConfigMap) error) func(*corev1.ConfigMap) error { + return func(cmObj *corev1.ConfigMap) error { + cmObj.Data = expected.Data + cmObj.Labels = intctrlutil.MergeMetadataMaps(expected.Labels, cmObj.Labels) + cmObj.Annotations = intctrlutil.MergeMetadataMaps(expected.Annotations, cmObj.Annotations) + return setter(cmObj) + } +} + +func update(ctx context.Context, cli client.Client, expected, origin *corev1.ConfigMap, setter func(*corev1.ConfigMap) error) error { + objectDeep := expected.DeepCopy() + if err := setter(objectDeep); err != nil { + return err + } + if reflect.DeepEqual(objectDeep.Data, origin.Data) && + reflect.DeepEqual(objectDeep.Annotations, origin.Annotations) && + reflect.DeepEqual(objectDeep.Labels, origin.Labels) && + reflect.DeepEqual(objectDeep.Finalizers, origin.Finalizers) && + reflect.DeepEqual(objectDeep.OwnerReferences, origin.OwnerReferences) { + return nil + } + return cli.Patch(ctx, objectDeep, client.MergeFrom(origin)) +} + +func create(ctx context.Context, cli client.Client, expected *corev1.ConfigMap, setter func(*corev1.ConfigMap) error) error { + if err := setter(expected); err != nil { + return err + } + return cli.Create(ctx, expected) +} + +func updateReconcileObject(item parametersv1alpha1.ConfigTemplateItemDetail, + owner client.Object, + revision string) func(*corev1.ConfigMap) error { + return func(cmObj *corev1.ConfigMap) error { + if !controllerutil.ContainsFinalizer(cmObj, constant.ConfigFinalizerName) { + controllerutil.AddFinalizer(cmObj, constant.ConfigFinalizerName) + } + if !model.IsOwnerOf(owner, cmObj) { + if err := intctrlutil.SetControllerReference(owner, cmObj); err != nil { + return err + } + } + return updateConfigLabels(cmObj, item, revision) + } +} + +func updateConfigLabels(obj *corev1.ConfigMap, + item parametersv1alpha1.ConfigTemplateItemDetail, + revision string) error { + if obj.Annotations == nil { + obj.Annotations = make(map[string]string) + } + b, err := json.Marshal(&item) + if err != nil { + return err + } + obj.Annotations[constant.ConfigAppliedVersionAnnotationKey] = string(b) + obj.Annotations[constant.ConfigurationRevision] = revision + + if obj.Labels == nil { + obj.Labels = make(map[string]string) + } + hash, _ := cfgutil.ComputeHash(obj.Data) + obj.Labels[constant.CMInsConfigurationHashLabelKey] = hash + obj.Labels[constant.CMConfigurationSpecProviderLabelKey] = item.Name + obj.Labels[constant.CMConfigurationTemplateNameLabelKey] = item.ConfigSpec.TemplateRef + return nil +} + func syncStatus(configMap *corev1.ConfigMap, status *parametersv1alpha1.ConfigTemplateItemDetailStatus) (err error) { annotations := configMap.GetAnnotations() // status.CurrentRevision = GetCurrentRevision(annotations) @@ -209,13 +376,3 @@ func prepareReconcileTask(reqCtx intctrlutil.RequestCtx, cli client.Client, comp fetcherTask.ComponentParameterObj = componentParameter return fetcherTask, err } - -func fromResourceCtx(ctx *render.ResourceCtx) *render.ResourceCtx { - return &render.ResourceCtx{ - Context: ctx.Context, - Client: ctx.Client, - Namespace: ctx.Namespace, - ClusterName: ctx.ClusterName, - ComponentName: ctx.ComponentName, - } -} diff --git a/controllers/parameters/suite_test.go b/controllers/parameters/suite_test.go index e0a00261af4..d92850debc3 100644 --- a/controllers/parameters/suite_test.go +++ b/controllers/parameters/suite_test.go @@ -154,6 +154,13 @@ var _ = BeforeSuite(func() { }).SetupWithManager(k8sManager, nil) Expect(err).ToNot(HaveOccurred()) + err = (&ComponentDrivenParameterReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("component-parameter-controller"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + testCtx = testutil.NewDefaultTestContext(ctx, k8sClient, testEnv) go func() { diff --git a/controllers/trace/desired_state_handler_test.go b/controllers/trace/desired_state_handler_test.go index 44f24b36b3b..b6181379eb9 100644 --- a/controllers/trace/desired_state_handler_test.go +++ b/controllers/trace/desired_state_handler_test.go @@ -34,8 +34,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" "github.com/apecloud/kubeblocks/pkg/constant" "github.com/apecloud/kubeblocks/pkg/controller/kubebuilderx" @@ -271,9 +271,9 @@ var _ = Describe("desired_state_handler test", func() { return apierrors.NewNotFound(kbappsv1.Resource(kbappsv1.ComponentKind), objKey.Name) }).AnyTimes() k8sMock.EXPECT(). - Get(gomock.Any(), gomock.Any(), &appsv1alpha1.Configuration{}, gomock.Any()). - DoAndReturn(func(_ context.Context, objKey client.ObjectKey, obj *appsv1alpha1.Configuration, _ ...client.GetOption) error { - return apierrors.NewNotFound(appsv1alpha1.Resource(constant.ConfigurationKind), objKey.Name) + Get(gomock.Any(), gomock.Any(), ¶metersv1alpha1.ComponentParameter{}, gomock.Any()). + DoAndReturn(func(_ context.Context, objKey client.ObjectKey, obj *parametersv1alpha1.ComponentParameter, _ ...client.GetOption) error { + return apierrors.NewNotFound(parametersv1alpha1.Resource(constant.ComponentParameterKind), objKey.Name) }).AnyTimes() k8sMock.EXPECT(). Get(gomock.Any(), gomock.Any(), &corev1.ConfigMap{}, gomock.Any()). diff --git a/controllers/trace/dry_run_handler_test.go b/controllers/trace/dry_run_handler_test.go index 120a3cdf33b..d484e73dea8 100644 --- a/controllers/trace/dry_run_handler_test.go +++ b/controllers/trace/dry_run_handler_test.go @@ -36,8 +36,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" "github.com/apecloud/kubeblocks/pkg/constant" "github.com/apecloud/kubeblocks/pkg/controller/kubebuilderx" @@ -264,9 +264,9 @@ var _ = Describe("dry_run_handler test", func() { return apierrors.NewNotFound(kbappsv1.Resource(kbappsv1.ComponentKind), objKey.Name) }).AnyTimes() k8sMock.EXPECT(). - Get(gomock.Any(), gomock.Any(), &appsv1alpha1.Configuration{}, gomock.Any()). - DoAndReturn(func(_ context.Context, objKey client.ObjectKey, obj *appsv1alpha1.Configuration, _ ...client.GetOption) error { - return apierrors.NewNotFound(appsv1alpha1.Resource(constant.ConfigurationKind), objKey.Name) + Get(gomock.Any(), gomock.Any(), ¶metersv1alpha1.ComponentParameter{}, gomock.Any()). + DoAndReturn(func(_ context.Context, objKey client.ObjectKey, obj *parametersv1alpha1.ComponentParameter, _ ...client.GetOption) error { + return apierrors.NewNotFound(parametersv1alpha1.Resource(constant.ComponentParameterKind), objKey.Name) }).AnyTimes() k8sMock.EXPECT(). Get(gomock.Any(), gomock.Any(), &corev1.ConfigMap{}, gomock.Any()). diff --git a/controllers/trace/reconciler_tree.go b/controllers/trace/reconciler_tree.go index a8fcbe1fb1f..9ced3b00b4e 100644 --- a/controllers/trace/reconciler_tree.go +++ b/controllers/trace/reconciler_tree.go @@ -41,8 +41,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" workloadsAPI "github.com/apecloud/kubeblocks/apis/workloads/v1" "github.com/apecloud/kubeblocks/controllers/apps" @@ -64,24 +64,24 @@ type ReconcilerTree interface { type reconcilerFunc func(client.Client, record.EventRecorder) reconcile.Reconciler var reconcilerFuncMap = map[tracev1.ObjectType]reconcilerFunc{ - objectType(kbappsv1.SchemeGroupVersion.String(), kbappsv1.ClusterKind): newClusterReconciler, - objectType(kbappsv1.SchemeGroupVersion.String(), kbappsv1.ComponentKind): newComponentReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.SecretKind): newSecretReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.ServiceKind): newServiceReconciler, - objectType(workloadsAPI.SchemeGroupVersion.String(), workloadsAPI.InstanceSetKind): newInstanceSetReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.ConfigMapKind): newConfigMapReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.PersistentVolumeClaimKind): newPVCReconciler, - objectType(rbacv1.SchemeGroupVersion.String(), constant.RoleBindingKind): newRoleBindingReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.ServiceAccountKind): newSAReconciler, - objectType(batchv1.SchemeGroupVersion.String(), constant.JobKind): newJobReconciler, - objectType(dpv1alpha1.SchemeGroupVersion.String(), types.BackupKind): newBackupReconciler, - objectType(dpv1alpha1.SchemeGroupVersion.String(), types.RestoreKind): newRestoreReconciler, - objectType(appsv1alpha1.SchemeGroupVersion.String(), constant.ConfigurationKind): newConfigurationReconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.PodKind): newPodReconciler, - objectType(appsv1.SchemeGroupVersion.String(), constant.StatefulSetKind): newSTSReconciler, - objectType(vsv1.SchemeGroupVersion.String(), constant.VolumeSnapshotKind): newVolumeSnapshotV1Reconciler, - objectType(vsv1beta1.SchemeGroupVersion.String(), constant.VolumeSnapshotKind): newVolumeSnapshotV1Beta1Reconciler, - objectType(corev1.SchemeGroupVersion.String(), constant.PersistentVolumeKind): newPVReconciler, + objectType(kbappsv1.SchemeGroupVersion.String(), kbappsv1.ClusterKind): newClusterReconciler, + objectType(kbappsv1.SchemeGroupVersion.String(), kbappsv1.ComponentKind): newComponentReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.SecretKind): newSecretReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.ServiceKind): newServiceReconciler, + objectType(workloadsAPI.SchemeGroupVersion.String(), workloadsAPI.InstanceSetKind): newInstanceSetReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.ConfigMapKind): newConfigMapReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.PersistentVolumeClaimKind): newPVCReconciler, + objectType(rbacv1.SchemeGroupVersion.String(), constant.RoleBindingKind): newRoleBindingReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.ServiceAccountKind): newSAReconciler, + objectType(batchv1.SchemeGroupVersion.String(), constant.JobKind): newJobReconciler, + objectType(dpv1alpha1.SchemeGroupVersion.String(), types.BackupKind): newBackupReconciler, + objectType(dpv1alpha1.SchemeGroupVersion.String(), types.RestoreKind): newRestoreReconciler, + objectType(parametersv1alpha1.SchemeGroupVersion.String(), constant.ComponentParameterKind): newConfigurationReconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.PodKind): newPodReconciler, + objectType(appsv1.SchemeGroupVersion.String(), constant.StatefulSetKind): newSTSReconciler, + objectType(vsv1.SchemeGroupVersion.String(), constant.VolumeSnapshotKind): newVolumeSnapshotV1Reconciler, + objectType(vsv1beta1.SchemeGroupVersion.String(), constant.VolumeSnapshotKind): newVolumeSnapshotV1Beta1Reconciler, + objectType(corev1.SchemeGroupVersion.String(), constant.PersistentVolumeKind): newPVReconciler, } type reconcilerTree struct { diff --git a/controllers/trace/suite_test.go b/controllers/trace/suite_test.go index 35023e8c896..6195e011b3b 100644 --- a/controllers/trace/suite_test.go +++ b/controllers/trace/suite_test.go @@ -42,10 +42,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log/zap" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" workloadsv1 "github.com/apecloud/kubeblocks/apis/workloads/v1" "github.com/apecloud/kubeblocks/pkg/controller/builder" @@ -93,17 +92,13 @@ var _ = BeforeSuite(func() { initKBOwnershipRulesForTest(cfg) - err = appsv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - model.AddScheme(appsv1alpha1.AddToScheme) - err = opsv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) model.AddScheme(opsv1alpha1.AddToScheme) - err = appsv1beta1.AddToScheme(scheme.Scheme) + err = parametersv1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) - model.AddScheme(appsv1beta1.AddToScheme) + model.AddScheme(parametersv1alpha1.AddToScheme) err = kbappsv1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) @@ -178,7 +173,7 @@ func mockObjects(k8sMock *mocks.MockClient) (*kbappsv1.Cluster, []kbappsv1.Compo &batchv1.JobList{}, &dpv1alpha1.BackupList{}, &dpv1alpha1.RestoreList{}, - &appsv1alpha1.ConfigurationList{}, + ¶metersv1alpha1.ComponentParameterList{}, } for _, secondary := range componentSecondaries { k8sMock.EXPECT(). diff --git a/controllers/trace/type.go b/controllers/trace/type.go index 146bdc618f6..b06439aff77 100644 --- a/controllers/trace/type.go +++ b/controllers/trace/type.go @@ -39,8 +39,8 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" tracev1 "github.com/apecloud/kubeblocks/apis/trace/v1" workloads "github.com/apecloud/kubeblocks/apis/workloads/v1" "github.com/apecloud/kubeblocks/pkg/constant" @@ -159,7 +159,7 @@ var ( Criteria: componentCriteria, }, { - Secondary: objectType(appsv1alpha1.SchemeGroupVersion.String(), constant.ConfigurationKind), + Secondary: objectType(parametersv1alpha1.SchemeGroupVersion.String(), constant.ComponentParameterKind), Criteria: componentCriteria, }, }, @@ -186,7 +186,7 @@ var ( }, }, { - Primary: objectType(kbappsv1.SchemeGroupVersion.String(), constant.ConfigurationKind), + Primary: objectType(parametersv1alpha1.SchemeGroupVersion.String(), constant.ComponentParameterKind), OwnedResources: []OwnedResource{ { Secondary: objectType(corev1.SchemeGroupVersion.String(), constant.ConfigMapKind), diff --git a/pkg/constant/const.go b/pkg/constant/const.go index ee517475f19..b37ea1f7fe8 100644 --- a/pkg/constant/const.go +++ b/pkg/constant/const.go @@ -43,7 +43,7 @@ const ( ConfigMapKind = "ConfigMap" PersistentVolumeClaimKind = "PersistentVolumeClaim" PersistentVolumeKind = "PersistentVolume" - ConfigurationKind = "Configuration" + ComponentParameterKind = "ComponentParameter" RoleBindingKind = "RoleBinding" ServiceAccountKind = "ServiceAccount" EventKind = "Event" diff --git a/pkg/controller/component/service_reference_test.go b/pkg/controller/component/service_reference_test.go index b05cf84304d..d07d585f21e 100644 --- a/pkg/controller/component/service_reference_test.go +++ b/pkg/controller/component/service_reference_test.go @@ -52,7 +52,6 @@ var _ = Describe("service references", func() { // resources should be released in following order // non-namespaced testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ClusterDefinitionSignature, true, ml) - testapps.ClearResources(&testCtx, generics.ConfigConstraintSignature, ml) // namespaced testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ConfigMapSignature, true, inNS, ml) diff --git a/pkg/controller/configuration/config_utils.go b/pkg/controller/configuration/config_utils.go index c0e9cbcac72..606544575aa 100644 --- a/pkg/controller/configuration/config_utils.go +++ b/pkg/controller/configuration/config_utils.go @@ -22,13 +22,10 @@ package configuration import ( "context" "fmt" - "reflect" "slices" "strings" corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" @@ -38,7 +35,6 @@ import ( "github.com/apecloud/kubeblocks/pkg/configuration/core" cfgutil "github.com/apecloud/kubeblocks/pkg/configuration/util" "github.com/apecloud/kubeblocks/pkg/constant" - "github.com/apecloud/kubeblocks/pkg/controller/builder" "github.com/apecloud/kubeblocks/pkg/controller/component" "github.com/apecloud/kubeblocks/pkg/controller/factory" "github.com/apecloud/kubeblocks/pkg/controller/render" @@ -46,28 +42,9 @@ import ( viper "github.com/apecloud/kubeblocks/pkg/viperx" ) -func createConfigObjects(cli client.Client, ctx context.Context, objs []*corev1.ConfigMap) error { - for _, obj := range objs { - if err := cli.Create(ctx, obj, inDataContext()); err != nil { - - if !apierrors.IsAlreadyExists(err) { - return err - } - // for update script cm - if core.IsSchedulableConfigResource(obj) { - continue - } - if err := cli.Update(ctx, obj, inDataContext()); err != nil { - return err - } - } - } - return nil -} - -// buildConfigManagerWithComponent build the configmgr sidecar container and update it +// BuildReloadActionContainer build the configmgr sidecar container and update it // into PodSpec if configuration reload option is on -func buildConfigManagerWithComponent(rctx *render.ResourceCtx, cluster *appsv1.Cluster, synthesizedComp *component.SynthesizedComponent, cmpd *appsv1.ComponentDefinition) error { +func BuildReloadActionContainer(resourceCtx *render.ResourceCtx, cluster *appsv1.Cluster, synthesizedComp *component.SynthesizedComponent, cmpd *appsv1.ComponentDefinition, configmaps []*corev1.ConfigMap) error { var ( err error buildParams *cfgcm.CfgManagerBuildParams @@ -82,12 +59,13 @@ func buildConfigManagerWithComponent(rctx *render.ResourceCtx, cluster *appsv1.C if len(volumeDirs) == 0 { return nil } - if configRender, paramsDefs, err = intctrlutil.ResolveCmpdParametersDefs(rctx.Context, rctx.Client, cmpd); err != nil { + if configRender, paramsDefs, err = resolveComponentParameterDefs(resourceCtx.Context, resourceCtx.Client, cmpd, configmaps, synthesizedComp); err != nil { return err } if configRender == nil || len(configRender.Spec.Configs) == 0 { return nil } + configSpecMetas, err := cfgcm.GetSupportReloadConfigSpecs(usingConfigSpecs, configRender.Spec.Configs, paramsDefs) if err != nil { return err @@ -98,7 +76,7 @@ func buildConfigManagerWithComponent(rctx *render.ResourceCtx, cluster *appsv1.C if len(configSpecMetas) == 0 { return nil } - if buildParams, err = buildConfigManagerParams(rctx.Client, rctx.Context, cluster, synthesizedComp, configSpecMetas, volumeDirs, podSpec); err != nil { + if buildParams, err = buildConfigManagerParams(resourceCtx.Client, resourceCtx.Context, cluster, synthesizedComp, configSpecMetas, volumeDirs, podSpec); err != nil { return err } if buildParams == nil { @@ -130,6 +108,34 @@ func buildConfigManagerWithComponent(rctx *render.ResourceCtx, cluster *appsv1.C return nil } +func resolveComponentParameterDefs(ctx context.Context, cli client.Client, cmpd *appsv1.ComponentDefinition, configmaps []*corev1.ConfigMap, comp *component.SynthesizedComponent) (*parametersv1alpha1.ParameterDrivenConfigRender, []*parametersv1alpha1.ParametersDefinition, error) { + configRender, paramsDefs, err := intctrlutil.ResolveCmpdParametersDefs(ctx, cli, cmpd) + if err != nil { + return nil, nil, err + } + if err = handleInjectEnv(ctx, cli, comp, configRender, configmaps); err != nil { + return nil, nil, err + } + return configRender, paramsDefs, nil +} + +func handleInjectEnv(ctx context.Context, + cli client.Client, + comp *component.SynthesizedComponent, + configRender *parametersv1alpha1.ParameterDrivenConfigRender, + configmaps []*corev1.ConfigMap) error { + envObjs, err := InjectTemplateEnvFrom(comp, comp.PodSpec, configRender, configmaps) + if err != nil { + return err + } + for _, obj := range envObjs { + if err = intctrlutil.IgnoreIsAlreadyExists(cli.Create(ctx, obj, inDataContext())); err != nil { + return err + } + } + return nil +} + func checkAndUpdateSharProcessNamespace(podSpec *corev1.PodSpec, buildParams *cfgcm.CfgManagerBuildParams, configSpecMetas []cfgcm.ConfigSpecMeta) { shared := cfgcm.NeedSharedProcessNamespace(configSpecMetas) if shared { @@ -324,67 +330,3 @@ func enableVScaleTrigger(configDescs []parametersv1alpha1.ComponentConfigDescrip func enableTLSTrigger(configDescs []parametersv1alpha1.ComponentConfigDescription) bool { return rerenderConfigEnabled(configDescs, parametersv1alpha1.ComponentTLSType) } - -func runningComponentParameter(ctx context.Context, reader client.Reader, comp *component.SynthesizedComponent) (*parametersv1alpha1.ComponentParameter, error) { - var componentParameter = ¶metersv1alpha1.ComponentParameter{} - - parameterKey := types.NamespacedName{ - Name: core.GenerateComponentConfigurationName(comp.ClusterName, comp.Name), - Namespace: comp.Namespace, - } - if err := reader.Get(ctx, parameterKey, componentParameter); err != nil { - return nil, client.IgnoreNotFound(err) - } - return componentParameter, nil -} - -func buildComponentParameter(ctx context.Context, reader client.Reader, comp *component.SynthesizedComponent, owner *appsv1.Component, cmpd *appsv1.ComponentDefinition, configRender *parametersv1alpha1.ParameterDrivenConfigRender, paramsDefs []*parametersv1alpha1.ParametersDefinition) (*parametersv1alpha1.ComponentParameter, error) { - tpls, err := resolveComponentTemplate(ctx, reader, cmpd) - if err != nil { - return nil, err - } - - parameterObj := builder.NewComponentParameterBuilder(comp.Namespace, - core.GenerateComponentConfigurationName(comp.ClusterName, comp.Name)). - AddLabelsInMap(constant.GetCompLabelsWithDef(comp.ClusterName, comp.Name, cmpd.Name)). - ClusterRef(comp.ClusterName). - Component(comp.Name). - SetConfigurationItem(ClassifyParamsFromConfigTemplate(intctrlutil.TransformComponentParameters(owner.Spec.InitParameters), cmpd, paramsDefs, tpls)). - GetObject() - if err = intctrlutil.SetOwnerReference(owner, parameterObj); err != nil { - return nil, err - } - if configRender != nil { - err = UpdateConfigPayload(¶meterObj.Spec, &owner.Spec, &configRender.Spec) - } - return parameterObj, err -} - -func mergeComponentParameter(expected *parametersv1alpha1.ComponentParameter, existing *parametersv1alpha1.ComponentParameter) *parametersv1alpha1.ComponentParameter { - return MergeComponentParameter(expected, existing, func(dest, expected *parametersv1alpha1.ConfigTemplateItemDetail) { - if len(dest.ConfigFileParams) == 0 && len(expected.ConfigFileParams) != 0 { - dest.ConfigFileParams = expected.ConfigFileParams - } - dest.Payload = expected.Payload - }) -} - -func resolveComponentTemplate(ctx context.Context, reader client.Reader, cmpd *appsv1.ComponentDefinition) (map[string]*corev1.ConfigMap, error) { - tpls := make(map[string]*corev1.ConfigMap, len(cmpd.Spec.Configs)) - for _, config := range cmpd.Spec.Configs { - cm := &corev1.ConfigMap{} - if err := reader.Get(ctx, client.ObjectKey{Name: config.TemplateRef, Namespace: config.Namespace}, cm); err != nil { - return nil, err - } - tpls[config.Name] = cm - } - return tpls, nil -} - -func updateComponentParameters(ctx context.Context, cli client.Client, expected, existing *parametersv1alpha1.ComponentParameter) error { - mergedObject := mergeComponentParameter(expected, existing) - if reflect.DeepEqual(mergedObject, existing) { - return nil - } - return cli.Patch(ctx, mergedObject, client.MergeFrom(existing)) -} diff --git a/pkg/controller/configuration/configuration_test.go b/pkg/controller/configuration/configuration_test.go index ff93a1b7505..6f2c0d14a51 100644 --- a/pkg/controller/configuration/configuration_test.go +++ b/pkg/controller/configuration/configuration_test.go @@ -90,11 +90,6 @@ func newAllFieldsSynthesizedComponent(compDef *appsv1.ComponentDefinition, clust return synthesizeComp } -func newAllFieldsComponent(cluster *appsv1.Cluster) *appsv1.Component { - comp, _ := component.BuildComponent(cluster, &cluster.Spec.ComponentSpecs[0], nil, nil) - return comp -} - func addTestVolumeMount(spec *corev1.PodSpec, containerName string) { for i := range spec.Containers { container := &spec.Containers[i] diff --git a/pkg/controller/configuration/operator.go b/pkg/controller/configuration/operator.go deleted file mode 100644 index 2299755ed93..00000000000 --- a/pkg/controller/configuration/operator.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package configuration - -import ( - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/render" -) - -type configOperator struct { - render.ReconcileCtx -} - -func NewConfigReconcileTask(resourceCtx *render.ResourceCtx, - cluster *appsv1.Cluster, - component *appsv1.Component, - synthesizedComponent *component.SynthesizedComponent, - podSpec *corev1.PodSpec, - localObjs []client.Object, -) *configOperator { - return &configOperator{ - render.ReconcileCtx{ - ResourceCtx: resourceCtx, - Cluster: cluster, - Component: component, - SynthesizedComponent: synthesizedComponent, - PodSpec: podSpec, - Cache: localObjs, - }, - } -} - -func (c *configOperator) Reconcile() error { - var synthesizedComponent = c.SynthesizedComponent - - if len(synthesizedComponent.ConfigTemplates) == 0 && len(synthesizedComponent.ScriptTemplates) == 0 { - return c.UpdateConfiguration() - } - - return NewCreatePipeline(c.ReconcileCtx). - ComponentAndComponentDef(). - Prepare(). - RenderScriptTemplate(). - SyncComponentParameter(). - ComponentParameter(). - CreateConfigTemplate(). - UpdatePodVolumes(). - BuildConfigManagerSidecar(). - UpdateConfigRelatedObject(). - Complete() -} - -func (c *configOperator) UpdateConfiguration() error { - return NewCreatePipeline(c.ReconcileCtx). - ComponentAndComponentDef(). - SyncComponentParameter(). - UpdateConfigRelatedObject(). - Complete() -} diff --git a/pkg/controller/configuration/operator_test.go b/pkg/controller/configuration/operator_test.go deleted file mode 100644 index a9873991584..00000000000 --- a/pkg/controller/configuration/operator_test.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package configuration - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "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" - "github.com/apecloud/kubeblocks/pkg/controller/builder" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/render" - testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" - testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" - testparameters "github.com/apecloud/kubeblocks/pkg/testutil/parameters" -) - -var _ = Describe("ConfigurationOperatorTest", func() { - var clusterObj *appsv1.Cluster - var compDefObj *appsv1.ComponentDefinition - var componentObj *appsv1.Component - var synthesizedComponent *component.SynthesizedComponent - var configMapObj *corev1.ConfigMap - var scriptsObj *corev1.ConfigMap - var parametersDef *parametersv1alpha1.ParametersDefinition - var configRender *parametersv1alpha1.ParameterDrivenConfigRender - var componentParameter *parametersv1alpha1.ComponentParameter - var k8sMockClient *testutil.K8sClientMockHelper - - createConfigReconcileTask := func() *configOperator { - task := NewConfigReconcileTask(&render.ResourceCtx{ - Client: k8sMockClient.Client(), - Context: ctx, - Namespace: testCtx.DefaultNamespace, - ClusterName: clusterName, - ComponentName: mysqlCompName, - }, - clusterObj, - componentObj, - synthesizedComponent, - synthesizedComponent.PodSpec, - nil) - return task - } - - BeforeEach(func() { - // Add any setup steps that needs to be executed before each test - k8sMockClient = testutil.NewK8sMockClient() - clusterObj, compDefObj, _ = newAllFieldsClusterObj(nil, false) - synthesizedComponent = newAllFieldsSynthesizedComponent(compDefObj, clusterObj) - componentObj = newAllFieldsComponent(clusterObj) - configMapObj = testapps.NewConfigMap("default", mysqlConfigName, - testapps.SetConfigMapData("test", "test")) - scriptsObj = testapps.NewConfigMap("default", mysqlScriptsTemplateName, - testapps.SetConfigMapData("script.sh", "echo \"hello\"")) - componentParameter = builder.NewComponentParameterBuilder(testCtx.DefaultNamespace, - cfgcore.GenerateComponentConfigurationName(clusterName, mysqlCompName)). - ClusterRef(clusterName). - Component(mysqlCompName). - GetObject() - parametersDef = testparameters.NewParametersDefinitionFactory(paramsDefName).GetObject() - configRender = testparameters.NewParametersDrivenConfigFactory(pdcrName). - SetComponentDefinition(compDefObj.Name). - SetParametersDefs(paramsDefName). - GetObject() - parametersDef.Status.Phase = parametersv1alpha1.PDAvailablePhase - configRender.Status.Phase = parametersv1alpha1.PDAvailablePhase - }) - - AfterEach(func() { - k8sMockClient.Finish() - }) - - Context("ConfigOperatorTest", func() { - It("NormalTest", func() { - k8sMockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult( - []client.Object{ - compDefObj, - componentObj, - clusterObj, - scriptsObj, - configMapObj, - parametersDef, - configRender, - componentParameter, - }, - ), testutil.WithAnyTimes())) - k8sMockClient.MockCreateMethod(testutil.WithCreateReturned(testutil.WithCreatedSucceedResult(), testutil.WithAnyTimes())) - k8sMockClient.MockPatchMethod(testutil.WithPatchReturned(func(obj client.Object, patch client.Patch) error { - switch v := obj.(type) { - case *parametersv1alpha1.ComponentParameter: - if client.ObjectKeyFromObject(obj) == client.ObjectKeyFromObject(componentParameter) { - componentParameter.Spec = *v.Spec.DeepCopy() - componentParameter.Status = *v.Status.DeepCopy() - } - } - return nil - })) - k8sMockClient.MockNListMethod(0, testutil.WithListReturned( - testutil.WithConstructListReturnedResult([]runtime.Object{configRender}), - testutil.WithAnyTimes(), - )) - Expect(createConfigReconcileTask().Reconcile()).Should(Succeed()) - }) - - It("EmptyConfigSpecTest", func() { - - // k8sMockClient.MockCreateMethod(testutil.WithCreateReturned(testutil.WithCreatedSucceedResult(), testutil.WithTimes(1))) - k8sMockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult( - []client.Object{ - compDefObj, - componentObj, - clusterObj, - configMapObj, - componentParameter, - }, - ), testutil.WithAnyTimes())) - k8sMockClient.MockPatchMethod(testutil.WithSucceed(), testutil.WithAnyTimes()) - k8sMockClient.MockNListMethod(0, testutil.WithListReturned( - testutil.WithConstructListReturnedResult([]runtime.Object{configRender}), - testutil.WithAnyTimes(), - )) - - synthesizedComponent.ConfigTemplates = nil - synthesizedComponent.ScriptTemplates = nil - Expect(createConfigReconcileTask().Reconcile()).Should(Succeed()) - }) - - }) - -}) diff --git a/pkg/controller/configuration/pipeline.go b/pkg/controller/configuration/pipeline.go deleted file mode 100644 index 32aa0534ddf..00000000000 --- a/pkg/controller/configuration/pipeline.go +++ /dev/null @@ -1,309 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package configuration - -import ( - "context" - "strconv" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "sigs.k8s.io/controller-runtime/pkg/client" - - appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/configuration/core" - "github.com/apecloud/kubeblocks/pkg/constant" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/render" - intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" - "github.com/apecloud/kubeblocks/pkg/generics" -) - -type pipeline struct { - // configuration *appsv1alpha1.Configuration - renderWrapper renderWrapper - - ctx render.ReconcileCtx - ResourceFetcher[pipeline] - - configRender *parametersv1alpha1.ParameterDrivenConfigRender - parametersDefs []*parametersv1alpha1.ParametersDefinition -} - -type updatePipeline struct { - renderWrapper renderWrapper - - reconcile bool - item parametersv1alpha1.ConfigTemplateItemDetail - itemStatus *parametersv1alpha1.ConfigTemplateItemDetailStatus - configSpec *appsv1.ComponentTemplateSpec - // replace of ConfigMapObj - // originalCM *corev1.ConfigMap - newCM *corev1.ConfigMap - configPatch *core.ConfigPatchInfo - - ctx render.ReconcileCtx - ResourceFetcher[updatePipeline] - - configRender *parametersv1alpha1.ParameterDrivenConfigRender - parametersDefs []*parametersv1alpha1.ParametersDefinition -} - -func NewCreatePipeline(ctx render.ReconcileCtx) *pipeline { - p := &pipeline{ctx: ctx} - return p.Init(ctx.ResourceCtx, p) -} - -func NewReconcilePipeline(ctx render.ReconcileCtx, item parametersv1alpha1.ConfigTemplateItemDetail, itemStatus *parametersv1alpha1.ConfigTemplateItemDetailStatus, cm *corev1.ConfigMap, componentParameter *parametersv1alpha1.ComponentParameter) *updatePipeline { - p := &updatePipeline{ - reconcile: true, - item: item, - itemStatus: itemStatus, - ctx: ctx, - configSpec: item.ConfigSpec, - } - p.Init(ctx.ResourceCtx, p) - p.ConfigMapObj = cm - p.ComponentParameterObj = componentParameter - return p -} - -func (p *pipeline) Prepare() *pipeline { - buildTemplate := func() (err error) { - ctx := p.ctx - p.renderWrapper = newTemplateRenderWrapper(p.Context, ctx.Client, render.NewTemplateBuilder(&ctx), ctx.Cluster, ctx.Component) - p.configRender, p.parametersDefs, err = intctrlutil.ResolveCmpdParametersDefs(ctx.Context, ctx.Client, p.ComponentDefObj) - return err - } - - return p.Wrap(buildTemplate) -} - -func (p *pipeline) RenderScriptTemplate() *pipeline { - return p.Wrap(func() error { - ctx := p.ctx - return p.renderWrapper.renderScriptTemplate(ctx.Cluster, ctx.SynthesizedComponent, ctx.Cache, ctx.Component) - }) -} - -func (p *pipeline) SyncComponentParameter() *pipeline { - buildConfiguration := func() (err error) { - var existingObject *parametersv1alpha1.ComponentParameter - var expectedObject *parametersv1alpha1.ComponentParameter - - if existingObject, err = runningComponentParameter(p.Context, p.Client, p.ctx.SynthesizedComponent); err != nil { - return err - } - if expectedObject, err = buildComponentParameter(p.Context, p.Client, p.ctx.SynthesizedComponent, p.ctx.Component, p.ComponentDefObj, p.configRender, p.parametersDefs); err != nil { - return err - } - - switch { - case expectedObject == nil: - return p.Client.Delete(p.Context, existingObject) - case existingObject == nil: - return p.Client.Create(p.Context, expectedObject) - default: - return updateComponentParameters(p.Context, p.Client, expectedObject, existingObject) - } - } - return p.Wrap(buildConfiguration) -} - -func (p *pipeline) CreateConfigTemplate() *pipeline { - return p.Wrap(func() error { - ctx := p.ctx - revision := strconv.FormatInt(p.ComponentParameterObj.GetGeneration(), 10) - return p.renderWrapper.renderConfigTemplate(ctx.Cluster, ctx.SynthesizedComponent, ctx.Cache, p.ComponentParameterObj, p.configRender, p.parametersDefs, revision) - }) -} - -func (p *pipeline) UpdatePodVolumes() *pipeline { - mapName := func(tpl appsv1.ComponentTemplateSpec) string { - return tpl.Name - } - return p.Wrap(func() error { - return intctrlutil.CreateOrUpdatePodVolumes(p.ctx.PodSpec, - p.renderWrapper.volumes, - generics.Map(p.ctx.SynthesizedComponent.ConfigTemplates, mapName)) - }) -} - -func (p *pipeline) BuildConfigManagerSidecar() *pipeline { - return p.Wrap(func() error { - return buildConfigManagerWithComponent(p.ctx.ResourceCtx, p.ctx.Cluster, p.ctx.SynthesizedComponent, p.ComponentDefObj) - }) -} - -func (p *pipeline) UpdateConfigRelatedObject() *pipeline { - updateMeta := func() error { - if err := syncInjectEnvFromCM(p.Context, p.Client, p.ctx.SynthesizedComponent, p.configRender, p.renderWrapper.renderedObjs, true); err != nil { - return err - } - return createConfigObjects(p.Client, p.Context, p.renderWrapper.renderedObjs) - } - - return p.Wrap(updateMeta) -} - -func (p *updatePipeline) isDone() bool { - return !p.reconcile -} - -func (p *updatePipeline) PrepareForTemplate() *updatePipeline { - buildTemplate := func() (err error) { - p.reconcile = !intctrlutil.IsApplyConfigChanged(p.ConfigMapObj, p.item) - if p.isDone() { - return - } - p.renderWrapper = newTemplateRenderWrapper(p.Context, p.Client, render.NewTemplateBuilder(&p.ctx), p.ctx.Cluster, p.ctx.Component) - p.configRender, p.parametersDefs, err = intctrlutil.ResolveCmpdParametersDefs(p.Context, p.Client, p.ComponentDefObj) - return - } - return p.Wrap(buildTemplate) -} - -func (p *updatePipeline) RerenderTemplate() *updatePipeline { - return p.Wrap(func() (err error) { - if p.isDone() { - return - } - if intctrlutil.IsRerender(p.ConfigMapObj, p.item) { - p.newCM, err = p.renderWrapper.rerenderConfigTemplate(p.ctx.Cluster, p.ctx.SynthesizedComponent, *p.configSpec, &p.item, p.configRender, p.parametersDefs) - } else { - p.newCM = p.ConfigMapObj.DeepCopy() - } - return - }) -} - -func (p *updatePipeline) ApplyParameters() *updatePipeline { - patchMerge := func(p *updatePipeline, cm *corev1.ConfigMap, item parametersv1alpha1.ConfigTemplateItemDetail) error { - if p.isDone() || len(item.ConfigFileParams) == 0 || p.configRender == nil { - return nil - } - newData, err := DoMerge(cm.Data, item.ConfigFileParams, p.parametersDefs, p.configRender.Spec.Configs) - if err != nil { - return err - } - if p.configRender == nil { - cm.Data = newData - return nil - } - - p.configPatch, _, err = core.CreateConfigPatch(cm.Data, - newData, - p.configRender.Spec, - false) - if err != nil { - return err - } - cm.Data = newData - return nil - } - - return p.Wrap(func() error { - if p.isDone() { - return nil - } - return patchMerge(p, p.newCM, p.item) - }) -} - -func (p *updatePipeline) UpdateConfigVersion(revision string) *updatePipeline { - return p.Wrap(func() error { - if p.isDone() { - return nil - } - - if err := updateConfigMetaForCM(p.newCM, &p.item, revision); err != nil { - return err - } - annotations := p.newCM.Annotations - if annotations == nil { - annotations = make(map[string]string) - } - - // delete disable reconcile annotation - if _, ok := annotations[constant.DisableUpgradeInsConfigurationAnnotationKey]; ok { - annotations[constant.DisableUpgradeInsConfigurationAnnotationKey] = strconv.FormatBool(false) - } - p.newCM.Annotations = annotations - // p.itemStatus.UpdateRevision = revision - return nil - }) -} - -// TODO(leon) -func (p *updatePipeline) Sync() *updatePipeline { - return p.Wrap(func() error { - if !p.isDone() { - if err := syncInjectEnvFromCM(p.Context, p.Client, p.ctx.SynthesizedComponent, p.configRender, []*corev1.ConfigMap{p.newCM}, false); err != nil { - return err - } - } - if err := intctrlutil.SetControllerReference(p.ComponentParameterObj, p.newCM); err != nil { - return err - } - - switch { - case p.isDone(): - return nil - case p.ConfigMapObj == nil && p.newCM != nil: - return p.Client.Create(p.Context, p.newCM) - case p.ConfigMapObj != nil: - patch := client.MergeFrom(p.ConfigMapObj) - if p.ConfigMapObj != nil { - p.newCM.Labels = intctrlutil.MergeMetadataMaps(p.newCM.Labels, p.ConfigMapObj.Labels) - p.newCM.Annotations = intctrlutil.MergeMetadataMaps(p.newCM.Annotations, p.ConfigMapObj.Annotations) - } - return p.Client.Patch(p.Context, p.newCM, patch) - } - return core.MakeError("unexpected condition") - }) -} - -func syncInjectEnvFromCM(ctx context.Context, cli client.Client, synthesizedComp *component.SynthesizedComponent, configRender *parametersv1alpha1.ParameterDrivenConfigRender, configMaps []*corev1.ConfigMap, onlyCreate bool) error { - var podSpec *corev1.PodSpec - - if onlyCreate { - podSpec = synthesizedComp.PodSpec - } - envObjs, err := InjectTemplateEnvFrom(synthesizedComp, podSpec, configRender, configMaps) - if err != nil { - return err - } - for _, obj := range envObjs { - if err = cli.Create(ctx, obj, inDataContext()); err == nil { - continue - } - if !apierrors.IsAlreadyExists(err) { - return err - } - if onlyCreate { - continue - } - if err = cli.Update(ctx, obj, inDataContext()); err != nil { - return err - } - } - return nil -} diff --git a/pkg/controller/configuration/pipeline_test.go b/pkg/controller/configuration/pipeline_test.go deleted file mode 100644 index f250495596c..00000000000 --- a/pkg/controller/configuration/pipeline_test.go +++ /dev/null @@ -1,219 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package configuration - -import ( - "context" - "strconv" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "github.com/golang/mock/gomock" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" - "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" - "github.com/apecloud/kubeblocks/pkg/controller/builder" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/render" - testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" - testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" - testparameters "github.com/apecloud/kubeblocks/pkg/testutil/parameters" -) - -var _ = Describe("ConfigurationPipelineTest", func() { - const testConfigFile = "postgresql.conf" - - var clusterObj *appsv1.Cluster - var componentObj *appsv1.Component - var compDefObj *appsv1.ComponentDefinition - var synthesizedComponent *component.SynthesizedComponent - var configMapObj *corev1.ConfigMap - var parametersDef *parametersv1alpha1.ParametersDefinition - var componentParameter *parametersv1alpha1.ComponentParameter - var configRender *parametersv1alpha1.ParameterDrivenConfigRender - var k8sMockClient *testutil.K8sClientMockHelper - - mockAPIResource := func(lazyFetcher testutil.Getter) { - k8sMockClient.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult( - []client.Object{ - compDefObj, - clusterObj, - clusterObj, - configMapObj, - parametersDef, - componentObj, - componentParameter, - configRender, - }, lazyFetcher), testutil.WithAnyTimes())) - k8sMockClient.MockCreateMethod(testutil.WithCreateReturned(testutil.WithCreatedSucceedResult(), testutil.WithAnyTimes())) - k8sMockClient.MockPatchMethod(testutil.WithPatchReturned(func(obj client.Object, patch client.Patch) error { - switch v := obj.(type) { - case *parametersv1alpha1.ComponentParameter: - if client.ObjectKeyFromObject(obj) == client.ObjectKeyFromObject(componentParameter) { - componentParameter.Spec = *v.Spec.DeepCopy() - componentParameter.Status = *v.Status.DeepCopy() - } - } - return nil - }, testutil.WithAnyTimes())) - k8sMockClient.MockNListMethod(0, testutil.WithListReturned( - testutil.WithConstructListReturnedResult([]runtime.Object{configRender}), - testutil.WithAnyTimes(), - )) - k8sMockClient.MockStatusMethod(). - EXPECT(). - Patch(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()). - DoAndReturn(func(ctx context.Context, obj client.Object, patch client.Patch, opts ...client.SubResourcePatchOption) error { - switch v := obj.(type) { - case *parametersv1alpha1.ComponentParameter: - if client.ObjectKeyFromObject(obj) == client.ObjectKeyFromObject(componentParameter) { - componentParameter.Status = *v.Status.DeepCopy() - } - } - return nil - }).AnyTimes() - } - - BeforeEach(func() { - // Add any setup steps that needs to be executed before each test - k8sMockClient = testutil.NewK8sMockClient() - clusterObj, compDefObj, _ = newAllFieldsClusterObj(nil, false) - componentObj = newAllFieldsComponent(clusterObj) - synthesizedComponent = newAllFieldsSynthesizedComponent(compDefObj, clusterObj) - configMapObj = testapps.NewConfigMap("default", mysqlConfigName, - testapps.SetConfigMapData(testConfigFile, ` -bgwriter_delay = '200ms' -bgwriter_flush_after = '64' -bgwriter_lru_maxpages = '1000' -bgwriter_lru_multiplier = '10.0' -bytea_output = 'hex' -check_function_bodies = 'True' -checkpoint_completion_target = '0.9' -checkpoint_flush_after = '32' -checkpoint_timeout = '15min' -max_connections = '1000' -`)) - componentParameter = builder.NewComponentParameterBuilder(testCtx.DefaultNamespace, - cfgcore.GenerateComponentConfigurationName(clusterName, mysqlCompName)). - ClusterRef(clusterName). - Component(mysqlCompName). - GetObject() - - parametersDef = testparameters.NewParametersDefinitionFactory(paramsDefName).GetObject() - configRender = testparameters.NewParametersDrivenConfigFactory(pdcrName). - SetConfigDescription(testConfigFile, configTemplateName, parametersv1alpha1.FileFormatConfig{Format: parametersv1alpha1.Properties}). - SetComponentDefinition(compDefObj.Name). - SetParametersDefs(paramsDefName). - GetObject() - parametersDef.Status.Phase = parametersv1alpha1.PDAvailablePhase - configRender.Status.Phase = parametersv1alpha1.PDAvailablePhase - }) - - AfterEach(func() { - k8sMockClient.Finish() - }) - - Context("ConfigPipelineTest", func() { - It("NormalTest", func() { - By("create configuration resource") - createPipeline := NewCreatePipeline(render.ReconcileCtx{ - ResourceCtx: &render.ResourceCtx{ - Client: k8sMockClient.Client(), - Context: ctx, - Namespace: testCtx.DefaultNamespace, - ClusterName: clusterName, - ComponentName: mysqlCompName, - }, - Cluster: clusterObj, - Component: componentObj, - SynthesizedComponent: synthesizedComponent, - PodSpec: synthesizedComponent.PodSpec, - }) - - By("mock api resource for configuration") - mockAPIResource(func(key client.ObjectKey, obj client.Object) (bool, error) { - switch obj.(type) { - case *corev1.ConfigMap: - for _, renderedObj := range createPipeline.renderWrapper.renderedObjs { - if client.ObjectKeyFromObject(renderedObj) == key { - testutil.SetGetReturnedObject(obj, renderedObj) - return true, nil - } - } - } - return false, nil - }) - - err := createPipeline. - ComponentAndComponentDef(). - Prepare(). - SyncComponentParameter(). - ComponentParameter(). - CreateConfigTemplate(). - UpdatePodVolumes(). - BuildConfigManagerSidecar(). - UpdateConfigRelatedObject(). - Complete() - Expect(err).Should(Succeed()) - - By("update configuration resource for mocking reconfiguring") - item := componentParameter.Spec.ConfigItemDetails[0] - status := ¶metersv1alpha1.ConfigTemplateItemDetailStatus{ - Name: item.Name, - Phase: parametersv1alpha1.CInitPhase, - } - item.ConfigFileParams = map[string]parametersv1alpha1.ParametersInFile{ - testConfigFile: { - Parameters: map[string]*string{ - "max_connections": cfgutil.ToPointer("2000"), - }, - }, - "other.conf": { - Content: cfgutil.ToPointer(`for test`), - }, - } - reconcileTask := NewReconcilePipeline(render.ReconcileCtx{ - ResourceCtx: createPipeline.ResourceCtx, - Cluster: clusterObj, - Component: componentObj, - SynthesizedComponent: synthesizedComponent, - PodSpec: synthesizedComponent.PodSpec, - }, item, status, configMapObj, componentParameter) - - By("update configuration resource") - err = reconcileTask. - ComponentAndComponentDef(). - PrepareForTemplate(). - RerenderTemplate(). - ApplyParameters(). - UpdateConfigVersion(strconv.FormatInt(reconcileTask.ComponentParameterObj.GetGeneration(), 10)). - Sync(). - Complete() - Expect(err).Should(Succeed()) - }) - }) - -}) diff --git a/pkg/controller/configuration/resource_wrapper.go b/pkg/controller/configuration/resource_wrapper.go index b327a591786..75d1a6470b1 100644 --- a/pkg/controller/configuration/resource_wrapper.go +++ b/pkg/controller/configuration/resource_wrapper.go @@ -28,8 +28,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" cfgcore "github.com/apecloud/kubeblocks/pkg/configuration/core" "github.com/apecloud/kubeblocks/pkg/constant" @@ -46,10 +44,7 @@ type ResourceFetcher[T any] struct { ComponentDefObj *appsv1.ComponentDefinition ClusterComObj *appsv1.ClusterComponentSpec - ConfigMapObj *corev1.ConfigMap - ConfigurationObj *appsv1alpha1.Configuration - ConfigConstraintObj *appsv1beta1.ConfigConstraint - + ConfigMapObj *corev1.ConfigMap ComponentParameterObj *parametersv1alpha1.ComponentParameter } @@ -121,22 +116,6 @@ func (r *ResourceFetcher[T]) ComponentSpec() *T { }) } -func (r *ResourceFetcher[T]) Configuration() *T { - configKey := client.ObjectKey{ - Name: cfgcore.GenerateComponentConfigurationName(r.ClusterName, r.ComponentName), - Namespace: r.Namespace, - } - return r.Wrap(func() (err error) { - configuration := appsv1alpha1.Configuration{} - err = r.Client.Get(r.Context, configKey, &configuration) - if err != nil { - return client.IgnoreNotFound(err) - } - r.ConfigurationObj = &configuration - return - }) -} - func (r *ResourceFetcher[T]) ConfigMap(configSpec string) *T { cmKey := client.ObjectKey{ Name: cfgcore.GetComponentCfgName(r.ClusterName, r.ComponentName, configSpec), @@ -149,16 +128,6 @@ func (r *ResourceFetcher[T]) ConfigMap(configSpec string) *T { }) } -func (r *ResourceFetcher[T]) ConfigConstraints(ccName string) *T { - return r.Wrap(func() error { - if ccName != "" { - r.ConfigConstraintObj = &appsv1beta1.ConfigConstraint{} - return r.Client.Get(r.Context, client.ObjectKey{Name: ccName}, r.ConfigConstraintObj) - } - return nil - }) -} - func (r *ResourceFetcher[T]) ComponentParameter() *T { configKey := client.ObjectKey{ Name: cfgcore.GenerateComponentConfigurationName(r.ClusterName, r.ComponentName), diff --git a/pkg/controller/configuration/resource_wrapper_test.go b/pkg/controller/configuration/resource_wrapper_test.go index 25435694ede..68bef7e9dc4 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" "github.com/apecloud/kubeblocks/pkg/controller/render" testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" @@ -71,19 +70,14 @@ 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.GenerateComponentConfigurationName(clusterName, mysqlCompName)).GetObject(), }, ), testutil.WithAnyTimes())) err := NewTest(k8sMockClient.Client(), ctx). Cluster(). ComponentSpec(). ConfigMap(mysqlConfigName). - ConfigConstraints(mysqlConfigName). - Configuration(). + ComponentParameter(). Complete() Expect(err).Should(Succeed()) }) diff --git a/pkg/controller/configuration/template_render.go b/pkg/controller/configuration/template_render.go new file mode 100644 index 00000000000..4f87271aa39 --- /dev/null +++ b/pkg/controller/configuration/template_render.go @@ -0,0 +1,77 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configuration + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + configcore "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/controller/render" +) + +func RerenderParametersTemplate(reconcileCtx *render.ReconcileCtx, + item parametersv1alpha1.ConfigTemplateItemDetail, + configRender *parametersv1alpha1.ParameterDrivenConfigRender, + parametersDefs []*parametersv1alpha1.ParametersDefinition) (*corev1.ConfigMap, error) { + parametersValidate := func(m map[string]string) error { + return validateRenderedData(m, parametersDefs, configRender) + } + + configSpec := *item.ConfigSpec + templateRender := render.NewTemplateBuilder(reconcileCtx) + rerenderCMObj, err := templateRender.RenderComponentTemplate(configSpec, + configcore.GetComponentCfgName(reconcileCtx.SynthesizedComponent.ClusterName, + reconcileCtx.SynthesizedComponent.Name, + item.ConfigSpec.Name), + parametersValidate) + if err != nil { + return nil, err + } + if item.CustomTemplates == nil { + return rerenderCMObj, nil + } + + mergedData, err := mergerConfigTemplate(*item.CustomTemplates, + templateRender, + configSpec, + rerenderCMObj.Data, + parametersDefs, + configRender) + if err != nil { + return nil, err + } + rerenderCMObj.Data = mergedData + return rerenderCMObj, nil +} + +func ApplyParameters(item parametersv1alpha1.ConfigTemplateItemDetail, orig *corev1.ConfigMap, configRender *parametersv1alpha1.ParameterDrivenConfigRender, paramsDefs []*parametersv1alpha1.ParametersDefinition) (*corev1.ConfigMap, error) { + if configRender == nil || len(configRender.Spec.Configs) == 0 { + return nil, fmt.Errorf("not support parameter reconfigure") + } + + newData, err := DoMerge(orig.Data, item.ConfigFileParams, paramsDefs, configRender.Spec.Configs) + if err != nil { + return nil, err + } + + expected := orig.DeepCopy() + expected.Data = newData + return expected, nil +} diff --git a/pkg/controller/configuration/template_render_test.go b/pkg/controller/configuration/template_render_test.go new file mode 100644 index 00000000000..2359f8ea2d4 --- /dev/null +++ b/pkg/controller/configuration/template_render_test.go @@ -0,0 +1,165 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package configuration + +import ( + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + "k8s.io/utils/pointer" + "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" + "github.com/apecloud/kubeblocks/pkg/controller/component" + "github.com/apecloud/kubeblocks/pkg/controller/render" + testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" + testutil "github.com/apecloud/kubeblocks/pkg/testutil/k8s" + testparameters "github.com/apecloud/kubeblocks/pkg/testutil/parameters" + "github.com/apecloud/kubeblocks/pkg/unstructured" +) + +var _ = Describe("ToolsImageBuilderTest", func() { + + var mockK8sCli *testutil.K8sClientMockHelper + var clusterObj *appsv1.Cluster + var compDefObj *appsv1.ComponentDefinition + var clusterComponent *component.SynthesizedComponent + var paramsDef *parametersv1alpha1.ParametersDefinition + var pdcr *parametersv1alpha1.ParameterDrivenConfigRender + + BeforeEach(func() { + mockK8sCli = testutil.NewK8sMockClient() + + // Add any setup steps that needs to be executed before each test + clusterObj, compDefObj, _ = newAllFieldsClusterObj(nil, false) + paramsDef = testparameters.NewParametersDefinitionFactory(paramsDefName). + SetReloadAction(testparameters.WithNoneAction()). + GetObject() + clusterComponent = newAllFieldsSynthesizedComponent(compDefObj, clusterObj) + + pdcr = testparameters.NewParametersDrivenConfigFactory(pdcrName). + SetParametersDefs(paramsDef.Name). + SetComponentDefinition(compDefObj.GetName()). + SetTemplateName(configTemplateName). + GetObject() + }) + + AfterEach(func() { + // Add any teardown steps that needs to be executed after each test + mockK8sCli.Finish() + }) + + deRef := func(m map[string]*parametersv1alpha1.ParametersInFile) map[string]parametersv1alpha1.ParametersInFile { + r := make(map[string]parametersv1alpha1.ParametersInFile, len(m)) + for key, param := range m { + r[key] = *param + } + return r + } + + rerender := func(parameters parametersv1alpha1.ComponentParameters, tpl *corev1.ConfigMap, customTemplate *appsv1.ConfigTemplateExtension) unstructured.ConfigObject { + rctx := &render.ReconcileCtx{ + ResourceCtx: &render.ResourceCtx{ + Context: testCtx.Ctx, + Client: mockK8sCli.Client(), + ClusterName: clusterObj.Name, + ComponentName: mysqlCompName, + }, + Cluster: clusterObj, + SynthesizedComponent: clusterComponent, + PodSpec: &corev1.PodSpec{}, + } + item := parametersv1alpha1.ConfigTemplateItemDetail{ + Name: configTemplateName, + ConfigSpec: &appsv1.ComponentTemplateSpec{ + Name: configTemplateName, + Namespace: testCtx.DefaultNamespace, + TemplateRef: mysqlConfigName, + VolumeName: testapps.ConfVolumeName, + }, + CustomTemplates: customTemplate, + } + pds := []*parametersv1alpha1.ParametersDefinition{paramsDef} + cmObj, err := RerenderParametersTemplate(rctx, item, pdcr, pds) + Expect(err).Should(Succeed()) + configdesc := pdcr.Spec.Configs[0] + if len(parameters) == 0 { + configReaders, err := cfgcore.LoadRawConfigObject(cmObj.Data, configdesc.FileFormatConfig, []string{configdesc.Name}) + Expect(err).Should(Succeed()) + return configReaders[configdesc.Name] + } + params := ClassifyComponentParameters(parameters, pds, []appsv1.ComponentTemplateSpec{*item.ConfigSpec}, map[string]*corev1.ConfigMap{configTemplateName: tpl}) + + tplParams, ok := params[configTemplateName] + Expect(ok).Should(BeTrue()) + item.ConfigFileParams = deRef(tplParams) + result, err := ApplyParameters(item, cmObj, pdcr, pds) + Expect(err).Should(Succeed()) + configReaders, err := cfgcore.LoadRawConfigObject(result.Data, configdesc.FileFormatConfig, []string{configdesc.Name}) + Expect(err).Should(Succeed()) + return configReaders[configdesc.Name] + } + + Context("RerenderParametersTemplateTest", func() { + It("Render config with parameters", func() { + configMapObj := testparameters.NewComponentTemplateFactory(mysqlConfigName, testCtx.DefaultNamespace). + GetObject() + + mockK8sCli.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]client.Object{ + configMapObj, + }), testutil.WithAnyTimes())) + + renderedObj := rerender(map[string]*string{ + "max_connections": pointer.String("100"), + "read_buffer_size": pointer.String("55288"), + }, configMapObj, nil) + Expect(renderedObj).ShouldNot(BeNil()) + Expect(renderedObj.Get("max_connections")).Should(BeEquivalentTo("100")) + Expect(renderedObj.Get("read_buffer_size")).Should(BeEquivalentTo("55288")) + }) + + It("Render config with custom template", func() { + configMapObj := testparameters.NewComponentTemplateFactory(mysqlConfigName, testCtx.DefaultNamespace). + GetObject() + customTemplate := testparameters.NewComponentTemplateFactory(mysqlConfigName, testCtx.DefaultNamespace). + AddConfigFile(testparameters.MysqlConfigFile, ` +[mysqld] +innodb_buffer_pool_size=512M +gtid_mode=OFF +`). + GetObject() + + mockK8sCli.MockGetMethod(testutil.WithGetReturned(testutil.WithConstructSimpleGetResult([]client.Object{ + configMapObj, + customTemplate, + }), testutil.WithAnyTimes())) + + renderedObj := rerender(nil, nil, &appsv1.ConfigTemplateExtension{ + TemplateRef: customTemplate.Name, + Namespace: testCtx.DefaultNamespace, + Policy: appsv1.ReplacePolicy, + }) + Expect(renderedObj).ShouldNot(BeNil()) + Expect(renderedObj.Get("innodb_buffer_pool_size")).Should(BeEquivalentTo("512M")) + Expect(renderedObj.Get("gtid_mode")).Should(BeEquivalentTo("OFF")) + Expect(renderedObj.Get("max_connections")).Should(BeNil()) + }) + }) +}) diff --git a/pkg/controller/configuration/template_validate.go b/pkg/controller/configuration/template_validate.go new file mode 100644 index 00000000000..f1acf543df2 --- /dev/null +++ b/pkg/controller/configuration/template_validate.go @@ -0,0 +1,66 @@ +/* +Copyright (C) 2022-2024 ApeCloud Co., Ltd + +This file is part of KubeBlocks project + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +package configuration + +import ( + parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" + "github.com/apecloud/kubeblocks/pkg/configuration/core" + "github.com/apecloud/kubeblocks/pkg/configuration/validate" +) + +// validateRenderedData validates config file against constraint +func validateRenderedData(renderedData map[string]string, paramsDefs []*parametersv1alpha1.ParametersDefinition, configRender *parametersv1alpha1.ParameterDrivenConfigRender) error { + if len(paramsDefs) == 0 || configRender == nil || len(configRender.Spec.Configs) == 0 { + return nil + } + for _, paramsDef := range paramsDefs { + fileName := paramsDef.Spec.FileName + if paramsDef.Spec.ParametersSchema == nil { + continue + } + if _, ok := renderedData[fileName]; !ok { + continue + } + if fileConfig := resolveFileFormatConfig(configRender.Spec.Configs, fileName); fileConfig != nil { + if err := validateConfigContent(renderedData[fileName], ¶msDef.Spec, fileConfig); err != nil { + return err + } + } + } + return nil +} + +func resolveFileFormatConfig(configDescs []parametersv1alpha1.ComponentConfigDescription, fileName string) *parametersv1alpha1.FileFormatConfig { + for i, configDesc := range configDescs { + if fileName == configDesc.Name { + return configDescs[i].FileFormatConfig + } + } + return nil +} + +func validateConfigContent(renderedData string, paramsDef *parametersv1alpha1.ParametersDefinitionSpec, fileFormat *parametersv1alpha1.FileFormatConfig) error { + configChecker := validate.NewConfigValidator(paramsDef.ParametersSchema, fileFormat) + // NOTE: It is necessary to verify the correctness of the data + if err := configChecker.Validate(renderedData); err != nil { + return core.WrapError(err, "failed to validate configmap") + } + return nil +} diff --git a/pkg/controller/configuration/template_wrapper.go b/pkg/controller/configuration/template_wrapper.go deleted file mode 100644 index f2585a3e9e4..00000000000 --- a/pkg/controller/configuration/template_wrapper.go +++ /dev/null @@ -1,337 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package configuration - -import ( - "context" - "encoding/json" - "fmt" - "reflect" - - corev1 "k8s.io/api/core/v1" - apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - - appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - parametersv1alpha1 "github.com/apecloud/kubeblocks/apis/parameters/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/configuration/core" - cfgutil "github.com/apecloud/kubeblocks/pkg/configuration/util" - "github.com/apecloud/kubeblocks/pkg/configuration/validate" - "github.com/apecloud/kubeblocks/pkg/constant" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/render" - intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" - "github.com/apecloud/kubeblocks/pkg/generics" -) - -type renderWrapper struct { - render.TemplateRender - - volumes map[string]appsv1.ComponentTemplateSpec - templateAnnotations map[string]string - renderedObjs []*corev1.ConfigMap - - ctx context.Context - cli client.Client - cluster *appsv1.Cluster - component *appsv1.Component -} - -func newTemplateRenderWrapper(ctx context.Context, cli client.Client, templateBuilder render.TemplateRender, - cluster *appsv1.Cluster, component *appsv1.Component) renderWrapper { - - return renderWrapper{ - ctx: ctx, - cli: cli, - cluster: cluster, - component: component, - - TemplateRender: templateBuilder, - templateAnnotations: make(map[string]string), - volumes: make(map[string]appsv1.ComponentTemplateSpec), - } -} - -func (wrapper *renderWrapper) checkRerenderTemplateSpec(cfgCMName string, localObjs []client.Object) (*corev1.ConfigMap, error) { - cmKey := client.ObjectKey{ - Name: cfgCMName, - Namespace: wrapper.cluster.Namespace, - } - - cmObj := &corev1.ConfigMap{} - localObject := findMatchedLocalObject(localObjs, cmKey, generics.ToGVK(cmObj)) - if localObject != nil { - if cm, ok := localObject.(*corev1.ConfigMap); ok { - return cm, nil - } - } - - cmErr := wrapper.cli.Get(wrapper.ctx, cmKey, cmObj, inDataContext()) - if cmErr != nil && !apierrors.IsNotFound(cmErr) { - // An unexpected error occurs - return nil, cmErr - } - if cmErr != nil { - // Config is not exists - return nil, nil - } - - return cmObj, nil -} - -func (wrapper *renderWrapper) renderConfigTemplate(cluster *appsv1.Cluster, - component *component.SynthesizedComponent, - localObjs []client.Object, - componentParameter *parametersv1alpha1.ComponentParameter, - configRender *parametersv1alpha1.ParameterDrivenConfigRender, - defs []*parametersv1alpha1.ParametersDefinition, revision string) error { - for _, configSpec := range component.ConfigTemplates { - var item *parametersv1alpha1.ConfigTemplateItemDetail - cmName := core.GetComponentCfgName(cluster.Name, component.Name, configSpec.Name) - origCMObj, err := wrapper.checkRerenderTemplateSpec(cmName, localObjs) - if err != nil { - return err - } - // If ConfigMap already exists, skip the rendering process. - // In this way, the Component controller only creates ConfigMap objects for the first time, - // and does not update the ConfigMap objects in the subsequent reconfiguration process. - // The subsequent reconfiguration process is handled by the Configuration controller. - if origCMObj != nil { - wrapper.addVolumeMountMeta(configSpec, origCMObj, false) - continue - } - item = intctrlutil.GetConfigTemplateItem(&componentParameter.Spec, configSpec.Name) - if item == nil { - return fmt.Errorf("config template item not found: %s", configSpec.Name) - } - newCMObj, err := wrapper.rerenderConfigTemplate(cluster, component, configSpec, item, configRender, defs) - if err != nil { - return err - } - if newCMObj, err = applyUpdatedParameters(item, newCMObj, configRender, defs); err != nil { - return err - } - if err := wrapper.addRenderedObject(configSpec, newCMObj, componentParameter); err != nil { - return err - } - if err := updateConfigMetaForCM(newCMObj, item, revision); err != nil { - return err - } - } - return nil -} - -func updateConfigMetaForCM(newCMObj *corev1.ConfigMap, item *parametersv1alpha1.ConfigTemplateItemDetail, revision string) (err error) { - if item == nil { - return - } - - annotations := newCMObj.GetAnnotations() - if annotations == nil { - annotations = make(map[string]string) - } - b, err := json.Marshal(item) - if err != nil { - return err - } - annotations[constant.ConfigAppliedVersionAnnotationKey] = string(b) - hash, _ := cfgutil.ComputeHash(newCMObj.Data) - annotations[constant.CMInsCurrentConfigurationHashLabelKey] = hash - annotations[constant.ConfigurationRevision] = revision - newCMObj.Annotations = annotations - return -} - -func applyUpdatedParameters(item *parametersv1alpha1.ConfigTemplateItemDetail, orig *corev1.ConfigMap, configRender *parametersv1alpha1.ParameterDrivenConfigRender, paramsDefs []*parametersv1alpha1.ParametersDefinition) (*corev1.ConfigMap, error) { - if configRender == nil || len(configRender.Spec.Configs) == 0 { - return nil, fmt.Errorf("not support parameter reconfigure") - } - - newData, err := DoMerge(orig.Data, item.ConfigFileParams, paramsDefs, configRender.Spec.Configs) - if err != nil { - return nil, err - } - - expected := orig.DeepCopy() - expected.Data = newData - return expected, nil -} - -func (wrapper *renderWrapper) rerenderConfigTemplate(cluster *appsv1.Cluster, - component *component.SynthesizedComponent, - configSpec appsv1.ComponentTemplateSpec, - item *parametersv1alpha1.ConfigTemplateItemDetail, - configRender *parametersv1alpha1.ParameterDrivenConfigRender, - defs []*parametersv1alpha1.ParametersDefinition) (*corev1.ConfigMap, error) { - cmName := core.GetComponentCfgName(cluster.Name, component.Name, configSpec.Name) - newCMObj, err := wrapper.RenderComponentTemplate(configSpec, cmName, func(m map[string]string) error { - return validateRenderedData(m, defs, configRender) - }) - if err != nil { - return nil, err - } - // render user specified template - if item != nil && item.CustomTemplates != nil { - newData, err := mergerConfigTemplate( - appsv1.ConfigTemplateExtension{ - TemplateRef: item.CustomTemplates.TemplateRef, - Namespace: item.CustomTemplates.Namespace, - Policy: item.CustomTemplates.Policy, - }, - wrapper.TemplateRender, - configSpec, - newCMObj.Data, - defs, - configRender) - if err != nil { - return nil, err - } - newCMObj.Data = newData - } - UpdateCMConfigSpecLabels(newCMObj, configSpec) - return newCMObj, nil -} - -func (wrapper *renderWrapper) renderScriptTemplate(cluster *appsv1.Cluster, component *component.SynthesizedComponent, - localObjs []client.Object, owner client.Object) error { - for _, templateSpec := range component.ScriptTemplates { - cmName := core.GetComponentCfgName(cluster.Name, component.Name, templateSpec.Name) - object := findMatchedLocalObject(localObjs, client.ObjectKey{ - Name: cmName, - Namespace: wrapper.cluster.Namespace}, generics.ToGVK(&corev1.ConfigMap{})) - if object != nil { - wrapper.addVolumeMountMeta(templateSpec, object.(*corev1.ConfigMap), false) - continue - } - - // Generate ConfigMap objects for config files - cm, err := wrapper.RenderComponentTemplate(templateSpec, cmName, nil) - if err != nil { - return err - } - if err := wrapper.addRenderedObject(templateSpec, cm, owner); err != nil { - return err - } - } - return nil -} - -func (wrapper *renderWrapper) addRenderedObject(templateSpec appsv1.ComponentTemplateSpec, cm *corev1.ConfigMap, owner client.Object) (err error) { - // The owner of the configmap object is a cluster, - // in order to manage the life cycle of configmap - if err = intctrlutil.SetControllerReference(owner, cm); err != nil { - return err - } - - core.SetParametersUpdateSource(cm, constant.ReconfigureManagerSource) - wrapper.addVolumeMountMeta(templateSpec, cm, true) - return nil -} - -func (wrapper *renderWrapper) addVolumeMountMeta(templateSpec appsv1.ComponentTemplateSpec, object *corev1.ConfigMap, rendered bool) { - wrapper.volumes[object.GetName()] = templateSpec - if rendered { - wrapper.renderedObjs = append(wrapper.renderedObjs, object) - } - wrapper.templateAnnotations[core.GenerateTPLUniqLabelKeyWithConfig(templateSpec.Name)] = object.GetName() -} - -func (wrapper *renderWrapper) CheckAndPatchConfigResource(origCMObj *corev1.ConfigMap, newData map[string]string) error { - if origCMObj == nil { - return nil - } - if reflect.DeepEqual(origCMObj.Data, newData) { - return nil - } - - patch := client.MergeFrom(origCMObj.DeepCopy()) - origCMObj.Data = newData - if origCMObj.Annotations == nil { - origCMObj.Annotations = make(map[string]string) - } - core.SetParametersUpdateSource(origCMObj, constant.ReconfigureManagerSource) - rawData, err := json.Marshal(origCMObj.Data) - if err != nil { - return err - } - - origCMObj.Annotations[corev1.LastAppliedConfigAnnotation] = string(rawData) - return wrapper.cli.Patch(wrapper.ctx, origCMObj, patch) -} - -func findMatchedLocalObject(localObjs []client.Object, objKey client.ObjectKey, gvk schema.GroupVersionKind) client.Object { - for _, obj := range localObjs { - if obj.GetName() == objKey.Name && obj.GetNamespace() == objKey.Namespace { - if generics.ToGVK(obj) == gvk { - return obj - } - } - } - return nil -} - -func UpdateCMConfigSpecLabels(cm *corev1.ConfigMap, configSpec appsv1.ComponentTemplateSpec) { - if cm.Labels == nil { - cm.Labels = make(map[string]string) - } - cm.Labels[constant.CMConfigurationSpecProviderLabelKey] = configSpec.Name - cm.Labels[constant.CMConfigurationTemplateNameLabelKey] = configSpec.TemplateRef -} - -// validateRenderedData validates config file against constraint -func validateRenderedData(renderedData map[string]string, paramsDefs []*parametersv1alpha1.ParametersDefinition, configRender *parametersv1alpha1.ParameterDrivenConfigRender) error { - if len(paramsDefs) == 0 || configRender == nil || len(configRender.Spec.Configs) == 0 { - return nil - } - for _, paramsDef := range paramsDefs { - fileName := paramsDef.Spec.FileName - if paramsDef.Spec.ParametersSchema == nil { - continue - } - if _, ok := renderedData[fileName]; !ok { - continue - } - if fileConfig := resolveFileFormatConfig(configRender.Spec.Configs, fileName); fileConfig != nil { - if err := validateConfigContent(renderedData[fileName], ¶msDef.Spec, fileConfig); err != nil { - return err - } - } - } - return nil -} - -func resolveFileFormatConfig(configDescs []parametersv1alpha1.ComponentConfigDescription, fileName string) *parametersv1alpha1.FileFormatConfig { - for i, configDesc := range configDescs { - if fileName == configDesc.Name { - return configDescs[i].FileFormatConfig - } - } - return nil -} - -func validateConfigContent(renderedData string, paramsDef *parametersv1alpha1.ParametersDefinitionSpec, fileFormat *parametersv1alpha1.FileFormatConfig) error { - configChecker := validate.NewConfigValidator(paramsDef.ParametersSchema, fileFormat) - // NOTE: It is necessary to verify the correctness of the data - if err := configChecker.Validate(renderedData); err != nil { - return core.WrapError(err, "failed to validate configmap") - } - return nil -} diff --git a/pkg/controller/plan/prepare.go b/pkg/controller/plan/prepare.go deleted file mode 100644 index 143ec886931..00000000000 --- a/pkg/controller/plan/prepare.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package plan - -import ( - corev1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/configuration" - "github.com/apecloud/kubeblocks/pkg/controller/render" -) - -// RenderConfigNScriptFiles generates volumes for PodTemplate, volumeMount for container, rendered configTemplate and scriptTemplate, -// and generates configManager sidecar for the reconfigure operation. -func RenderConfigNScriptFiles(resourceCtx *render.ResourceCtx, - cluster *appsv1.Cluster, - component *appsv1.Component, - synthesizedComponent *component.SynthesizedComponent, - podSpec *corev1.PodSpec, - localObjs []client.Object) error { - return configuration.NewConfigReconcileTask( - resourceCtx, - cluster, - component, - synthesizedComponent, - podSpec, - localObjs).Reconcile() -} diff --git a/pkg/controller/plan/prepare_test.go b/pkg/controller/plan/prepare_test.go deleted file mode 100644 index 07440023fea..00000000000 --- a/pkg/controller/plan/prepare_test.go +++ /dev/null @@ -1,172 +0,0 @@ -/* -Copyright (C) 2022-2024 ApeCloud Co., Ltd - -This file is part of KubeBlocks project - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . -*/ - -package plan - -import ( - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - corev1 "k8s.io/api/core/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" - "github.com/apecloud/kubeblocks/pkg/controller/component" - "github.com/apecloud/kubeblocks/pkg/controller/configuration" - "github.com/apecloud/kubeblocks/pkg/controller/render" - intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" - "github.com/apecloud/kubeblocks/pkg/generics" - testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps" - testparameters "github.com/apecloud/kubeblocks/pkg/testutil/parameters" -) - -var _ = Describe("Prepare Test", func() { - cleanEnv := func() { - // must wait until resources deleted and no longer exist before the testcases start, - // otherwise if later it needs to create some new resource objects with the same name, - // in race conditions, it will find the existence of old objects, resulting failure to - // create the new objects. - By("clean resources") - - inNS := client.InNamespace(testCtx.DefaultNamespace) - ml := client.HasLabels{testCtx.TestObjLabelKey} - - // non-namespaced - testapps.ClearResources(&testCtx, generics.ParameterDrivenConfigRenderSignature, ml) - testapps.ClearResources(&testCtx, generics.ParametersDefinitionSignature, ml) - testapps.ClearResources(&testCtx, generics.ComponentDefinitionSignature, ml) - - // namespaced - testapps.ClearResources(&testCtx, generics.ConfigMapSignature, inNS, ml) - testapps.ClearResources(&testCtx, generics.ComponentSignature, inNS, ml) - } - - BeforeEach(func() { - cleanEnv() - }) - - AfterEach(func() { - cleanEnv() - }) - - const ( - compDefName = "test-compdef" - clusterName = "test-cluster" - mysqlCompName = "mysql" - paramsDefName = "mysql-params-def" - pdcrName = "mysql-pdcr" - envFileName = "test" - ) - - var ( - compDefObj *appsv1.ComponentDefinition - cluster *appsv1.Cluster - comp *appsv1.Component - ) - - Context("create cluster with component and component definition API, testing render configuration", func() { - createAllTypesClusterDef := func() { - By("Create a componentDefinition obj") - compDefObj = testapps.NewComponentDefinitionFactory(compDefName). - WithRandomName(). - SetDefaultSpec(). - AddConfigs(testapps.DefaultCompDefConfigs). - AddScripts(testapps.DefaultCompDefScripts). - AddVolumeMounts("mysql", []corev1.VolumeMount{{Name: testapps.DefaultConfigSpecVolumeName, MountPath: "/mnt/config"}}). - Create(&testCtx). - GetObject() - Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(compDefObj), func(obj *appsv1.ComponentDefinition) { - obj.Status.Phase = appsv1.AvailablePhase - })()).Should(Succeed()) - } - - BeforeEach(func() { - createAllTypesClusterDef() - - parametersDef := testparameters.NewParametersDefinitionFactory(paramsDefName). - SetConfigFile(envFileName). - Schema(""). - Create(&testCtx). - GetObject() - Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(parametersDef), func(obj *parametersv1alpha1.ParametersDefinition) { - obj.Status.Phase = parametersv1alpha1.PDAvailablePhase - })()).Should(Succeed()) - - configRender := testparameters.NewParametersDrivenConfigFactory(pdcrName). - SetConfigDescription(envFileName, testapps.DefaultConfigSpecName, parametersv1alpha1.FileFormatConfig{Format: parametersv1alpha1.Properties}). - SetComponentDefinition(compDefObj.Name). - SetParametersDefs(paramsDefName). - Create(&testCtx). - GetObject() - - Expect(testapps.GetAndChangeObj(&testCtx, client.ObjectKeyFromObject(configRender), func(obj *parametersv1alpha1.ParameterDrivenConfigRender) { - config := intctrlutil.GetComponentConfigDescription(&obj.Spec, envFileName) - config.InjectEnvTo = []string{compDefObj.Spec.Runtime.Containers[0].Name} - })()).Should(Succeed()) - - Expect(testapps.GetAndChangeObjStatus(&testCtx, client.ObjectKeyFromObject(configRender), func(obj *parametersv1alpha1.ParameterDrivenConfigRender) { - obj.Status.Phase = parametersv1alpha1.PDAvailablePhase - })()).Should(Succeed()) - - testparameters.NewComponentTemplateFactory(testapps.DefaultConfigSpecTplRef, testCtx.DefaultNamespace). - AddConfigFile(envFileName, ` -dbStorage_rocksDB_writeBufferSizeMB=8 -dbStorage_rocksDB_sstSizeInMB=64 -dbStorage_rocksDB_blockSize=65536 -dbStorage_rocksDB_bloomFilterBitsPerKey=10 -dbStorage_rocksDB_numLevels=-1 -dbStorage_rocksDB_numFilesInLevel0=4 -dbStorage_rocksDB_maxSizeInLevel1MB=256 -`). - Create(&testCtx) - - pvcSpec := testapps.NewPVCSpec("1Gi") - cluster = testapps.NewClusterFactory(testCtx.DefaultNamespace, clusterName, ""). - AddComponent(mysqlCompName, compDefObj.Name). - SetReplicas(1). - AddVolumeClaimTemplate(testapps.DataVolumeName, pvcSpec). - Create(&testCtx). - GetObject() - - var err error - comp, err = component.BuildComponent(cluster, &cluster.Spec.ComponentSpecs[0], nil, nil) - Expect(err).Should(Succeed()) - comp.SetUID("test-uid") - Expect(testCtx.CreateObj(ctx, comp)).Should(Succeed()) - }) - - It("render configuration should success", func() { - synthesizeComp, err := component.BuildSynthesizedComponent(ctx, testCtx.Cli, compDefObj, comp, cluster) - Expect(err).Should(Succeed()) - Expect(synthesizeComp.PodSpec).ShouldNot(BeNil()) - resCtx := &render.ResourceCtx{ - Context: testCtx.Ctx, - Client: testCtx.Cli, - Namespace: synthesizeComp.Namespace, - ClusterName: synthesizeComp.ClusterName, - ComponentName: synthesizeComp.Name, - } - err = RenderConfigNScriptFiles(resCtx, cluster, comp, synthesizeComp, synthesizeComp.PodSpec, nil) - Expect(err).Should(Succeed()) - Expect(configuration.CheckEnvFrom(&synthesizeComp.PodSpec.Containers[0], cfgcore.GenerateEnvFromName(cfgcore.GetComponentCfgName(cluster.Name, synthesizeComp.Name, testapps.DefaultConfigSpecName)))).Should(BeTrue()) - }) - }) -}) diff --git a/pkg/controllerutil/util.go b/pkg/controllerutil/util.go index ebeb8717320..faa8dfab80f 100644 --- a/pkg/controllerutil/util.go +++ b/pkg/controllerutil/util.go @@ -79,7 +79,6 @@ func GetUncachedObjects() []client.Object { &corev1.ConfigMap{}, &corev1.Secret{}, &appsv1.Cluster{}, - &appsv1alpha1.Configuration{}, } } diff --git a/pkg/generics/type.go b/pkg/generics/type.go index e80350006de..4ae8a8ef4cc 100644 --- a/pkg/generics/type.go +++ b/pkg/generics/type.go @@ -30,8 +30,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1" - appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" - appsv1beta1 "github.com/apecloud/kubeblocks/apis/apps/v1beta1" dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1" extensionsv1alpha1 "github.com/apecloud/kubeblocks/apis/extensions/v1alpha1" opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1" @@ -105,11 +103,6 @@ var OpsDefinitionSignature = func(_ opsv1alpha1.OpsDefinition, _ *opsv1alpha1.Op } var OpsRequestSignature = func(_ opsv1alpha1.OpsRequest, _ *opsv1alpha1.OpsRequest, _ opsv1alpha1.OpsRequestList, _ *opsv1alpha1.OpsRequestList) { } -var ConfigConstraintSignature = func(_ appsv1beta1.ConfigConstraint, _ *appsv1beta1.ConfigConstraint, _ appsv1beta1.ConfigConstraintList, _ *appsv1beta1.ConfigConstraintList) { -} -var ConfigurationSignature = func(_ appsv1alpha1.Configuration, _ *appsv1alpha1.Configuration, _ appsv1alpha1.ConfigurationList, _ *appsv1alpha1.ConfigurationList) { -} - var BackupPolicyTemplateSignature = func(_ dpv1alpha1.BackupPolicyTemplate, _ *dpv1alpha1.BackupPolicyTemplate, _ dpv1alpha1.BackupPolicyTemplateList, _ *dpv1alpha1.BackupPolicyTemplateList) { } var BackupPolicySignature = func(_ dpv1alpha1.BackupPolicy, _ *dpv1alpha1.BackupPolicy, _ dpv1alpha1.BackupPolicyList, _ *dpv1alpha1.BackupPolicyList) { diff --git a/pkg/operations/reconfigure_test.go b/pkg/operations/reconfigure_test.go index c8bb32641ec..220dcea364f 100644 --- a/pkg/operations/reconfigure_test.go +++ b/pkg/operations/reconfigure_test.go @@ -59,8 +59,6 @@ var _ = Describe("Reconfigure OpsRequest", func() { // namespaced testapps.ClearResources(&testCtx, generics.OpsRequestSignature, inNS, ml) testapps.ClearResources(&testCtx, generics.ConfigMapSignature, inNS, ml) - // non-namespaced - testapps.ClearResources(&testCtx, generics.ConfigConstraintSignature, ml) } BeforeEach(cleanEnv) diff --git a/test/testdata/config/config-constraint.yaml b/test/testdata/config/config-constraint.yaml deleted file mode 100644 index 48452da60a5..00000000000 --- a/test/testdata/config/config-constraint.yaml +++ /dev/null @@ -1,41 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1beta1 -kind: ConfigConstraint -metadata: - name: mysql-tree-node-template-8.0 - namespace: default -spec: - reloadAction: - unixSignalTrigger: - signal: SIGHUP - processName: mysqld - - - # ConfigurationSchema that impose restrictions on engine parameter's rule - parametersSchema: - # top level mysql configuration type - topLevelKey: MysqlParameter - - # schema: auto generate from cue scripts - # example: ../../pkg/configuration/testdata/mysql_openapi.json - cue: |- - [SectionName=_]: { - // [OFF|ON] default ON - automatic_sp_privileges: string & "OFF" | "ON" | *"ON" - // [1~65535] default ON - auto_increment_increment: int & >= 1 & <= 65535 | *1 - // [4096~16777216] default 2G - binlog_stmt_cache_size?: int & >= 4096 & <= 16777216 | *2097152 - // [0|1|2] default: 2 - innodb_autoinc_lock_mode?: int & 0 | 1 | 2 | *2 - ... - } - - # require db instance restart - staticParameters: - - automatic_sp_privileges - - # mysql configuration file format - fileFormatConfig: - format: ini - iniConfig: - sectionName: mysqld \ No newline at end of file diff --git a/test/testdata/config/config-template.yaml b/test/testdata/config/config-template.yaml deleted file mode 100644 index 08774cf6231..00000000000 --- a/test/testdata/config/config-template.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: mysql-tree-node-template-8.0 - namespace: default -data: - my.cnf: |- - [mysqld] - innodb-buffer-pool-size=512M - log-bin=master-bin - gtid_mode=OFF - consensus_auto_leader_transfer=ON - - pid-file=/var/run/mysqld/mysqld.pid - socket=/var/run/mysqld/mysqld.sock - - port=3306 - general_log=0 - server-id=1 - slow_query_log=0 - - [client] - socket=/var/run/mysqld/mysqld.sock - host=localhost \ No newline at end of file diff --git a/test/testdata/config/envfrom-config.yaml b/test/testdata/config/envfrom-config.yaml deleted file mode 100644 index 9c2a56d51c4..00000000000 --- a/test/testdata/config/envfrom-config.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: env-from-config-tpl - namespace: default -data: - env-config: |- - dbStorage_rocksDB_writeBufferSizeMB=8 - dbStorage_rocksDB_sstSizeInMB=64 - dbStorage_rocksDB_blockSize=65536 - dbStorage_rocksDB_bloomFilterBitsPerKey=10 - dbStorage_rocksDB_numLevels=-1 - dbStorage_rocksDB_numFilesInLevel0=4 - dbStorage_rocksDB_maxSizeInLevel1MB=256 \ No newline at end of file diff --git a/test/testdata/config/envfrom-constraint.yaml b/test/testdata/config/envfrom-constraint.yaml deleted file mode 100644 index 1b215bf562d..00000000000 --- a/test/testdata/config/envfrom-constraint.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1beta1 -kind: ConfigConstraint -metadata: - name: env-from-config-test -spec: - fileFormatConfig: - format: properties \ No newline at end of file diff --git a/test/testdata/operations_config/config-constraint.yaml b/test/testdata/operations_config/config-constraint.yaml deleted file mode 100644 index bcd4b12e55f..00000000000 --- a/test/testdata/operations_config/config-constraint.yaml +++ /dev/null @@ -1,33 +0,0 @@ -apiVersion: apps.kubeblocks.io/v1beta1 -kind: ConfigConstraint -metadata: - name: mysql-tree-node-template-8.0 -spec: - # ConfigurationSchema that impose restrictions on engine parameter's rule - parametersSchema: - # schema: auto generate from cue scripts - # example: ../../pkg/configuration/testdata/mysql_openapi.json - cue: |- - #PARAM: { - // [OFF|ON] default ON - automatic_sp_privileges: string & "OFF" | "ON" | *"ON" - // [1~65535] default ON - auto_increment_increment: int & >= 1 & <= 65535 | *1 - // [4096~16777216] default 2G - binlog_stmt_cache_size?: int & >= 4096 & <= 16777216 | *2097152 - // [0|1|2] default: 2 - innodb_autoinc_lock_mode?: int & 0 | 1 | 2 | *2 - ... - } - [SectionName=_]: #PARAM - - dynamicParameters: - - binlog_stmt_cache_size - - x - - y - - # mysql configuration file format - fileFormatConfig: - format: ini - iniConfig: - sectionName: mysqld \ No newline at end of file diff --git a/test/testdata/operations_config/config-template.yaml b/test/testdata/operations_config/config-template.yaml deleted file mode 100644 index 3f6bf005d83..00000000000 --- a/test/testdata/operations_config/config-template.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: mysql-tree-node-template-8.0 -data: - my.cnf: |- - [mysqld] - innodb-buffer-pool-size=512M - log-bin=master-bin - gtid_mode=OFF - consensus_auto_leader_transfer=ON - - pid-file=/var/run/mysqld/mysqld.pid - socket=/var/run/mysqld/mysqld.sock - - port=3306 - general_log=0 - server-id=1 - slow_query_log=0 - - [client] - socket=/var/run/mysqld/mysqld.sock - host=localhost \ No newline at end of file