diff --git a/e2e/rbd.go b/e2e/rbd.go index 7f5a28d89d24..1a26cb8194db 100644 --- a/e2e/rbd.go +++ b/e2e/rbd.go @@ -1089,6 +1089,45 @@ var _ = Describe("RBD", func() { } }) + By("create a PVC and bind it to an app with ext4 as the FS and 1024 inodes ", func() { + err := deleteResource(rbdExamplePath + "storageclass.yaml") + if err != nil { + framework.Failf("failed to delete storageclass: %v", err) + } + err = createRBDStorageClass( + f.ClientSet, + f, + defaultSCName, + nil, + map[string]string{ + "csi.storage.k8s.io/fstype": "ext4", + "mkfsOptions": "-N1024", // 1024 inodes + }, + deletePolicy) + if err != nil { + framework.Failf("failed to create storageclass: %v", err) + } + err = validatePVCAndAppBinding(pvcPath, appPath, f) + if err != nil { + framework.Failf("failed to validate pvc and application binding: %v", err) + } + err = validateInodeCount(pvcPath, f, 1024) + if err != nil { + framework.Failf("failed to validate pvc and application binding: %v", err) + } + // validate created backend rbd images + validateRBDImageCount(f, 0, defaultRBDPool) + validateOmapCount(f, 0, rbdType, defaultRBDPool, volumesType) + err = deleteResource(rbdExamplePath + "storageclass.yaml") + if err != nil { + framework.Failf("failed to delete storageclass: %v", err) + } + err = createRBDStorageClass(f.ClientSet, f, defaultSCName, nil, nil, deletePolicy) + if err != nil { + framework.Failf("failed to create storageclass: %v", err) + } + }) + By("create a PVC and bind it to an app using rbd-nbd mounter", func() { if !testNBD { framework.Logf("skipping NBD test") diff --git a/e2e/utils.go b/e2e/utils.go index 9273223a959a..a22032a1f1d9 100644 --- a/e2e/utils.go +++ b/e2e/utils.go @@ -486,6 +486,7 @@ func validatePVCAndAppBinding(pvcPath, appPath string, f *framework.Framework) e if err != nil { return err } + err = deletePVCAndApp("", f, pvc, app) return err @@ -508,6 +509,50 @@ func getMountType(selector, mountPath string, f *framework.Framework) (string, e } func validateNormalUserPVCAccess(pvcPath string, f *framework.Framework) error { + writeTest := func(ns string, opts *metav1.ListOptions) error { + _, stdErr, err := execCommandInPod(f, "echo testing > /target/testing", ns, opts) + if err != nil { + return fmt.Errorf("failed to exec command in pod: %w", err) + } + if stdErr != "" { + return fmt.Errorf("failed to touch a file as non-root user %v", stdErr) + } + + return nil + } + + return validateNormalUserPVCAccessFunc(pvcPath, f, writeTest) +} + +func validateInodeCount(pvcPath string, f *framework.Framework, inodes int) error { + countInodes := func(ns string, opts *metav1.ListOptions) error { + stdOut, stdErr, err := execCommandInPod(f, "df --output=itotal /target | tail -n1", ns, opts) + if err != nil { + return fmt.Errorf("failed to exec command in pod: %w", err) + } + if stdErr != "" { + return fmt.Errorf("failed to list inodes in pod: %v", stdErr) + } + + itotal, err := strconv.Atoi(stdOut) + if err != nil { + return fmt.Errorf("failed to parse itotal %q to int: %w", stdOut, err) + } + if inodes != itotal { + return fmt.Errorf("expected inodes (%d) do not match itotal on volume (%d)", inodes, itotal) + } + + return nil + } + + return validateNormalUserPVCAccessFunc(pvcPath, f, countInodes) +} + +func validateNormalUserPVCAccessFunc( + pvcPath string, + f *framework.Framework, + validate func(ns string, opts *metav1.ListOptions) error, +) error { pvc, err := loadPVC(pvcPath) if err != nil { return err @@ -571,12 +616,10 @@ func validateNormalUserPVCAccess(pvcPath string, f *framework.Framework) error { opt := metav1.ListOptions{ LabelSelector: "app=pod-run-as-non-root", } - _, stdErr, err := execCommandInPod(f, "echo testing > /target/testing", app.Namespace, &opt) + + err = validate(app.Namespace, &opt) if err != nil { - return fmt.Errorf("failed to exec command in pod: %w", err) - } - if stdErr != "" { - return fmt.Errorf("failed to touch a file as non-root user %v", stdErr) + return fmt.Errorf("failed to run validation function: %w", err) } // metrics for BlockMode was added in Kubernetes 1.22