diff --git a/Makefile b/Makefile index c011f026a..9593ccc4a 100644 --- a/Makefile +++ b/Makefile @@ -151,6 +151,8 @@ endif .PHONY: install-nfs-server install-nfs-server: kubectl apply -f ./deploy/example/nfs-provisioner/nfs-server.yaml + kubectl delete secret mount-options --ignore-not-found + kubectl create secret generic mount-options --from-literal mountOptions="nfsvers=4.1" .PHONY: install-helm install-helm: diff --git a/charts/latest/csi-driver-nfs-v3.1.0.tgz b/charts/latest/csi-driver-nfs-v3.1.0.tgz index 9716a4e2f..37037adab 100644 Binary files a/charts/latest/csi-driver-nfs-v3.1.0.tgz and b/charts/latest/csi-driver-nfs-v3.1.0.tgz differ diff --git a/charts/latest/csi-driver-nfs/templates/rbac-csi-nfs-controller.yaml b/charts/latest/csi-driver-nfs/templates/rbac-csi-nfs-controller.yaml index 5fd44e2f0..73e4ae07d 100644 --- a/charts/latest/csi-driver-nfs/templates/rbac-csi-nfs-controller.yaml +++ b/charts/latest/csi-driver-nfs/templates/rbac-csi-nfs-controller.yaml @@ -37,6 +37,9 @@ rules: - apiGroups: ["coordination.k8s.io"] resources: ["leases"] verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 diff --git a/deploy/rbac-csi-nfs-controller.yaml b/deploy/rbac-csi-nfs-controller.yaml index 20860e594..2ac4a680a 100644 --- a/deploy/rbac-csi-nfs-controller.yaml +++ b/deploy/rbac-csi-nfs-controller.yaml @@ -33,6 +33,9 @@ rules: - apiGroups: ["coordination.k8s.io"] resources: ["leases"] verbs: ["get", "list", "watch", "create", "update", "patch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get"] --- kind: ClusterRoleBinding diff --git a/docs/driver-parameters.md b/docs/driver-parameters.md index 351f53471..d2cdc542d 100644 --- a/docs/driver-parameters.md +++ b/docs/driver-parameters.md @@ -1,5 +1,5 @@ ## Driver Parameters -> This plugin driver itself only provides a communication layer between resources in the cluser and the NFS server, you need to bring your own NFS server before using this driver. +> This driver requires existing and already configured NFSv3 or NFSv4 server, it supports dynamic provisioning of Persistent Volumes via Persistent Volume Claims by creating a new sub directory under NFS server. ### Storage Class Usage (Dynamic Provisioning) > [`StorageClass` example](../deploy/example/storageclass-nfs.yaml) diff --git a/pkg/nfs/controllerserver.go b/pkg/nfs/controllerserver.go index 3478f1b84..f596a34ee 100644 --- a/pkg/nfs/controllerserver.go +++ b/pkg/nfs/controllerserver.go @@ -127,8 +127,21 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol return &csi.DeleteVolumeResponse{}, nil } + var volCap *csi.VolumeCapability + mountOptions := getMountOptions(req.GetSecrets()) + if mountOptions != "" { + klog.V(2).Infof("DeleteVolume: found mountOptions(%s) for volume(%s)", mountOptions, volumeID) + volCap = &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{ + MountFlags: []string{mountOptions}, + }, + }, + } + } + // Mount nfs base share so we can delete the subdirectory - if err = cs.internalMount(ctx, nfsVol, nil); err != nil { + if err = cs.internalMount(ctx, nfsVol, volCap); err != nil { return nil, status.Errorf(codes.Internal, "failed to mount nfs server: %v", err.Error()) } defer func() { @@ -285,8 +298,7 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str baseDir string ) - // Validate parameters (case-insensitive). - // TODO do more strict validation. + // validate parameters (case-insensitive) for k, v := range params { switch strings.ToLower(k) { case paramServer: @@ -298,7 +310,7 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str } } - // Validate required parameters + // validate required parameters if server == "" { return nil, fmt.Errorf("%v is a required parameter", paramServer) } diff --git a/pkg/nfs/nfs.go b/pkg/nfs/nfs.go index b728eb41e..516fdea90 100644 --- a/pkg/nfs/nfs.go +++ b/pkg/nfs/nfs.go @@ -49,7 +49,8 @@ const ( // The base directory must be a direct child of the root directory. // The root directory is omitted from the string, for example: // "base" instead of "/base" - paramShare = "share" + paramShare = "share" + mountOptionsField = "mountoptions" ) func NewDriver(nodeID, driverName, endpoint string, perm *uint32) *Driver { diff --git a/pkg/nfs/utils.go b/pkg/nfs/utils.go index 1c20569f0..d0810bb5f 100644 --- a/pkg/nfs/utils.go +++ b/pkg/nfs/utils.go @@ -121,3 +121,14 @@ func (vl *VolumeLocks) Release(volumeID string) { defer vl.mux.Unlock() vl.locks.Delete(volumeID) } + +// getMountOptions get mountOptions value from a map +func getMountOptions(context map[string]string) string { + for k, v := range context { + switch strings.ToLower(k) { + case mountOptionsField: + return v + } + } + return "" +} diff --git a/pkg/nfs/utils_test.go b/pkg/nfs/utils_test.go index ca761893d..9a8f0c9d0 100644 --- a/pkg/nfs/utils_test.go +++ b/pkg/nfs/utils_test.go @@ -118,3 +118,39 @@ func TestGetLogLevel(t *testing.T) { } } } + +func TestGetMountOptions(t *testing.T) { + tests := []struct { + desc string + context map[string]string + result string + }{ + { + desc: "nil context", + context: nil, + result: "", + }, + { + desc: "empty context", + context: map[string]string{}, + result: "", + }, + { + desc: "valid mountOptions", + context: map[string]string{"mountOptions": "nfsvers=3"}, + result: "nfsvers=3", + }, + { + desc: "valid mountOptions(lowercase)", + context: map[string]string{"mountoptions": "nfsvers=4"}, + result: "nfsvers=4", + }, + } + + for _, test := range tests { + result := getMountOptions(test.context) + if result != test.result { + t.Errorf("Unexpected result: %s, expected: %s", result, test.result) + } + } +} diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index a3b69328d..09680c676 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -47,6 +47,8 @@ var ( defaultStorageClassParameters = map[string]string{ "server": "nfs-server.default.svc.cluster.local", "share": "/", + "csi.storage.k8s.io/provisioner-secret-name": "mount-options", + "csi.storage.k8s.io/provisioner-secret-namespace": "default", } controllerServer *nfs.ControllerServer )