Skip to content

Commit be9657e

Browse files
committed
Map PV access modes to CSI access modes
Additionally enforce ReadWriteOncePod can only be used by itself
1 parent 622cdd7 commit be9657e

File tree

2 files changed

+244
-0
lines changed

2 files changed

+244
-0
lines changed

accessmodes/access_modes.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package accessmodes
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/container-storage-interface/spec/lib/go/csi"
23+
v1 "k8s.io/api/core/v1"
24+
)
25+
26+
// ToCSIAccessMode maps PersistentVolume access modes in Kubernetes to CSI
27+
// access modes. Which mapping is used depends if the driver supports the
28+
// SINGLE_NODE_MULTI_WRITER capability.
29+
func ToCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode, supportsSingleNodeMultiWriter bool) (csi.VolumeCapability_AccessMode_Mode, error) {
30+
if supportsSingleNodeMultiWriter {
31+
return toSingleNodeMultiWriterCapableCSIAccessMode(pvAccessModes)
32+
}
33+
return toCSIAccessMode(pvAccessModes)
34+
}
35+
36+
// toCSIAccessMode maps PersistentVolume access modes in Kubernetes to CSI
37+
// access modes.
38+
func toCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode) (csi.VolumeCapability_AccessMode_Mode, error) {
39+
m := uniqueAccessModes(pvAccessModes)
40+
41+
switch {
42+
// This mapping exists to enable CSI drivers that lack the
43+
// SINGLE_NODE_MULTI_WRITER capability to work with the
44+
// ReadWriteOncePod access mode.
45+
case m[v1.ReadWriteOncePod]:
46+
if len(m) > 1 {
47+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("Kubernetes does not support use of ReadWriteOncePod with other access modes on the same PersistentVolume")
48+
}
49+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil
50+
51+
case m[v1.ReadWriteMany]:
52+
// ReadWriteMany takes precedence, regardless of what other
53+
// modes are set.
54+
return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil
55+
56+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
57+
// This is not possible in the CSI spec.
58+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")
59+
60+
case m[v1.ReadOnlyMany]:
61+
return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil
62+
63+
case m[v1.ReadWriteOnce]:
64+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil
65+
66+
default:
67+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes)
68+
}
69+
}
70+
71+
// toSingleNodeMultiWriterCapableCSIAccessMode maps PersistentVolume access
72+
// modes in Kubernetes to CSI access modes for drivers that support the
73+
// SINGLE_NODE_MULTI_WRITER capability.
74+
func toSingleNodeMultiWriterCapableCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode) (csi.VolumeCapability_AccessMode_Mode, error) {
75+
m := uniqueAccessModes(pvAccessModes)
76+
77+
switch {
78+
case m[v1.ReadWriteOncePod]:
79+
if len(m) > 1 {
80+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("Kubernetes does not support use of ReadWriteOncePod with other access modes on the same PersistentVolume")
81+
}
82+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER, nil
83+
84+
case m[v1.ReadWriteMany]:
85+
// ReadWriteMany trumps everything, regardless of what other
86+
// modes are set.
87+
return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil
88+
89+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
90+
// This is not possible in the CSI spec.
91+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")
92+
93+
case m[v1.ReadOnlyMany]:
94+
return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil
95+
96+
case m[v1.ReadWriteOnce]:
97+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER, nil
98+
99+
default:
100+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes)
101+
}
102+
}
103+
104+
func uniqueAccessModes(pvAccessModes []v1.PersistentVolumeAccessMode) map[v1.PersistentVolumeAccessMode]bool {
105+
m := map[v1.PersistentVolumeAccessMode]bool{}
106+
for _, mode := range pvAccessModes {
107+
m[mode] = true
108+
}
109+
return m
110+
}

accessmodes/access_modes_test.go

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package accessmodes
18+
19+
import (
20+
"testing"
21+
22+
"github.com/container-storage-interface/spec/lib/go/csi"
23+
v1 "k8s.io/api/core/v1"
24+
)
25+
26+
func TestToCSIAccessMode(t *testing.T) {
27+
tests := []struct {
28+
name string
29+
pvAccessModes []v1.PersistentVolumeAccessMode
30+
expectedCSIAccessMode csi.VolumeCapability_AccessMode_Mode
31+
expectError bool
32+
supportsSingleNodeMultiWriter bool
33+
}{
34+
{
35+
name: "RWX",
36+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
37+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
38+
},
39+
{
40+
name: "ROX + RWO",
41+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOnce},
42+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
43+
expectError: true,
44+
},
45+
{
46+
name: "ROX + RWOP",
47+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOncePod},
48+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
49+
expectError: true,
50+
},
51+
{
52+
name: "ROX",
53+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
54+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
55+
},
56+
{
57+
name: "RWO",
58+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
59+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
60+
},
61+
{
62+
name: "RWOP",
63+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod},
64+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
65+
},
66+
{
67+
name: "empty",
68+
pvAccessModes: []v1.PersistentVolumeAccessMode{},
69+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
70+
expectError: true,
71+
},
72+
{
73+
name: "RWX with SINGLE_NODE_MULTI_WRITER capable driver",
74+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
75+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
76+
supportsSingleNodeMultiWriter: true,
77+
},
78+
{
79+
name: "ROX + RWO with SINGLE_NODE_MULTI_WRITER capable driver",
80+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOnce},
81+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
82+
expectError: true,
83+
supportsSingleNodeMultiWriter: true,
84+
},
85+
{
86+
name: "ROX + RWOP with SINGLE_NODE_MULTI_WRITER capable driver",
87+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOncePod},
88+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
89+
expectError: true,
90+
supportsSingleNodeMultiWriter: true,
91+
},
92+
{
93+
name: "ROX with SINGLE_NODE_MULTI_WRITER capable driver",
94+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
95+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
96+
supportsSingleNodeMultiWriter: true,
97+
},
98+
{
99+
name: "RWO with SINGLE_NODE_MULTI_WRITER capable driver",
100+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
101+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
102+
supportsSingleNodeMultiWriter: true,
103+
},
104+
{
105+
name: "RWOP with SINGLE_NODE_MULTI_WRITER capable driver",
106+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod},
107+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
108+
supportsSingleNodeMultiWriter: true,
109+
},
110+
{
111+
name: "empty with SINGLE_NODE_MULTI_WRITER capable driver",
112+
pvAccessModes: []v1.PersistentVolumeAccessMode{},
113+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
114+
expectError: true,
115+
supportsSingleNodeMultiWriter: true,
116+
},
117+
}
118+
119+
for _, test := range tests {
120+
t.Run(test.name, func(t *testing.T) {
121+
csiAccessMode, err := ToCSIAccessMode(test.pvAccessModes, test.supportsSingleNodeMultiWriter)
122+
123+
if err == nil && test.expectError {
124+
t.Errorf("test %s: expected error, got none", test.name)
125+
}
126+
if err != nil && !test.expectError {
127+
t.Errorf("test %s: got error: %s", test.name, err)
128+
}
129+
if !test.expectError && csiAccessMode != test.expectedCSIAccessMode {
130+
t.Errorf("test %s: unexpected access mode: %+v", test.name, csiAccessMode)
131+
}
132+
})
133+
}
134+
}

0 commit comments

Comments
 (0)