github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/testutil/dataprotection/backup_utils.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 corev1 "k8s.io/api/core/v1" 30 "k8s.io/apimachinery/pkg/api/meta" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/types" 33 "sigs.k8s.io/controller-runtime/pkg/client" 34 35 dpv1alpha1 "github.com/1aal/kubeblocks/apis/dataprotection/v1alpha1" 36 storagev1alpha1 "github.com/1aal/kubeblocks/apis/storage/v1alpha1" 37 "github.com/1aal/kubeblocks/pkg/constant" 38 "github.com/1aal/kubeblocks/pkg/dataprotection/utils/boolptr" 39 "github.com/1aal/kubeblocks/pkg/testutil" 40 testapps "github.com/1aal/kubeblocks/pkg/testutil/apps" 41 viper "github.com/1aal/kubeblocks/pkg/viperx" 42 ) 43 44 func NewFakeActionSet(testCtx *testutil.TestContext) *dpv1alpha1.ActionSet { 45 as := testapps.CreateCustomizedObj(testCtx, "backup/actionset.yaml", 46 &dpv1alpha1.ActionSet{}, testapps.WithName(ActionSetName)) 47 Eventually(testapps.CheckObj(testCtx, client.ObjectKeyFromObject(as), 48 func(g Gomega, as *dpv1alpha1.ActionSet) { 49 g.Expect(as.Status.Phase).Should(BeEquivalentTo(dpv1alpha1.AvailablePhase)) 50 })).Should(Succeed()) 51 return as 52 } 53 54 func NewFakeBackupPolicy(testCtx *testutil.TestContext, 55 change func(backupPolicy *dpv1alpha1.BackupPolicy)) *dpv1alpha1.BackupPolicy { 56 bp := NewBackupPolicyFactory(testCtx.DefaultNamespace, BackupPolicyName). 57 SetBackupRepoName(BackupRepoName). 58 SetTarget(constant.AppInstanceLabelKey, ClusterName, 59 constant.KBAppComponentLabelKey, ComponentName, 60 constant.RoleLabelKey, constant.Leader). 61 SetPathPrefix(BackupPathPrefix). 62 SetTargetConnectionCredential(ClusterName). 63 AddBackupMethod(BackupMethodName, false, ActionSetName). 64 SetBackupMethodVolumeMounts(DataVolumeName, DataVolumeMountPath, 65 LogVolumeName, LogVolumeMountPath). 66 AddBackupMethod(VSBackupMethodName, true, ""). 67 SetBackupMethodVolumes([]string{DataVolumeName}). 68 Apply(change). 69 Create(testCtx).GetObject() 70 71 secret := &corev1.Secret{ 72 ObjectMeta: metav1.ObjectMeta{ 73 Name: ClusterName, 74 Namespace: testCtx.DefaultNamespace, 75 }, 76 StringData: map[string]string{ 77 "password": "test-passw0rd", 78 }, 79 } 80 Expect(testCtx.CreateObj(testCtx.Ctx, secret)).Should(Succeed()) 81 Eventually(testapps.CheckObj(testCtx, client.ObjectKeyFromObject(bp), 82 func(g Gomega, bp *dpv1alpha1.BackupPolicy) { 83 g.Expect(bp.Status.Phase).Should(BeEquivalentTo(dpv1alpha1.AvailablePhase)) 84 })).Should(Succeed()) 85 return bp 86 } 87 88 func NewFakeStorageProvider(testCtx *testutil.TestContext, 89 change func(sp *storagev1alpha1.StorageProvider)) *storagev1alpha1.StorageProvider { 90 sp := testapps.CreateCustomizedObj(testCtx, "backup/storageprovider.yaml", 91 &storagev1alpha1.StorageProvider{}, func(obj *storagev1alpha1.StorageProvider) { 92 obj.Name = StorageProviderName 93 if change != nil { 94 change(obj) 95 } 96 }) 97 // the storage provider controller is not running, so set the status manually 98 Expect(testapps.ChangeObjStatus(testCtx, sp, func() { 99 sp.Status.Phase = storagev1alpha1.StorageProviderReady 100 meta.SetStatusCondition(&sp.Status.Conditions, metav1.Condition{ 101 Type: storagev1alpha1.ConditionTypeCSIDriverInstalled, 102 Status: metav1.ConditionTrue, 103 Reason: "CSIDriverInstalled", 104 }) 105 })).Should(Succeed()) 106 return sp 107 } 108 109 func NewFakeBackupRepo(testCtx *testutil.TestContext, 110 change func(repo *dpv1alpha1.BackupRepo)) (*dpv1alpha1.BackupRepo, string) { 111 repo := testapps.CreateCustomizedObj(testCtx, "backup/backuprepo.yaml", 112 &dpv1alpha1.BackupRepo{}, func(obj *dpv1alpha1.BackupRepo) { 113 obj.Name = BackupRepoName 114 obj.Spec.StorageProviderRef = StorageProviderName 115 if change != nil { 116 change(obj) 117 } 118 }) 119 jobName := fmt.Sprintf("pre-check-%s-%s", repo.UID[:8], repo.Name) 120 namespace := viper.GetString(constant.CfgKeyCtrlrMgrNS) 121 Eventually(testapps.GetAndChangeObjStatus(testCtx, types.NamespacedName{Name: jobName, Namespace: namespace}, 122 func(job *batchv1.Job) { 123 job.Status.Conditions = append(job.Status.Conditions, batchv1.JobCondition{ 124 Type: batchv1.JobComplete, 125 Status: corev1.ConditionTrue, 126 }) 127 })).Should(Succeed()) 128 var name string 129 Eventually(testapps.CheckObj(testCtx, client.ObjectKeyFromObject(repo), 130 func(g Gomega, repo *dpv1alpha1.BackupRepo) { 131 g.Expect(repo.Status.Phase).Should(BeEquivalentTo(dpv1alpha1.BackupRepoReady)) 132 g.Expect(repo.Status.BackupPVCName).ShouldNot(BeEmpty()) 133 name = repo.Status.BackupPVCName 134 })).Should(Succeed()) 135 return repo, name 136 } 137 138 func NewFakeBackup(testCtx *testutil.TestContext, 139 change func(backup *dpv1alpha1.Backup)) *dpv1alpha1.Backup { 140 if change == nil { 141 change = func(*dpv1alpha1.Backup) {} // set nop 142 } 143 backup := NewBackupFactory(testCtx.DefaultNamespace, BackupName). 144 SetBackupPolicyName(BackupPolicyName). 145 SetBackupMethod(BackupMethodName). 146 Apply(change). 147 Create(testCtx).GetObject() 148 return backup 149 } 150 151 func NewFakeCluster(testCtx *testutil.TestContext) *BackupClusterInfo { 152 createPVCAndPV := func(name string) *corev1.PersistentVolumeClaim { 153 pvName := "pv-" + name 154 pvc := testapps.NewPersistentVolumeClaimFactory( 155 testCtx.DefaultNamespace, name, ClusterName, ComponentName, "data"). 156 SetVolumeName(pvName). 157 SetStorage("1Gi"). 158 SetStorageClass(StorageClassName). 159 Create(testCtx).GetObject() 160 161 testapps.NewPersistentVolumeFactory(testCtx.DefaultNamespace, pvName, name). 162 SetStorage("1Gi"). 163 SetClaimRef(pvc).SetCSIDriver(testutil.DefaultCSIDriver).Create(testCtx) 164 return pvc 165 } 166 167 podFactory := func(name string) *testapps.MockPodFactory { 168 return testapps.NewPodFactory(testCtx.DefaultNamespace, name). 169 AddAppInstanceLabel(ClusterName). 170 AddAppComponentLabel(ComponentName). 171 AddContainer(corev1.Container{Name: ContainerName, Image: testapps.ApeCloudMySQLImage}) 172 } 173 174 By("mocking a cluster") 175 cluster := testapps.NewClusterFactory(testCtx.DefaultNamespace, ClusterName, 176 "test-cd", "test-cv").Create(testCtx).GetObject() 177 podName := ClusterName + "-" + ComponentName 178 179 By("mocking a storage class") 180 _ = testapps.CreateStorageClass(testCtx, StorageClassName, true) 181 182 By("mocking a pvc belonging to the pod 0") 183 pvc := createPVCAndPV("data-" + podName + "-0") 184 185 By("mocking a pvc belonging to the pod 1") 186 pvc1 := createPVCAndPV("data-" + podName + "-1") 187 188 By("mocking pod 0 belonging to the statefulset") 189 volume := corev1.Volume{Name: DataVolumeName, VolumeSource: corev1.VolumeSource{ 190 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pvc.Name}}} 191 pod := podFactory(podName + "-0"). 192 AddRoleLabel("leader"). 193 AddVolume(volume). 194 Create(testCtx).GetObject() 195 196 By("mocking pod 1 belonging to the statefulset") 197 volume2 := corev1.Volume{Name: DataVolumeName, VolumeSource: corev1.VolumeSource{ 198 PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ClaimName: pvc1.Name}}} 199 _ = podFactory(podName + "-1"). 200 AddVolume(volume2). 201 Create(testCtx).GetObject() 202 203 return &BackupClusterInfo{ 204 Cluster: cluster, 205 TargetPod: pod, 206 TargetPVC: pvc.Name, 207 } 208 } 209 210 func NewFakeBackupSchedule(testCtx *testutil.TestContext, 211 change func(schedule *dpv1alpha1.BackupSchedule)) *dpv1alpha1.BackupSchedule { 212 schedule := NewBackupScheduleFactory(testCtx.DefaultNamespace, BackupScheduleName). 213 SetBackupPolicyName(BackupPolicyName). 214 SetStartingDeadlineMinutes(StartingDeadlineMinutes). 215 AddSchedulePolicy(dpv1alpha1.SchedulePolicy{ 216 Enabled: boolptr.False(), 217 BackupMethod: BackupMethodName, 218 CronExpression: BackupScheduleCron, 219 RetentionPeriod: BackupRetention, 220 }). 221 AddSchedulePolicy(dpv1alpha1.SchedulePolicy{ 222 Enabled: boolptr.False(), 223 BackupMethod: VSBackupMethodName, 224 CronExpression: BackupScheduleCron, 225 RetentionPeriod: BackupRetention, 226 }). 227 Apply(change). 228 Create(testCtx).GetObject() 229 return schedule 230 } 231 232 // EnableBackupSchedule enables the backup schedule that matches the given method. 233 func EnableBackupSchedule(testCtx *testutil.TestContext, 234 backupSchedule *dpv1alpha1.BackupSchedule, method string) { 235 Eventually(testapps.ChangeObj(testCtx, backupSchedule, func(schedule *dpv1alpha1.BackupSchedule) { 236 for i := range schedule.Spec.Schedules { 237 if schedule.Spec.Schedules[i].BackupMethod == method { 238 schedule.Spec.Schedules[i].Enabled = boolptr.True() 239 break 240 } 241 } 242 })).Should(Succeed()) 243 } 244 245 func MockBackupStatusMethod(backup *dpv1alpha1.Backup, backupMethodName, targetVolume, actionSetName string) { 246 var snapshot bool 247 if backupMethodName == VSBackupMethodName { 248 snapshot = true 249 } 250 backup.Status.BackupMethod = &dpv1alpha1.BackupMethod{ 251 Name: backupMethodName, 252 SnapshotVolumes: &snapshot, 253 ActionSetName: actionSetName, 254 TargetVolumes: &dpv1alpha1.TargetVolumeInfo{ 255 Volumes: []string{targetVolume}, 256 VolumeMounts: []corev1.VolumeMount{ 257 {Name: targetVolume, MountPath: "/"}, 258 }, 259 }, 260 } 261 }