Skip to content

Commit

Permalink
feat: add test for dataprotection (#5597)
Browse files Browse the repository at this point in the history
  • Loading branch information
fengluodb authored Oct 24, 2023
1 parent ec41b69 commit 0bc5411
Show file tree
Hide file tree
Showing 5 changed files with 379 additions and 1 deletion.
125 changes: 124 additions & 1 deletion controllers/dataprotection/backup_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

"time"

vsv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -194,11 +196,58 @@ var _ = Describe("Backup Controller test", func() {
})
backupKey := client.ObjectKeyFromObject(backup)

By("check backup failed and its expiration when retentionPeriod is not set")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
g.Expect(fetched.Status.Expiration).Should(BeNil())
})).Should(Succeed())
})
})

Context("creates a backup with retentionPeriod", func() {
It("create an valid backup", func() {
By("creating a backup from backupPolicy " + testdp.BackupPolicyName)
backup := testdp.NewFakeBackup(&testCtx, func(backup *dpv1alpha1.Backup) {
backup.Spec.RetentionPeriod = "1h"
})
backupKey := client.ObjectKeyFromObject(backup)

getJobKey := func() client.ObjectKey {
return client.ObjectKey{
Name: dpbackup.GenerateBackupJobName(backup, dpbackup.BackupDataJobNamePrefix),
Namespace: backup.Namespace,
}
}

By("check backup expiration is set by start time when backup is running")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).Should(Equal(dpv1alpha1.BackupPhaseRunning))
g.Expect(fetched.Status.Expiration.Second()).Should(Equal(fetched.Status.StartTimestamp.Add(time.Hour).Second()))
})).Should(Succeed())

testdp.PatchK8sJobStatus(&testCtx, getJobKey(), batchv1.JobComplete)

By("check backup expiration is update by completion time when backup is completed")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseCompleted))
g.Expect(fetched.Status.CompletionTimestamp).ShouldNot(BeNil())
g.Expect(fetched.Status.Expiration.Second()).Should(Equal(fetched.Status.CompletionTimestamp.Add(time.Hour).Second()))
})).Should(Succeed())
})

It("create an invalid backup", func() {
By("creating a backup using a not found backupPolicy")
backup := testdp.NewFakeBackup(&testCtx, func(backup *dpv1alpha1.Backup) {
backup.Spec.BackupPolicyName = "not-found"
backup.Spec.RetentionPeriod = "1h"
})
backupKey := client.ObjectKeyFromObject(backup)

By("check backup failed and its expiration is set")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
g.Expect(fetched.Status.Expiration).ShouldNot(BeNil())
}))
})).Should(Succeed())
})
})

Expand Down Expand Up @@ -361,6 +410,10 @@ var _ = Describe("Backup Controller test", func() {
})

When("with exceptional settings", func() {
var (
backupPolicy *dpv1alpha1.BackupPolicy
)

Context("creates a backup with non-existent backup policy", func() {
var backupKey types.NamespacedName
BeforeEach(func() {
Expand All @@ -375,6 +428,76 @@ var _ = Describe("Backup Controller test", func() {
})).Should(Succeed())
})
})

Context("creates a backup using non-existent backup method", func() {
BeforeEach(func() {
By("creating a backupPolicy without backup method")
backupPolicy = testdp.NewFakeBackupPolicy(&testCtx, nil)
})

It("should fail because of no-existent backup method", func() {
backup := testdp.NewFakeBackup(&testCtx, func(backup *dpv1alpha1.Backup) {
backup.Spec.BackupPolicyName = backupPolicy.Name
backup.Spec.BackupMethod = "non-existent"
})
backupKey := client.ObjectKeyFromObject(backup)

By("check backup status failed")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
})).Should(Succeed())
})
})

