Skip to content

Commit 7cc649b

Browse files
authored
fix: avoid eager backup delete worker setup (#10343)
1 parent ea7b8e3 commit 7cc649b

4 files changed

Lines changed: 116 additions & 10 deletions

File tree

controllers/dataprotection/backup_controller.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,15 +225,11 @@ func (r *BackupReconciler) deleteBackupFiles(reqCtx intctrlutil.RequestCtx, back
225225
RequestCtx: reqCtx,
226226
Client: r.Client,
227227
Scheme: r.Scheme,
228+
EnsureWorkerServiceAccount: func() (string, error) {
229+
return r.ensureWorkerServiceAccountForBackupDeletion(reqCtx, backup.Namespace)
230+
},
228231
}
229232

230-
// TODO: update the mcMgr param
231-
saName, err := EnsureWorkerServiceAccount(reqCtx, r.Client, backup.Namespace, nil)
232-
if err != nil {
233-
return fmt.Errorf("failed to get worker service account: %w", err)
234-
}
235-
deleter.WorkerServiceAccount = saName
236-
237233
status, err := deleter.DeleteBackupFiles(backup)
238234
switch status {
239235
case dpbackup.DeletionStatusSucceeded:
@@ -255,6 +251,18 @@ func (r *BackupReconciler) deleteBackupFiles(reqCtx intctrlutil.RequestCtx, back
255251
return err
256252
}
257253

254+
func (r *BackupReconciler) ensureWorkerServiceAccountForBackupDeletion(reqCtx intctrlutil.RequestCtx, namespace string) (string, error) {
255+
ns := &corev1.Namespace{}
256+
if err := r.Client.Get(reqCtx.Ctx, types.NamespacedName{Name: namespace}, ns); err != nil {
257+
return "", fmt.Errorf("failed to get backup namespace %q before deleting backup files: %w", namespace, err)
258+
}
259+
if !ns.DeletionTimestamp.IsZero() {
260+
return "", fmt.Errorf("backup namespace %q is terminating; cannot create worker resources to delete backup files, delete the Backup and wait until it is gone before deleting the namespace", namespace)
261+
}
262+
// TODO: update the mcMgr param
263+
return EnsureWorkerServiceAccount(reqCtx, r.Client, namespace, nil)
264+
}
265+
258266
// handleDeletingPhase handles the deletion of backup. It will delete the backup CR
259267
// and the backup workload(job).
260268
func (r *BackupReconciler) handleDeletingPhase(reqCtx intctrlutil.RequestCtx, backup *dpv1alpha1.Backup) (ctrl.Result, error) {

controllers/dataprotection/backup_controller_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,11 @@ import (
3232
appsv1 "k8s.io/api/apps/v1"
3333
batchv1 "k8s.io/api/batch/v1"
3434
corev1 "k8s.io/api/core/v1"
35+
apierrors "k8s.io/apimachinery/pkg/api/errors"
3536
"k8s.io/apimachinery/pkg/api/resource"
3637
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3738
"k8s.io/apimachinery/pkg/types"
39+
"k8s.io/client-go/kubernetes"
3840
"k8s.io/utils/pointer"
3941
ctrl "sigs.k8s.io/controller-runtime"
4042
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -789,6 +791,48 @@ var _ = Describe("Backup Controller test", func() {
789791

790792
// TODO: add delete backup test case with the pvc not exists
791793
})
794+
795+
It("should not create worker resources when backup namespace is terminating", func() {
796+
nsName := "backup-delete-terminating"
797+
ns := &corev1.Namespace{
798+
ObjectMeta: metav1.ObjectMeta{
799+
Name: nsName,
800+
},
801+
}
802+
Expect(client.IgnoreAlreadyExists(testCtx.Cli.Create(testCtx.Ctx, ns))).Should(Succeed())
803+
804+
finalizeNamespace := func() {
805+
Eventually(func(g Gomega) {
806+
current := &corev1.Namespace{}
807+
err := testCtx.Cli.Get(testCtx.Ctx, types.NamespacedName{Name: nsName}, current)
808+
if apierrors.IsNotFound(err) {
809+
return
810+
}
811+
g.Expect(err).ShouldNot(HaveOccurred())
812+
current.Spec.Finalizers = []corev1.FinalizerName{}
813+
clientGo, err := kubernetes.NewForConfig(testEnv.Config)
814+
g.Expect(err).ShouldNot(HaveOccurred())
815+
_, err = clientGo.CoreV1().Namespaces().Finalize(testCtx.Ctx, current, metav1.UpdateOptions{})
816+
g.Expect(err).ShouldNot(HaveOccurred())
817+
}).Should(Succeed())
818+
}
819+
DeferCleanup(finalizeNamespace)
820+
821+
By("marking the namespace terminating")
822+
Expect(testCtx.Cli.Delete(testCtx.Ctx, ns)).Should(Succeed())
823+
Eventually(func(g Gomega) {
824+
current := &corev1.Namespace{}
825+
g.Expect(testCtx.Cli.Get(testCtx.Ctx, types.NamespacedName{Name: nsName}, current)).Should(Succeed())
826+
g.Expect(current.DeletionTimestamp.IsZero()).Should(BeFalse())
827+
}).Should(Succeed())
828+
829+
reconciler := &BackupReconciler{Client: testCtx.Cli}
830+
_, err := reconciler.ensureWorkerServiceAccountForBackupDeletion(
831+
intctrlutil.RequestCtx{Ctx: testCtx.Ctx},
832+
nsName)
833+
Expect(err).Should(HaveOccurred())
834+
Expect(err.Error()).Should(ContainSubstring("is terminating; cannot create worker resources to delete backup files"))
835+
})
792836
})
793837

794838
Context("creates a snapshot backup", func() {

pkg/dataprotection/backup/deleter.go

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,30 @@ const (
5959

6060
type Deleter struct {
6161
ctrlutil.RequestCtx
62-
Client client.Client
63-
Scheme *runtime.Scheme
64-
WorkerServiceAccount string
62+
Client client.Client
63+
Scheme *runtime.Scheme
64+
WorkerServiceAccount string
65+
EnsureWorkerServiceAccount func() (string, error)
6566

6667
actionSet *dpv1alpha1.ActionSet
6768
}
6869

70+
func (d *Deleter) ensureWorkerServiceAccount() error {
71+
if d.WorkerServiceAccount != "" {
72+
return nil
73+
}
74+
if d.EnsureWorkerServiceAccount == nil {
75+
d.WorkerServiceAccount = viper.GetString(dptypes.CfgKeyWorkerServiceAccountName)
76+
return nil
77+
}
78+
saName, err := d.EnsureWorkerServiceAccount()
79+
if err != nil {
80+
return err
81+
}
82+
d.WorkerServiceAccount = saName
83+
return nil
84+
}
85+
6986
// DeleteBackupFiles builds a job to delete backup files, and returns the deletion status.
7087
// If the deletion job exists, it will check the job status and return the corresponding
7188
// deletion status.
@@ -246,6 +263,9 @@ func (d *Deleter) createDeleteJob(container corev1.Container,
246263
backup *dpv1alpha1.Backup,
247264
backupRepo *dpv1alpha1.BackupRepo,
248265
legacyPVCName string) error {
266+
if err := d.ensureWorkerServiceAccount(); err != nil {
267+
return err
268+
}
249269
ctrlutil.InjectZeroResourcesLimitsForDataProtection(&container)
250270

251271
// build pod

pkg/dataprotection/backup/deleter_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
. "github.com/onsi/gomega"
2525

2626
batchv1 "k8s.io/api/batch/v1"
27+
"k8s.io/utils/pointer"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
2829

2930
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
@@ -60,6 +61,7 @@ var _ = Describe("Backup Deleter Test", func() {
6061
inNS := client.InNamespace(testCtx.DefaultNamespace)
6162
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupSignature, true, inNS)
6263
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.JobSignature, true, inNS)
64+
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeClaimSignature, true, inNS)
6365
testapps.ClearResources(&testCtx, generics.VolumeSnapshotSignature, inNS)
6466
}
6567

@@ -107,6 +109,38 @@ var _ = Describe("Backup Deleter Test", func() {
107109
Expect(status).Should(Equal(DeletionStatusSucceeded))
108110
})
109111

112+
It("should ensure worker service account only when creating a deletion job", func() {
113+
ensureWorkerServiceAccountCalls := 0
114+
deleter.EnsureWorkerServiceAccount = func() (string, error) {
115+
ensureWorkerServiceAccountCalls++
116+
return "worker-sa", nil
117+
}
118+
119+
By("skipping worker service account creation for snapshot backups")
120+
backup.Status.BackupMethod = &dpv1alpha1.BackupMethod{
121+
SnapshotVolumes: pointer.Bool(true),
122+
}
123+
status, err := deleter.DeleteBackupFiles(backup)
124+
Expect(err).ShouldNot(HaveOccurred())
125+
Expect(status).Should(Equal(DeletionStatusSucceeded))
126+
Expect(ensureWorkerServiceAccountCalls).Should(Equal(0))
127+
128+
By("creating worker service account when a deletion job is needed")
129+
backup.Status.BackupMethod = nil
130+
backupRepoPVC := testdp.NewFakePVC(&testCtx, backupRepoPVCName)
131+
backup.Status.PersistentVolumeClaimName = backupRepoPVC.Name
132+
backup.Status.Path = backupPath
133+
status, err = deleter.DeleteBackupFiles(backup)
134+
Expect(err).ShouldNot(HaveOccurred())
135+
Expect(status).Should(Equal(DeletionStatusDeleting))
136+
Expect(ensureWorkerServiceAccountCalls).Should(Equal(1))
137+
138+
key := BuildDeleteBackupFilesJobKey(backup, false)
139+
Eventually(testapps.CheckObj(&testCtx, key, func(g Gomega, fetched *batchv1.Job) {
140+
g.Expect(fetched.Spec.Template.Spec.ServiceAccountName).Should(Equal("worker-sa"))
141+
})).Should(Succeed())
142+
})
143+
110144
It("should create job to delete backup file", func() {
111145
By("mock backup repo PVC")
112146
backupRepoPVC := testdp.NewFakePVC(&testCtx, backupRepoPVCName)

0 commit comments

Comments
 (0)