Skip to content

Commit 4875556

Browse files
authored
Merge pull request #619 from andyzhangx/mountPermissions
feat: add mountPermissions parameter in storage class
2 parents 5dbf6cd + a931ef8 commit 4875556

File tree

7 files changed

+138
-42
lines changed

7 files changed

+138
-42
lines changed

docs/driver-parameters.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ storeAccountKey | whether store account key to k8s secret <br><br> Note: <br> `
2323
secretName | specify secret name to store account key | | No |
2424
secretNamespace | specify the namespace of secret to store account key | `default`,`kube-system`, etc | No | `default`
2525
isHnsEnabled | enable `Hierarchical namespace` for Azure DataLake storage account | `true`,`false` | No | `false`
26+
--- | **Following parameters are only for NFS protocol** | --- | --- |
27+
mountPermissions | mounted folder permissions | `0777` | No |
2628

2729
- `fsGroup` securityContext setting
2830

@@ -62,6 +64,8 @@ volumeAttributes.secretName | secret name that stores storage account name and k
6264
volumeAttributes.secretNamespace | secret namespace | `default`,`kube-system`, etc | No | `default`
6365
nodeStageSecretRef.name | secret name that stores(check below examples):<br>`azurestorageaccountkey`<br>`azurestorageaccountsastoken`<br>`msisecret`<br>`azurestoragespnclientsecret` | existing Kubernetes secret name | No |
6466
nodeStageSecretRef.namespace | secret namespace | k8s namespace | Yes |
67+
--- | **Following parameters are only for NFS protocol** | --- | --- |
68+
volumeAttributes.mountPermissions | mounted folder permissions | `0777` | No |
6569
--- | **Following parameters are only for NFS vnet setting** | --- | --- |
6670
vnetResourceGroup | specify vnet resource group where virtual network is | existing resource group name | No | if empty, driver will use the `vnetResourceGroup` value in azure cloud config file
6771
vnetName | virtual network name | existing virtual network name | No | if empty, driver will use the `vnetName` value in azure cloud config file

pkg/blob/blob.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ const (
8383
vnetResourceGroupField = "vnetresourcegroup"
8484
vnetNameField = "vnetname"
8585
subnetNameField = "subnetname"
86+
mountPermissionsField = "mountpermissions"
8687

8788
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
8889
containerNameMinLength = 3

pkg/blob/controllerserver.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package blob
1919
import (
2020
"context"
2121
"fmt"
22+
"strconv"
2223
"strings"
2324

2425
"google.golang.org/grpc/codes"
@@ -130,8 +131,15 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
130131
vnetName = v
131132
case subnetNameField:
132133
subnetName = v
134+
case mountPermissionsField:
135+
// only do validations here, used in NodeStageVolume, NodePublishVolume
136+
if v != "" {
137+
if _, err := strconv.ParseUint(v, 8, 32); err != nil {
138+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s in storage class", v))
139+
}
140+
}
133141
default:
134-
return nil, fmt.Errorf("invalid parameter %s in storage class", k)
142+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid parameter %q in storage class", k))
135143
}
136144
}
137145

@@ -226,7 +234,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
226234
})
227235
d.volLockMap.UnlockEntry(lockKey)
228236
if err != nil {
229-
return nil, status.Errorf(codes.Internal, "failed to ensure storage account: %v", err)
237+
return nil, status.Errorf(codes.Internal, "ensure storage account failed with %v", err)
230238
}
231239
d.accountSearchCache.Set(lockKey, accountName)
232240
d.volMap.Store(volName, accountName)

pkg/blob/controllerserver_test.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,13 +122,14 @@ func TestCreateVolume(t *testing.T) {
122122
d := NewFakeDriver()
123123
d.cloud = &azure.Cloud{}
124124
mp := make(map[string]string)
125-
mp["protocol"] = "unit-test"
125+
mp[protocolField] = "unit-test"
126126
mp[skuNameField] = "unit-test"
127127
mp[storageAccountTypeField] = "unit-test"
128128
mp[locationField] = "unit-test"
129129
mp[storageAccountField] = "unit-test"
130130
mp[resourceGroupField] = "unit-test"
131-
mp["containername"] = "unit-test"
131+
mp[containerNameField] = "unit-test"
132+
mp[mountPermissionsField] = "0750"
132133
req := &csi.CreateVolumeRequest{
133134
Name: "unit-test",
134135
VolumeCapabilities: stdVolumeCapabilities,
@@ -150,8 +151,9 @@ func TestCreateVolume(t *testing.T) {
150151
d := NewFakeDriver()
151152
d.cloud = &azure.Cloud{}
152153
mp := make(map[string]string)
153-
mp["tags"] = "unit-test"
154+
mp[tagsField] = "unit-test"
154155
mp[storageAccountTypeField] = "premium"
156+
mp[mountPermissionsField] = "0700"
155157
req := &csi.CreateVolumeRequest{
156158
Name: "unit-test",
157159
VolumeCapabilities: stdVolumeCapabilities,
@@ -177,7 +179,8 @@ func TestCreateVolume(t *testing.T) {
177179
mp[locationField] = "unit-test"
178180
mp[storageAccountField] = "unit-test"
179181
mp[resourceGroupField] = "unit-test"
180-
mp["containername"] = "unit-test"
182+
mp[containerNameField] = "unit-test"
183+
mp[mountPermissionsField] = "0755"
181184
req := &csi.CreateVolumeRequest{
182185
Name: "unit-test",
183186
VolumeCapabilities: stdVolumeCapabilities,
@@ -196,7 +199,7 @@ func TestCreateVolume(t *testing.T) {
196199
}
197200
mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any()).Return(nil, rerr).AnyTimes()
198201
_, err := d.CreateVolume(context.Background(), req)
199-
expectedErr := status.Errorf(codes.Internal, "failed to ensure storage account: could not list storage accounts for account type : Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
202+
expectedErr := status.Errorf(codes.Internal, "ensure storage account failed with could not list storage accounts for account type : Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
200203
if !reflect.DeepEqual(err, expectedErr) {
201204
t.Errorf("actualErr: (%v), expectedErr: (%v)", err, expectedErr)
202205
}
@@ -223,7 +226,29 @@ func TestCreateVolume(t *testing.T) {
223226
controllerServiceCapability,
224227
}
225228

226-
expectedErr := fmt.Errorf("invalid parameter %s in storage class", "invalidparameter")
229+
expectedErr := status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid parameter %q in storage class", "invalidparameter"))
230+
_, err := d.CreateVolume(context.Background(), req)
231+
if !reflect.DeepEqual(err, expectedErr) {
232+
t.Errorf("Unexpected error: %v", err)
233+
}
234+
},
235+
},
236+
{
237+
name: "invalid mountPermissions",
238+
testFunc: func(t *testing.T) {
239+
d := NewFakeDriver()
240+
mp := make(map[string]string)
241+
mp[mountPermissionsField] = "0abc"
242+
req := &csi.CreateVolumeRequest{
243+
Name: "unit-test",
244+
VolumeCapabilities: stdVolumeCapabilities,
245+
Parameters: mp,
246+
}
247+
d.Cap = []*csi.ControllerServiceCapability{
248+
controllerServiceCapability,
249+
}
250+
251+
expectedErr := status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid %s %s in storage class", "mountPermissions", "0abc"))
227252
_, err := d.CreateVolume(context.Background(), req)
228253
if !reflect.DeepEqual(err, expectedErr) {
229254
t.Errorf("Unexpected error: %v", err)

pkg/blob/nodeserver.go

Lines changed: 49 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ package blob
1818

1919
import (
2020
"fmt"
21+
"io/fs"
2122
"io/ioutil"
2223
"os"
2324
"os/exec"
2425
"path/filepath"
26+
"strconv"
2527
"strings"
2628
"time"
2729

@@ -70,20 +72,30 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
7072
return nil, status.Error(codes.InvalidArgument, "Target path not provided")
7173
}
7274

75+
mountPermissions := d.mountPermissions
7376
context := req.GetVolumeContext()
74-
if context != nil && strings.EqualFold(context[ephemeralField], trueValue) {
75-
context[secretNamespaceField] = context[podNamespaceField]
76-
// only get storage account from secret
77-
context[getAccountKeyFromSecretField] = trueValue
78-
context[storageAccountField] = ""
79-
klog.V(2).Infof("NodePublishVolume: ephemeral volume(%s) mount on %s, VolumeContext: %v", volumeID, target, context)
80-
_, err := d.NodeStageVolume(ctx, &csi.NodeStageVolumeRequest{
81-
StagingTargetPath: target,
82-
VolumeContext: context,
83-
VolumeCapability: volCap,
84-
VolumeId: volumeID,
85-
})
86-
return &csi.NodePublishVolumeResponse{}, err
77+
if context != nil {
78+
if strings.EqualFold(context[ephemeralField], trueValue) {
79+
context[secretNamespaceField] = context[podNamespaceField]
80+
// only get storage account from secret
81+
context[getAccountKeyFromSecretField] = trueValue
82+
context[storageAccountField] = ""
83+
klog.V(2).Infof("NodePublishVolume: ephemeral volume(%s) mount on %s, VolumeContext: %v", volumeID, target, context)
84+
_, err := d.NodeStageVolume(ctx, &csi.NodeStageVolumeRequest{
85+
StagingTargetPath: target,
86+
VolumeContext: context,
87+
VolumeCapability: volCap,
88+
VolumeId: volumeID,
89+
})
90+
return &csi.NodePublishVolumeResponse{}, err
91+
}
92+
93+
if perm := context[mountPermissionsField]; perm != "" {
94+
var err error
95+
if mountPermissions, err = strconv.ParseUint(perm, 8, 32); err != nil {
96+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s", perm))
97+
}
98+
}
8799
}
88100

89101
source := req.GetStagingTargetPath()
@@ -96,7 +108,7 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
96108
mountOptions = append(mountOptions, "ro")
97109
}
98110

99-
mnt, err := d.ensureMountPoint(target)
111+
mnt, err := d.ensureMountPoint(target, fs.FileMode(mountPermissions))
100112
if err != nil {
101113
return nil, status.Errorf(codes.Internal, "Could not mount target %q: %v", target, err)
102114
}
@@ -108,7 +120,7 @@ func (d *Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolu
108120
klog.V(2).Infof("NodePublishVolume: volume %s mounting %s at %s with mountOptions: %v", volumeID, source, target, mountOptions)
109121
if d.enableBlobMockMount {
110122
klog.Warningf("NodePublishVolume: mock mount on volumeID(%s), this is only for TESTING!!!", volumeID)
111-
if err := volumehelper.MakeDir(target, os.FileMode(d.mountPermissions)); err != nil {
123+
if err := volumehelper.MakeDir(target, os.FileMode(mountPermissions)); err != nil {
112124
klog.Errorf("MakeDir failed on target: %s (%v)", target, err)
113125
return nil, err
114126
}
@@ -199,21 +211,13 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
199211
}
200212
defer d.volumeLocks.Release(volumeID)
201213

202-
mnt, err := d.ensureMountPoint(targetPath)
203-
if err != nil {
204-
return nil, status.Errorf(codes.Internal, "Could not mount target %q: %v", targetPath, err)
205-
}
206-
if mnt {
207-
klog.V(2).Infof("NodeStageVolume: volume %s is already mounted on %s", volumeID, targetPath)
208-
return &csi.NodeStageVolumeResponse{}, nil
209-
}
210-
211214
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
212215
attrib := req.GetVolumeContext()
213216
secrets := req.GetSecrets()
214217

215218
var serverAddress, storageEndpointSuffix, protocol, ephemeralVolMountOptions string
216219
var ephemeralVol, isHnsEnabled bool
220+
mountPermissions := d.mountPermissions
217221
for k, v := range attrib {
218222
switch strings.ToLower(k) {
219223
case serverNameField:
@@ -228,9 +232,25 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
228232
ephemeralVolMountOptions = v
229233
case isHnsEnabledField:
230234
isHnsEnabled = strings.EqualFold(v, trueValue)
235+
case mountPermissionsField:
236+
if v != "" {
237+
var err error
238+
if mountPermissions, err = strconv.ParseUint(v, 8, 32); err != nil {
239+
return nil, status.Errorf(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s", v))
240+
}
241+
}
231242
}
232243
}
233244

245+
mnt, err := d.ensureMountPoint(targetPath, fs.FileMode(mountPermissions))
246+
if err != nil {
247+
return nil, status.Errorf(codes.Internal, "Could not mount target %q: %v", targetPath, err)
248+
}
249+
if mnt {
250+
klog.V(2).Infof("NodeStageVolume: volume %s is already mounted on %s", volumeID, targetPath)
251+
return &csi.NodeStageVolumeResponse{}, nil
252+
}
253+
234254
_, accountName, _, containerName, authEnv, err := d.GetAuthEnv(ctx, volumeID, protocol, attrib, secrets)
235255
if err != nil {
236256
return nil, err
@@ -262,10 +282,10 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
262282
}
263283

264284
// set permissions for NFSv3 root folder
265-
if err := os.Chmod(targetPath, os.FileMode(d.mountPermissions)); err != nil {
285+
if err := os.Chmod(targetPath, os.FileMode(mountPermissions)); err != nil {
266286
return nil, status.Error(codes.Internal, fmt.Sprintf("Chmod(%s) failed with %v", targetPath, err))
267287
}
268-
klog.V(2).Infof("volume(%s) mount %q on %q with 0%o succeeded", volumeID, source, targetPath, d.mountPermissions)
288+
klog.V(2).Infof("volume(%s) mount %q on %q with 0%o succeeded", volumeID, source, targetPath, mountPermissions)
269289

270290
return &csi.NodeStageVolumeResponse{}, nil
271291
}
@@ -295,7 +315,7 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
295315
authEnv = append(authEnv, "AZURE_STORAGE_ACCOUNT="+accountName, "AZURE_STORAGE_BLOB_ENDPOINT="+serverAddress)
296316
if d.enableBlobMockMount {
297317
klog.Warningf("NodeStageVolume: mock mount on volumeID(%s), this is only for TESTING!!!", volumeID)
298-
if err := volumehelper.MakeDir(targetPath, os.FileMode(d.mountPermissions)); err != nil {
318+
if err := volumehelper.MakeDir(targetPath, os.FileMode(mountPermissions)); err != nil {
299319
klog.Errorf("MakeDir failed on target: %s (%v)", targetPath, err)
300320
return nil, err
301321
}
@@ -454,7 +474,7 @@ func (d *Driver) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeS
454474

455475
// ensureMountPoint: create mount point if not exists
456476
// return <true, nil> if it's already a mounted point otherwise return <false, nil>
457-
func (d *Driver) ensureMountPoint(target string) (bool, error) {
477+
func (d *Driver) ensureMountPoint(target string, perm os.FileMode) (bool, error) {
458478
notMnt, err := d.mounter.IsLikelyNotMountPoint(target)
459479
if err != nil && !os.IsNotExist(err) {
460480
if IsCorruptedDir(target) {
@@ -500,7 +520,7 @@ func (d *Driver) ensureMountPoint(target string) (bool, error) {
500520
notMnt = true
501521
return !notMnt, err
502522
}
503-
if err := volumehelper.MakeDir(target, os.FileMode(d.mountPermissions)); err != nil {
523+
if err := volumehelper.MakeDir(target, perm); err != nil {
504524
klog.Errorf("MakeDir failed on target: %s (%v)", target, err)
505525
return !notMnt, err
506526
}

pkg/blob/nodeserver_test.go

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func TestEnsureMountPoint(t *testing.T) {
122122
}
123123

124124
for _, test := range tests {
125-
_, err := d.ensureMountPoint(test.target)
125+
_, err := d.ensureMountPoint(test.target, 0777)
126126
if !reflect.DeepEqual(err, test.expectedErr) {
127127
t.Errorf("[%s]: Unexpected Error: %v, expected error: %v", test.desc, err, test.expectedErr)
128128
}
@@ -186,11 +186,16 @@ func TestNodePublishVolume(t *testing.T) {
186186
},
187187
{
188188
desc: "Valid request read only",
189-
req: csi.NodePublishVolumeRequest{VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
189+
req: csi.NodePublishVolumeRequest{
190+
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
190191
VolumeId: "vol_1",
191192
TargetPath: targetTest,
192193
StagingTargetPath: sourceTest,
193-
Readonly: true},
194+
VolumeContext: map[string]string{
195+
mountPermissionsField: "0755",
196+
},
197+
Readonly: true,
198+
},
194199
expectedErr: nil,
195200
},
196201
{
@@ -211,6 +216,19 @@ func TestNodePublishVolume(t *testing.T) {
211216
Readonly: true},
212217
expectedErr: nil,
213218
},
219+
{
220+
desc: "[Error] invalid mountPermissions",
221+
req: csi.NodePublishVolumeRequest{
222+
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
223+
VolumeId: "vol_1",
224+
TargetPath: targetTest,
225+
StagingTargetPath: sourceTest,
226+
VolumeContext: map[string]string{
227+
mountPermissionsField: "07ab",
228+
},
229+
},
230+
expectedErr: status.Error(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s", "07ab")),
231+
},
214232
}
215233

216234
// Setup
@@ -419,6 +437,25 @@ func TestNodeStageVolume(t *testing.T) {
419437
}
420438
},
421439
},
440+
{
441+
name: "[Error] invalid mountPermissions",
442+
testFunc: func(t *testing.T) {
443+
req := &csi.NodeStageVolumeRequest{
444+
VolumeId: "unit-test",
445+
StagingTargetPath: "unit-test",
446+
VolumeCapability: &csi.VolumeCapability{AccessMode: &volumeCap},
447+
VolumeContext: map[string]string{
448+
mountPermissionsField: "07ab",
449+
},
450+
}
451+
d := NewFakeDriver()
452+
_, err := d.NodeStageVolume(context.TODO(), req)
453+
expectedErr := status.Error(codes.InvalidArgument, fmt.Sprintf("invalid mountPermissions %s", "07ab"))
454+
if !reflect.DeepEqual(err, expectedErr) {
455+
t.Errorf("actualErr: (%v), expectedErr: (%v)", err, expectedErr)
456+
}
457+
},
458+
},
422459
}
423460
for _, tc := range testCases {
424461
t.Run(tc.name, tc.testFunc)

test/e2e/dynamic_provisioning_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,9 @@ var _ = ginkgo.Describe("[blob-csi-e2e] Dynamic Provisioning", func() {
520520
CSIDriver: testDriver,
521521
Pods: pods,
522522
StorageClassParameters: map[string]string{
523-
"skuName": "Premium_LRS",
524-
"protocol": "nfs",
523+
"skuName": "Premium_LRS",
524+
"protocol": "nfs",
525+
"mountPermissions": "0755",
525526
},
526527
}
527528
test.Run(cs, ns)

0 commit comments

Comments
 (0)