Context("creates a backup with invalid backup method", func() {
BeforeEach(func() {
backupPolicy = testdp.NewFakeBackupPolicy(&testCtx, func(backupPolicy *dpv1alpha1.BackupPolicy) {
backupPolicy.Spec.BackupMethods = append(backupPolicy.Spec.BackupMethods, dpv1alpha1.BackupMethod{
Name: "invalid",
ActionSetName: "",
})
})
})

It("should fail because backup method doesn't specify snapshotVolumes with empty actionSet", func() {
backup := testdp.NewFakeBackup(&testCtx, func(backup *dpv1alpha1.Backup) {
backup.Spec.BackupPolicyName = backupPolicy.Name
backup.Spec.BackupMethod = "invalid"
})
backupKey := client.ObjectKeyFromObject(backup)

By("check backup status failed")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
})).Should(Succeed())
})

It("should fail because of no-existing actionSet", func() {
backup := testdp.NewFakeBackup(&testCtx, nil)
backupKey := client.ObjectKeyFromObject(backup)

By("check backup status failed")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
})).Should(Succeed())
})

It("should fail because actionSet's backup type isn't Full", func() {
actionSet := testdp.NewFakeActionSet(&testCtx)
actionSetKey := client.ObjectKeyFromObject(actionSet)
Eventually(testapps.GetAndChangeObj(&testCtx, actionSetKey, func(fetched *dpv1alpha1.ActionSet) {
fetched.Spec.BackupType = dpv1alpha1.BackupTypeIncremental
}))

backup := testdp.NewFakeBackup(&testCtx, nil)
backupKey := client.ObjectKeyFromObject(backup)

By("check backup status failed")
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
g.Expect(fetched.Status.Phase).To(Equal(dpv1alpha1.BackupPhaseFailed))
})).Should(Succeed())
})
})
})

When("with backup repo", func() {
Expand Down
31 changes: 31 additions & 0 deletions pkg/dataprotection/backup/deleter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,37 @@ var _ = Describe("Backup Deleter Test", func() {
job := &batchv1.Job{}
key := BuildDeleteBackupFilesJobKey(backup)
Eventually(testapps.CheckObjExists(&testCtx, key, job, true)).Should(Succeed())

By("delete backup with job running")
backupKey := client.ObjectKeyFromObject(backup)
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
status, err := deleter.DeleteBackupFiles(fetched)
Expect(err).ShouldNot(HaveOccurred())
Expect(status).Should(Equal(DeletionStatusDeleting))
})).Should(Succeed())

By("delete backup with job succeed")
testdp.ReplaceK8sJobStatus(&testCtx, key, batchv1.JobComplete)
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
status, err := deleter.DeleteBackupFiles(fetched)
Expect(err).ShouldNot(HaveOccurred())
Expect(status).Should(Equal(DeletionStatusSucceeded))
})).Should(Succeed())

By("delete backup with job failed")
testdp.ReplaceK8sJobStatus(&testCtx, key, batchv1.JobFailed)
Eventually(testapps.CheckObj(&testCtx, backupKey, func(g Gomega, fetched *dpv1alpha1.Backup) {
status, err := deleter.DeleteBackupFiles(fetched)
Expect(err).Should(HaveOccurred())
Expect(status).Should(Equal(DeletionStatusFailed))
})).Should(Succeed())
})

It("delete backup with backup repo", func() {
backup.Status.BackupRepoName = testdp.BackupRepoName
status, err := deleter.DeleteBackupFiles(backup)
Expect(err).ShouldNot(HaveOccurred())
Expect(status).Should(Equal(DeletionStatusSucceeded))
})
})

Expand Down
127 changes: 127 additions & 0 deletions pkg/dataprotection/backup/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,130 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package backup

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

corev1 "k8s.io/api/core/v1"
"sigs.k8s.io/controller-runtime/pkg/client"

dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/constant"
ctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
"github.com/apecloud/kubeblocks/pkg/dataprotection/utils/boolptr"
"github.com/apecloud/kubeblocks/pkg/generics"
testapps "github.com/apecloud/kubeblocks/pkg/testutil/apps"
testdp "github.com/apecloud/kubeblocks/pkg/testutil/dataprotection"
)

