Skip to content

Commit bd1d58e

Browse files
authored
Merge pull request #349 from andyzhangx/subDir
feat: add subDir in storage class parameters
2 parents c1fe33f + cc729e9 commit bd1d58e

File tree

9 files changed

+237
-47
lines changed

9 files changed

+237
-47
lines changed

docs/driver-parameters.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Name | Meaning | Example Value | Mandatory | Default value
88
--- | --- | --- | --- | ---
99
server | NFS Server address | domain name `nfs-server.default.svc.cluster.local` <br>or IP address `127.0.0.1` | Yes |
1010
share | NFS share path | `/` | Yes |
11+
subDir | sub directory under nfs share | | No | if sub directory does not exist, this driver would create a new one
1112
mountPermissions | mounted folder permissions. The default is `0777`, if set as `0`, driver will not perform `chmod` after mount | | No |
1213

1314
### PV/PVC usage (static provisioning)

pkg/nfs/controllerserver.go

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ type nfsVolume struct {
5252
subDir string
5353
// size of volume
5454
size int64
55+
// pv name when subDir is not empty
56+
uuid string
5557
}
5658

5759
// Ordering of elements in the CSI volume id.
@@ -64,6 +66,7 @@ const (
6466
idServer = iota
6567
idBaseDir
6668
idSubDir
69+
idUUID
6770
totalIDElements // Always last
6871
)
6972

@@ -93,6 +96,8 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
9396
// no op
9497
case paramShare:
9598
// no op
99+
case paramSubDir:
100+
// no op
96101
case mountPermissionsField:
97102
if v != "" {
98103
var err error
@@ -126,7 +131,7 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
126131

127132
fileMode := os.FileMode(mountPermissions)
128133
// Create subdirectory under base-dir
129-
internalVolumePath := cs.getInternalVolumePath(nfsVol)
134+
internalVolumePath := getInternalVolumePath(cs.Driver.workingMountDir, nfsVol)
130135
if err = os.Mkdir(internalVolumePath, fileMode); err != nil && !os.IsExist(err) {
131136
return nil, status.Errorf(codes.Internal, "failed to make subdirectory: %v", err.Error())
132137
}
@@ -135,8 +140,7 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
135140
klog.Warningf("failed to chmod subdirectory: %v", err.Error())
136141
}
137142

