From a59f681377da7a156b1fd705416cc4766b8bcd69 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Thu, 9 Jan 2025 10:05:45 +0800 Subject: [PATCH 1/3] chore: support to rebuild a shard instance --- apis/operations/v1alpha1/opsrequest_types.go | 4 +++ .../operations.kubeblocks.io_opsrequests.yaml | 4 +++ .../operations.kubeblocks.io_opsrequests.yaml | 4 +++ examples/redis/cluster-cmpd.yaml | 18 +++++++++++++ pkg/operations/rebuild_instance.go | 26 ++++++++++--------- pkg/operations/rebuild_instance_inplace.go | 25 ++++++++++-------- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/apis/operations/v1alpha1/opsrequest_types.go b/apis/operations/v1alpha1/opsrequest_types.go index 8a1d32005b1..7642a853cc0 100644 --- a/apis/operations/v1alpha1/opsrequest_types.go +++ b/apis/operations/v1alpha1/opsrequest_types.go @@ -270,6 +270,10 @@ type RebuildInstance struct { // +optional BackupName string `json:"backupName,omitempty"` + // When multiple source targets exist of the backup, you must specify the source target to restore. + // +optional + SourceBackupTargetName string `json:"sourceBackupTargetName,omitempty"` + // Defines container environment variables for the restore process. // merged with the ones specified in the Backup and ActionSet resources. // diff --git a/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml b/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml index bbfd8546b49..2cdeda3128d 100644 --- a/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml +++ b/config/crd/bases/operations.kubeblocks.io_opsrequests.yaml @@ -4200,6 +4200,10 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true + sourceBackupTargetName: + description: When multiple source targets exist of the backup, + you must specify the source target to restore. + type: string required: - componentName - instances diff --git a/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml b/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml index bbfd8546b49..2cdeda3128d 100755 --- a/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml +++ b/deploy/helm/crds/operations.kubeblocks.io_opsrequests.yaml @@ -4200,6 +4200,10 @@ spec: type: object type: array x-kubernetes-preserve-unknown-fields: true + sourceBackupTargetName: + description: When multiple source targets exist of the backup, + you must specify the source target to restore. + type: string required: - componentName - instances diff --git a/examples/redis/cluster-cmpd.yaml b/examples/redis/cluster-cmpd.yaml index 9bdc6eb3e6e..79c645bcdd1 100644 --- a/examples/redis/cluster-cmpd.yaml +++ b/examples/redis/cluster-cmpd.yaml @@ -3,6 +3,8 @@ kind: Cluster metadata: name: redis-cluster namespace: default + annotations: + kubeblocks.io/crd-api-version: apps.kubeblocks.io/v1alpha1 spec: # Specifies the behavior when a Cluster is deleted. # - `DoNotTerminate`: Prevents deletion of the Cluster. This policy ensures that all resources remain intact. @@ -14,10 +16,26 @@ spec: componentSpecs: # Specifies the name of the Component. This name is also part of the Service DNS name and must comply with the IANA service naming rule. When ClusterComponentSpec is referenced as a template, the name is optional. Otherwise, it is required. - name: redis + instances: + - annotations: + xxx: yyy + labels: {} + name: tpl-0 + replicas: 2 + resources: + limits: + cpu: '0.5' + memory: 0.5Gi + requests: + cpu: 100m + memory: 500M # References the name of a ComponentDefinition. The ComponentDefinition specifies the behavior and characteristics of the Component. If both `componentDefRef` and `componentDef` are provided, the `componentDef` will take precedence over `componentDefRef`. componentDef: redis-7 # Determines whether the metrics exporter needs to be published to the service endpoint. disableExporter: true + serviceVersion: "7.2.4" + labels: {} + # podUpdatePolicy: StrictInPlace # Specifies which types of logs should be collected for the Cluster. enabledLogs: - running diff --git a/pkg/operations/rebuild_instance.go b/pkg/operations/rebuild_instance.go index c20f6067481..4a90484ec49 100644 --- a/pkg/operations/rebuild_instance.go +++ b/pkg/operations/rebuild_instance.go @@ -276,7 +276,7 @@ func (r rebuildInstanceOpsHandler) rebuildInstanceInPlace(reqCtx intctrlutil.Req instance opsv1alpha1.Instance, index int) (bool, error) { inPlaceHelper, err := r.prepareInplaceRebuildHelper(reqCtx, cli, opsRes, rebuildFrom.RestoreEnv, - instance, rebuildFrom.BackupName, index) + instance, rebuildFrom.BackupName, rebuildFrom.SourceBackupTargetName, index) if err != nil { return false, err } @@ -554,6 +554,7 @@ func (r rebuildInstanceOpsHandler) prepareInplaceRebuildHelper(reqCtx intctrluti envForRestore []corev1.EnvVar, instance opsv1alpha1.Instance, backupName string, + sourceBackupTargetName string, index int) (*inplaceRebuildHelper, error) { var ( backup *dpv1alpha1.Backup @@ -595,17 +596,18 @@ func (r rebuildInstanceOpsHandler) prepareInplaceRebuildHelper(reqCtx intctrluti return nil, err } return &inplaceRebuildHelper{ - index: index, - backup: backup, - instance: instance, - actionSet: actionSet, - synthesizedComp: synthesizedComp, - pvcMap: pvcMap, - volumes: volumes, - targetPod: targetPod, - volumeMounts: volumeMounts, - rebuildPrefix: rebuildPrefix, - envForRestore: envForRestore, + index: index, + backup: backup, + instance: instance, + actionSet: actionSet, + synthesizedComp: synthesizedComp, + sourceBackupTargetName: sourceBackupTargetName, + pvcMap: pvcMap, + volumes: volumes, + targetPod: targetPod, + volumeMounts: volumeMounts, + rebuildPrefix: rebuildPrefix, + envForRestore: envForRestore, }, nil } diff --git a/pkg/operations/rebuild_instance_inplace.go b/pkg/operations/rebuild_instance_inplace.go index 7bc71a3c410..5a6462d3a49 100644 --- a/pkg/operations/rebuild_instance_inplace.go +++ b/pkg/operations/rebuild_instance_inplace.go @@ -60,13 +60,14 @@ type inplaceRebuildHelper struct { instance opsv1alpha1.Instance actionSet *dpv1alpha1.ActionSet // key: source pvc name, value: the tmp pvc which using to rebuild - pvcMap map[string]*corev1.PersistentVolumeClaim - synthesizedComp *component.SynthesizedComponent - volumes []corev1.Volume - volumeMounts []corev1.VolumeMount - envForRestore []corev1.EnvVar - rebuildPrefix string - index int + pvcMap map[string]*corev1.PersistentVolumeClaim + synthesizedComp *component.SynthesizedComponent + volumes []corev1.Volume + volumeMounts []corev1.VolumeMount + envForRestore []corev1.EnvVar + sourceBackupTargetName string + rebuildPrefix string + index int } // rebuildPodWithNoBackup rebuilds the instance with no backup. @@ -219,8 +220,9 @@ func (inPlaceHelper *inplaceRebuildHelper) createPrepareDataRestore(reqCtx intct ObjectMeta: inPlaceHelper.buildRestoreMetaObject(opsRequest, restoreName), Spec: dpv1alpha1.RestoreSpec{ Backup: dpv1alpha1.BackupRef{ - Name: inPlaceHelper.backup.Name, - Namespace: opsRequest.Namespace, + Name: inPlaceHelper.backup.Name, + Namespace: opsRequest.Namespace, + SourceTargetName: inPlaceHelper.sourceBackupTargetName, }, Env: inPlaceHelper.envForRestore, PrepareDataConfig: &dpv1alpha1.PrepareDataConfig{ @@ -262,8 +264,9 @@ func (inPlaceHelper *inplaceRebuildHelper) createPostReadyRestore(reqCtx intctrl ObjectMeta: inPlaceHelper.buildRestoreMetaObject(opsRequest, restoreName), Spec: dpv1alpha1.RestoreSpec{ Backup: dpv1alpha1.BackupRef{ - Name: inPlaceHelper.backup.Name, - Namespace: inPlaceHelper.backup.Namespace, + Name: inPlaceHelper.backup.Name, + Namespace: inPlaceHelper.backup.Namespace, + SourceTargetName: inPlaceHelper.sourceBackupTargetName, }, Env: inPlaceHelper.envForRestore, ReadyConfig: &dpv1alpha1.ReadyConfig{ From 1cc66a7d6db012acc58cbf8b7a914702c2d64a10 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Thu, 9 Jan 2025 17:00:39 +0800 Subject: [PATCH 2/3] fix bug --- pkg/operations/rebuild_instance.go | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/pkg/operations/rebuild_instance.go b/pkg/operations/rebuild_instance.go index 4a90484ec49..cd301e94854 100644 --- a/pkg/operations/rebuild_instance.go +++ b/pkg/operations/rebuild_instance.go @@ -275,8 +275,7 @@ func (r rebuildInstanceOpsHandler) rebuildInstanceInPlace(reqCtx intctrlutil.Req rebuildFrom opsv1alpha1.RebuildInstance, instance opsv1alpha1.Instance, index int) (bool, error) { - inPlaceHelper, err := r.prepareInplaceRebuildHelper(reqCtx, cli, opsRes, rebuildFrom.RestoreEnv, - instance, rebuildFrom.BackupName, rebuildFrom.SourceBackupTargetName, index) + inPlaceHelper, err := r.prepareInplaceRebuildHelper(reqCtx, cli, opsRes, rebuildFrom, instance, index) if err != nil { return false, err } @@ -444,10 +443,6 @@ func (r rebuildInstanceOpsHandler) checkProgressForScalingOutPods(reqCtx intctrl failedCount int completedCount int ) - synthesizedComp, err := r.buildSynthesizedComponent(reqCtx.Ctx, cli, opsRes.Cluster, rebuildInstance.ComponentName) - if err != nil { - return 0, 0, nil, err - } currPodSet, _ := component.GenerateAllPodNamesToSet(compSpec.Replicas, compSpec.Instances, compSpec.OfflineInstances, opsRes.Cluster.Name, compSpec.Name) for _, instance := range rebuildInstance.Instances { @@ -464,6 +459,11 @@ func (r rebuildInstanceOpsHandler) checkProgressForScalingOutPods(reqCtx intctrl reqCtx.Log.Info(fmt.Sprintf("waiting to create the pod %s", scalingOutPodName)) continue } + + synthesizedComp, err := r.buildSynthesizedComponent(reqCtx.Ctx, cli, opsRes.Cluster, pod.Labels[constant.KBAppComponentLabelKey]) + if err != nil { + return 0, 0, nil, err + } isAvailable, err := instanceIsAvailable(synthesizedComp, pod, opsRes.OpsRequest.Annotations[ignoreRoleCheckAnnotationKey]) if err != nil { // set progress status to failed when new pod is failed @@ -551,10 +551,8 @@ func (r rebuildInstanceOpsHandler) buildSynthesizedComponent(ctx context.Context func (r rebuildInstanceOpsHandler) prepareInplaceRebuildHelper(reqCtx intctrlutil.RequestCtx, cli client.Client, opsRes *OpsResource, - envForRestore []corev1.EnvVar, + rebuildInstance opsv1alpha1.RebuildInstance, instance opsv1alpha1.Instance, - backupName string, - sourceBackupTargetName string, index int) (*inplaceRebuildHelper, error) { var ( backup *dpv1alpha1.Backup @@ -562,20 +560,20 @@ func (r rebuildInstanceOpsHandler) prepareInplaceRebuildHelper(reqCtx intctrluti synthesizedComp *component.SynthesizedComponent err error ) - if backupName != "" { + if rebuildInstance.BackupName != "" { // prepare backup infos backup = &dpv1alpha1.Backup{} - if err = cli.Get(reqCtx.Ctx, client.ObjectKey{Name: backupName, Namespace: opsRes.Cluster.Namespace}, backup); err != nil { + if err = cli.Get(reqCtx.Ctx, client.ObjectKey{Name: rebuildInstance.BackupName, Namespace: opsRes.Cluster.Namespace}, backup); err != nil { return nil, err } if backup.Labels[dptypes.BackupTypeLabelKey] != string(dpv1alpha1.BackupTypeFull) { - return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backup "%s" is not a Full backup`, backupName)) + return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backup "%s" is not a Full backup`, rebuildInstance.BackupName)) } if backup.Status.Phase != dpv1alpha1.BackupPhaseCompleted { - return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backup "%s" phase is not Completed`, backupName)) + return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backup "%s" phase is not Completed`, rebuildInstance.BackupName)) } if backup.Status.BackupMethod == nil { - return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backupMethod of the backup "%s" can not be empty`, backupName)) + return nil, intctrlutil.NewFatalError(fmt.Sprintf(`the backupMethod of the backup "%s" can not be empty`, rebuildInstance.BackupName)) } actionSet, err = dputils.GetActionSetByName(reqCtx, cli, backup.Status.BackupMethod.ActionSetName) if err != nil { @@ -601,13 +599,13 @@ func (r rebuildInstanceOpsHandler) prepareInplaceRebuildHelper(reqCtx intctrluti instance: instance, actionSet: actionSet, synthesizedComp: synthesizedComp, - sourceBackupTargetName: sourceBackupTargetName, + sourceBackupTargetName: rebuildInstance.SourceBackupTargetName, pvcMap: pvcMap, volumes: volumes, targetPod: targetPod, volumeMounts: volumeMounts, rebuildPrefix: rebuildPrefix, - envForRestore: envForRestore, + envForRestore: rebuildInstance.RestoreEnv, }, nil } From cbe37fb9725584a975954ed025c3a9a52ff5e311 Mon Sep 17 00:00:00 2001 From: wangyelei Date: Fri, 10 Jan 2025 09:50:26 +0800 Subject: [PATCH 3/3] fix make test bug --- pkg/operations/rebuild_instance_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/operations/rebuild_instance_test.go b/pkg/operations/rebuild_instance_test.go index 3a2d5980a38..f5eb34af998 100644 --- a/pkg/operations/rebuild_instance_test.go +++ b/pkg/operations/rebuild_instance_test.go @@ -71,12 +71,12 @@ var _ = Describe("OpsUtil functions", func() { inNS := client.InNamespace(testCtx.DefaultNamespace) ml := client.HasLabels{testCtx.TestObjLabelKey} // namespaced - testapps.ClearResources(&testCtx, generics.OpsRequestSignature, inNS, ml) - testapps.ClearResources(&testCtx, generics.BackupSignature, inNS, ml) - testapps.ClearResources(&testCtx, generics.RestoreSignature, inNS, ml) - testapps.ClearResources(&testCtx, generics.InstanceSetSignature, inNS, ml) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.OpsRequestSignature, true, inNS, ml) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupSignature, true, inNS, ml) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.RestoreSignature, true, inNS, ml) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.InstanceSetSignature, true, inNS, ml) // default GracePeriod is 30s - testapps.ClearResources(&testCtx, generics.PodSignature, inNS, ml, client.GracePeriodSeconds(0)) + testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PodSignature, true, inNS, ml, client.GracePeriodSeconds(0)) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeClaimSignature, true, inNS, ml) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeSignature, true, ml) testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ActionSetSignature, true, ml)