Skip to content

Commit c09bbc9

Browse files
committed
implement e2e test for lock feature
Signed-off-by: Yuji Ito <[email protected]>
1 parent ed3124f commit c09bbc9

File tree

5 files changed

+192
-9
lines changed

5 files changed

+192
-9
lines changed

test/e2e/e2e_suite_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ var _ = Describe("Fin", func() {
7474
Context("wait environment", waitEnvironment)
7575
Context("full backup", Label("full-backup"), Ordered, fullBackupTestSuite)
7676
Context("incremental backup", Label("incremental-backup"), Ordered, incrementalBackupTestSuite)
77+
Context("lock", Label("lock"), Label("misc"), Ordered, lockTestSuite)
7778
Context("verification", Label("verification"), Label("misc"), Ordered, verificationTestSuite)
7879
Context("delete incremental backup", Label("delete-incremental-backup"), Label("misc"), Ordered,
7980
deleteIncrementalBackupTestSuite)

test/e2e/full_backup_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ func fullBackupTestSuite() {
195195

196196
VerifyNonExistenceOfRawImage(pvc, nodes[0])
197197
VerifyDeletionOfJobsForBackup(ctx, k8sClient, finbackup)
198-
VerifyDeletionOfSnapshotInFinBackup(ctx, ctrlClient, finbackup)
198+
err = VerifyDeletionOfSnapshotInFinBackup(ctx, finbackup)
199+
Expect(err).NotTo(HaveOccurred())
199200
})
200201

201202
// CSATEST-1547
@@ -325,7 +326,8 @@ func fullBackupTestSuite() {
325326
err = WaitForFinBackupDeletion(ctx, ctrlClient, fb, 2*time.Minute)
326327
Expect(err).NotTo(HaveOccurred())
327328
VerifyDeletionOfJobsForBackup(ctx, k8sClient, fb)
328-
VerifyDeletionOfSnapshotInFinBackup(ctx, ctrlClient, fb)
329+
err = VerifyDeletionOfSnapshotInFinBackup(ctx, fb)
330+
Expect(err).NotTo(HaveOccurred())
329331
}
330332
VerifyNonExistenceOfRawImage(pvc, nodes[0])
331333
VerifyNonExistenceOfRawImage(pvc, nodes[1])

test/e2e/incremental_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,8 @@ func incrementalBackupTestSuite() {
225225
Expect(rawImageData).To(Equal(dataOnIncrementalBackup), "Data in raw.img does not match the expected data")
226226

227227
VerifyDeletionOfJobsForBackup(ctx, k8sClient, finbackup1)
228-
VerifyDeletionOfSnapshotInFinBackup(ctx, ctrlClient, finbackup1)
228+
err = VerifyDeletionOfSnapshotInFinBackup(ctx, finbackup1)
229+
Expect(err).NotTo(HaveOccurred())
229230
})
230231

231232
// Description:

