Skip to content

Commit 9e27f40

Browse files
committed
fix: respect ReadOnly AccessMode in volume mount
1 parent 29f7873 commit 9e27f40

File tree

6 files changed

+142
-1
lines changed

6 files changed

+142
-1
lines changed

pkg/azurefile/nodeserver.go

+5
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
178178
volumeMountGroup := req.GetVolumeCapability().GetMount().GetVolumeMountGroup()
179179
gidPresent := checkGidPresentInMountFlags(mountFlags)
180180

181+
if isReadOnlyFromCapability(volumeCapability) {
182+
mountFlags = util.JoinMountOptions(mountFlags, []string{"ro"})
183+
klog.V(2).Infof("CSI volume is read-only, mounting with extra option ro")
184+
}
185+
181186
mc := metrics.NewMetricContext(azureFileCSIDriverName, "node_stage_volume", d.cloud.ResourceGroup, "", d.Name)
182187
isOperationSucceeded := false
183188
defer func() {

pkg/azurefile/utils.go

+10
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"sync"
2626
"time"
2727

28+
"github.com/container-storage-interface/spec/lib/go/csi"
2829
v1 "k8s.io/api/core/v1"
2930
"k8s.io/klog/v2"
3031
"k8s.io/kubernetes/pkg/volume"
@@ -307,3 +308,12 @@ func replaceWithMap(str string, m map[string]string) string {
307308
}
308309
return str
309310
}
311+
312+
func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
313+
if vc.GetAccessMode() == nil {
314+
return false
315+
}
316+
mode := vc.GetAccessMode().GetMode()
317+
return (mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
318+
mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY)
319+
}

pkg/azurefile/utils_test.go

+57
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"testing"
2626
"time"
2727

28+
"github.com/container-storage-interface/spec/lib/go/csi"
2829
utiltesting "k8s.io/client-go/util/testing"
2930
)
3031

@@ -692,3 +693,59 @@ func TestReplaceWithMap(t *testing.T) {
692693
}
693694
}
694695
}
696+
697+
func TestIsReadOnlyFromCapability(t *testing.T) {
698+
testCases := []struct {
699+
name string
700+
vc *csi.VolumeCapability
701+
expectedResult bool
702+
}{
703+
{
704+
name: "false with empty capabilities",
705+
vc: &csi.VolumeCapability{},
706+
expectedResult: false,
707+
},
708+
{
709+
name: "fail with capabilities no access mode",
710+
vc: &csi.VolumeCapability{
711+
AccessType: &csi.VolumeCapability_Mount{
712+
Mount: &csi.VolumeCapability_MountVolume{},
713+
},
714+
},
715+
},
716+
{
717+
name: "false with SINGLE_NODE_WRITER capabilities",
718+
vc: &csi.VolumeCapability{
719+
AccessMode: &csi.VolumeCapability_AccessMode{
720+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
721+
},
722+
},
723+
expectedResult: false,
724+
},
725+
{
726+
name: "true with MULTI_NODE_READER_ONLY capabilities",
727+
vc: &csi.VolumeCapability{
728+
AccessMode: &csi.VolumeCapability_AccessMode{
729+
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
730+
},
731+
},
732+
expectedResult: true,
733+
},
734+
{
735+
name: "true with SINGLE_NODE_READER_ONLY capabilities",
736+
vc: &csi.VolumeCapability{
737+
AccessMode: &csi.VolumeCapability_AccessMode{
738+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
739+
},
740+
},
741+
expectedResult: true,
742+
},
743+
}
744+
745+
for _, test := range testCases {
746+
result := isReadOnlyFromCapability(test.vc)
747+
if result != test.expectedResult {
748+
t.Errorf("case(%s): isReadOnlyFromCapability returned with %v, not equal to %v", test.name, result, test.expectedResult)
749+
}
750+
}
751+
}

test/e2e/dynamic_provisioning_test.go

