11package e2e
22
33import (
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/controller"
18+ "github.com/cybozu-go/fin/internal/model"
1419 "github.com/cybozu-go/fin/test/utils"
1520 . "github.com/onsi/ginkgo/v2"
1621 . "github.com/onsi/gomega"
@@ -26,6 +31,7 @@ import (
2631)
2732
2833const (
34+ finDeploymentName = "fin-controller-manager"
2935 rookNamespace = "rook-ceph"
3036 rookStorageClass = "rook-ceph-block"
3137 poolName = "rook-ceph-block-pool"
@@ -80,7 +86,7 @@ func checkDeploymentReady(namespace, name string) error {
8086func waitEnvironment () {
8187 It ("wait for fin-controller to be ready" , func () {
8288 Eventually (func () error {
83- return checkDeploymentReady (rookNamespace , "fin-controller-manager" )
89+ return checkDeploymentReady (rookNamespace , finDeploymentName )
8490 }).Should (Succeed ())
8591 })
8692}
@@ -372,16 +378,31 @@ func DeleteFinRestore(ctx context.Context, client client.Client, finrestore *fin
372378 return client .Delete (ctx , target )
373379}
374380
375- func WaitForFinBackupStoredToNodeAndVerified (ctx context.Context , c client.Client , finbackup * finv1.FinBackup , timeout time.Duration ) error {
376- return wait .PollUntilContextTimeout (ctx , time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
377- fb := & finv1.FinBackup {}
378- err := c .Get (ctx , client .ObjectKeyFromObject (finbackup ), fb )
381+ func WaitForPVCBound (ctx context.Context , c client.Client , pvc * corev1.PersistentVolumeClaim , timeout time.Duration ) (* corev1.PersistentVolumeClaim , error ) {
382+ GinkgoHelper ()
383+ currentPVC := & corev1.PersistentVolumeClaim {}
384+ err := wait .PollUntilContextTimeout (ctx , time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
385+ err := c .Get (ctx , client .ObjectKeyFromObject (pvc ), currentPVC )
386+ if err != nil {
387+ return false , err
388+ }
389+ return currentPVC .Status .Phase == corev1 .ClaimBound , nil
390+ })
391+ return currentPVC , err
392+ }
393+
394+ func WaitForFinBackupStoredToNodeAndVerified (ctx context.Context , c client.Client , finbackup * finv1.FinBackup , timeout time.Duration ) (* finv1.FinBackup , error ) {
395+ GinkgoHelper ()
396+ currentFB := & finv1.FinBackup {}
397+ err := wait .PollUntilContextTimeout (ctx , time .Second , timeout , true , func (ctx context.Context ) (bool , error ) {
398+ err := c .Get (ctx , client .ObjectKeyFromObject (finbackup ), currentFB )
379399 if err != nil {
380400 return false , err
381401 }
382402
383- return fb .IsStoredToNode () && fb .IsVerifiedTrue (), nil
403+ return currentFB .IsStoredToNode () && currentFB .IsVerifiedTrue (), nil
384404 })
405+ return currentFB , err
385406}
386407
387408func WaitForFinRestoreReady (ctx context.Context , c client.Client , finrestore * finv1.FinRestore , timeout time.Duration ) error {
@@ -455,6 +476,58 @@ func WaitForPVCDeletion(ctx context.Context, k8sClient kubernetes.Interface, pvc
455476 })
456477}
457478
479+ // WaitControllerLog waits until the controller log matches the given pattern or the duration is exceeded.
480+ func WaitControllerLog (ctx SpecContext , pattern string , duration time.Duration ) error {
481+ GinkgoHelper ()
482+
483+ timeoutCtx , cancel := context .WithTimeout (ctx , duration )
484+ defer cancel ()
485+
486+ matcher := regexp .MustCompile (pattern )
487+
488+ command := exec .CommandContext (timeoutCtx , "kubectl" , "logs" , "-n" , rookNamespace , "deployment/" + finDeploymentName , "-f" )
489+ stdoutPipe , err := command .StdoutPipe ()
490+ if err != nil {
491+ panic (err )
492+ }
493+ err = command .Start ()
494+ if err != nil {
495+ panic (err )
496+ }
497+ defer func () {
498+ _ = command .Process .Kill ()
499+ _ = command .Wait ()
500+ }()
501+
502+ // read stdout line by line until the pattern is found
503+ scanner := bufio .NewScanner (stdoutPipe )
504+ found := make (chan struct {})
505+ go func () {
506+ for scanner .Scan () {
507+ select {
508+ case <- timeoutCtx .Done ():
509+ return
510+ default :
511+ }
512+ line := scanner .Text ()
513+ if matcher .MatchString (line ) {
514+ close (found )
515+ return
516+ }
517+ }
518+ if scanner .Err () != nil {
519+ panic (scanner .Err ())
520+ }
521+ }()
522+
523+ select {
524+ case <- timeoutCtx .Done ():
525+ return timeoutCtx .Err ()
526+ case <- found :
527+ return nil
528+ }
529+ }
530+
458531func VerifySizeOfRestorePVC (ctx context.Context , c client.Client , restore * finv1.FinRestore ) {
459532 GinkgoHelper ()
460533
@@ -679,6 +752,30 @@ func GetNodeNames(ctx context.Context, k8sClient kubernetes.Interface) ([]string
679752 return nodeNames , nil
680753}
681754
755+ func GetPvByPvc (ctx context.Context , k8sClient kubernetes.Interface , pvc * corev1.PersistentVolumeClaim ) (* corev1.PersistentVolume , error ) {
756+ GinkgoHelper ()
757+
758+ return k8sClient .CoreV1 ().PersistentVolumes ().Get (ctx , pvc .Spec .VolumeName , metav1.GetOptions {})
759+ }
760+
761+ func ListRBDSnapshots (ctx context.Context , poolName , imageName string ) ([]* model.RBDSnapshot , error ) {
762+ GinkgoHelper ()
763+
764+ stdout , stderr , err := kubectl ("exec" , "-n" , rookNamespace , "deploy/rook-ceph-tools" , "--" ,
765+ "rbd" , "-p" , poolName , "snap" , "ls" , imageName , "--format" , "json" )
766+ if err != nil {
767+ return nil , fmt .Errorf ("failed to list RBD snapshots. stdout: %s, stderr: %s, err: %w" ,
768+ string (stdout ), string (stderr ), err )
769+ }
770+
771+ var snapshots []* model.RBDSnapshot
772+ if err := json .Unmarshal (stdout , & snapshots ); err != nil {
773+ return nil , fmt .Errorf ("failed to unmarshal RBD snapshot list. err: %w" , err )
774+ }
775+
776+ return snapshots , nil
777+ }
778+
682779func VerifyRawImage (pvc * corev1.PersistentVolumeClaim , node string , expected []byte ) {
683780 GinkgoHelper ()
684781
@@ -706,13 +803,26 @@ func VerifyDeletionOfJobsForBackup(ctx context.Context, client kubernetes.Interf
706803 Expect (err ).NotTo (HaveOccurred (), "Deletion job should be deleted." )
707804}
708805
709- func VerifyDeletionOfSnapshotInFinBackup (ctx context.Context , ctrlClient client. Client , finbackup * finv1.FinBackup ) {
806+ func VerifyDeletionOfSnapshotInFinBackup (ctx context.Context , finbackup * finv1.FinBackup ) error {
710807 GinkgoHelper ()
711808
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 )
809+ imageName := finbackup .Annotations [controller .AnnotationBackupTargetRBDImage ]
810+ if len (imageName ) == 0 {
811+ return fmt .Errorf ("finbackup %s/%s does not have %s annotation" ,
812+ finbackup .Namespace , finbackup .Name , controller .AnnotationBackupTargetRBDImage )
813+ }
814+ snapshots , err := ListRBDSnapshots (ctx , poolName , imageName )
815+ if err != nil {
816+ return err
817+ }
818+
819+ expectedSnapName := fmt .Sprintf ("fin-backup-%s" , finbackup .UID )
820+ for _ , snapshot := range snapshots {
821+ if snapshot .Name == expectedSnapName {
822+ return fmt .Errorf ("snapshot %s still exists" , expectedSnapName )
823+ }
824+ }
825+ return nil
716826}
717827
718828func VerifyDeletionOfResourcesForRestore (
0 commit comments