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  }