Skip to content

Commit 4541a1a

Browse files
authored
Merge pull request #400 from andyzhangx/subdir
feat: add subDir in storage class parameters
2 parents a669d22 + d116c6a commit 4541a1a

File tree

10 files changed

+248
-85
lines changed

10 files changed

+248
-85
lines changed

.github/workflows/static.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ jobs:
1313
uses: golangci/golangci-lint-action@v2
1414
with:
1515
version: v1.29
16-
args: -E=gofmt,golint,misspell --timeout=30m0s
16+
args: -E=gofmt,deadcode,unused,varcheck,ineffassign,golint,misspell --timeout=30m0s

deploy/example/e2e_usage.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ provisioner: smb.csi.k8s.io
2222
parameters:
2323
source: "//smb-server.default.svc.cluster.local/share"
2424
# if csi.storage.k8s.io/provisioner-secret is provided, will create a sub directory
25-
# with PV name under source
25+
# with PV name by default under source
2626
csi.storage.k8s.io/provisioner-secret-name: "smbcreds"
2727
csi.storage.k8s.io/provisioner-secret-namespace: "default"
2828
csi.storage.k8s.io/node-stage-secret-name: "smbcreds"
@@ -52,6 +52,8 @@ provisioner: smb.csi.k8s.io
5252
parameters:
5353
# On Windows, "*.default.svc.cluster.local" could not be recognized by csi-proxy
5454
source: "//smb-server.default.svc.cluster.local/share"
55+
# if csi.storage.k8s.io/provisioner-secret is provided, will create a sub directory
56+
# with PV name by default under source
5557
csi.storage.k8s.io/provisioner-secret-name: "smbcreds"
5658
csi.storage.k8s.io/provisioner-secret-namespace: "default"
5759
csi.storage.k8s.io/node-stage-secret-name: "smbcreds"

deploy/example/storageclass-smb.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ parameters:
88
# On Windows, "*.default.svc.cluster.local" could not be recognized by csi-proxy
99
source: "//smb-server.default.svc.cluster.local/share"
1010
# if csi.storage.k8s.io/provisioner-secret is provided, will create a sub directory
11-
# with PV name under source
11+
# with PV name by default under source
1212
csi.storage.k8s.io/provisioner-secret-name: "smbcreds"
1313
csi.storage.k8s.io/provisioner-secret-namespace: "default"
1414
csi.storage.k8s.io/node-stage-secret-name: "smbcreds"

docs/driver-parameters.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
Name | Meaning | Available Value | Mandatory | Default value
77
--- | --- | --- | --- | ---
88
source | Samba Server address | `//smb-server-address/sharename` </br>([Azure File](https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction) format: `//accountname.file.core.windows.net/filesharename`) | Yes |
9+
subDir | existing sub directory under smb share | | No | sub directory must exist otherwise mount would fail
910
csi.storage.k8s.io/provisioner-secret-name | secret name that stores `username`, `password`(`domain` is optional); if secret is provided, driver will create a sub directory with PV name under `source` | existing secret name | No |
1011
csi.storage.k8s.io/provisioner-secret-namespace | namespace where the secret is | existing secret namespace | No |
1112
csi.storage.k8s.io/node-stage-secret-name | secret name that stores `username`, `password`(`domain` is optional) | existing secret name | Yes |
@@ -17,5 +18,11 @@ csi.storage.k8s.io/node-stage-secret-namespace | namespace where the secret is |
1718
Name | Meaning | Available Value | Mandatory | Default value
1819
--- | --- | --- | --- | ---
1920
volumeAttributes.source | Samba Server address | `//smb-server-address/sharename` </br>([Azure File](https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction) format: `//accountname.file.core.windows.net/filesharename`) | Yes |
21+
volumeAttributes.subDir | existing sub directory under smb share | | No | sub directory must exist otherwise mount would fail
2022
nodeStageSecretRef.name | secret name that stores `username`, `password`(`domain` is optional) | existing secret name | Yes |
2123
nodeStageSecretRef.namespace | namespace where the secret is | k8s namespace | Yes |
24+
25+
- Use `kubectl create secret` to create `smbcreds` secret to store Samba Server username, password
26+
```console
27+
kubectl create secret generic smbcreds --from-literal username=USERNAME --from-literal password="PASSWORD"
28+
```

pkg/smb/controllerserver.go

Lines changed: 81 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"fmt"
2222
"os"
2323
"path/filepath"
24-
"regexp"
2524
"strings"
2625

2726
"github.com/container-storage-interface/spec/lib/go/csi"
@@ -31,8 +30,7 @@ import (
3130
)
3231

3332
const (
34-
volumeIDRegex = "^(.*)#(.*)$"
35-
sourceAndSubDirSeperator = "#"
33+
separator = "#"
3634
)
3735

