Skip to content

Commit edb9683

Browse files
authored
Merge pull request #1537 from k8s-infra-cherrypick-robot/cherry-pick-1533-to-release-1.24
[release-1.24] fix: respect ReadOnly AccessMode in volume mount
2 parents 1b0f8de + d1d1ecc commit edb9683

File tree

6 files changed

+147
-2
lines changed

6 files changed

+147
-2
lines changed

pkg/blob/blob.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1096,3 +1096,12 @@ func isSupportedFSGroupChangePolicy(policy string) bool {
10961096
}
10971097
return false
10981098
}
1099+
1100+
func isReadOnlyFromCapability(vc *csi.VolumeCapability) bool {
1101+
if vc.GetAccessMode() == nil {
1102+
return false
1103+
}
1104+
mode := vc.GetAccessMode().GetMode()
1105+
return (mode == csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY ||
1106+
mode == csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY)
1107+
}

pkg/blob/blob_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"time"
3030

3131
"github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2021-09-01/storage"
32+
"github.com/container-storage-interface/spec/lib/go/csi"
3233
"github.com/stretchr/testify/assert"
3334
"go.uber.org/mock/gomock"
3435
"golang.org/x/sync/errgroup"
@@ -1822,3 +1823,59 @@ func TestIsSupportedFSGroupChangePolicy(t *testing.T) {
18221823
}
18231824
}
18241825
}
1826+
1827+
func TestIsReadOnlyFromCapability(t *testing.T) {
1828+
testCases := []struct {
1829+
name string
1830+
vc *csi.VolumeCapability
1831+
expectedResult bool
1832+
}{
1833+
{
1834+
name: "false with empty capabilities",
1835+
vc: &csi.VolumeCapability{},
1836+
expectedResult: false,
1837+
},
1838+
{
1839+
name: "fail with capabilities no access mode",
1840+
vc: &csi.VolumeCapability{
1841+
AccessType: &csi.VolumeCapability_Mount{
1842+
Mount: &csi.VolumeCapability_MountVolume{},
1843+
},
1844+
},
1845+
},
1846+
{
1847+
name: "false with SINGLE_NODE_WRITER capabilities",
1848+
vc: &csi.VolumeCapability{
1849+
AccessMode: &csi.VolumeCapability_AccessMode{
1850+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
1851+
},
1852+
},
1853+
expectedResult: false,
1854+
},
1855+
{
1856+
name: "true with MULTI_NODE_READER_ONLY capabilities",
1857+
vc: &csi.VolumeCapability{
1858+
AccessMode: &csi.VolumeCapability_AccessMode{
1859+
Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
1860+
},
1861+
},
1862+
expectedResult: true,
1863+
},
1864+
{
1865+
name: "true with SINGLE_NODE_READER_ONLY capabilities",
1866+
vc: &csi.VolumeCapability{
1867+
AccessMode: &csi.VolumeCapability_AccessMode{
1868+
Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
1869+
},
1870+
},
1871+
expectedResult: true,
1872+
},
1873+
}
1874+
1875+
for _, test := range testCases {
1876+
result := isReadOnlyFromCapability(test.vc)
1877+
if result != test.expectedResult {
1878+
t.Errorf("case(%s): isReadOnlyFromCapability returned with %v, not equal to %v", test.name, result, test.expectedResult)
1879+
}
1880+
}
1881+
}

pkg/blob/nodeserver.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,15 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
340340
serverAddress = fmt.Sprintf("%s.blob.%s", accountName, storageEndpointSuffix)
341341
}
342342

343+
if isReadOnlyFromCapability(volumeCapability) {
344+
if isNFSProtocol(protocol) {
345+
mountFlags = util.JoinMountOptions(mountFlags, []string{"ro"})
346+
} else {
347+
mountFlags = util.JoinMountOptions(mountFlags, []string{"-o ro"})
348+
}
349+
klog.V(2).Infof("CSI volume is read-only, mounting with extra option ro")
350+
}
351+
343352
if isNFSProtocol(protocol) {
344353
klog.V(2).Infof("target %v\nprotocol %v\n\nvolumeId %v\nmountflags %v\nserverAddress %v",
345354
targetPath, protocol, volumeID, mountFlags, serverAddress)
@@ -412,7 +421,7 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
412421
args = args + " " + opt
413422
}
414423

