Skip to content

Commit 4eb795e

Browse files
e2e: test for PVC with volumeBindingMode on helm installation
Test PVC binding with WaitForFirstConsumer in Helm installation. Signed-off-by: ShravaniVangur <shravanivangur@gmail.com>
1 parent b6ef888 commit 4eb795e

4 files changed

Lines changed: 126 additions & 5 deletions

File tree

e2e/cephfs.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,23 @@ var _ = Describe(cephfsType, func() {
331331
})
332332
}
333333

334+
By("verify PVC and App Binding with volumeBindingMode:WaitForFirstConsumer", func() {
335+
err := createCephfsStorageClassWaitForFirstConsumer(f.ClientSet, f, true, nil)
336+
if err != nil {
337+
framework.Failf("failed to create CephFS storageclass: %v", err)
338+
}
339+
340+
err = validatePVCAndAppWaitForFirstConsumer(pvcPath, appPath, f)
341+
if err != nil {
342+
framework.Failf("failed to validate CephFS pvc and application binding: %v", err)
343+
}
344+
345+
err = deleteResource(cephFSExamplePath + "storageclass.yaml")
346+
if err != nil {
347+
framework.Failf("failed to delete CephFS storageclass: %v", err)
348+
}
349+
})
350+
334351
By("verify mountOptions support", func() {
335352
err := createCephfsStorageClass(f.ClientSet, f, true, nil)
336353
if err != nil {

e2e/cephfs_helper.go

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626

2727
snapapi "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
2828
v1 "k8s.io/api/core/v1"
29+
scv1 "k8s.io/api/storage/v1"
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/util/wait"
3132
"k8s.io/client-go/kubernetes"
@@ -62,11 +63,49 @@ func createCephfsStorageClass(
6263
params map[string]string,
6364
) error {
6465
scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml")
65-
sc, err := getStorageClass(scPath)
66+
scValue, err := getStorageClass(scPath)
6667
if err != nil {
6768
return err
6869
}
6970

71+
sc := &scValue
72+
73+
err = updateStorageClassParameters(&scValue, params, enablePool, f)
74+
if err != nil {
75+
return err
76+
}
77+
78+
return createStorageClass(c, sc)
79+
}
80+
81+
func createCephfsStorageClassWaitForFirstConsumer(c kubernetes.Interface, f *framework.Framework,
82+
enablePool bool,
83+
params map[string]string) error {
84+
scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml")
85+
scValue, err := getStorageClass(scPath)
86+
if err != nil {
87+
return err
88+
}
89+
90+
sc := &scValue
91+
92+
err = updateStorageClassParameters(&scValue, params, enablePool, f)
93+
if err != nil {
94+
return err
95+
}
96+
97+
// Set the volume binding mode to WaitForFirstConsumer
98+
value := scv1.VolumeBindingWaitForFirstConsumer
99+
sc.VolumeBindingMode = &value
100+
101+
return createStorageClass(c, sc)
102+
}
103+
104+
func updateStorageClassParameters(sc *scv1.StorageClass, params map[string]string, enablePool bool, f *framework.Framework) error {
105+
if sc == nil {
106+
return fmt.Errorf("StorageClass is nil")
107+
}
108+
70109
sc.Parameters["fsName"] = fileSystemName
71110
sc.Parameters["csi.storage.k8s.io/provisioner-secret-namespace"] = cephCSINamespace
72111
sc.Parameters["csi.storage.k8s.io/provisioner-secret-name"] = cephFSProvisionerSecretName
@@ -93,18 +132,20 @@ func createCephfsStorageClass(
93132

94133
// fetch and set fsID from the cluster if not set in params
95134
if _, found := params["clusterID"]; !found {
96-
var fsID string
97-
fsID, err = getClusterID(f)
135+
fsID, err := getClusterID(f)
98136
if err != nil {
99137
return fmt.Errorf("failed to get clusterID: %w", err)
100138
}
101139
sc.Parameters["clusterID"] = fsID
102140
}
103141

104-
timeout := time.Duration(deployTimeout) * time.Minute
142+
return nil
143+
}
105144

145+
func createStorageClass(c kubernetes.Interface, sc *scv1.StorageClass) error {
146+
timeout := time.Duration(deployTimeout) * time.Minute
106147
return wait.PollUntilContextTimeout(context.TODO(), poll, timeout, true, func(ctx context.Context) (bool, error) {
107-
_, err = c.StorageV1().StorageClasses().Create(ctx, &sc, metav1.CreateOptions{})
148+
_, err := c.StorageV1().StorageClasses().Create(ctx, sc, metav1.CreateOptions{})
108149
if err != nil {
109150
framework.Logf("error creating StorageClass %q: %v", sc.Name, err)
110151
if isRetryableAPIError(err) {

e2e/pvc.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,15 @@ func createPVCAndPV(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim, pv *v
130130
return err
131131
}
132132

133+
func createPVC(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim) error {
134+
_, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(context.TODO(), pvc, metav1.CreateOptions{})
135+
if err != nil {
136+
return fmt.Errorf("failed to create pvc: %w", err)
137+
}
138+
139+
return err
140+
}
141+
133142
func deletePVCAndPV(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, t int) error {
134143
ctx := context.TODO()
135144
err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{})

e2e/utils.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,60 @@ func validatePVCAndAppBinding(pvcPath, appPath string, f *framework.Framework) e
508508
return err
509509
}
510510

511+
// validatePVCAndAppWaitForFirstConsumer creates a Pending PVC, starts an app, and verifies it to become Bound.
512+
func validatePVCAndAppWaitForFirstConsumer(pvcPath, appPath string, f *framework.Framework) error {
513+
pvc, err := loadPVC(pvcPath)
514+
if err != nil {
515+
return err
516+
}
517+
pvc.Namespace = f.UniqueName
518+
519+
app, err := loadApp(appPath)
520+
if err != nil {
521+
return err
522+
}
523+
app.Namespace = f.UniqueName
524+
525+
err = createPVC(f.ClientSet, pvc)
526+
if err != nil {
527+
return err
528+
}
529+
530+
err = waitForPVCPhase(f.ClientSet, pvc.Namespace, pvc.Name, v1.ClaimPending, 30*time.Second)
531+
if err != nil {
532+
return fmt.Errorf("PVC did not stay in Pending state as expected: %w", err)
533+
}
534+
535+
err = createApp(f.ClientSet, app, deployTimeout)
536+
if err != nil {
537+
return err
538+
}
539+
540+
err = waitForPVCPhase(f.ClientSet, pvc.Namespace, pvc.Name, v1.ClaimBound, 30*time.Second)
541+
if err != nil {
542+
return fmt.Errorf("PVC did not reach Bound state after creating the pod: %w", err)
543+
}
544+
545+
err = deletePVCAndApp("", f, pvc, app)
546+
547+
return err
548+
}
549+
550+
func waitForPVCPhase(c kubernetes.Interface, namespace, name string, phase v1.PersistentVolumeClaimPhase, timeout time.Duration) error {
551+
return wait.PollUntilContextTimeout(context.TODO(), poll, timeout, true, func(ctx context.Context) (bool, error) {
552+
pvc, err := c.CoreV1().PersistentVolumeClaims(namespace).Get(context.TODO(), name, metav1.GetOptions{})
553+
if err != nil {
554+
return false, fmt.Errorf("error fetching PVC %q: %w", name, err)
555+
}
556+
if pvc.Status.Phase == phase {
557+
return true, nil
558+
}
559+
framework.Logf("Waiting for PVC %q to reach phase %q, current phase: %q", name, phase, pvc.Status.Phase)
560+
561+
return false, nil
562+
})
563+
}
564+
511565
func getMountType(selector, mountPath string, f *framework.Framework) (string, error) {
512566
opt := metav1.ListOptions{
513567
LabelSelector: selector,

0 commit comments

Comments
 (0)