github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/dataprotection/backupschedule_controller_test.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package dataprotection 21 22 import ( 23 "fmt" 24 25 . "github.com/onsi/ginkgo/v2" 26 . "github.com/onsi/gomega" 27 28 batchv1 "k8s.io/api/batch/v1" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 31 dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1" 32 dpbackup "github.com/1aal/kubeblocks/pkg/dataprotection/backup" 33 "github.com/1aal/kubeblocks/pkg/dataprotection/utils/boolptr" 34 "github.com/1aal/kubeblocks/pkg/generics" 35 testapps "github.com/1aal/kubeblocks/pkg/testutil/apps" 36 testdp "github.com/1aal/kubeblocks/pkg/testutil/dataprotection" 37 ) 38 39 var _ = Describe("Backup Schedule Controller", func() { 40 cleanEnv := func() { 41 // must wait till resources deleted and no longer existed before the testcases start, 42 // otherwise if later it needs to create some new resource objects with the same name, 43 // in race conditions, it will find the existence of old objects, resulting failure to 44 // create the new objects. 45 By("clean resources") 46 // delete rest mocked objects 47 inNS := client.InNamespace(testCtx.DefaultNamespace) 48 ml := client.HasLabels{testCtx.TestObjLabelKey} 49 50 // namespaced 51 testapps.ClearResources(&testCtx, generics.ClusterSignature, inNS, ml) 52 testapps.ClearResources(&testCtx, generics.PodSignature, inNS, ml) 53 testapps.ClearResources(&testCtx, generics.SecretSignature, inNS, ml) 54 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupPolicySignature, true, inNS) 55 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupScheduleSignature, true, inNS) 56 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupSignature, true, inNS) 57 58 // wait all backup to be deleted, otherwise the controller maybe create 59 // job to delete the backup between the ClearResources function delete 60 // the job and get the job list, resulting the ClearResources panic. 61 Eventually(testapps.List(&testCtx, generics.BackupSignature, inNS)).Should(HaveLen(0)) 62 63 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.JobSignature, true, inNS) 64 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeClaimSignature, true, inNS) 65 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.CronJobSignature, true, inNS) 66 testapps.ClearResources(&testCtx, generics.SecretSignature, inNS, ml) 67 68 // non-namespaced 69 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ActionSetSignature, true, ml) 70 testapps.ClearResources(&testCtx, generics.StorageClassSignature, ml) 71 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeSignature, true, ml) 72 testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupRepoSignature, true, ml) 73 testapps.ClearResources(&testCtx, generics.StorageProviderSignature, ml) 74 } 75 76 BeforeEach(func() { 77 cleanEnv() 78 _ = testdp.NewFakeCluster(&testCtx) 79 }) 80 81 AfterEach(cleanEnv) 82 83 When("creating backup schedule with default settings", func() { 84 var ( 85 backupPolicy *dpv1alpha1.BackupPolicy 86 ) 87 88 getCronjobKey := func(backupSchedule *dpv1alpha1.BackupSchedule, 89 method string) client.ObjectKey { 90 return client.ObjectKey{ 91 Name: dpbackup.GenerateCRNameByBackupSchedule(backupSchedule, method), 92 Namespace: backupPolicy.Namespace, 93 } 94 } 95 96 BeforeEach(func() { 97 By("creating an actionSet") 98 actionSet := testdp.NewFakeActionSet(&testCtx) 99 100 By("creating storage provider") 101 _ = testdp.NewFakeStorageProvider(&testCtx, nil) 102 103 By("creating backup repo") 104 _, _ = testdp.NewFakeBackupRepo(&testCtx, nil) 105 106 By("By creating a backupPolicy from actionSet " + actionSet.Name) 107 backupPolicy = testdp.NewFakeBackupPolicy(&testCtx, nil) 108 }) 109 110 AfterEach(func() { 111 }) 112 113 Context("creates a backup schedule", func() { 114 var ( 115 backupSchedule *dpv1alpha1.BackupSchedule 116 backupScheduleKey client.ObjectKey 117 ) 118 BeforeEach(func() { 119 By("creating a backupSchedule") 120 backupSchedule = testdp.NewFakeBackupSchedule(&testCtx, nil) 121 backupScheduleKey = client.ObjectKeyFromObject(backupSchedule) 122 }) 123 124 It("should success", func() { 125 By("checking backupSchedule status, should be available") 126 Eventually(testapps.CheckObj(&testCtx, backupScheduleKey, func(g Gomega, fetched *dpv1alpha1.BackupSchedule) { 127 g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupSchedulePhaseAvailable)) 128 })).Should(Succeed()) 129 130 By("checking cronjob, should not exist because all schedule policies of methods are disabled") 131 Eventually(testapps.CheckObjExists(&testCtx, getCronjobKey(backupSchedule, testdp.BackupMethodName), 132 &batchv1.CronJob{}, false)).Should(Succeed()) 133 Eventually(testapps.CheckObjExists(&testCtx, getCronjobKey(backupSchedule, testdp.VSBackupMethodName), 134 &batchv1.CronJob{}, false)).Should(Succeed()) 135 136 By(fmt.Sprintf("enabling %s method schedule", testdp.BackupMethodName)) 137 testdp.EnableBackupSchedule(&testCtx, backupSchedule, testdp.BackupMethodName) 138 139 By("checking cronjob, should exist one cronjob to create backup") 140 Eventually(testapps.CheckObj(&testCtx, getCronjobKey(backupSchedule, testdp.BackupMethodName), func(g Gomega, fetched *batchv1.CronJob) { 141 schedulePolicy := dpbackup.GetSchedulePolicyByMethod(backupSchedule, testdp.BackupMethodName) 142 g.Expect(boolptr.IsSetToTrue(schedulePolicy.Enabled)).To(BeTrue()) 143 g.Expect(fetched.Spec.Schedule).To(Equal(schedulePolicy.CronExpression)) 144 g.Expect(fetched.Spec.StartingDeadlineSeconds).ShouldNot(BeNil()) 145 g.Expect(*fetched.Spec.StartingDeadlineSeconds).To(Equal(getStartingDeadlineSeconds(backupSchedule))) 146 })).Should(Succeed()) 147 }) 148 }) 149 150 Context("creates a backup schedule with empty schedule", func() { 151 It("should fail when create a backupSchedule without nil schedule policy", func() { 152 backupScheduleObj := testdp.NewBackupScheduleFactory(testCtx.DefaultNamespace, testdp.BackupScheduleName). 153 SetBackupPolicyName(testdp.BackupPolicyName). 154 SetSchedules(nil). 155 GetObject() 156 Expect(testCtx.CheckedCreateObj(testCtx.Ctx, backupScheduleObj)).Should(HaveOccurred()) 157 }) 158 159 It("should fail when create a backupSchedule without empty schedule policy", func() { 160 backupScheduleObj := testdp.NewBackupScheduleFactory(testCtx.DefaultNamespace, testdp.BackupScheduleName). 161 SetBackupPolicyName(testdp.BackupPolicyName). 162 GetObject() 163 Expect(testCtx.CheckedCreateObj(testCtx.Ctx, backupScheduleObj)).Should(HaveOccurred()) 164 }) 165 }) 166 167 Context("creates a backup schedule with invalid field", func() { 168 var ( 169 backupScheduleKey client.ObjectKey 170 backupSchedule *dpv1alpha1.BackupSchedule 171 ) 172 173 BeforeEach(func() { 174 By("creating a backupSchedule") 175 backupSchedule = testdp.NewFakeBackupSchedule(&testCtx, func(schedule *dpv1alpha1.BackupSchedule) { 176 schedule.Spec.Schedules[0].CronExpression = "invalid" 177 }) 178 backupScheduleKey = client.ObjectKeyFromObject(backupSchedule) 179 }) 180 181 It("should fail", func() { 182 Eventually(testapps.CheckObj(&testCtx, backupScheduleKey, func(g Gomega, fetched *dpv1alpha1.BackupSchedule) { 183 g.Expect(fetched.Status.Phase).NotTo(Equal(dpv1alpha1.BackupSchedulePhaseAvailable)) 184 })).Should(Succeed()) 185 }) 186 }) 187 }) 188 }) 189 190 func getStartingDeadlineSeconds(backupSchedule *dpv1alpha1.BackupSchedule) int64 { 191 if backupSchedule.Spec.StartingDeadlineMinutes == nil { 192 return 0 193 } 194 return *backupSchedule.Spec.StartingDeadlineMinutes * 60 195 }