test/e2e/lock_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package e2e
2+
3+
import (
4+
"encoding/json"
5+
"time"
6+
7+
finv1 "github.com/cybozu-go/fin/api/v1"
8+
"github.com/cybozu-go/fin/internal/model"
9+
"github.com/cybozu-go/fin/test/utils"
10+
. "github.com/onsi/ginkgo/v2"
11+
. "github.com/onsi/gomega"
12+
corev1 "k8s.io/api/core/v1"
13+
)
14+
15+
func lockTestSuite() {
16+
var ns *corev1.Namespace
17+
var pvc *corev1.PersistentVolumeClaim
18+
var fb *finv1.FinBackup
19+
dummyLockID := "dummy-lock-id"
20+
var poolName, imageName string
21+
22+
It("should setup environment", func(ctx SpecContext) {
23+
ns = NewNamespace(utils.GetUniqueName("ns-"))
24+
err := CreateNamespace(ctx, k8sClient, ns)
25+
Expect(err).NotTo(HaveOccurred())
26+
27+
pvc = CreateBackupTargetPVC(ctx, k8sClient, ns, "Filesystem", rookStorageClass, "ReadWriteOnce", "100Mi")
28+
})
29+
30+
It("should lock the volume", func(ctx SpecContext) {
31+
pv, err := GetPvByPvc(ctx, k8sClient, pvc)
32+
Expect(err).NotTo(HaveOccurred())
33+
poolName = pv.Spec.CSI.VolumeAttributes["pool"]
34+
imageName = pv.Spec.CSI.VolumeAttributes["imageName"]
35+
36+
// locked
37+
_, _, err = kubectl("exec", "-n", rookNamespace, "deployment/"+finDeploymentName, "--",
38+
"rbd", "-p", poolName, "lock", "add", imageName, dummyLockID)
39+
Expect(err).NotTo(HaveOccurred())
40+
})
41+
42+
It("should create backup and wait for the log", func(ctx SpecContext) {
43+
fb, err := NewFinBackup(rookNamespace, utils.GetUniqueName("fb-"), pvc, nodes[0])
44+
Expect(err).NotTo(HaveOccurred())
45+
err = CreateFinBackup(ctx, ctrlClient, fb)
46+
Expect(err).NotTo(HaveOccurred())
47+
48+
err = WaitControllerLog(ctx,
49+
"the volume is locked by another process.*"+string(fb.GetUID()),
50+
3*time.Minute)
51+
Expect(err).NotTo(HaveOccurred())
52+
})
53+
54+
It("checks that the snapshot is not created", func(ctx SpecContext) {
55+
snapshots, err := ListRBDSnapshots(ctx, poolName, imageName)
56+
Expect(err).NotTo(HaveOccurred())
57+
Expect(snapshots).To(BeEmpty())
58+
})
59+
60+
It("should unlock the volume", func() {
61+
stdout, _, err := kubectl("exec", "-n", rookNamespace, "deployment/"+finDeploymentName, "--",
62+
"rbd", "-p", poolName, "--format", "json", "lock", "ls", imageName)
63+
Expect(err).NotTo(HaveOccurred())
64+
var locks []*model.RBDLock
65+
err = json.Unmarshal(stdout, &locks)
66+
Expect(err).NotTo(HaveOccurred())
67+
Expect(locks).To(HaveLen(1))
68+
69+
// unlock
70+
_, _, err = kubectl("exec", "-n", rookNamespace, "deployment/"+finDeploymentName, "--",
71+
"rbd", "-p", poolName, "lock", "rm", imageName, dummyLockID, locks[0].Locker)
72+
Expect(err).NotTo(HaveOccurred())
73+
})
74+
75+
It("should resume backup creation and complete it", func(ctx SpecContext) {
76+
err := WaitForFinBackupStoredToNodeAndVerified(ctx, ctrlClient, fb, 1*time.Minute)
77+
Expect(err).NotTo(HaveOccurred())
78+
})
79+
80+
It("should not exist locks after backup completion", func() {
81+
stdout, _, err := kubectl("exec", "-n", rookNamespace, "deployment/"+finDeploymentName, "--",
82+
"rbd", "-p", poolName, "--format", "json", "lock", "ls", imageName)
83+
Expect(err).NotTo(HaveOccurred())
84+
var locks []*model.RBDLock
85+
err = json.Unmarshal(stdout, &locks)
86+
Expect(err).NotTo(HaveOccurred())
87+
Expect(locks).To(HaveLen(0))
88+
})
89+
}

test/e2e/util_test.go

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
package e2e
22

