@@ -27,15 +27,21 @@ import (
27
27
"github.com/onsi/ginkgo/v2"
28
28
"github.com/onsi/gomega"
29
29
v1 "k8s.io/api/core/v1"
30
+ storagev1 "k8s.io/api/storage/v1"
30
31
apierrors "k8s.io/apimachinery/pkg/api/errors"
31
32
"k8s.io/apimachinery/pkg/api/resource"
32
33
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33
34
"k8s.io/apimachinery/pkg/util/wait"
35
+ clientset "k8s.io/client-go/kubernetes"
36
+ "k8s.io/client-go/rest"
34
37
"k8s.io/client-go/tools/clientcmd"
35
38
"k8s.io/kubernetes/test/e2e/framework"
39
+ e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
40
+ fpv "k8s.io/kubernetes/test/e2e/framework/pv"
36
41
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/config"
37
42
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/constants"
38
43
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/env"
44
+ "sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/k8testutil"
39
45
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/vcutil"
40
46
)
41
47
@@ -389,3 +395,283 @@ func WaitForVolumeSnapshotContentToBeDeleted(client snapclient.Clientset, ctx co
389
395
})
390
396
return waitErr
391
397
}
398
+
399
+ // GetRestConfigClientForGuestCluster can be used to get the KUBECONFIG
400
+ func GetRestConfigClientForGuestCluster (guestClusterRestConfig * rest.Config ) * rest.Config {
401
+ var err error
402
+ if guestClusterRestConfig == nil {
403
+ if k8senv := env .GetAndExpectStringEnvVar ("KUBECONFIG" ); k8senv != "" {
404
+ guestClusterRestConfig , err = clientcmd .BuildConfigFromFlags ("" , k8senv )
405
+ }
406
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
407
+
408
+ }
409
+ return guestClusterRestConfig
410
+ }
411
+
412
+ // verifyVolumeRestoreOperation verifies if volume(PVC) restore from given snapshot
413
+ // and creates pod and checks attach volume operation if verifyPodCreation is set to true
414
+ func VerifyVolumeRestoreOperation (ctx context.Context , e2eTestConfig * config.E2eTestConfig , client clientset.Interface ,
415
+ namespace string , storageclass * storagev1.StorageClass ,
416
+ volumeSnapshot * snapV1.VolumeSnapshot , diskSize string ,
417
+ verifyPodCreation bool ) (* v1.PersistentVolumeClaim , []* v1.PersistentVolume , * v1.Pod ) {
418
+
419
+ ginkgo .By ("Create PVC from snapshot" )
420
+ pvcSpec := GetPersistentVolumeClaimSpecWithDatasource (namespace , diskSize , storageclass , nil ,
421
+ v1 .ReadWriteOnce , volumeSnapshot .Name , constants .Snapshotapigroup )
422
+
423
+ pvclaim2 , err := fpv .CreatePVC (ctx , client , namespace , pvcSpec )
424
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
425
+
426
+ persistentvolumes2 , err := fpv .WaitForPVClaimBoundPhase (ctx , client ,
427
+ []* v1.PersistentVolumeClaim {pvclaim2 }, framework .ClaimProvisionTimeout )
428
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
429
+ volHandle2 := persistentvolumes2 [0 ].Spec .CSI .VolumeHandle
430
+ if e2eTestConfig .TestInput .ClusterFlavor .GuestCluster {
431
+ volHandle2 = k8testutil .GetVolumeIDFromSupervisorCluster (volHandle2 )
432
+ }
433
+ gomega .Expect (volHandle2 ).NotTo (gomega .BeEmpty ())
434
+
435
+ var pod * v1.Pod
436
+ if verifyPodCreation {
437
+ // Create a Pod to use this PVC, and verify volume has been attached
438
+ ginkgo .By ("Creating pod to attach PV to the node" )
439
+ pod , err = k8testutil .CreatePod (ctx , e2eTestConfig , client , namespace , nil ,
440
+ []* v1.PersistentVolumeClaim {pvclaim2 }, false , constants .ExecRWXCommandPod1 )
441
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
442
+
443
+ var vmUUID string
444
+ var exists bool
445
+ nodeName := pod .Spec .NodeName
446
+
447
+ if e2eTestConfig .TestInput .ClusterFlavor .VanillaCluster {
448
+ vmUUID = k8testutil .GetNodeUUID (ctx , client , pod .Spec .NodeName )
449
+ } else if e2eTestConfig .TestInput .ClusterFlavor .GuestCluster {
450
+ vmUUID , err = vcutil .GetVMUUIDFromNodeName (e2eTestConfig , pod .Spec .NodeName )
451
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
452
+ } else if e2eTestConfig .TestInput .ClusterFlavor .SupervisorCluster {
453
+ annotations := pod .Annotations
454
+ vmUUID , exists = annotations [constants .VmUUIDLabel ]
455
+ gomega .Expect (exists ).To (gomega .BeTrue (), fmt .Sprintf ("Pod doesn't have %s annotation" , constants .VmUUIDLabel ))
456
+ _ , err := vcutil .GetVMByUUID (ctx , e2eTestConfig , vmUUID )
457
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
458
+ }
459
+
460
+ ginkgo .By (fmt .Sprintf ("Verify volume: %s is attached to the node: %s" , volHandle2 , nodeName ))
461
+ isDiskAttached , err := vcutil .IsVolumeAttachedToVM (client , e2eTestConfig , volHandle2 , vmUUID )
462
+ gomega .Expect (err ).NotTo (gomega .HaveOccurred ())
463
+ gomega .Expect (isDiskAttached ).To (gomega .BeTrue (), "Volume is not attached to the node" )
464
+
465
+ ginkgo .By ("Verify the volume is accessible and Read/write is possible" )
466
+ var cmd []string
467
+ if e2eTestConfig .TestInput .TestBedInfo .WindowsEnv {
468
+ cmd = []string {"exec" , pod .Name , "--namespace=" + namespace , "powershell.exe" , "cat " , constants .FilePathPod1 }
469
+ } else {
470
+ cmd = []string {"exec" , pod .Name , "--namespace=" + namespace , "--" , "/bin/sh" , "-c" ,
471
+ "cat " , constants .FilePathPod1 }
472
+ }
473
+ output := e2ekubectl .RunKubectlOrDie (namespace , cmd ... )
474
+ gomega .Expect (strings .Contains (output , "Hello message from Pod1" )).NotTo (gomega .BeFalse ())
475
+
476
+ var wrtiecmd []string
477
+ if e2eTestConfig .TestInput .TestBedInfo .WindowsEnv {
478
+ wrtiecmd = []string {"exec" , pod .Name , "--namespace=" + namespace , "powershell.exe" ,
479
+ "Add-Content /mnt/volume1/Pod1.html 'Hello message from test into Pod1'" }
480
+ } else {
481
+ wrtiecmd = []string {"exec" , pod .Name , "--namespace=" + namespace , "--" , "/bin/sh" , "-c" ,
482
+ "echo 'Hello message from test into Pod1' >> /mnt/volume1/Pod1.html" }
483
+ }
484
+ e2ekubectl .RunKubectlOrDie (namespace , wrtiecmd ... )
485
+ output = e2ekubectl .RunKubectlOrDie (namespace , cmd ... )
486
+ gomega .Expect (strings .Contains (output , "Hello message from test into Pod1" )).NotTo (gomega .BeFalse ())
487
+ return pvclaim2 , persistentvolumes2 , pod
488
+ }
489
+ return pvclaim2 , persistentvolumes2 , pod
490
+ }
491
+
492
+ /* createPreProvisionedSnapshotInGuestCluster created pre-provisioned snaphot on Guest cluster */
493
+ func CreatePreProvisionedSnapshotInGuestCluster (ctx context.Context , volumeSnapshot * snapV1.VolumeSnapshot ,
494
+ updatedSnapshotContent * snapV1.VolumeSnapshotContent ,
495
+ snapc * snapclient.Clientset , namespace string , pandoraSyncWaitTime int ,
496
+ svcVolumeSnapshotName string , diskSize string ) (* snapV1.VolumeSnapshotContent ,
497
+ * snapV1.VolumeSnapshot , bool , bool , error ) {
498
+
499
+ framework .Logf ("Change the deletion policy of VolumeSnapshotContent from Delete to Retain " +
500
+ "in Guest Cluster" )
501
+
502
+ updatedSnapshotContent , err := ChangeDeletionPolicyOfVolumeSnapshotContent (ctx , updatedSnapshotContent ,
503
+ snapc , snapV1 .VolumeSnapshotContentRetain )
504
+ if err != nil {
505
+ return nil , nil , false , false , fmt .Errorf ("failed to change deletion policy of VolumeSnapshotContent: %v" , err )
506
+ }
507
+
508
+ framework .Logf ("Delete dynamic volume snapshot from Guest Cluster" )
509
+ DeleteVolumeSnapshotWithPandoraWait (ctx , snapc , namespace , volumeSnapshot .Name , pandoraSyncWaitTime )
510
+
511
+ framework .Logf ("Delete VolumeSnapshotContent from Guest Cluster explicitly" )
512
+ err = DeleteVolumeSnapshotContent (ctx , updatedSnapshotContent , snapc , pandoraSyncWaitTime )
513
+ if err != nil {
514
+ return nil , nil , false , false , fmt .Errorf ("failed to delete VolumeSnapshotContent: %v" , err )
515
+ }
516
+
517
+ framework .Logf ("Creating static VolumeSnapshotContent in Guest Cluster using " +
518
+ "supervisor VolumeSnapshotName %s" , svcVolumeSnapshotName )
519
+ staticSnapshotContent , err := snapc .SnapshotV1 ().VolumeSnapshotContents ().Create (ctx ,
520
+ GetVolumeSnapshotContentSpec (snapV1 .DeletionPolicy ("Delete" ), svcVolumeSnapshotName ,
521
+ "static-vs" , namespace ), metav1.CreateOptions {})
522
+ if err != nil {
523
+ return nil , nil , false , false , fmt .Errorf ("failed to create static VolumeSnapshotContent: %v" , err )
524
+ }
525
+
526
+ framework .Logf ("Verify VolumeSnapshotContent is created or not in Guest Cluster" )
527
+ staticSnapshotContent , err = snapc .SnapshotV1 ().VolumeSnapshotContents ().Get (ctx ,
528
+ staticSnapshotContent .Name , metav1.GetOptions {})
529
+ if err != nil {
530
+ return nil , nil , false , false , fmt .Errorf ("failed to get static VolumeSnapshotContent: %v" , err )
531
+ }
532
+ framework .Logf ("Snapshotcontent name is %s" , staticSnapshotContent .ObjectMeta .Name )
533
+
534
+ staticSnapshotContent , err = WaitForVolumeSnapshotContentReadyToUse (* snapc , ctx , staticSnapshotContent .Name )
535
+ if err != nil {
536
+ return nil , nil , false , false , fmt .Errorf ("volume snapshot content is not ready to use" )
537
+ }
538
+ staticSnapshotContentCreated := true
539
+
540
+ ginkgo .By ("Create a static volume snapshot by static snapshotcontent" )
541
+ staticVolumeSnapshot , err := snapc .SnapshotV1 ().VolumeSnapshots (namespace ).Create (ctx ,
542
+ GetVolumeSnapshotSpecByName (namespace , "static-vs" ,
543
+ staticSnapshotContent .ObjectMeta .Name ), metav1.CreateOptions {})
544
+ if err != nil {
545
+ return nil , nil , false , false , fmt .Errorf ("failed to create static volume snapshot: %v" , err )
546
+ }
547
+ framework .Logf ("Volume snapshot name is : %s" , staticVolumeSnapshot .Name )
548
+
549
+ ginkgo .By ("Verify static volume snapshot is created" )
550
+ staticSnapshot , err := WaitForVolumeSnapshotReadyToUse (* snapc , ctx , namespace , staticVolumeSnapshot .Name )
551
+ if err != nil {
552
+ return nil , nil , false , false , fmt .Errorf ("volumeSnapshot is still not ready to use: %v" , err )
553
+ }
554
+ if staticSnapshot .Status .RestoreSize .Cmp (resource .MustParse (diskSize )) != 0 {
555
+ return nil , nil , false , false , fmt .Errorf ("expected RestoreSize does not match" )
556
+ }
557
+ framework .Logf ("Snapshot details is %+v" , staticSnapshot )
558
+ staticSnapshotCreated := true
559
+
560
+ return staticSnapshotContent , staticSnapshot , staticSnapshotContentCreated , staticSnapshotCreated , nil
561
+ }
562
+
563
+ /*
564
+ changeDeletionPolicyOfVolumeSnapshotContentOnGuest changes the deletion policy
565
+ of volume snapshot content from delete to retain in Guest Cluster
566
+ */
567
+ func ChangeDeletionPolicyOfVolumeSnapshotContent (ctx context.Context ,
568
+ snapshotContent * snapV1.VolumeSnapshotContent , snapc * snapclient.Clientset ,
569
+ policyName snapV1.DeletionPolicy ) (* snapV1.VolumeSnapshotContent , error ) {
570
+
571
+ // Retrieve the latest version of the object
572
+ latestSnapshotContent , err := snapc .SnapshotV1 ().VolumeSnapshotContents ().Get (ctx , snapshotContent .Name ,
573
+ metav1.GetOptions {})
574
+ if err != nil {
575
+ return nil , err
576
+ }
577
+
578
+ // Apply changes to the latest version
579
+ latestSnapshotContent .Spec .DeletionPolicy = policyName
580
+
581
+ // Update the object
582
+ updatedSnapshotContent , err := snapc .SnapshotV1 ().VolumeSnapshotContents ().Update (ctx ,
583
+ latestSnapshotContent , metav1.UpdateOptions {})
584
+ if err != nil {
585
+ return nil , err
586
+ }
587
+
588
+ return updatedSnapshotContent , nil
589
+ }
590
+
591
+ // getVolumeSnapshotContentSpec returns a spec for the volume snapshot content
592
+ func GetVolumeSnapshotContentSpec (deletionPolicy snapV1.DeletionPolicy , snapshotHandle string ,
593
+ futureSnapshotName string , namespace string ) * snapV1.VolumeSnapshotContent {
594
+ var volumesnapshotContentSpec = & snapV1.VolumeSnapshotContent {
595
+ TypeMeta : metav1.TypeMeta {
596
+ Kind : "VolumeSnapshotContent" ,
597
+ },
598
+ ObjectMeta : metav1.ObjectMeta {
599
+ GenerateName : "snapshotcontent-" ,
600
+ },
601
+ Spec : snapV1.VolumeSnapshotContentSpec {
602
+ DeletionPolicy : deletionPolicy ,
603
+ Driver : constants .E2evSphereCSIDriverName ,
604
+ Source : snapV1.VolumeSnapshotContentSource {
605
+ SnapshotHandle : & snapshotHandle ,
606
+ },
607
+ VolumeSnapshotRef : v1.ObjectReference {
608
+ Name : futureSnapshotName ,
609
+ Namespace : namespace ,
610
+ },
611
+ },
612
+ }
613
+ return volumesnapshotContentSpec
614
+ }
615
+
616
+ // getVolumeSnapshotSpecByName returns a spec for the volume snapshot by name
617
+ func GetVolumeSnapshotSpecByName (namespace string , snapshotName string ,
618
+ snapshotcontentname string ) * snapV1.VolumeSnapshot {
619
+ var volumesnapshotSpec = & snapV1.VolumeSnapshot {
620
+ TypeMeta : metav1.TypeMeta {
621
+ Kind : "VolumeSnapshot" ,
622
+ },
623
+ ObjectMeta : metav1.ObjectMeta {
624
+ Name : snapshotName ,
625
+ Namespace : namespace ,
626
+ },
627
+ Spec : snapV1.VolumeSnapshotSpec {
628
+ Source : snapV1.VolumeSnapshotSource {
629
+ VolumeSnapshotContentName : & snapshotcontentname ,
630
+ },
631
+ },
632
+ }
633
+ return volumesnapshotSpec
634
+ }
635
+
636
+ // GetPersistentVolumeClaimSpecWithDatasource return the PersistentVolumeClaim
637
+ // spec with specified storage class.
638
+ func GetPersistentVolumeClaimSpecWithDatasource (namespace string , ds string , storageclass * storagev1.StorageClass ,
639
+ pvclaimlabels map [string ]string , accessMode v1.PersistentVolumeAccessMode ,
640
+ datasourceName string , snapshotapigroup string ) * v1.PersistentVolumeClaim {
641
+ disksize := constants .DiskSize
642
+ if ds != "" {
643
+ disksize = ds
644
+ }
645
+ if accessMode == "" {
646
+ // If accessMode is not specified, set the default accessMode.
647
+ accessMode = v1 .ReadWriteOnce
648
+ }
649
+ claim := & v1.PersistentVolumeClaim {
650
+ ObjectMeta : metav1.ObjectMeta {
651
+ GenerateName : "pvc-" ,
652
+ Namespace : namespace ,
653
+ },
654
+ Spec : v1.PersistentVolumeClaimSpec {
655
+ AccessModes : []v1.PersistentVolumeAccessMode {
656
+ accessMode ,
657
+ },
658
+ Resources : v1.VolumeResourceRequirements {
659
+ Requests : v1.ResourceList {
660
+ v1 .ResourceName (v1 .ResourceStorage ): resource .MustParse (disksize ),
661
+ },
662
+ },
663
+ StorageClassName : & (storageclass .Name ),
664
+ DataSource : & v1.TypedLocalObjectReference {
665
+ APIGroup : & snapshotapigroup ,
666
+ Kind : "VolumeSnapshot" ,
667
+ Name : datasourceName ,
668
+ },
669
+ },
670
+ }
671
+
672
+ if pvclaimlabels != nil {
673
+ claim .Labels = pvclaimlabels
674
+ }
675
+
676
+ return claim
677
+ }
0 commit comments