@@ -38,6 +38,7 @@ import (
3838 "github.com/apecloud/kubeblocks/pkg/constant"
3939 "github.com/apecloud/kubeblocks/pkg/controller/component"
4040 intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
41+ dptypes "github.com/apecloud/kubeblocks/pkg/dataprotection/types"
4142 "github.com/apecloud/kubeblocks/pkg/generics"
4243 testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
4344 testdp "github.com/apecloud/kubeblocks/pkg/testutil/dataprotection"
@@ -257,6 +258,97 @@ var _ = Describe("Restore", func() {
257258 g .Expect (tmpCluster .Annotations [constant .RestoreFromBackupAnnotationKey ]).Should (BeEmpty ())
258259 })).Should (Succeed ())
259260 })
261+
262+ It ("creates prepareData from the base backup when annotation restore references a continuous backup" , func () {
263+ continuousActionSetName := "continuous-actionset-" + randomStr
264+ continuousActionSet := testapps .CreateCustomizedObj (& testCtx , "backup/actionset.yaml" ,
265+ & dpv1alpha1.ActionSet {}, testapps .WithName (continuousActionSetName ), func (actionSet * dpv1alpha1.ActionSet ) {
266+ actionSet .Spec .BackupType = dpv1alpha1 .BackupTypeContinuous
267+ })
268+ Expect (continuousActionSet .Name ).Should (Equal (continuousActionSetName ))
269+
270+ baseStatus := backup .Status
271+ baseStatus .BackupMethod .ActionSetName = fullBackupActionSetName
272+ baseStatus .TimeRange = & dpv1alpha1.BackupTimeRange {
273+ Start : baseStatus .StartTimestamp ,
274+ End : baseStatus .CompletionTimestamp ,
275+ }
276+ patchBackupStatus (baseStatus , client .ObjectKeyFromObject (backup ))
277+ Expect (testapps .ChangeObj (& testCtx , backup , func (baseBackup * dpv1alpha1.Backup ) {
278+ baseBackup .Labels [dptypes .BackupTypeLabelKey ] = string (dpv1alpha1 .BackupTypeFull )
279+ baseBackup .Labels [dptypes .BackupPolicyLabelKey ] = testdp .BackupPolicyName
280+ })).Should (Succeed ())
281+
282+ restoreTime := now .Add (time .Minute * 30 )
283+ continuousBackupName := "continuous-backup-" + randomStr
284+ continuousBackup := testdp .NewBackupFactory (testCtx .DefaultNamespace , continuousBackupName ).
285+ SetLabels (map [string ]string {
286+ constant .AppInstanceLabelKey : sourceCluster ,
287+ constant .KBAppComponentLabelKey : defaultCompName ,
288+ dptypes .BackupTypeLabelKey : string (dpv1alpha1 .BackupTypeContinuous ),
289+ dptypes .BackupPolicyLabelKey : testdp .BackupPolicyName ,
290+ }).
291+ SetBackupPolicyName (testdp .BackupPolicyName ).
292+ SetBackupMethod (testdp .ContinuousMethodName ).
293+ Create (& testCtx ).GetObject ()
294+ continuousStart := & metav1.Time {Time : now .Time }
295+ continuousStop := & metav1.Time {Time : now .Add (time .Hour )}
296+ continuousStatus := dpv1alpha1.BackupStatus {
297+ Phase : dpv1alpha1 .BackupPhaseCompleted ,
298+ StartTimestamp : continuousStart ,
299+ CompletionTimestamp : continuousStop ,
300+ TimeRange : & dpv1alpha1.BackupTimeRange {
301+ Start : continuousStart ,
302+ End : continuousStop ,
303+ },
304+ BackupMethod : & dpv1alpha1.BackupMethod {
305+ Name : testdp .ContinuousMethodName ,
306+ ActionSetName : continuousActionSetName ,
307+ },
308+ }
309+ patchBackupStatus (continuousStatus , client .ObjectKeyFromObject (continuousBackup ))
310+
311+ By ("restore from a continuous backup annotation" )
312+ restoreFromBackup := fmt .Sprintf (`{"%s": {"name":"%s", "restoreTime":"%s"}}` ,
313+ defaultCompName , continuousBackup .Name , restoreTime .UTC ().Format (time .RFC3339 ))
314+ Expect (testapps .ChangeObj (& testCtx , cluster , func (tmpCluster * appsv1.Cluster ) {
315+ tmpCluster .Annotations = map [string ]string {
316+ constant .RestoreFromBackupAnnotationKey : restoreFromBackup ,
317+ }
318+ })).Should (Succeed ())
319+
320+ restoreMGR := NewRestoreManager (ctx , k8sClient , cluster , scheme .Scheme , nil , 3 , 0 )
321+ err := restoreMGR .DoRestore (synthesizedComponent , compObj , true )
322+ Expect (intctrlutil .IsTargetError (err , intctrlutil .ErrorTypeNeedWaiting )).Should (BeTrue ())
323+
324+ By ("checking prepareData is created from the resolved base backup" )
325+ restoreMeta := restoreMGR .GetRestoreObjectMeta (synthesizedComponent , dpv1alpha1 .PrepareData , "" )
326+ Eventually (testapps .CheckObj (& testCtx , types.NamespacedName {Name : restoreMeta .Name , Namespace : restoreMeta .Namespace }, func (g Gomega , restore * dpv1alpha1.Restore ) {
327+ g .Expect (restore .Spec .Backup .Name ).Should (Equal (backup .Name ))
328+ })).Should (Succeed ())
329+
330+ By ("checking postReady still replays from the continuous backup" )
331+ Expect (testapps .GetAndChangeObjStatus (& testCtx , types.NamespacedName {Name : restoreMeta .Name , Namespace : restoreMeta .Namespace }, func (restore * dpv1alpha1.Restore ) {
332+ restore .Status .Phase = dpv1alpha1 .RestorePhaseCompleted
333+ })()).Should (Succeed ())
334+ Expect (testapps .ChangeObjStatus (& testCtx , cluster , func () {
335+ cluster .Status .Phase = appsv1 .RunningClusterPhase
336+ cluster .Status .Components = map [string ]appsv1.ClusterComponentStatus {
337+ defaultCompName : {
338+ Phase : appsv1 .RunningComponentPhase ,
339+ },
340+ }
341+ })).Should (Succeed ())
342+ Expect (testapps .ChangeObjStatus (& testCtx , compObj , func () {
343+ compObj .Status .Phase = appsv1 .RunningComponentPhase
344+ })).Should (Succeed ())
345+
346+ _ = restoreMGR .DoRestore (synthesizedComponent , compObj , true )
347+ restoreMeta = restoreMGR .GetRestoreObjectMeta (synthesizedComponent , dpv1alpha1 .PostReady , "" )
348+ Eventually (testapps .CheckObj (& testCtx , types.NamespacedName {Name : restoreMeta .Name , Namespace : restoreMeta .Namespace }, func (g Gomega , restore * dpv1alpha1.Restore ) {
349+ g .Expect (restore .Spec .Backup .Name ).Should (Equal (continuousBackup .Name ))
350+ })).Should (Succeed ())
351+ })
260352 })
261353 Context ("Cluster InstanceTemplate ranges Restore" , func () {
262354 const (
0 commit comments