var _ = Describe("Request Test", func() {
buildRequest := func() *Request {
return &Request{
RequestCtx: ctrlutil.RequestCtx{
Log: logger,
Ctx: testCtx.Ctx,
Recorder: recorder,
},
Client: testCtx.Cli,
}
}

cleanEnv := func() {
// must wait till resources deleted and no longer existed before the testcases start,
// otherwise if later it needs to create some new resource objects with the same name,
// in race conditions, it will find the existence of old objects, resulting failure to
// create the new objects.
By("clean resources")

// delete rest mocked objects
inNS := client.InNamespace(testCtx.DefaultNamespace)
ml := client.HasLabels{testCtx.TestObjLabelKey}

// namespaced
testapps.ClearResources(&testCtx, generics.ClusterSignature, inNS, ml)
testapps.ClearResources(&testCtx, generics.PodSignature, inNS, ml)
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupSignature, true, inNS)

// wait all backup to be deleted, otherwise the controller maybe create
// job to delete the backup between the ClearResources function delete
// the job and get the job list, resulting the ClearResources panic.
Eventually(testapps.List(&testCtx, generics.BackupSignature, inNS)).Should(HaveLen(0))

testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupPolicySignature, true, inNS)
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.JobSignature, true, inNS)
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.PersistentVolumeClaimSignature, true, inNS)

// non-namespaced
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.ActionSetSignature, true, ml)
testapps.ClearResources(&testCtx, generics.StorageClassSignature, ml)
testapps.ClearResourcesWithRemoveFinalizerOption(&testCtx, generics.BackupRepoSignature, true, ml)
testapps.ClearResources(&testCtx, generics.StorageProviderSignature, ml)
testapps.ClearResources(&testCtx, generics.VolumeSnapshotClassSignature, ml)
}

var clusterInfo *testdp.BackupClusterInfo

BeforeEach(func() {
cleanEnv()
clusterInfo = testdp.NewFakeCluster(&testCtx)
})

AfterEach(func() {
cleanEnv()
})

When("with default settings", func() {
var (
backup *dpv1alpha1.Backup
actionSet *dpv1alpha1.ActionSet
backupPolicy *dpv1alpha1.BackupPolicy
backupRepo *dpv1alpha1.BackupRepo

targetPod *corev1.Pod
request *Request
)

BeforeEach(func() {
actionSet = testapps.CreateCustomizedObj(&testCtx, "backup/actionset.yaml",
&dpv1alpha1.ActionSet{}, testapps.WithName(testdp.ActionSetName))
backup = testdp.NewFakeBackup(&testCtx, nil)
backupRepo = testapps.CreateCustomizedObj(&testCtx, "backup/backuprepo.yaml", &dpv1alpha1.BackupRepo{}, nil)
backupPolicy = testdp.NewBackupPolicyFactory(testCtx.DefaultNamespace, testdp.BackupPolicyName).
SetBackupRepoName(testdp.BackupRepoName).
SetPathPrefix(testdp.BackupPathPrefix).
SetTarget(constant.AppInstanceLabelKey, testdp.ClusterName,
constant.KBAppComponentLabelKey, testdp.ComponentName,
constant.RoleLabelKey, constant.Leader).
AddBackupMethod(testdp.BackupMethodName, false, testdp.ActionSetName).
Create(&testCtx).GetObject()

targetPod = clusterInfo.TargetPod
request = buildRequest()
})

Context("build action", func() {
It("should build action", func() {
request.Backup = backup
request.ActionSet = actionSet
request.TargetPods = []*corev1.Pod{targetPod}
request.BackupPolicy = backupPolicy
request.BackupMethod = &backupPolicy.Spec.BackupMethods[0]
request.BackupRepo = backupRepo
_, err := request.BuildActions()
Expect(err).NotTo(HaveOccurred())
})

It("build create volume snapshot action", func() {
request.TargetPods = []*corev1.Pod{targetPod}
request.BackupMethod = &dpv1alpha1.BackupMethod{
Name: testdp.VSBackupMethodName,
SnapshotVolumes: boolptr.True(),
}
_, err := request.buildCreateVolumeSnapshotAction()
Expect(err).Should(HaveOccurred())
})

})
})
})
Loading

0 comments on commit 0bc5411

Please sign in to comment.