diff --git a/controllers/apps/cluster_controller.go b/controllers/apps/cluster_controller.go index 34609855ad3..ab498a618cf 100644 --- a/controllers/apps/cluster_controller.go +++ b/controllers/apps/cluster_controller.go @@ -140,13 +140,12 @@ func (r *ClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct &ClusterServiceTransformer{}, // create default cluster connection credential secret object &ClusterCredentialTransformer{}, - // TODO(component): handle restore before ClusterComponentTransformer + // handle restore before ClusterComponentTransformer &ClusterRestoreTransformer{Client: r.Client}, // create all cluster components objects &ClusterComponentTransformer{Client: r.Client}, - // TODO(component): transform backupPolicyTemplate to backuppolicy.dataprotection.kubeblocks.io - // and backupschedule.dataprotection.kubeblocks.io - &BackupPolicyTplTransformer{}, + // build backuppolicy and backupschedule from backupPolicyTemplate + &clusterBackupPolicyTransformer{}, // add our finalizer to all objects &ClusterOwnershipTransformer{}, // make all workload objects depending on credential secret diff --git a/controllers/apps/component_controller.go b/controllers/apps/component_controller.go index 0c3bf1e97ef..840bae3667b 100644 --- a/controllers/apps/component_controller.go +++ b/controllers/apps/component_controller.go @@ -21,23 +21,23 @@ package apps import ( "context" - workloads "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1" - "github.com/apecloud/kubeblocks/pkg/constant" + "time" + corev1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "time" - "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/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" + workloads "github.com/apecloud/kubeblocks/apis/workloads/v1alpha1" + "github.com/apecloud/kubeblocks/pkg/constant" intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil" viper "github.com/apecloud/kubeblocks/pkg/viperx" ) @@ -124,27 +124,24 @@ func (r *ComponentReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( AddTransformer( // handle component deletion first &componentDeletionTransformer{}, + // handle finalizers and referenced definition labels &componentMetaTransformer{}, - // validate referenced componentDefinition objects existence and availability, and build synthesized component - &componentLoadResourcesTransformer{}, - // do spec & definition consistency validation + // validate referenced componentDefinition objects, and build synthesized component + &componentLoadResourcesTransformer{Client: r.Client}, + // do validation for the spec & definition consistency &componentValidationTransformer{}, - // handle the component PDB + // handle component PDB &componentPDBTransformer{}, - // handle the component services + // handle component services &componentServiceTransformer{}, - // handle the connection credentials + // handle component connection credentials &componentCredentialTransformer{}, // handle tls volume and cert &componentTLSTransformer{}, - // render the component configurations + // render component configurations &componentConfigurationTransformer{Client: r.Client}, - // TODO(component): handle restore before component transformer - &componentRestoreTransformer{}, // handle the component workload &componentWorkloadTransformer{Client: r.Client}, - // TODO(component): transform backupPolicyTemplate to backuppolicy.dataprotection.kubeblocks.io and backupschedule.dataprotection.kubeblocks.io - &componentBackupPolicyTransformer{}, // handle RBAC for component workloads &componentRBACTransformer{}, // add our finalizer to all objects diff --git a/controllers/apps/transformer_backup_policy_tpl.go b/controllers/apps/transformer_cluster_backup_policy.go similarity index 91% rename from controllers/apps/transformer_backup_policy_tpl.go rename to controllers/apps/transformer_cluster_backup_policy.go index a4bf695386f..909710532d1 100644 --- a/controllers/apps/transformer_backup_policy_tpl.go +++ b/controllers/apps/transformer_cluster_backup_policy.go @@ -21,7 +21,7 @@ package apps import ( "fmt" - + "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -38,9 +38,8 @@ import ( dputils "github.com/apecloud/kubeblocks/pkg/dataprotection/utils" ) -// BackupPolicyTplTransformer transforms the backup policy template to the data -// protection backup policy and backup schedule. -type BackupPolicyTplTransformer struct { +// clusterBackupPolicyTransformer transforms the backup policy template to the data protection backup policy and backup schedule. +type clusterBackupPolicyTransformer struct { *clusterTransformContext tplCount int @@ -52,12 +51,15 @@ type BackupPolicyTplTransformer struct { compWorkloadType appsv1alpha1.WorkloadType } -var _ graph.Transformer = &BackupPolicyTplTransformer{} +var _ graph.Transformer = &clusterBackupPolicyTransformer{} -// Transform transforms the backup policy template to the backup policy and -// backup schedule. -func (r *BackupPolicyTplTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { +// Transform transforms the backup policy template to the backup policy and backup schedule. +func (r *clusterBackupPolicyTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { r.clusterTransformContext = ctx.(*clusterTransformContext) + if model.IsObjectDeleting(r.clusterTransformContext.OrigCluster) { + return nil + } + graphCli, _ := r.clusterTransformContext.Client.(model.GraphClient) clusterDefName := r.ClusterDef.Name @@ -178,7 +180,7 @@ func (r *BackupPolicyTplTransformer) Transform(ctx graph.TransformContext, dag * } // transformBackupPolicy transforms backup policy template to backup policy. -func (r *BackupPolicyTplTransformer) transformBackupPolicy() (*dpv1alpha1.BackupPolicy, *model.Action) { +func (r *clusterBackupPolicyTransformer) transformBackupPolicy() (*dpv1alpha1.BackupPolicy, *model.Action) { cluster := r.OrigCluster backupPolicyName := generateBackupPolicyName(cluster.Name, r.backupPolicy.ComponentDefRef, r.tplIdentifier) backupPolicy := &dpv1alpha1.BackupPolicy{} @@ -199,7 +201,7 @@ func (r *BackupPolicyTplTransformer) transformBackupPolicy() (*dpv1alpha1.Backup return backupPolicy, model.ActionUpdatePtr() } -func (r *BackupPolicyTplTransformer) transformBackupSchedule( +func (r *clusterBackupPolicyTransformer) transformBackupSchedule( backupPolicy *dpv1alpha1.BackupPolicy) (*dpv1alpha1.BackupSchedule, *model.Action) { cluster := r.OrigCluster scheduleName := generateBackupScheduleName(cluster.Name, r.backupPolicy.ComponentDefRef, r.tplIdentifier) @@ -220,7 +222,7 @@ func (r *BackupPolicyTplTransformer) transformBackupSchedule( return backupSchedule, model.ActionUpdatePtr() } -func (r *BackupPolicyTplTransformer) buildBackupSchedule( +func (r *clusterBackupPolicyTransformer) buildBackupSchedule( name string, backupPolicy *dpv1alpha1.BackupPolicy) *dpv1alpha1.BackupSchedule { cluster := r.OrigCluster @@ -249,7 +251,7 @@ func (r *BackupPolicyTplTransformer) buildBackupSchedule( return backupSchedule } -func (r *BackupPolicyTplTransformer) syncBackupSchedule(backupSchedule *dpv1alpha1.BackupSchedule) { +func (r *clusterBackupPolicyTransformer) syncBackupSchedule(backupSchedule *dpv1alpha1.BackupSchedule) { scheduleMethodMap := map[string]struct{}{} for _, s := range backupSchedule.Spec.Schedules { scheduleMethodMap[s.BackupMethod] = struct{}{} @@ -269,7 +271,7 @@ func (r *BackupPolicyTplTransformer) syncBackupSchedule(backupSchedule *dpv1alph } // syncBackupPolicy syncs labels and annotations of the backup policy with the cluster changes. -func (r *BackupPolicyTplTransformer) syncBackupPolicy(backupPolicy *dpv1alpha1.BackupPolicy) { +func (r *clusterBackupPolicyTransformer) syncBackupPolicy(backupPolicy *dpv1alpha1.BackupPolicy) { // update labels and annotations of the backup policy. if backupPolicy.Annotations == nil { backupPolicy.Annotations = map[string]string{} @@ -319,7 +321,7 @@ func (r *BackupPolicyTplTransformer) syncBackupPolicy(backupPolicy *dpv1alpha1.B } } -func (r *BackupPolicyTplTransformer) getCompReplicas() int32 { +func (r *clusterBackupPolicyTransformer) getCompReplicas() int32 { rsm := &workloads.ReplicatedStateMachine{} compSpec := r.getClusterComponentSpec() rsmName := fmt.Sprintf("%s-%s", r.Cluster.Name, compSpec.Name) @@ -330,7 +332,7 @@ func (r *BackupPolicyTplTransformer) getCompReplicas() int32 { } // buildBackupPolicy builds a new backup policy by the backup policy template. -func (r *BackupPolicyTplTransformer) buildBackupPolicy(backupPolicyName string) *dpv1alpha1.BackupPolicy { +func (r *clusterBackupPolicyTransformer) buildBackupPolicy(backupPolicyName string) *dpv1alpha1.BackupPolicy { comp := r.getClusterComponentSpec() if comp == nil { return nil @@ -358,7 +360,7 @@ func (r *BackupPolicyTplTransformer) buildBackupPolicy(backupPolicyName string) } // syncBackupMethods syncs the backupMethod of tpl to backupPolicy. -func (r *BackupPolicyTplTransformer) syncBackupMethods(backupPolicy *dpv1alpha1.BackupPolicy) { +func (r *clusterBackupPolicyTransformer) syncBackupMethods(backupPolicy *dpv1alpha1.BackupPolicy) { var backupMethods []dpv1alpha1.BackupMethod for _, v := range r.backupPolicy.BackupMethods { mappingEnv := r.doEnvMapping(v.EnvMapping) @@ -368,7 +370,7 @@ func (r *BackupPolicyTplTransformer) syncBackupMethods(backupPolicy *dpv1alpha1. backupPolicy.Spec.BackupMethods = backupMethods } -func (r *BackupPolicyTplTransformer) doEnvMapping(envMapping []appsv1alpha1.EnvMappingVar) []corev1.EnvVar { +func (r *clusterBackupPolicyTransformer) doEnvMapping(envMapping []appsv1alpha1.EnvMappingVar) []corev1.EnvVar { var env []corev1.EnvVar for _, v := range envMapping { for _, cv := range v.ValueFrom.ClusterVersionRef { @@ -384,7 +386,7 @@ func (r *BackupPolicyTplTransformer) doEnvMapping(envMapping []appsv1alpha1.EnvM return env } -func (r *BackupPolicyTplTransformer) buildBackupTarget( +func (r *clusterBackupPolicyTransformer) buildBackupTarget( comp *appsv1alpha1.ClusterComponentSpec) *dpv1alpha1.BackupTarget { targetTpl := r.backupPolicy.Target clusterName := r.OrigCluster.Name @@ -432,7 +434,7 @@ func (r *BackupPolicyTplTransformer) buildBackupTarget( return target } -func (r *BackupPolicyTplTransformer) mergeClusterBackup( +func (r *clusterBackupPolicyTransformer) mergeClusterBackup( backupPolicy *dpv1alpha1.BackupPolicy, backupSchedule *dpv1alpha1.BackupSchedule) *dpv1alpha1.BackupSchedule { cluster := r.OrigCluster @@ -491,7 +493,7 @@ func (r *BackupPolicyTplTransformer) mergeClusterBackup( } // getClusterComponentSpec returns the first component name of the componentDefRef. -func (r *BackupPolicyTplTransformer) getClusterComponentSpec() *appsv1alpha1.ClusterComponentSpec { +func (r *clusterBackupPolicyTransformer) getClusterComponentSpec() *appsv1alpha1.ClusterComponentSpec { for _, v := range r.clusterTransformContext.ComponentSpecs { if v.ComponentDefRef == r.backupPolicy.ComponentDefRef { return v @@ -500,14 +502,14 @@ func (r *BackupPolicyTplTransformer) getClusterComponentSpec() *appsv1alpha1.Clu return nil } -func (r *BackupPolicyTplTransformer) defaultPolicyAnnotationValue() string { +func (r *clusterBackupPolicyTransformer) defaultPolicyAnnotationValue() string { if r.tplCount > 1 && r.isDefaultTemplate != trueVal { return "false" } return trueVal } -func (r *BackupPolicyTplTransformer) buildAnnotations() map[string]string { +func (r *clusterBackupPolicyTransformer) buildAnnotations() map[string]string { annotations := map[string]string{ dptypes.DefaultBackupPolicyAnnotationKey: r.defaultPolicyAnnotationValue(), constant.BackupPolicyTemplateAnnotationKey: r.backupPolicyTpl.Name, @@ -518,7 +520,7 @@ func (r *BackupPolicyTplTransformer) buildAnnotations() map[string]string { return annotations } -func (r *BackupPolicyTplTransformer) buildLabels() map[string]string { +func (r *clusterBackupPolicyTransformer) buildLabels() map[string]string { return map[string]string{ constant.AppInstanceLabelKey: r.OrigCluster.Name, constant.KBAppComponentDefRefLabelKey: r.backupPolicy.ComponentDefRef, @@ -528,7 +530,7 @@ func (r *BackupPolicyTplTransformer) buildLabels() map[string]string { // buildTargetPodLabels builds the target labels for the backup policy that will be // used to select the target pod. -func (r *BackupPolicyTplTransformer) buildTargetPodLabels(comp *appsv1alpha1.ClusterComponentSpec) map[string]string { +func (r *clusterBackupPolicyTransformer) buildTargetPodLabels(comp *appsv1alpha1.ClusterComponentSpec) map[string]string { labels := map[string]string{ constant.AppInstanceLabelKey: r.OrigCluster.Name, constant.KBAppComponentLabelKey: comp.Name, diff --git a/controllers/apps/transformer_component_backup_policy.go b/controllers/apps/transformer_component_backup_policy.go deleted file mode 100644 index 4e1b8621fee..00000000000 --- a/controllers/apps/transformer_component_backup_policy.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright (C) 2022-2023 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 ( - "github.com/apecloud/kubeblocks/pkg/controller/graph" - "github.com/apecloud/kubeblocks/pkg/controller/model" -) - -// componentBackupPolicyTransformer handles the component PDB -type componentBackupPolicyTransformer struct{} - -var _ graph.Transformer = &componentBackupPolicyTransformer{} - -func (t *componentBackupPolicyTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { - cctx, _ := ctx.(*componentTransformContext) - if model.IsObjectDeleting(cctx.ComponentOrig) { - return nil - } - // TODO(component) - return nil -} diff --git a/controllers/apps/transformer_component_load_resources.go b/controllers/apps/transformer_component_load_resources.go index 5ade116b4e8..b8ad4545585 100644 --- a/controllers/apps/transformer_component_load_resources.go +++ b/controllers/apps/transformer_component_load_resources.go @@ -23,6 +23,7 @@ import ( "fmt" "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" appsv1alpha1 "github.com/apecloud/kubeblocks/apis/apps/v1alpha1" "github.com/apecloud/kubeblocks/pkg/controller/component" @@ -31,7 +32,9 @@ import ( ) // componentLoadResourcesTransformer handles referenced resources validation and load them into context -type componentLoadResourcesTransformer struct{} +type componentLoadResourcesTransformer struct { + client.Client +} var _ graph.Transformer = &componentLoadResourcesTransformer{} @@ -49,34 +52,75 @@ func (t *componentLoadResourcesTransformer) Transform(ctx graph.TransformContext setProvisioningStartedCondition(&comp.Status.Conditions, comp.Name, comp.Generation, err) }() - // get and init component definition context - compDef := &appsv1alpha1.ComponentDefinition{} - err = transCtx.Client.Get(transCtx.Context, types.NamespacedName{Name: comp.Spec.CompDef}, compDef) + // TODO(xingran): In order to backward compatibility in KubeBlocks version 0.8.0, the cluster field is still required. However, if in the future the Component objects can be used independently, the Cluster field should be removed from the component.Spec + cluster := &appsv1alpha1.Cluster{} + err = transCtx.Client.Get(transCtx.Context, types.NamespacedName{Name: comp.Spec.Cluster, Namespace: comp.Namespace}, cluster) if err != nil { return newRequeueError(requeueDuration, err.Error()) } + compDef, err := t.getOrBuildCompDef(reqCtx, transCtx, cluster) + if err != nil { + return newRequeueError(requeueDuration, err.Error()) + } if compDef.Status.Phase != appsv1alpha1.AvailablePhase { message := fmt.Sprintf("ComponentDefinition referenced is unavailable: %s", compDef.Name) return newRequeueError(requeueDuration, message) } - // get and init cluster context - // TODO(xingran): In order to backward compatibility in KubeBlocks version 0.8.0, the cluster field is still required. However, if in the future the Component objects can be used independently, the Cluster field should be removed from the component.Spec - cluster := &appsv1alpha1.Cluster{} - err = transCtx.Client.Get(transCtx.Context, types.NamespacedName{Name: comp.Spec.Cluster, Namespace: comp.Namespace}, cluster) - if err != nil { - return newRequeueError(requeueDuration, err.Error()) - } - transCtx.CompDef = compDef transCtx.Cluster = cluster synthesizeComp, err := component.BuildSynthesizedComponent(reqCtx, transCtx.Client, compDef, cluster, comp) if err != nil { - message := fmt.Sprintf("Component %s BuildSynthesizedComponent failed: %s", comp.Name, err.Error()) + message := fmt.Sprintf("build synthesized component for %s failed: %s", comp.Name, err.Error()) return newRequeueError(requeueDuration, message) } transCtx.SynthesizeComponent = synthesizeComp return nil } + +func (t *componentLoadResourcesTransformer) getOrBuildCompDef(reqCtx ictrlutil.RequestCtx, + transCtx *componentTransformContext, cluster *appsv1alpha1.Cluster) (*appsv1alpha1.ComponentDefinition, error) { + clusterCompSpec, err := t.isLegacyComponent(cluster, transCtx.Component) + if err != nil { + return nil, err + } + var compDef *appsv1alpha1.ComponentDefinition + if clusterCompSpec != nil { + compDef, err = component.BuildComponentDefinition(reqCtx, t.Client, cluster, clusterCompSpec) + if err != nil { + return nil, err + } + } else { + compDef = &appsv1alpha1.ComponentDefinition{} + err = transCtx.Client.Get(transCtx.Context, types.NamespacedName{Name: transCtx.Component.Spec.CompDef}, compDef) + if err != nil { + return nil, err + } + } + return compDef, nil +} + +func (t *componentLoadResourcesTransformer) isLegacyComponent(cluster *appsv1alpha1.Cluster, + comp *appsv1alpha1.Component) (*appsv1alpha1.ClusterComponentSpec, error) { + compName, err := component.ShortName(cluster.Name, comp.Name) + if err != nil { + return nil, err + } + var targetCompSpec *appsv1alpha1.ClusterComponentSpec + for i, compSpec := range cluster.Spec.ComponentSpecs { + if compSpec.Name == compName { + if len(compSpec.ComponentDef) > 0 { + if compSpec.ComponentDef == comp.Spec.CompDef { + return nil, nil + } + return nil, fmt.Errorf("runtime error - comp definitions refered in cluster and component are different: %s vs %s", + compSpec.ComponentDef, comp.Spec.CompDef) + } + targetCompSpec = &cluster.Spec.ComponentSpecs[i] + break + } + } + return targetCompSpec, nil +} diff --git a/controllers/apps/transformer_component_restore.go b/controllers/apps/transformer_component_restore.go deleted file mode 100644 index 47696536dd7..00000000000 --- a/controllers/apps/transformer_component_restore.go +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright (C) 2022-2023 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 ( - "github.com/apecloud/kubeblocks/pkg/controller/graph" - "github.com/apecloud/kubeblocks/pkg/controller/model" -) - -// componentRestoreTransformer handles the component PDB -type componentRestoreTransformer struct{} - -var _ graph.Transformer = &componentRestoreTransformer{} - -func (t *componentRestoreTransformer) Transform(ctx graph.TransformContext, dag *graph.DAG) error { - cctx, _ := ctx.(*componentTransformContext) - if model.IsObjectDeleting(cctx.ComponentOrig) { - return nil - } - // TODO(component) - return nil -}