Skip to content

Commit a68cb53

Browse files
authored
new changes (#3651)
1 parent 783c0db commit a68cb53

File tree

8 files changed

+858
-57
lines changed

8 files changed

+858
-57
lines changed

tests/e2e/bootstrap/bootstrap.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
cnstypes "github.com/vmware/govmomi/cns/types"
2727
"k8s.io/kubernetes/test/e2e/framework"
2828
"k8s.io/kubernetes/test/e2e/framework/testfiles"
29+
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/clients/cns"
2930
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/clients/vc"
3031
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/config"
3132
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/constants"
@@ -53,6 +54,8 @@ func Bootstrap(optionArgs ...bool) *config.E2eTestConfig {
5354
e2eTestConfig.TestInput.Global.VCenterPort, e2eTestConfig.TestInput.Global.User,
5455
e2eTestConfig.TestInput.Global.Password)
5556

57+
e2eTestConfig.CnsClient, _ = cns.NewCnsClient(ctx, e2eTestConfig.VcClient.Client)
58+
5659
if framework.TestContext.RepoRoot != "" {
5760
testfiles.AddFileSource(testfiles.RootFileSource{Root: framework.TestContext.RepoRoot})
5861
}

tests/e2e/config/test_config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ type TestBedConfig struct {
134134
Multivc bool
135135
StretchedSVC bool
136136
IsPrivateNetwork bool
137+
LinkedClone bool
137138
K8sMasterIp1PortNum string
138139
K8sMasterIp2PortNum string
139140
K8sMasterIp3PortNum string

tests/e2e/constants/env_constants.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,3 +225,9 @@ const (
225225
EnvIsDevopsUser = "IS_DEVOPS_USER"
226226
EnvDevopsUserName = "DEVOPS_USERNAME"
227227
)
228+
229+
// For linked clone
230+
var (
231+
EnvStoragePolicy = "STORAGE_POLICY_NAME-LC"
232+
EnvTargetDSUrlForRelocate = "TARGET_DS_URL-LC"
233+
)

tests/e2e/constants/kubernetes.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ const (
138138
StorageQuotaWebhookPrefix = "storage-quota-webhook"
139139
DevopsKubeConf = "DEV_OPS_USER_KUBECONFIG"
140140
QuotaSupportedVCVersion = "9.0.0"
141+
LinkedCloneAnnotationKey = "csi.vsphere.volume/fast-provisioning"
141142
)
142143

143144
// For busybox pod image

tests/e2e/constants/test_suite_level.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,5 @@ const (
8787
VcptocsiTest = "vcptocsiTest"
8888
StretchedSvc = "stretchedSvc"
8989
Devops = "devops"
90+
LinkedClone = "lc"
9091
)

tests/e2e/constants/vsphere_constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const (
2323
DiskSize1GB = "1Gi"
2424
DiskSize = "2Gi"
2525
DiskSizeLarge = "100Gi"
26+
DiskSize10GB = "10Gi"
2627
DiskSizeInMb = int64(2048)
2728
DiskSizeInMinMb = int64(200)
2829
E2eTestPassword = "E2E-test-password!23"

tests/e2e/csisnapshot/util.go

Lines changed: 286 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,21 @@ import (
2727
"github.com/onsi/ginkgo/v2"
2828
"github.com/onsi/gomega"
2929
v1 "k8s.io/api/core/v1"
30+
storagev1 "k8s.io/api/storage/v1"
3031
apierrors "k8s.io/apimachinery/pkg/api/errors"
3132
"k8s.io/apimachinery/pkg/api/resource"
3233
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3334
"k8s.io/apimachinery/pkg/util/wait"
35+
clientset "k8s.io/client-go/kubernetes"
36+
"k8s.io/client-go/rest"
3437
"k8s.io/client-go/tools/clientcmd"
3538
"k8s.io/kubernetes/test/e2e/framework"
39+
e2ekubectl "k8s.io/kubernetes/test/e2e/framework/kubectl"
40+
fpv "k8s.io/kubernetes/test/e2e/framework/pv"
3641
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/config"
3742
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/constants"
3843
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/env"
44+
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/k8testutil"
3945
"sigs.k8s.io/vsphere-csi-driver/v3/tests/e2e/vcutil"
4046
)
4147

@@ -389,3 +395,283 @@ func WaitForVolumeSnapshotContentToBeDeleted(client snapclient.Clientset, ctx co
389395
})
390396
return waitErr
391397
}
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

Comments
 (0)