Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/driver-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Name | Meaning | Example Value | Mandatory | Default value
--- | --- | --- | --- | ---
server | NFS Server address | domain name `nfs-server.default.svc.cluster.local` <br>or IP address `127.0.0.1` | Yes |
share | NFS share path | `/` | Yes |
mountPermissions | mounted folder permissions. The default is `0777` | | No |

### PV/PVC usage (static provisioning)
> [`PersistentVolume` example](../deploy/example/pv-nfs-csi.yaml)
Expand All @@ -16,7 +17,7 @@ Name | Meaning | Example Value | Mandatory | Default value
--- | --- | --- | --- | ---
volumeAttributes.server | NFS Server address | domain name `nfs-server.default.svc.cluster.local` <br>or IP address `127.0.0.1` | Yes |
volumeAttributes.share | NFS share path | `/` | Yes |

volumeAttributes.mountPermissions | mounted folder permissions. The default is `0777` | | No |

### Tips
#### provide `mountOptions` for `DeleteVolume`
Expand Down
73 changes: 48 additions & 25 deletions pkg/nfs/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"

"github.com/container-storage-interface/spec/lib/go/csi"
Expand Down Expand Up @@ -78,8 +79,32 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
return nil, status.Error(codes.InvalidArgument, err.Error())
}

mountPermissions := cs.Driver.mountPermissions
reqCapacity := req.GetCapacityRange().GetRequiredBytes()
nfsVol, err := cs.newNFSVolume(name, reqCapacity, req.GetParameters())
parameters := req.GetParameters()
if parameters == nil {
parameters = make(map[string]string)
}
// validate parameters (case-insensitive)
for k, v := range parameters {
switch strings.ToLower(k) {
case paramServer:
// no op
case paramShare:
// no op
case mountPermissionsField:
if v != "" {
var err error
if mountPermissions, err = strconv.ParseUint(v, 8, 32); err != nil {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s in storage class", v))
}
}
default:
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid parameter %q in storage class", k))
}
}

nfsVol, err := cs.newNFSVolume(name, reqCapacity, parameters)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
Expand All @@ -89,7 +114,7 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
volCap = req.GetVolumeCapabilities()[0]
}
// Mount nfs base share so we can create a subdirectory
if err = cs.internalMount(ctx, nfsVol, volCap); err != nil {
if err = cs.internalMount(ctx, nfsVol, parameters, volCap); err != nil {
return nil, status.Errorf(codes.Internal, "failed to mount nfs server: %v", err.Error())
}
defer func() {
Expand All @@ -98,7 +123,7 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
}
}()

fileMode := os.FileMode(cs.Driver.mountPermissions)
fileMode := os.FileMode(mountPermissions)
// Create subdirectory under base-dir
internalVolumePath := cs.getInternalVolumePath(nfsVol)
if err = os.Mkdir(internalVolumePath, fileMode); err != nil && !os.IsExist(err) {
Expand All @@ -108,7 +133,16 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
if err = os.Chmod(internalVolumePath, fileMode); err != nil {
klog.Warningf("failed to chmod subdirectory: %v", err.Error())
}
return &csi.CreateVolumeResponse{Volume: cs.nfsVolToCSI(nfsVol)}, nil

parameters[paramServer] = nfsVol.server
parameters[paramShare] = cs.getVolumeSharePath(nfsVol)
return &csi.CreateVolumeResponse{
Volume: &csi.Volume{
VolumeId: nfsVol.id,
CapacityBytes: 0, // by setting it to zero, Provisioner will use PVC requested size as PV size
VolumeContext: parameters,
},
}, nil
}

// DeleteVolume delete a volume
Expand Down Expand Up @@ -138,7 +172,7 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
}

// mount nfs base share so we can delete the subdirectory
if err = cs.internalMount(ctx, nfsVol, volCap); err != nil {
if err = cs.internalMount(ctx, nfsVol, nil, volCap); err != nil {
return nil, status.Errorf(codes.Internal, "failed to mount nfs server: %v", err.Error())
}
defer func() {
Expand Down Expand Up @@ -254,7 +288,7 @@ func (cs *ControllerServer) validateVolumeCapability(c *csi.VolumeCapability) er
}

// Mount nfs server at base-dir
func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, volCap *csi.VolumeCapability) error {
func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, volumeContext map[string]string, volCap *csi.VolumeCapability) error {
sharePath := filepath.Join(string(filepath.Separator) + vol.baseDir)
targetPath := cs.getInternalMountPath(vol)

Expand All @@ -266,13 +300,16 @@ func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, v
}
}

if volumeContext == nil {
volumeContext = make(map[string]string)
}
volumeContext[paramServer] = vol.server
volumeContext[paramShare] = sharePath

klog.V(2).Infof("internally mounting %v:%v at %v", vol.server, sharePath, targetPath)
_, err := cs.Driver.ns.NodePublishVolume(ctx, &csi.NodePublishVolumeRequest{
TargetPath: targetPath,
VolumeContext: map[string]string{
paramServer: vol.server,
paramShare: sharePath,
},
TargetPath: targetPath,
VolumeContext: volumeContext,
VolumeCapability: volCap,
VolumeId: vol.id,
})
Expand Down Expand Up @@ -303,8 +340,6 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str
server = v
case paramShare:
baseDir = v
default:
return nil, fmt.Errorf("invalid parameter %q", k)
}
}