3836
// smbVolume is an internal representation of a volume
@@ -46,17 +44,19 @@ type smbVolume struct {
4644
subDir string
4745
// size of volume
4846
size int64
47+
// pv name when subDir is not empty
48+
uuid string
4949
}
5050

5151
// Ordering of elements in the CSI volume id.
5252
// ID is of the form {server}/{subDir}.
5353
const (
5454
idsourceField = iota
5555
idSubDir
56+
idUUID
5657
totalIDElements // Always last
5758
)
5859

59-
// CreateVolume only supports static provisioning, no create volume action
6060
func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
6161
name := req.GetName()
6262
if len(name) == 0 {
@@ -74,41 +74,38 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7474

7575
reqCapacity := req.GetCapacityRange().GetRequiredBytes()
7676
parameters := req.GetParameters()
77-
// Validate parameters (case-insensitive).
78-
for k := range parameters {
79-
switch strings.ToLower(k) {
80-
case paramSource:
81-
// no op
82-
default:
83-
return nil, fmt.Errorf("invalid parameter %s in storage class", k)
84-
}
77+
if parameters == nil {
78+
parameters = make(map[string]string)
8579
}
86-
87-
smbVol, err := d.newSMBVolume(name, reqCapacity, parameters)
80+
smbVol, err := newSMBVolume(name, reqCapacity, parameters)
8881
if err != nil {
8982
return nil, status.Error(codes.InvalidArgument, err.Error())
9083
}
9184

9285
secrets := req.GetSecrets()
9386
if len(secrets) > 0 {
94-
// Mount smb base share so we can create a subdirectory
95-
if err := d.internalMount(ctx, smbVol, volCap, secrets); err != nil {
96-
return nil, status.Errorf(codes.Internal, "failed to mount smb server: %v", err.Error())
97-
}
98-
defer func() {
99-
if err = d.internalUnmount(ctx, smbVol); err != nil {
100-
klog.Warningf("failed to unmount smb server: %v", err.Error())
87+
if len(smbVol.uuid) > 0 {
88+
klog.V(2).Infof("existing subDir(%s) is provided, skip subdirectory creation", smbVol.subDir)
89+
} else {
90+
// Mount smb base share so we can create a subdirectory
91+
if err := d.internalMount(ctx, smbVol, volCap, secrets); err != nil {
92+
return nil, status.Errorf(codes.Internal, "failed to mount smb server: %v", err.Error())
10193
}
102-
}()
103-
// Create subdirectory under base-dir
104-
// TODO: revisit permissions
105-
internalVolumePath := d.getInternalVolumePath(smbVol)
106-
if err = os.Mkdir(internalVolumePath, 0777); err != nil && !os.IsExist(err) {
107-
return nil, status.Errorf(codes.Internal, "failed to make subdirectory: %v", err.Error())
94+
defer func() {
95+
if err = d.internalUnmount(ctx, smbVol); err != nil {
96+
klog.Warningf("failed to unmount smb server: %v", err.Error())
97+
}
98+
}()
99+
// Create subdirectory under base-dir
100+
// TODO: revisit permissions
101+
internalVolumePath := d.getInternalVolumePath(smbVol)
102+
if err = os.Mkdir(internalVolumePath, 0777); err != nil && !os.IsExist(err) {
103+
return nil, status.Errorf(codes.Internal, "failed to make subdirectory: %v", err.Error())
104+
}
105+
parameters[subDirField] = smbVol.subDir
108106
}
109-
parameters[sourceField] = parameters[sourceField] + "/" + smbVol.subDir
110107
} else {
111-
klog.Infof("CreateVolume(%s) does not provide secrets", name)
108+
klog.V(2).Infof("CreateVolume(%s) does not provide secrets", name)
112109
}
113110
return &csi.CreateVolumeResponse{Volume: d.smbVolToCSI(smbVol, parameters)}, nil
114111
}
@@ -128,21 +125,25 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
128125

129126
secrets := req.GetSecrets()
130127
if len(secrets) > 0 {
131-
// Mount smb base share so we can delete the subdirectory
132-
if err = d.internalMount(ctx, smbVol, nil, secrets); err != nil {
133-
return nil, status.Errorf(codes.Internal, "failed to mount smb server: %v", err.Error())
134-
}
135-
defer func() {
136-
if err = d.internalUnmount(ctx, smbVol); err != nil {
137-
klog.Warningf("failed to unmount smb server: %v", err.Error())
128+
if len(smbVol.uuid) > 0 {
129+
klog.V(2).Infof("existing subDir(%s) is provided, skip subdirectory creation", smbVol.subDir)
130+
} else {
131+
// Mount smb base share so we can delete the subdirectory
132+
if err = d.internalMount(ctx, smbVol, nil, secrets); err != nil {
133+
return nil, status.Errorf(codes.Internal, "failed to mount smb server: %v", err.Error())
134+
}
135+
defer func() {
136+
if err = d.internalUnmount(ctx, smbVol); err != nil {
137+
klog.Warningf("failed to unmount smb server: %v", err.Error())
138+
}
139+
}()
140+
141+
// Delete subdirectory under base-dir
142+
internalVolumePath := d.getInternalVolumePath(smbVol)
143+
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
144+
if err = os.RemoveAll(internalVolumePath); err != nil {
145+
return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error())
138146
}
139-
}()
140-
141-
// Delete subdirectory under base-dir
142-
internalVolumePath := d.getInternalVolumePath(smbVol)
143-
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
144-
if err = os.RemoveAll(internalVolumePath); err != nil {
145-
return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error())
146147
}
147148
} else {
148149
klog.Infof("DeleteVolume(%s) does not provide secrets", volumeID)
@@ -211,11 +212,12 @@ func (d *Driver) ListSnapshots(ctx context.Context, req *csi.ListSnapshotsReques
211212
}
212213

213214
// Given a smbVolume, return a CSI volume id
214-
func (d *Driver) getVolumeIDFromSmbVol(vol *smbVolume) string {
215+
func getVolumeIDFromSmbVol(vol *smbVolume) string {
215216
idElements := make([]string, totalIDElements)
216217
idElements[idsourceField] = strings.Trim(vol.sourceField, "/")
217218
idElements[idSubDir] = strings.Trim(vol.subDir, "/")
218-
return strings.Join(idElements, sourceAndSubDirSeperator)
219+
idElements[idUUID] = vol.uuid
220+
return strings.Join(idElements, separator)
219221
}
220222

221223
// Get working directory for CreateVolume and DeleteVolume
@@ -266,29 +268,38 @@ func (d *Driver) internalUnmount(ctx context.Context, vol *smbVolume) error {
266268
}
267269

268270
// Convert VolumeCreate parameters to an smbVolume
269-
func (d *Driver) newSMBVolume(name string, size int64, params map[string]string) (*smbVolume, error) {
270-
var sourceField string
271+
func newSMBVolume(name string, size int64, params map[string]string) (*smbVolume, error) {
272+
var source, subDir string
271273

272-
// Validate parameters (case-insensitive).
274+
// validate parameters (case-insensitive).
273275
for k, v := range params {
274276
switch strings.ToLower(k) {
275-
case paramSource:
276-
sourceField = v
277+
case sourceField:
278+
source = v
279+
case subDirField:
280+
subDir = v
281+
default:
282+
return nil, fmt.Errorf("invalid parameter %s in storage class", k)
277283
}
278284
}
279285

280-
// Validate required parameters
281-
if sourceField == "" {
282-
return nil, fmt.Errorf("%v is a required parameter", paramSource)
286+
if source == "" {
287+
return nil, fmt.Errorf("%v is a required parameter", sourceField)
283288
}
284289

285290
vol := &smbVolume{
286-
sourceField: sourceField,
287-
subDir: name,
291+
sourceField: source,
288292
size: size,
289293
}
290-
vol.id = d.getVolumeIDFromSmbVol(vol)
291-
294+
if subDir == "" {
295+
// use pv name by default if not specified
296+
vol.subDir = name
297+
} else {
298+
vol.subDir = subDir
299+
// make volume id unique if subDir is provided
300+
vol.uuid = name
301+
}
302+
vol.id = getVolumeIDFromSmbVol(vol)
292303
return vol, nil
293304
}
294305

@@ -313,17 +324,22 @@ func (d *Driver) smbVolToCSI(vol *smbVolume, parameters map[string]string) *csi.
313324
}
314325

315326
// Given a CSI volume id, return a smbVolume
316-
// a sample volume Id: smb-server.default.svc.cluster.local/share/pvc-4729891a-f57e-4982-9c60-e9884af1be2f
327+
// sample volume Id:
328+
// smb-server.default.svc.cluster.local/share#pvc-4729891a-f57e-4982-9c60-e9884af1be2f
329+
// smb-server.default.svc.cluster.local/share#subdir#pvc-4729891a-f57e-4982-9c60-e9884af1be2f
317330
func getSmbVolFromID(id string) (*smbVolume, error) {
318-
volRegex := regexp.MustCompile(volumeIDRegex)
319-
tokens := volRegex.FindStringSubmatch(id)
320-
if tokens == nil || len(tokens) != 3 {
331+
segments := strings.Split(id, separator)
332+
if len(segments) < 2 {
321333
return nil, fmt.Errorf("Could not split %q into server and subDir", id)
322334
}
323-
source := "//" + tokens[1]
324-
return &smbVolume{
335+
source := "//" + segments[0]
336+
vol := &smbVolume{
325337
id: id,
326338
sourceField: source,
327-
subDir: tokens[2],
328-
}, nil
339+
subDir: segments[1],
340+
}
341+
if len(segments) >= 3 {
342+
vol.uuid = segments[2]
343+
}
344+
return vol, nil
329345
}

0 commit comments

Comments
 (0)