138-
parameters[paramServer] = nfsVol.server
139-
parameters[paramShare] = cs.getVolumeSharePath(nfsVol)
143+
setKeyValueInMap(parameters, paramSubDir, nfsVol.subDir)
140144
return &csi.CreateVolumeResponse{
141145
Volume: &csi.Volume{
142146
VolumeId: nfsVol.id,
@@ -183,7 +187,7 @@ func (cs *ControllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
183187
}()
184188

185189
// delete subdirectory under base-dir
186-
internalVolumePath := cs.getInternalVolumePath(nfsVol)
190+
internalVolumePath := getInternalVolumePath(cs.Driver.workingMountDir, nfsVol)
187191

188192
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
189193
if err = os.RemoveAll(internalVolumePath); err != nil {
@@ -255,9 +259,6 @@ func (cs *ControllerServer) ControllerExpandVolume(ctx context.Context, req *csi
255259

256260
// Mount nfs server at base-dir
257261
func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, volumeContext map[string]string, volCap *csi.VolumeCapability) error {
258-
sharePath := filepath.Join(string(filepath.Separator) + vol.baseDir)
259-
targetPath := cs.getInternalMountPath(vol)
260-
261262
if volCap == nil {
262263
volCap = &csi.VolumeCapability{
263264
AccessType: &csi.VolumeCapability_Mount{
@@ -266,16 +267,24 @@ func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, v
266267
}
267268
}
268269

269-
if volumeContext == nil {
270-
volumeContext = make(map[string]string)
270+
sharePath := filepath.Join(string(filepath.Separator) + vol.baseDir)
271+
targetPath := getInternalMountPath(cs.Driver.workingMountDir, vol)
272+
273+
volContext := map[string]string{
274+
paramServer: vol.server,
275+
paramShare: sharePath,
276+
}
277+
for k, v := range volumeContext {
278+
// don't set subDir field since only nfs-server:/share should be mounted in CreateVolume/DeleteVolume
279+
if strings.ToLower(k) != paramSubDir {
280+
volContext[k] = v
281+
}
271282
}
272-
volumeContext[paramServer] = vol.server
273-
volumeContext[paramShare] = sharePath
274283

275-
klog.V(2).Infof("internally mounting %v:%v at %v", vol.server, sharePath, targetPath)
284+
klog.V(2).Infof("internally mounting %s:%s at %s", vol.server, sharePath, targetPath)
276285
_, err := cs.Driver.ns.NodePublishVolume(ctx, &csi.NodePublishVolumeRequest{
277286
TargetPath: targetPath,
278-
VolumeContext: volumeContext,
287+
VolumeContext: volContext,
279288
VolumeCapability: volCap,
280289
VolumeId: vol.id,
281290
})
@@ -284,20 +293,20 @@ func (cs *ControllerServer) internalMount(ctx context.Context, vol *nfsVolume, v
284293

285294
// Unmount nfs server at base-dir
286295
func (cs *ControllerServer) internalUnmount(ctx context.Context, vol *nfsVolume) error {
287-
targetPath := cs.getInternalMountPath(vol)
296+
targetPath := getInternalMountPath(cs.Driver.workingMountDir, vol)
288297

289298
// Unmount nfs server at base-dir
290299
klog.V(4).Infof("internally unmounting %v", targetPath)
291300
_, err := cs.Driver.ns.NodeUnpublishVolume(ctx, &csi.NodeUnpublishVolumeRequest{
292301
VolumeId: vol.id,
293-
TargetPath: cs.getInternalMountPath(vol),
302+
TargetPath: targetPath,
294303
})
295304
return err
296305
}
297306

298307
// Convert VolumeCreate parameters to an nfsVolume
299308
func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[string]string) (*nfsVolume, error) {
300-
var server, baseDir string
309+
var server, baseDir, subDir string
301310

302311
// validate parameters (case-insensitive)
303312
for k, v := range params {
@@ -306,6 +315,8 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str
306315
server = v
307316
case paramShare:
308317
baseDir = v
318+
case paramSubDir:
319+
subDir = v
309320
}
310321
}
311322

@@ -319,17 +330,30 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str
319330
vol := &nfsVolume{
320331
server: server,
321332
baseDir: baseDir,
322-
subDir: name,
323333
size: size,
324334
}
335+
if subDir == "" {
336+
// use pv name by default if not specified
337+
vol.subDir = name
338+
} else {
339+
vol.subDir = subDir
340+
// make volume id unique if subDir is provided
341+
vol.uuid = name
342+
}
325343
vol.id = cs.getVolumeIDFromNfsVol(vol)
326-
327344
return vol, nil
328345
}
329346

330-
// Get working directory for CreateVolume and DeleteVolume
331-
func (cs *ControllerServer) getInternalMountPath(vol *nfsVolume) string {
332-
return filepath.Join(cs.Driver.workingMountDir, vol.subDir)
347+
// getInternalMountPath: get working directory for CreateVolume and DeleteVolume
348+
func getInternalMountPath(workingMountDir string, vol *nfsVolume) string {
349+
if vol == nil {
350+
return ""
351+
}
352+
mountDir := vol.uuid
353+
if vol.uuid == "" {
354+
mountDir = vol.subDir
355+
}
356+
return filepath.Join(workingMountDir, mountDir)
333357
}
334358

335359
// Get internal path where the volume is created
@@ -339,13 +363,8 @@ func (cs *ControllerServer) getInternalMountPath(vol *nfsVolume) string {
339363
// CreateVolume calls in parallel and they may use the same underlying share.
340364
// Instead of refcounting how many CreateVolume calls are using the same
341365
// share, it's simpler to just do a mount per request.
342-
func (cs *ControllerServer) getInternalVolumePath(vol *nfsVolume) string {
343-
return filepath.Join(cs.getInternalMountPath(vol), vol.subDir)
344-
}
345-
346-
// Get user-visible share path for the volume
347-
func (cs *ControllerServer) getVolumeSharePath(vol *nfsVolume) string {
348-
return filepath.Join(string(filepath.Separator), vol.baseDir, vol.subDir)
366+
func getInternalVolumePath(workingMountDir string, vol *nfsVolume) string {
367+
return filepath.Join(getInternalMountPath(workingMountDir, vol), vol.subDir)
349368
}
350369

351370
// Given a nfsVolume, return a CSI volume id
@@ -354,22 +373,25 @@ func (cs *ControllerServer) getVolumeIDFromNfsVol(vol *nfsVolume) string {
354373
idElements[idServer] = strings.Trim(vol.server, "/")
355374
idElements[idBaseDir] = strings.Trim(vol.baseDir, "/")
356375
idElements[idSubDir] = strings.Trim(vol.subDir, "/")
376+
idElements[idUUID] = vol.uuid
357377
return strings.Join(idElements, separator)
358378
}
359379

360380
// Given a CSI volume id, return a nfsVolume
361381
// sample volume Id:
362-
// new volumeID: nfs-server.default.svc.cluster.local#share#pvc-4bcbf944-b6f7-4bd0-b50f-3c3dd00efc64
382+
// new volumeID:
383+
// nfs-server.default.svc.cluster.local#share#pvc-4bcbf944-b6f7-4bd0-b50f-3c3dd00efc64
384+
// nfs-server.default.svc.cluster.local#share#subdir#pvc-4bcbf944-b6f7-4bd0-b50f-3c3dd00efc64
363385
// old volumeID: nfs-server.default.svc.cluster.local/share/pvc-4bcbf944-b6f7-4bd0-b50f-3c3dd00efc64
364386
func getNfsVolFromID(id string) (*nfsVolume, error) {
365-
var server, baseDir, subDir string
387+
var server, baseDir, subDir, uuid string
366388
segments := strings.Split(id, separator)
367389
if len(segments) < 3 {
368390
klog.V(2).Infof("could not split %s into server, baseDir and subDir with separator(%s)", id, separator)
369-
// try with separator "/""
391+
// try with separator "/"
370392
volRegex := regexp.MustCompile("^([^/]+)/(.*)/([^/]+)$")
371393
tokens := volRegex.FindStringSubmatch(id)
372-
if tokens == nil {
394+
if tokens == nil || len(tokens) < 4 {
373395
return nil, fmt.Errorf("could not split %s into server, baseDir and subDir with separator(%s)", id, "/")
374396
}
375397
server = tokens[1]
@@ -379,13 +401,17 @@ func getNfsVolFromID(id string) (*nfsVolume, error) {
379401
server = segments[0]
380402
baseDir = segments[1]
381403
subDir = segments[2]
404+
if len(segments) >= 4 {
405+
uuid = segments[3]
406+
}
382407
}
383408

384409
return &nfsVolume{
385410
id: id,
386411
server: server,
387412
baseDir: baseDir,
388413
subDir: subDir,
414+
uuid: uuid,
389415
}, nil
390416
}
391417

pkg/nfs/controllerserver_test.go

Lines changed: 63 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"fmt"
2828

2929
"github.com/container-storage-interface/spec/lib/go/csi"
30+
"github.com/stretchr/testify/assert"
3031
"golang.org/x/net/context"
3132
"google.golang.org/grpc/codes"
3233
"google.golang.org/grpc/status"
@@ -37,16 +38,12 @@ const (
3738
testServer = "test-server"
3839
testBaseDir = "test-base-dir"
3940
testBaseDirNested = "test/base/dir"
40-
testCSIVolume = "test-csi"
41-
testVolumeID = "test-server/test-base-dir/test-csi"
42-
newTestVolumeID = "test-server#test-base-dir#test-csi"
43-
testVolumeIDNested = "test-server/test/base/dir/test-csi"
44-
newTestVolumeIDNested = "test-server#test/base/dir#test-csi"
45-
)
46-
47-
// for Windows support in the future
48-
var (
49-
testShare = filepath.Join(string(filepath.Separator), testBaseDir, string(filepath.Separator), testCSIVolume)
41+
testCSIVolume = "volume-name"
42+
testVolumeID = "test-server/test-base-dir/volume-name"
43+
newTestVolumeID = "test-server#test-base-dir#volume-name#"
44+
testVolumeIDNested = "test-server/test/base/dir/volume-name"
45+
newTestVolumeIDNested = "test-server#test/base/dir#volume-name#"
46+
newTestVolumeIDUUID = "test-server#test-base-dir#volume-name#uuid"
5047
)
5148

5249
func initTestController(t *testing.T) *ControllerServer {
@@ -110,7 +107,8 @@ func TestCreateVolume(t *testing.T) {
110107
VolumeId: newTestVolumeID,
111108
VolumeContext: map[string]string{
112109
paramServer: testServer,
113-
paramShare: testShare,
110+
paramShare: testBaseDir,
111+
paramSubDir: testCSIVolume,
114112
mountPermissionsField: "0750",
115113
},
116114
},
@@ -133,14 +131,16 @@ func TestCreateVolume(t *testing.T) {
133131
Parameters: map[string]string{
134132
paramServer: testServer,
135133
paramShare: testBaseDir,
134+
paramSubDir: testCSIVolume,
136135
},
137136
},
138137
resp: &csi.CreateVolumeResponse{
139138
Volume: &csi.Volume{
140-
VolumeId: newTestVolumeID,
139+
VolumeId: newTestVolumeID + testCSIVolume,
141140
VolumeContext: map[string]string{
142141
paramServer: testServer,
143-
paramShare: testShare,
142+
paramShare: testBaseDir,
143+
paramSubDir: testCSIVolume,
144144
},
145145
},
146146
},
@@ -417,6 +417,18 @@ func TestNfsVolFromId(t *testing.T) {
417417
},
418418
expectErr: false,
419419
},
420+
{
421+
name: "valid request nested baseDir with newTestVolumeIDNested",
422+
volumeID: newTestVolumeIDUUID,
423+
resp: &nfsVolume{
424+
id: newTestVolumeIDUUID,
425+
server: testServer,
426+
baseDir: testBaseDir,
427+
subDir: testCSIVolume,
428+
uuid: "uuid",
429+
},
430+
expectErr: false,
431+
},
420432
}
421433

422434
for _, test := range cases {
@@ -479,3 +491,41 @@ func TestIsValidVolumeCapabilities(t *testing.T) {
479491
}
480492
}
481493
}
494+
495+
func TestGetInternalMountPath(t *testing.T) {
496+
cases := []struct {
497+
desc string
498+
workingMountDir string
499+
vol *nfsVolume
500+
result string
501+
}{
502+
{
503+
desc: "nil volume",
504+
workingMountDir: "/tmp",
505+
result: "",
506+
},
507+
{
508+
desc: "uuid not empty",
509+
workingMountDir: "/tmp",
510+
vol: &nfsVolume{
511+
subDir: "subdir",
512+
uuid: "uuid",
513+
},
514+
result: filepath.Join("/tmp", "uuid"),
515+
},
516+
{
517+
desc: "uuid empty",
518+
workingMountDir: "/tmp",
519+
vol: &nfsVolume{
520+
subDir: "subdir",
521+
uuid: "",
522+
},
523+
result: filepath.Join("/tmp", "subdir"),
524+
},
525+
}
526+
527+
for _, test := range cases {
528+
path := getInternalMountPath(test.workingMountDir, test.vol)
529+
assert.Equal(t, path, test.result)
530+
}
531+
}

pkg/nfs/nfs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const (
5555
// The root directory is omitted from the string, for example:
5656
// "base" instead of "/base"
5757
paramShare = "share"
58+
paramSubDir = "subdir"
5859
mountOptionsField = "mountoptions"
5960
mountPermissionsField = "mountpermissions"
6061
)

pkg/nfs/nodeserver.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
5656
mountOptions = append(mountOptions, "ro")
5757
}
5858

59-
var server, baseDir string
59+
var server, baseDir, subDir string
6060
mountPermissions := ns.Driver.mountPermissions
6161
performChmodOp := (mountPermissions > 0)
6262
for k, v := range req.GetVolumeContext() {
@@ -65,6 +65,8 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
6565
server = v
6666
case paramShare:
6767
baseDir = v
68+
case paramSubDir:
69+
subDir = v
6870
case mountOptionsField:
6971
if v != "" {
7072
mountOptions = append(mountOptions, v)
@@ -93,6 +95,10 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
9395
}
9496
server = getServerFromSource(server)
9597
source := fmt.Sprintf("%s:%s", server, baseDir)
98+
if subDir != "" {
99+
source = strings.TrimRight(source, "/")
100+
source = fmt.Sprintf("%s/%s", source, subDir)
101+
}
96102

97103
notMnt, err := ns.mounter.IsLikelyNotMountPoint(targetPath)
98104
if err != nil {

0 commit comments

Comments
 (0)