Skip to content

Commit d01e8ff

Browse files
committed
chore: add an actionset api to delay postReady job after cluster running
1 parent 0f0ee74 commit d01e8ff

File tree

4 files changed

+93
-3
lines changed

4 files changed

+93
-3
lines changed

controllers/dataprotection/restore_controller.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import (
4141
"sigs.k8s.io/controller-runtime/pkg/log"
4242
"sigs.k8s.io/controller-runtime/pkg/reconcile"
4343

44+
kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
4445
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
4546
"github.com/apecloud/kubeblocks/pkg/constant"
4647
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
@@ -468,6 +469,28 @@ func (r *RestoreReconciler) handleBackupActionSet(reqCtx intctrlutil.RequestCtx,
468469
return isCompleted, err
469470
}
470471

472+
checkClusterRunning := func() (pass bool, err error) {
473+
v, ok := backupSet.ActionSet.Annotations[constant.DoReadyRestoreAfterClusterRunningAnnotationKey]
474+
if !ok || v != "true" {
475+
return true, nil
476+
}
477+
cluster := &kbappsv1.Cluster{}
478+
clusterName, ok := restoreMgr.Restore.Labels[constant.AppInstanceLabelKey]
479+
if !ok {
480+
reqCtx.Log.V(2).Info("restore cr missing AppInstanceLabel")
481+
return true, nil
482+
}
483+
if err := r.Client.Get(reqCtx.Ctx, client.ObjectKey{Name: clusterName, Namespace: restoreMgr.Restore.Namespace}, cluster); err != nil {
484+
return false, err
485+
}
486+
if cluster.Status.Phase != kbappsv1.RunningClusterPhase {
487+
reqCtx.Recorder.Event(restoreMgr.Restore, corev1.EventTypeWarning, dprestore.ReasonWaitForClusterRunning, "wait for cluster entering running phase")
488+
return false, nil
489+
}
490+
return true, nil
491+
}
492+
493+
// 2. build jobs
471494
var jobs []*batchv1.Job
472495
switch stage {
473496
case dpv1alpha1.PrepareData:
@@ -478,7 +501,13 @@ func (r *RestoreReconciler) handleBackupActionSet(reqCtx intctrlutil.RequestCtx,
478501
}
479502
jobs, err = restoreMgr.BuildPrepareDataJobs(reqCtx, r.Client, backupSet, target, actionName)
480503
case dpv1alpha1.PostReady:
481-
// 2. build jobs for postReady action
504+
// check if need to delay job creation until cluster running
505+
var pass bool
506+
pass, err = checkClusterRunning()
507+
if err != nil || !pass {
508+
return false, err
509+
}
510+
482511
jobs, err = restoreMgr.BuildPostReadyActionJobs(reqCtx, r.Client, backupSet, target, step)
483512
}
484513
if err != nil {

controllers/dataprotection/restore_controller_test.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import (
3535
testclocks "k8s.io/utils/clock/testing"
3636
"sigs.k8s.io/controller-runtime/pkg/client"
3737

38+
kbappsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
3839
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
3940
"github.com/apecloud/kubeblocks/pkg/constant"
4041
dprestore "github.com/apecloud/kubeblocks/pkg/dataprotection/restore"
@@ -483,10 +484,10 @@ var _ = Describe("Restore Controller test", func() {
483484
})
484485

485486
Context("test postReady stage", func() {
486-
var _ *testdp.BackupClusterInfo
487+
var backupClusterInfo *testdp.BackupClusterInfo
487488
BeforeEach(func() {
488489
By("fake a new cluster")
489-
_ = testdp.NewFakeCluster(&testCtx)
490+
backupClusterInfo = testdp.NewFakeCluster(&testCtx)
490491
})
491492

492493
It("test post ready actions", func() {
@@ -580,6 +581,7 @@ var _ = Describe("Restore Controller test", func() {
580581
})).Should(Succeed())
581582

582583
})
584+
583585
It("test parameters env", func() {
584586
By("set schema and parameters in actionSet")
585587
testdp.MockActionSetWithSchema(&testCtx, actionSet)
@@ -602,6 +604,56 @@ var _ = Describe("Restore Controller test", func() {
602604
By("expect parameters env in restore jobs")
603605
checkJobParametersEnv(restore)
604606
})
607+
608+
It("respects DoReadyRestoreAfterClusterRunning annotation", func() {
609+
By("set annotation for actionset")
610+
Expect(testapps.ChangeObj(&testCtx, actionSet, func(set *dpv1alpha1.ActionSet) {
611+
set.Spec.Restore.PrepareData = nil
612+
if set.Annotations == nil {
613+
set.Annotations = make(map[string]string)
614+
}
615+
set.Annotations[constant.DoReadyRestoreAfterClusterRunningAnnotationKey] = "true"
616+
})).Should(Succeed())
617+
618+
By("create restore cr")
619+
matchLabels := map[string]string{
620+
constant.AppInstanceLabelKey: testdp.ClusterName,
621+
}
622+
restore := initResourcesAndWaitRestore(true, false, false, "", dpv1alpha1.RestorePhaseRunning,
623+
func(f *testdp.MockRestoreFactory) {
624+
f.
625+
SetConnectCredential(testdp.ClusterName).
626+
SetJobActionConfig(matchLabels).
627+
SetExecActionConfig(matchLabels).
628+
SetLabels(matchLabels)
629+
}, nil)
630+
631+
By("check event fired")
632+
Eventually(func() bool {
633+
eventList := &corev1.EventList{}
634+
err := k8sClient.List(ctx, eventList, client.InNamespace(backupClusterInfo.Cluster.Namespace))
635+
if err != nil {
636+
return false
637+
}
638+
for _, e := range eventList.Items {
639+
if e.Reason == dprestore.ReasonWaitForClusterRunning && e.InvolvedObject.Name == restore.Name {
640+
return true
641+
}
642+
}
643+
return false
644+
645+
}).Should(BeTrue())
646+
647+
By("mock cluster running")
648+
Expect(testapps.ChangeObjStatus(&testCtx, backupClusterInfo.Cluster, func() {
649+
backupClusterInfo.Cluster.Status.Phase = kbappsv1.RunningClusterPhase
650+
})).Should(Succeed())
651+
652+
By("check job created")
653+
Eventually(testapps.List(&testCtx, generics.JobSignature,
654+
client.MatchingLabels{dprestore.DataProtectionRestoreLabelKey: restore.Name},
655+
client.InNamespace(testCtx.DefaultNamespace))).Should(HaveLen(2))
656+
})
605657
})
606658

607659
Context("test cross namespace", func() {

pkg/constant/annotations.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ const (
5959
MultiClusterServicePlacementKey = "apps.kubeblocks.io/multi-cluster-service-placement"
6060
)
6161

62+
// annotations for data protection
63+
const (
64+
// DoReadyRestoreAfterClusterRunningAnnotationKey is an experimental api to delay postReady restore job after cluster is running
65+
// It should be set to "true" in actionset cr.
66+
// This api may later added to action spec and replace the old api which is in cluster restore annotaion (kubeblocks.io/restore-from-backup)
67+
DoReadyRestoreAfterClusterRunningAnnotationKey = "dataprotection.kubeblocks.io/do-ready-restore-after-cluster-running"
68+
)
69+
6270
func InheritedAnnotations() []string {
6371
return []string{
6472
RestoreFromBackupAnnotationKey,

pkg/dataprotection/restore/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const (
4141
ReasonProcessing = "Processing"
4242
ReasonFailed = "Failed"
4343
ReasonSucceed = "Succeed"
44+
ReasonWaitForClusterRunning = "WaitForClusterRunning"
4445
reasonCreateRestoreJob = "CreateRestoreJob"
4546
reasonCreateRestorePVC = "CreateRestorePVC"
4647
)

0 commit comments

Comments
 (0)