415-
klog.V(2).Infof("target %v\nprotocol %v\n\nvolumeId %v\nountflags %v mountOptions %v volumeMountGroup %s\nargs %v\nserverAddress %v",
424+
klog.V(2).Infof("target %v protocol %v volumeId %v\nmountflags %v\nmountOptions %v volumeMountGroup %s\nargs %v\nserverAddress %v",
416425
targetPath, protocol, volumeID, mountFlags, mountOptions, volumeMountGroup, args, serverAddress)
417426

418427
authEnv = append(authEnv, "AZURE_STORAGE_ACCOUNT="+accountName, "AZURE_STORAGE_BLOB_ENDPOINT="+serverAddress)

test/e2e/dynamic_provisioning_test.go

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ var _ = ginkgo.Describe("[blob-csi-e2e] Dynamic Provisioning", func() {
205205
Cmd: "touch /mnt/test-1/data",
206206
Volumes: []testsuites.VolumeDetails{
207207
{
208-
FSType: "ext4",
209208
ClaimSize: "10Gi",
210209
VolumeMount: testsuites.VolumeMountDetails{
211210
NameGenerate: "test-volume-",
@@ -227,6 +226,66 @@ var _ = ginkgo.Describe("[blob-csi-e2e] Dynamic Provisioning", func() {
227226
test.Run(ctx, cs, ns)
228227
})
229228

229+
ginkgo.It("should create a blobfuse volume on demand and mount it as readOnly when volume access mode is readonly", func(ctx ginkgo.SpecContext) {
230+
pods := []testsuites.PodDetails{
231+
{
232+
Cmd: "touch /mnt/test-1/data",
233+
Volumes: []testsuites.VolumeDetails{
234+
{
235+
ClaimSize: "10Gi",
236+
VolumeMount: testsuites.VolumeMountDetails{
237+
NameGenerate: "test-volume-",
238+
MountPathGenerate: "/mnt/test-",
239+
},
240+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
241+
},
242+
},
243+
},
244+
}
245+
test := testsuites.DynamicallyProvisionedReadOnlyVolumeTest{
246+
CSIDriver: testDriver,
247+
Pods: pods,
248+
StorageClassParameters: map[string]string{"skuName": "Standard_GRS"},
249+
}
250+
if isAzureStackCloud {
251+
test.StorageClassParameters = map[string]string{"skuName": "Standard_LRS"}
252+
}
253+
test.Run(ctx, cs, ns)
254+
})
255+
256+
ginkgo.It("should create a nfs volume on demand and mount it as readOnly when volume access mode is readonly", func(ctx ginkgo.SpecContext) {
257+
if isAzureStackCloud {
258+
ginkgo.Skip("test case is not available for Azure Stack")
259+
}
260+
pods := []testsuites.PodDetails{
261+
{
262+
Cmd: "touch /mnt/test-1/data",
263+
Volumes: []testsuites.VolumeDetails{
264+
{
265+
ClaimSize: "10Gi",
266+
MountOptions: []string{
267+
"nconnect=8",
268+
},
269+
VolumeMount: testsuites.VolumeMountDetails{
270+
NameGenerate: "test-volume-",
271+
MountPathGenerate: "/mnt/test-",
272+
},
273+
AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
274+
},
275+
},
276+
},
277+
}
278+
test := testsuites.DynamicallyProvisionedReadOnlyVolumeTest{
279+
CSIDriver: testDriver,
280+
Pods: pods,
281+
StorageClassParameters: map[string]string{
282+
"skuName": "Premium_LRS",
283+
"protocol": "nfs",
284+
},
285+
}
286+
test.Run(ctx, cs, ns)
287+
})
288+
230289
ginkgo.It("should create multiple PV objects, bind to PVCs and attach all to different pods on the same node", func(ctx ginkgo.SpecContext) {
231290
pods := []testsuites.PodDetails{
232291
{

test/e2e/testsuites/specs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ type VolumeDetails struct {
5555
StorageClass *storagev1.StorageClass
5656
NodeStageSecretRef string
5757
Attrib map[string]string
58+
AccessModes []v1.PersistentVolumeAccessMode
5859
}
5960

6061
type VolumeMode int
@@ -196,6 +197,11 @@ func (volume *VolumeDetails) SetupDynamicPersistentVolumeClaim(ctx context.Conte
196197
} else {
197198
tpvc = NewTestPersistentVolumeClaim(client, namespace, volume.ClaimSize, volume.VolumeMode, &createdStorageClass)
198199
}
200+
201+
if len(volume.AccessModes) > 0 {
202+
ginkgo.By("setting up the PVC with access modes")
203+
tpvc.AccessModes = volume.AccessModes
204+
}
199205
tpvc.Create(ctx)
200206
cleanupFuncs = append(cleanupFuncs, tpvc.Cleanup)
201207
// PV will not be ready until PVC is used in a pod when volumeBindingMode: WaitForFirstConsumer

test/e2e/testsuites/testsuites.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ type TestPersistentVolumeClaim struct {
123123
persistentVolumeClaim *v1.PersistentVolumeClaim
124124
requestedPersistentVolumeClaim *v1.PersistentVolumeClaim
125125
dataSource *v1.TypedLocalObjectReference
126+
AccessModes []v1.PersistentVolumeAccessMode
126127
}
127128

128129
func NewTestPersistentVolumeClaim(c clientset.Interface, ns *v1.Namespace, claimSize string, volumeMode VolumeMode, sc *storagev1.StorageClass) *TestPersistentVolumeClaim {
@@ -163,6 +164,10 @@ func (t *TestPersistentVolumeClaim) Create(ctx context.Context) {
163164
storageClassName = t.storageClass.Name
164165
}
165166
t.requestedPersistentVolumeClaim = generatePVC(t.namespace.Name, storageClassName, t.claimSize, t.volumeMode, t.dataSource)
167+
if len(t.AccessModes) > 0 {
168+
ginkgo.By("setting access modes")
169+
t.requestedPersistentVolumeClaim.Spec.AccessModes = t.AccessModes
170+
}
166171
t.persistentVolumeClaim, err = t.client.CoreV1().PersistentVolumeClaims(t.namespace.Name).Create(ctx, t.requestedPersistentVolumeClaim, metav1.CreateOptions{})
167172
framework.ExpectNoError(err)
168173
}

0 commit comments

Comments
 (0)