33
import (
4+
"bufio"
45
"bytes"
56
"context"
7+
"encoding/json"
68
"fmt"
79
"html/template"
810
"os"
911
"os/exec"
1012
"path/filepath"
13+
"regexp"
1114
"time"
1215

1316
finv1 "github.com/cybozu-go/fin/api/v1"
17+
"github.com/cybozu-go/fin/internal/model"
1418
"github.com/cybozu-go/fin/test/utils"
1519
. "github.com/onsi/ginkgo/v2"
1620
. "github.com/onsi/gomega"
@@ -26,6 +30,7 @@ import (
2630
)
2731

2832
const (
33+
finDeploymentName = "fin-controller-manager"
2934
rookNamespace = "rook-ceph"
3035
rookStorageClass = "rook-ceph-block"
3136
poolName = "rook-ceph-block-pool"
@@ -80,7 +85,7 @@ func checkDeploymentReady(namespace, name string) error {
8085
func waitEnvironment() {
8186
It("wait for fin-controller to be ready", func() {
8287
Eventually(func() error {
83-
return checkDeploymentReady(rookNamespace, "fin-controller-manager")
88+
return checkDeploymentReady(rookNamespace, finDeploymentName)
8489
}).Should(Succeed())
8590
})
8691
}
@@ -455,6 +460,58 @@ func WaitForPVCDeletion(ctx context.Context, k8sClient kubernetes.Interface, pvc
455460
})
456461
}
457462

463+
// WaitControllerLog waits until the controller log matches the given pattern or the duration is exceeded.
464+
func WaitControllerLog(ctx SpecContext, pattern string, duration time.Duration) error {
465+
GinkgoHelper()
466+
467+
timeoutCtx, cancel := context.WithTimeout(ctx, duration)
468+
defer cancel()
469+
470+
matcher := regexp.MustCompile(pattern)
471+
472+
command := exec.CommandContext(timeoutCtx, "kubectl", "logs", "-n", rookNamespace, "deployment/"+finDeploymentName, "-f")
473+
stdoutPipe, err := command.StdoutPipe()
474+
if err != nil {
475+
panic(err)
476+
}
477+
err = command.Start()
478+
if err != nil {
479+
panic(err)
480+
}
481+
defer func() {
482+
_ = command.Process.Kill()
483+
_ = command.Wait()
484+
}()
485+
486+
// read stdout line by line until the pattern is found
487+
scanner := bufio.NewScanner(stdoutPipe)
488+
found := make(chan struct{})
489+
go func() {
490+
for scanner.Scan() {
491+
select {
492+
case <-timeoutCtx.Done():
493+
return
494+
default:
495+
}
496+
line := scanner.Text()
497+
if matcher.MatchString(line) {
498+
close(found)
499+
return
500+
}
501+
}
502+
if scanner.Err() != nil {
503+
panic(scanner.Err())
504+
}
505+
}()
506+
507+
select {
508+
case <-timeoutCtx.Done():
509+
return timeoutCtx.Err()
510+
case <-found:
511+
return nil
512+
}
513+
}
514+
458515
func VerifySizeOfRestorePVC(ctx context.Context, c client.Client, restore *finv1.FinRestore) {
459516
GinkgoHelper()
460517

@@ -679,6 +736,30 @@ func GetNodeNames(ctx context.Context, k8sClient kubernetes.Interface) ([]string
679736
return nodeNames, nil
680737
}
681738

739+
func GetPvByPvc(ctx context.Context, k8sClient kubernetes.Interface, pvc *corev1.PersistentVolumeClaim) (*corev1.PersistentVolume, error) {
740+
GinkgoHelper()
741+
742+
return k8sClient.CoreV1().PersistentVolumes().Get(ctx, pvc.Spec.VolumeName, metav1.GetOptions{})
743+
}
744+
745+
func ListRBDSnapshots(ctx context.Context, poolName, imageName string) ([]*model.RBDSnapshot, error) {
746+
GinkgoHelper()
747+
748+
stdout, stderr, err := kubectl("exec", "-n", rookNamespace, "deploy/rook-ceph-tools", "--",
749+
"rbd", "snap", "ls", "-p", poolName, imageName, "--format", "json")
750+
if err != nil {
751+
return nil, fmt.Errorf("failed to list RBD snapshots. stdout: %s, stderr: %s, err: %w",
752+
string(stdout), string(stderr), err)
753+
}
754+
755+
var snapshots []*model.RBDSnapshot
756+
if err := json.Unmarshal(stdout, &snapshots); err != nil {
757+
return nil, fmt.Errorf("failed to unmarshal RBD snapshot list. err: %w", err)
758+
}
759+
760+
return snapshots, nil
761+
}
762+
682763
func VerifyRawImage(pvc *corev1.PersistentVolumeClaim, node string, expected []byte) {
683764
GinkgoHelper()
684765

@@ -706,13 +787,22 @@ func VerifyDeletionOfJobsForBackup(ctx context.Context, client kubernetes.Interf
706787
Expect(err).NotTo(HaveOccurred(), "Deletion job should be deleted.")
707788
}
708789

709-
func VerifyDeletionOfSnapshotInFinBackup(ctx context.Context, ctrlClient client.Client, finbackup *finv1.FinBackup) {
790+
func VerifyDeletionOfSnapshotInFinBackup(ctx context.Context, finbackup *finv1.FinBackup) error {
710791
GinkgoHelper()
711792

712-
rbdImage := finbackup.Annotations["fin.cybozu.io/backup-target-rbd-image"]
713-
stdout, stderr, err := kubectl("exec", "-n", rookNamespace, "deploy/rook-ceph-tools", "--",
714-
"rbd", "info", fmt.Sprintf("%s/%s@fin-backup-%s", poolName, rbdImage, finbackup.UID))
715-
Expect(err).To(HaveOccurred(), "Snapshot should be deleted. stdout: %s, stderr: %s", stdout, stderr)
793+
imageName := finbackup.Annotations["fin.cybozu.io/backup-target-rbd-image"]
794+
snapshots, err := ListRBDSnapshots(ctx, poolName, imageName)
795+
if err != nil {
796+
return err
797+
}
798+
799+
expectedSnapName := fmt.Sprintf("fin-backup-%s", finbackup.UID)
800+
for _, snapshot := range snapshots {
801+
if snapshot.Name == expectedSnapName {
802+
return fmt.Errorf("snapshot %s still exists", expectedSnapName)
803+
}
804+
}
805+
return nil
716806
}
717807

718808
func VerifyDeletionOfResourcesForRestore(

0 commit comments

Comments
 (0)