Skip to content

Commit 933455a

Browse files
authored
Merge pull request #353 from andyzhangx/subdir-metadata
feat: support pv/pvc metadata in subDir parameter
2 parents a57d55e + 34d0e6d commit 933455a

File tree

11 files changed

+208
-12
lines changed

11 files changed

+208
-12
lines changed
18 Bytes
Binary file not shown.

charts/latest/csi-driver-nfs/templates/csi-nfs-controller.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ spec:
5050
- "--csi-address=$(ADDRESS)"
5151
- "--leader-election"
5252
- "--leader-election-namespace={{ .Release.Namespace }}"
53+
- "--extra-create-metadata=true"
5354
env:
5455
- name: ADDRESS
5556
value: /csi/csi.sock

deploy/csi-nfs-controller.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ spec:
3838
- "--csi-address=$(ADDRESS)"
3939
- "--leader-election"
4040
- "--leader-election-namespace=kube-system"
41+
- "--extra-create-metadata=true"
4142
env:
4243
- name: ADDRESS
4344
value: /csi/csi.sock

docs/driver-parameters.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ volumeAttributes.share | NFS share path | `/` | Yes |
2121
volumeAttributes.mountPermissions | mounted folder permissions. The default is `0777` | | No |
2222

2323
### Tips
24+
#### `subDir` parameter supports following pv/pvc metadata transform
25+
> if `subDir` value contains following strings, it would transforms into corresponding pv/pvc name or namespace
26+
- `${pvc.metadata.name}`
27+
- `${pvc.metadata.namespace}`
28+
- `${pv.metadata.name}`
29+
2430
#### provide `mountOptions` for `DeleteVolume`
2531
> since `DeleteVolumeRequest` does not provide `mountOptions`, following is the workaround to provide `mountOptions` for `DeleteVolume`, check details [here](https://github.com/kubernetes-csi/csi-driver-nfs/issues/260)
2632
- create a secret with `mountOptions`

pkg/nfs/controllerserver.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,11 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
9393
for k, v := range parameters {
9494
switch strings.ToLower(k) {
9595
case paramServer:
96-
// no op
9796
case paramShare:
98-
// no op
9997
case paramSubDir:
98+
case pvcNamespaceKey:
99+
case pvcNameKey:
100+
case pvNameKey:
100101
// no op
101102
case mountPermissionsField:
102103
if v != "" {
@@ -110,7 +111,7 @@ func (cs *ControllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
110111
}
111112
}
112113

113-
nfsVol, err := cs.newNFSVolume(name, reqCapacity, parameters)
114+
nfsVol, err := newNFSVolume(name, reqCapacity, parameters)
114115
if err != nil {
115116
return nil, status.Error(codes.InvalidArgument, err.Error())
116117
}
@@ -304,9 +305,10 @@ func (cs *ControllerServer) internalUnmount(ctx context.Context, vol *nfsVolume)
304305
return err
305306
}
306307

307-
// Convert VolumeCreate parameters to an nfsVolume
308-
func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[string]string) (*nfsVolume, error) {
308+
// newNFSVolume Convert VolumeCreate parameters to an nfsVolume
309+
func newNFSVolume(name string, size int64, params map[string]string) (*nfsVolume, error) {
309310
var server, baseDir, subDir string
311+
subDirReplaceMap := map[string]string{}
310312

311313
// validate parameters (case-insensitive)
312314
for k, v := range params {
@@ -317,15 +319,18 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str
317319
baseDir = v
318320
case paramSubDir:
319321
subDir = v
322+
case pvcNamespaceKey:
323+
subDirReplaceMap[pvcNamespaceMetadata] = v
324+
case pvcNameKey:
325+
subDirReplaceMap[pvcNameMetadata] = v
326+
case pvNameKey:
327+
subDirReplaceMap[pvNameMetadata] = v
320328
}
321329
}
322330

323331
if server == "" {
324332
return nil, fmt.Errorf("%v is a required parameter", paramServer)
325333
}
326-
if baseDir == "" {
327-
return nil, fmt.Errorf("%v is a required parameter", paramShare)
328-
}
329334

330335
vol := &nfsVolume{
331336
server: server,
@@ -336,11 +341,12 @@ func (cs *ControllerServer) newNFSVolume(name string, size int64, params map[str
336341
// use pv name by default if not specified
337342
vol.subDir = name
338343
} else {
339-
vol.subDir = subDir
344+
// replace pv/pvc name namespace metadata in subDir
345+
vol.subDir = replaceWithMap(subDir, subDirReplaceMap)
340346
// make volume id unique if subDir is provided
341347
vol.uuid = name
342348
}
343-
vol.id = cs.getVolumeIDFromNfsVol(vol)
349+
vol.id = getVolumeIDFromNfsVol(vol)
344350
return vol, nil
345351
}
346352

@@ -368,7 +374,7 @@ func getInternalVolumePath(workingMountDir string, vol *nfsVolume) string {
368374
}
369375

370376
// Given a nfsVolume, return a CSI volume id
371-
func (cs *ControllerServer) getVolumeIDFromNfsVol(vol *nfsVolume) string {
377+
func getVolumeIDFromNfsVol(vol *nfsVolume) string {
372378
idElements := make([]string, totalIDElements)
373379
idElements[idServer] = strings.Trim(vol.server, "/")
374380
idElements[idBaseDir] = strings.Trim(vol.baseDir, "/")

pkg/nfs/controllerserver_test.go

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,87 @@ func TestGetInternalMountPath(t *testing.T) {
529529
assert.Equal(t, path, test.result)
530530
}
531531
}
532+
533+
func TestNewNFSVolume(t *testing.T) {
534+
cases := []struct {
535+
desc string
536+
name string
537+
size int64
538+
params map[string]string
539+
expectVol *nfsVolume
540+
expectErr error
541+
}{
542+
{
543+
desc: "subDir is specified",
544+
name: "pv-name",
545+
size: 100,
546+
params: map[string]string{
547+
paramServer: "//nfs-server.default.svc.cluster.local",
548+
paramShare: "share",
549+
paramSubDir: "subdir",
550+
},
551+
expectVol: &nfsVolume{
552+
id: "nfs-server.default.svc.cluster.local#share#subdir#pv-name",
553+
server: "//nfs-server.default.svc.cluster.local",
554+
baseDir: "share",
555+
subDir: "subdir",
556+
size: 100,
557+
uuid: "pv-name",
558+
},
559+
},
560+
{
561+
desc: "subDir with pv/pvc metadata is specified",
562+
name: "pv-name",
563+
size: 100,
564+
params: map[string]string{
565+
paramServer: "//nfs-server.default.svc.cluster.local",
566+
paramShare: "share",
567+
paramSubDir: fmt.Sprintf("subdir-%s-%s-%s", pvcNameMetadata, pvcNamespaceMetadata, pvNameMetadata),
568+
pvcNameKey: "pvcname",
569+
pvcNamespaceKey: "pvcnamespace",
570+
pvNameKey: "pvname",
571+
},
572+
expectVol: &nfsVolume{
573+
id: "nfs-server.default.svc.cluster.local#share#subdir-pvcname-pvcnamespace-pvname#pv-name",
574+
server: "//nfs-server.default.svc.cluster.local",
575+
baseDir: "share",
576+
subDir: "subdir-pvcname-pvcnamespace-pvname",
577+
size: 100,
578+
uuid: "pv-name",
579+
},
580+
},
581+
{
582+
desc: "subDir not specified",
583+
name: "pv-name",
584+
size: 200,
585+
params: map[string]string{
586+
paramServer: "//nfs-server.default.svc.cluster.local",
587+
paramShare: "share",
588+
},
589+
expectVol: &nfsVolume{
590+
id: "nfs-server.default.svc.cluster.local#share#pv-name#",
591+
server: "//nfs-server.default.svc.cluster.local",
592+
baseDir: "share",
593+
subDir: "pv-name",
594+
size: 200,
595+
uuid: "",
596+
},
597+
},
598+
{
599+
desc: "server value is empty",
600+
params: map[string]string{},
601+
expectVol: nil,
602+
expectErr: fmt.Errorf("%s is a required parameter", paramServer),
603+
},
604+
}
605+
606+
for _, test := range cases {
607+
vol, err := newNFSVolume(test.name, test.size, test.params)
608+
if !reflect.DeepEqual(err, test.expectErr) {
609+
t.Errorf("[test: %s] Unexpected error: %v, expected error: %v", test.desc, err, test.expectErr)
610+
}
611+
if !reflect.DeepEqual(vol, test.expectVol) {
612+
t.Errorf("[test: %s] Unexpected vol: %v, expected vol: %v", test.desc, vol, test.expectVol)
613+
}
614+
}
615+
}

pkg/nfs/nfs.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package nfs
1818

1919
import (
20+
"strings"
21+
2022
"github.com/container-storage-interface/spec/lib/go/csi"
2123
"k8s.io/klog/v2"
2224
mount "k8s.io/mount-utils"
@@ -58,6 +60,12 @@ const (
5860
paramSubDir = "subdir"
5961
mountOptionsField = "mountoptions"
6062
mountPermissionsField = "mountpermissions"
63+
pvcNameKey = "csi.storage.k8s.io/pvc/name"
64+
pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
65+
pvNameKey = "csi.storage.k8s.io/pv/name"
66+
pvcNameMetadata = "${pvc.metadata.name}"
67+
pvcNamespaceMetadata = "${pvc.metadata.namespace}"
68+
pvNameMetadata = "${pv.metadata.name}"
6169
)
6270

6371
func NewDriver(options *DriverOptions) *Driver {
@@ -132,3 +140,13 @@ func IsCorruptedDir(dir string) bool {
132140
_, pathErr := mount.PathExists(dir)
133141
return pathErr != nil && mount.IsCorruptedMnt(pathErr)
134142
}
143+
144+
// replaceWithMap replace key with value for str
145+
func replaceWithMap(str string, m map[string]string) string {
146+
for k, v := range m {
147+
if k != "" {
148+
str = strings.ReplaceAll(str, k, v)
149+
}
150+
}
151+
return str
152+
}

pkg/nfs/nfs_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,55 @@ func TestNewNodeServiceCapability(t *testing.T) {
178178
assert.Equal(t, resp.XXX_sizecache, int32(0))
179179
}
180180
}
181+
182+
func TestReplaceWithMap(t *testing.T) {
183+
tests := []struct {
184+
desc string
185+
str string
186+
m map[string]string
187+
expected string
188+
}{
189+
{
190+
desc: "empty string",
191+
str: "",
192+
expected: "",
193+
},
194+
{
195+
desc: "empty map",
196+
str: "",
197+
m: map[string]string{},
198+
expected: "",
199+
},
200+
{
201+
desc: "empty key",
202+
str: "prefix-" + pvNameMetadata,
203+
m: map[string]string{"": "pv"},
204+
expected: "prefix-" + pvNameMetadata,
205+
},
206+
{
207+
desc: "empty value",
208+
str: "prefix-" + pvNameMetadata,
209+
m: map[string]string{pvNameMetadata: ""},
210+
expected: "prefix-",
211+
},
212+
{
213+
desc: "one replacement",
214+
str: "prefix-" + pvNameMetadata,
215+
m: map[string]string{pvNameMetadata: "pv"},
216+
expected: "prefix-pv",
217+
},
218+
{
219+
desc: "multiple replacements",
220+
str: pvcNamespaceMetadata + pvcNameMetadata,
221+
m: map[string]string{pvcNamespaceMetadata: "namespace", pvcNameMetadata: "pvcname"},
222+
expected: "namespacepvcname",
223+
},
224+
}
225+
226+
for _, test := range tests {
227+
result := replaceWithMap(test.str, test.m)
228+
if result != test.expected {
229+
t.Errorf("test[%s]: unexpected output: %v, expected result: %v", test.desc, result, test.expected)
230+
}
231+
}
232+
}

pkg/nfs/nodeserver.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
5757
}
5858

5959
var server, baseDir, subDir string
60+
subDirReplaceMap := map[string]string{}
61+
6062
mountPermissions := ns.Driver.mountPermissions
6163
performChmodOp := (mountPermissions > 0)
6264
for k, v := range req.GetVolumeContext() {
@@ -67,6 +69,12 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
6769
baseDir = v
6870
case paramSubDir:
6971
subDir = v
72+
case pvcNamespaceKey:
73+
subDirReplaceMap[pvcNamespaceMetadata] = v
74+
case pvcNameKey:
75+
subDirReplaceMap[pvcNameMetadata] = v
76+
case pvNameKey:
77+
subDirReplaceMap[pvNameMetadata] = v
7078
case mountOptionsField:
7179
if v != "" {
7280
mountOptions = append(mountOptions, v)
@@ -96,6 +104,9 @@ func (ns *NodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublis
96104
server = getServerFromSource(server)
97105
source := fmt.Sprintf("%s:%s", server, baseDir)
98106
if subDir != "" {
107+
// replace pv/pvc name namespace metadata in subDir
108+
subDir = replaceWithMap(subDir, subDirReplaceMap)
109+
99110
source = strings.TrimRight(source, "/")
100111
source = fmt.Sprintf("%s/%s", source, subDir)
101112
}

pkg/nfs/nodeserver_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ func TestNodePublishVolume(t *testing.T) {
4747
"share": "share",
4848
mountPermissionsField: "0755",
4949
}
50+
paramsWithMetadata := map[string]string{
51+
"server": "server",
52+
"share": "share",
53+
pvcNameKey: "pvcname",
54+
pvcNamespaceKey: "pvcnamespace",
55+
pvNameKey: "pvname",
56+
}
5057
paramsWithZeroPermissions := map[string]string{
5158
"server": "server",
5259
"share": "share",
@@ -126,6 +133,16 @@ func TestNodePublishVolume(t *testing.T) {
126133
Readonly: true},
127134
expectedErr: nil,
128135
},
136+
{
137+
desc: "[Success] Valid request with pv/pvc metadata",
138+
req: csi.NodePublishVolumeRequest{
139+
VolumeContext: paramsWithMetadata,
140+
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
141+
VolumeId: "vol_1",
142+
TargetPath: targetTest,
143+
Readonly: true},
144+
expectedErr: nil,
145+
},
129146
{
130147
desc: "[Success] Valid request with 0 mountPermissions",
131148
req: csi.NodePublishVolumeRequest{

0 commit comments

Comments
 (0)