e2e: test for PVC with volumeBindingMode on helm installation

Test PVC binding with WaitForFirstConsumer in Helm installation.

Signed-off-by: ShravaniVangur <shravanivangur@gmail.com>
This commit is contained in:
ShravaniVangur 2025-03-27 22:35:29 +05:30
parent b6ef888dd8
commit 4eb795e535
4 changed files with 126 additions and 5 deletions

View File

@ -331,6 +331,23 @@ var _ = Describe(cephfsType, func() {
}) })
} }
By("verify PVC and App Binding with volumeBindingMode:WaitForFirstConsumer", func() {
err := createCephfsStorageClassWaitForFirstConsumer(f.ClientSet, f, true, nil)
if err != nil {
framework.Failf("failed to create CephFS storageclass: %v", err)
}
err = validatePVCAndAppWaitForFirstConsumer(pvcPath, appPath, f)
if err != nil {
framework.Failf("failed to validate CephFS pvc and application binding: %v", err)
}
err = deleteResource(cephFSExamplePath + "storageclass.yaml")
if err != nil {
framework.Failf("failed to delete CephFS storageclass: %v", err)
}
})
By("verify mountOptions support", func() { By("verify mountOptions support", func() {
err := createCephfsStorageClass(f.ClientSet, f, true, nil) err := createCephfsStorageClass(f.ClientSet, f, true, nil)
if err != nil { if err != nil {

View File

@ -26,6 +26,7 @@ import (
snapapi "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1" snapapi "github.com/kubernetes-csi/external-snapshotter/client/v8/apis/volumesnapshot/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
scv1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
@ -62,11 +63,49 @@ func createCephfsStorageClass(
params map[string]string, params map[string]string,
) error { ) error {
scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml") scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml")
sc, err := getStorageClass(scPath) scValue, err := getStorageClass(scPath)
if err != nil { if err != nil {
return err return err
} }
sc := &scValue
err = updateStorageClassParameters(&scValue, params, enablePool, f)
if err != nil {
return err
}
return createStorageClass(c, sc)
}
func createCephfsStorageClassWaitForFirstConsumer(c kubernetes.Interface, f *framework.Framework,
enablePool bool,
params map[string]string) error {
scPath := fmt.Sprintf("%s/%s", cephFSExamplePath, "storageclass.yaml")
scValue, err := getStorageClass(scPath)
if err != nil {
return err
}
sc := &scValue
err = updateStorageClassParameters(&scValue, params, enablePool, f)
if err != nil {
return err
}
// Set the volume binding mode to WaitForFirstConsumer
value := scv1.VolumeBindingWaitForFirstConsumer
sc.VolumeBindingMode = &value
return createStorageClass(c, sc)
}
func updateStorageClassParameters(sc *scv1.StorageClass, params map[string]string, enablePool bool, f *framework.Framework) error {
if sc == nil {
return fmt.Errorf("StorageClass is nil")
}
sc.Parameters["fsName"] = fileSystemName sc.Parameters["fsName"] = fileSystemName
sc.Parameters["csi.storage.k8s.io/provisioner-secret-namespace"] = cephCSINamespace sc.Parameters["csi.storage.k8s.io/provisioner-secret-namespace"] = cephCSINamespace
sc.Parameters["csi.storage.k8s.io/provisioner-secret-name"] = cephFSProvisionerSecretName sc.Parameters["csi.storage.k8s.io/provisioner-secret-name"] = cephFSProvisionerSecretName
@ -93,18 +132,20 @@ func createCephfsStorageClass(
// fetch and set fsID from the cluster if not set in params // fetch and set fsID from the cluster if not set in params
if _, found := params["clusterID"]; !found { if _, found := params["clusterID"]; !found {
var fsID string fsID, err := getClusterID(f)
fsID, err = getClusterID(f)
if err != nil { if err != nil {
return fmt.Errorf("failed to get clusterID: %w", err) return fmt.Errorf("failed to get clusterID: %w", err)
} }
sc.Parameters["clusterID"] = fsID sc.Parameters["clusterID"] = fsID
} }
timeout := time.Duration(deployTimeout) * time.Minute return nil
}
func createStorageClass(c kubernetes.Interface, sc *scv1.StorageClass) error {
timeout := time.Duration(deployTimeout) * time.Minute
return wait.PollUntilContextTimeout(context.TODO(), poll, timeout, true, func(ctx context.Context) (bool, error) { return wait.PollUntilContextTimeout(context.TODO(), poll, timeout, true, func(ctx context.Context) (bool, error) {
_, err = c.StorageV1().StorageClasses().Create(ctx, &sc, metav1.CreateOptions{}) _, err := c.StorageV1().StorageClasses().Create(ctx, sc, metav1.CreateOptions{})
if err != nil { if err != nil {
framework.Logf("error creating StorageClass %q: %v", sc.Name, err) framework.Logf("error creating StorageClass %q: %v", sc.Name, err)
if isRetryableAPIError(err) { if isRetryableAPIError(err) {

View File

@ -130,6 +130,15 @@ func createPVCAndPV(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim, pv *v
return err return err
} }
func createPVC(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim) error {
_, err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Create(context.TODO(), pvc, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("failed to create pvc: %w", err)
}
return err
}
func deletePVCAndPV(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, t int) error { func deletePVCAndPV(c kubernetes.Interface, pvc *v1.PersistentVolumeClaim, pv *v1.PersistentVolume, t int) error {
ctx := context.TODO() ctx := context.TODO()
err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{}) err := c.CoreV1().PersistentVolumeClaims(pvc.Namespace).Delete(ctx, pvc.Name, metav1.DeleteOptions{})

View File

@ -508,6 +508,60 @@ func validatePVCAndAppBinding(pvcPath, appPath string, f *framework.Framework) e
return err return err
} }
// validatePVCAndAppWaitForFirstConsumer creates a Pending PVC, starts an app, and verifies it to become Bound.
func validatePVCAndAppWaitForFirstConsumer(pvcPath, appPath string, f *framework.Framework) error {
pvc, err := loadPVC(pvcPath)
if err != nil {
return err
}
pvc.Namespace = f.UniqueName
app, err := loadApp(appPath)
if err != nil {
return err
}
app.Namespace = f.UniqueName
err = createPVC(f.ClientSet, pvc)
if err != nil {
return err
}
err = waitForPVCPhase(f.ClientSet, pvc.Namespace, pvc.Name, v1.ClaimPending, 30*time.Second)
if err != nil {
return fmt.Errorf("PVC did not stay in Pending state as expected: %w", err)
}
err = createApp(f.ClientSet, app, deployTimeout)
if err != nil {
return err
}
err = waitForPVCPhase(f.ClientSet, pvc.Namespace, pvc.Name, v1.ClaimBound, 30*time.Second)
if err != nil {
return fmt.Errorf("PVC did not reach Bound state after creating the pod: %w", err)
}
err = deletePVCAndApp("", f, pvc, app)
return err
}
func waitForPVCPhase(c kubernetes.Interface, namespace, name string, phase v1.PersistentVolumeClaimPhase, timeout time.Duration) error {
return wait.PollUntilContextTimeout(context.TODO(), poll, timeout, true, func(ctx context.Context) (bool, error) {
pvc, err := c.CoreV1().PersistentVolumeClaims(namespace).Get(context.TODO(), name, metav1.GetOptions{})
if err != nil {
return false, fmt.Errorf("error fetching PVC %q: %w", name, err)
}
if pvc.Status.Phase == phase {
return true, nil
}
framework.Logf("Waiting for PVC %q to reach phase %q, current phase: %q", name, phase, pvc.Status.Phase)
return false, nil
})
}
func getMountType(selector, mountPath string, f *framework.Framework) (string, error) { func getMountType(selector, mountPath string, f *framework.Framework) (string, error) {
opt := metav1.ListOptions{ opt := metav1.ListOptions{
LabelSelector: selector, LabelSelector: selector,