+64-1
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,6 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
355355
Cmd: convertToPowershellCommandIfNecessary("touch /mnt/test-1/data"),
356356
Volumes: []testsuites.VolumeDetails{
357357
{
358-
FSType: "ext4",
359358
ClaimSize: "10Gi",
360359
VolumeMount: testsuites.VolumeMountDetails{
361360
NameGenerate: "test-volume-",
@@ -383,6 +382,70 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
383382
test.Run(ctx, cs, ns)
384383
})
385384

385+
ginkgo.It("should create a smb volume on demand and mount it as readOnly when volume access mode is readonly [kubernetes.io/azure-file] [file.csi.azure.com] [Windows]", func(ctx ginkgo.SpecContext) {
386+
skipIfUsingInTreeVolumePlugin()
387+
skipIfTestingInWindowsCluster()
388+
389+
pods := []testsuites.PodDetails{
390+
{
391+
Cmd: convertToPowershellCommandIfNecessary("touch /mnt/test-1/data"),
392+
Volumes: []testsuites.VolumeDetails{
393+
{
394+
ClaimSize: "10Gi",
395+
VolumeMount: testsuites.VolumeMountDetails{
396+
NameGenerate: "test-volume-",
397+
MountPathGenerate: "/mnt/test-",
398+
},
399+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
400+
},
401+
},
402+
IsWindows: isWindowsCluster,
403+
WinServerVer: winServerVer,
404+
},
405+
}
406+
scParameters := map[string]string{
407+
"skuName": "Standard_LRS",
408+
}
409+
test := testsuites.DynamicallyProvisionedReadOnlyVolumeTest{
410+
CSIDriver: testDriver,
411+
Pods: pods,
412+
StorageClassParameters: scParameters,
413+
}
414+
test.Run(ctx, cs, ns)
415+
})
416+
417+
ginkgo.It("should create a nfs volume on demand and mount it as readOnly when volume access mode is readonly [kubernetes.io/azure-file] [file.csi.azure.com] [Windows]", func(ctx ginkgo.SpecContext) {
418+
skipIfUsingInTreeVolumePlugin()
419+
skipIfTestingInWindowsCluster()
420+
421+
pods := []testsuites.PodDetails{
422+
{
423+
Cmd: convertToPowershellCommandIfNecessary("touch /mnt/test-1/data"),
424+
Volumes: []testsuites.VolumeDetails{
425+
{
426+
ClaimSize: "10Gi",
427+
VolumeMount: testsuites.VolumeMountDetails{
428+
NameGenerate: "test-volume-",
429+
MountPathGenerate: "/mnt/test-",
430+
},
431+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
432+
},
433+
},
434+
IsWindows: isWindowsCluster,
435+
WinServerVer: winServerVer,
436+
},
437+
}
438+
scParameters := map[string]string{
439+
"protocol": "nfs",
440+
}
441+
test := testsuites.DynamicallyProvisionedReadOnlyVolumeTest{
442+
CSIDriver: testDriver,
443+
Pods: pods,
444+
StorageClassParameters: scParameters,
445+
}
446+
test.Run(ctx, cs, ns)
447+
})
448+
386449
ginkgo.It("should create a deployment object, write and read to it, delete the pod and write and read to it again [kubernetes.io/azure-file] [file.csi.azure.com] [Windows]", func(ctx ginkgo.SpecContext) {
387450
pod := testsuites.PodDetails{
388451
Cmd: convertToPowershellCommandIfNecessary("echo 'hello world' >> /mnt/test-1/data && while true; do sleep 100; done"),

test/e2e/testsuites/specs.go

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ type VolumeDetails struct {
5757
StorageClass *storagev1.StorageClass
5858
ShareName string
5959
NodeStageSecretRef string
60+
AccessModes []v1.PersistentVolumeAccessMode
6061
}
6162

6263
type VolumeMode int

test/e2e/testsuites/testsuites.go

+5
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ type TestPersistentVolumeClaim struct {
148148
persistentVolumeClaim *v1.PersistentVolumeClaim
149149
requestedPersistentVolumeClaim *v1.PersistentVolumeClaim
150150
dataSource *v1.TypedLocalObjectReference
151+
AccessModes []v1.PersistentVolumeAccessMode
151152
}
152153

153154
func NewTestPersistentVolumeClaim(c clientset.Interface, ns *v1.Namespace, claimSize string, volumeMode VolumeMode, sc *storagev1.StorageClass) *TestPersistentVolumeClaim {
@@ -188,6 +189,10 @@ func (t *TestPersistentVolumeClaim) Create(ctx context.Context) {
188189
storageClassName = t.storageClass.Name
189190
}
190191
t.requestedPersistentVolumeClaim = generatePVC(t.namespace.Name, storageClassName, t.claimSize, t.volumeMode, t.dataSource)
192+
if len(t.AccessModes) > 0 {
193+
ginkgo.By("setting access modes")
194+
t.requestedPersistentVolumeClaim.Spec.AccessModes = t.AccessModes
195+
}
191196
t.persistentVolumeClaim, err = t.client.CoreV1().PersistentVolumeClaims(t.namespace.Name).Create(ctx, t.requestedPersistentVolumeClaim, metav1.CreateOptions{})
192197
framework.ExpectNoError(err)
193198
}

0 commit comments

Comments
 (0)