Expand Down Expand Up @@ -347,18 +382,6 @@ func (cs *ControllerServer) getVolumeSharePath(vol *nfsVolume) string {
return filepath.Join(string(filepath.Separator), vol.baseDir, vol.subDir)
}

// Convert into nfsVolume into a csi.Volume
func (cs *ControllerServer) nfsVolToCSI(vol *nfsVolume) *csi.Volume {
return &csi.Volume{
CapacityBytes: 0, // by setting it to zero, Provisioner will use PVC requested size as PV size
VolumeId: vol.id,
VolumeContext: map[string]string{
paramServer: vol.server,
paramShare: cs.getVolumeSharePath(vol),
},
}
}

// Given a nfsVolume, return a CSI volume id
func (cs *ControllerServer) getVolumeIDFromNfsVol(vol *nfsVolume) string {
idElements := make([]string, totalIDElements)
Expand Down
32 changes: 28 additions & 4 deletions pkg/nfs/controllerserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,18 @@ func TestCreateVolume(t *testing.T) {
},
},
Parameters: map[string]string{
paramServer: testServer,
paramShare: testBaseDir,
paramServer: testServer,
paramShare: testBaseDir,
mountPermissionsField: "0750",
},
},
resp: &csi.CreateVolumeResponse{
Volume: &csi.Volume{
VolumeId: newTestVolumeID,
VolumeContext: map[string]string{
paramServer: testServer,
paramShare: testShare,
paramServer: testServer,
paramShare: testShare,
mountPermissionsField: "0750",
},
},
},
Expand Down Expand Up @@ -201,6 +203,28 @@ func TestCreateVolume(t *testing.T) {
},
expectErr: true,
},
{
name: "[Error] invalid mountPermissions",
req: &csi.CreateVolumeRequest{
Name: testCSIVolume,
VolumeCapabilities: []*csi.VolumeCapability{
{
AccessType: &csi.VolumeCapability_Mount{
Mount: &csi.VolumeCapability_MountVolume{},
},
AccessMode: &csi.VolumeCapability_AccessMode{
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
},
},
},
Parameters: map[string]string{
paramServer: testServer,
paramShare: testBaseDir,
mountPermissionsField: "07ab",
},
},
expectErr: true,
},
}

for _, test := range cases {
Expand Down
5 changes: 3 additions & 2 deletions pkg/nfs/nfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ 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"
mountOptionsField = "mountoptions"
paramShare = "share"
mountOptionsField = "mountoptions"
mountPermissionsField = "mountpermissions"
)

func NewDriver(options *DriverOptions) *Driver {
Expand Down
15 changes: 12 additions & 3 deletions pkg/nfs/nodeserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package nfs
import (
"fmt"
"os"
"strconv"
"strings"

"github.com/container-storage-interface/spec/lib/go/csi"
Expand Down Expand Up @@ -56,6 +57,7 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
}

var server, baseDir string
mountPermissions := ns.Driver.mountPermissions
for k, v := range req.GetVolumeContext() {
switch strings.ToLower(k) {
case paramServer:
Expand All @@ -66,6 +68,13 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
if v != "" {
mountOptions = append(mountOptions, v)
}
case mountPermissionsField:
if v != "" {
var err error
if mountPermissions, err = strconv.ParseUint(v, 8, 32); err != nil {
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s", v))
}
}
}
}

Expand All @@ -80,7 +89,7 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
notMnt, err := ns.mounter.IsLikelyNotMountPoint(targetPath)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(targetPath, os.FileMode(ns.Driver.mountPermissions)); err != nil {
if err := os.MkdirAll(targetPath, os.FileMode(mountPermissions)); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
notMnt = true
Expand All @@ -104,8 +113,8 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
return nil, status.Error(codes.Internal, err.Error())
}

klog.V(2).Infof("volumeID(%v): mount targetPath(%s) with permissions(0%o)", volumeID, targetPath, ns.Driver.mountPermissions)
if err := os.Chmod(targetPath, os.FileMode(ns.Driver.mountPermissions)); err != nil {
klog.V(2).Infof("volumeID(%v): mount targetPath(%s) with permissions(0%o)", volumeID, targetPath, mountPermissions)
if err := os.Chmod(targetPath, os.FileMode(mountPermissions)); err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &csi.NodePublishVolumeResponse{}, nil
Expand Down
21 changes: 19 additions & 2 deletions pkg/nfs/nodeserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@ func TestNodePublishVolume(t *testing.T) {
}

params := map[string]string{
"server": "server",
"share": "share",
"server": "server",
"share": "share",
mountPermissionsField: "0755",
}

invalidParams := map[string]string{
"server": "server",
"share": "share",
mountPermissionsField: "07ab",
}

volumeCap := csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}
Expand Down Expand Up @@ -112,6 +119,16 @@ func TestNodePublishVolume(t *testing.T) {
Readonly: true},
expectedErr: nil,
},
{
desc: "[Error] invalid mountPermissions",
req: csi.NodePublishVolumeRequest{
VolumeContext: invalidParams,
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
VolumeId: "vol_1",
TargetPath: targetTest,
Readonly: true},
expectedErr: status.Error(codes.InvalidArgument, "invalid mountPermissions 07ab"),
},
}

// setup
Expand Down
1 change: 1 addition & 0 deletions test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ var (
"share": nfsShare,
"csi.storage.k8s.io/provisioner-secret-name": "mount-options",
"csi.storage.k8s.io/provisioner-secret-namespace": "default",
"mountPermissions": "0755",
}
controllerServer *nfs.ControllerServer
)
Expand Down