Skip to content

Commit 48c8713

Browse files
committed
Map PV access modes to CSI access modes
1 parent 917a15a commit 48c8713

File tree

2 files changed

+267
-0
lines changed

2 files changed

+267
-0
lines changed

accessmodes/access_modes.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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+
case m[v1.ReadWriteMany]:
43+
// ReadWriteMany takes precedence, regardless of what other
44+
// modes are set.
45+
return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil
46+
47+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
48+
// This is not possible in the CSI spec.
49+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")
50+
51+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOncePod]:
52+
// This is not possible in the CSI spec.
53+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOncePod on the same PersistentVolume")
54+
55+
case m[v1.ReadWriteOnce] && m[v1.ReadWriteOncePod]:
56+
// This is not possible in the CSI spec.
57+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadWriteOnce and ReadWriteOncePod on the same PersistentVolume")
58+
59+
case m[v1.ReadOnlyMany]:
60+
return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil
61+
62+
case m[v1.ReadWriteOnce]:
63+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil
64+
65+
// This mapping exists to enable CSI drivers that lack the
66+
// SINGLE_NODE_MULTI_WRITER capability to work with the
67+
// ReadWriteOncePod access mode.
68+
case m[v1.ReadWriteOncePod]:
69+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, nil
70+
71+
default:
72+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes)
73+
}
74+
}
75+
76+
// toSingleNodeMultiWriterCapableCSIAccessMode maps PersistentVolume access
77+
// modes in Kubernetes to CSI access modes for drivers that support the
78+
// SINGLE_NODE_MULTI_WRITER capability.
79+
func toSingleNodeMultiWriterCapableCSIAccessMode(pvAccessModes []v1.PersistentVolumeAccessMode) (csi.VolumeCapability_AccessMode_Mode, error) {
80+
m := uniqueAccessModes(pvAccessModes)
81+
82+
switch {
83+
case m[v1.ReadWriteMany]:
84+
// ReadWriteMany trumps everything, regardless of what other
85+
// modes are set.
86+
return csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER, nil
87+
88+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOnce]:
89+
// This is not possible in the CSI spec.
90+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOnce on the same PersistentVolume")
91+
92+
case m[v1.ReadOnlyMany] && m[v1.ReadWriteOncePod]:
93+
// This is not possible in the CSI spec.
94+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadOnlyMany and ReadWriteOncePod on the same PersistentVolume")
95+
96+
case m[v1.ReadWriteOnce] && m[v1.ReadWriteOncePod]:
97+
// This is not possible in the CSI spec.
98+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("CSI does not support ReadWriteOnce and ReadWriteOncePod on the same PersistentVolume")
99+
100+
case m[v1.ReadOnlyMany]:
101+
return csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY, nil
102+
103+
case m[v1.ReadWriteOnce]:
104+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER, nil
105+
106+
case m[v1.ReadWriteOncePod]:
107+
return csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER, nil
108+
109+
default:
110+
return csi.VolumeCapability_AccessMode_UNKNOWN, fmt.Errorf("unsupported AccessMode combination: %+v", pvAccessModes)
111+
}
112+
}
113+
114+
func uniqueAccessModes(pvAccessModes []v1.PersistentVolumeAccessMode) map[v1.PersistentVolumeAccessMode]bool {
115+
m := map[v1.PersistentVolumeAccessMode]bool{}
116+
for _, mode := range pvAccessModes {
117+
m[mode] = true
118+
}
119+
return m
120+
}

accessmodes/access_modes_test.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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: "RWO + RWOP",
53+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadWriteOncePod},
54+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
55+
expectError: true,
56+
},
57+
{
58+
name: "ROX",
59+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
60+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
61+
},
62+
{
63+
name: "RWO",
64+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
65+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
66+
},
67+
{
68+
name: "RWOP",
69+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod},
70+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
71+
},
72+
{
73+
name: "empty",
74+
pvAccessModes: []v1.PersistentVolumeAccessMode{},
75+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
76+
expectError: true,
77+
},
78+
{
79+
name: "RWX with SINGLE_NODE_MULTI_WRITER capable driver",
80+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany},
81+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER,
82+
supportsSingleNodeMultiWriter: true,
83+
},
84+
{
85+
name: "ROX + RWO with SINGLE_NODE_MULTI_WRITER capable driver",
86+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOnce},
87+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
88+
expectError: true,
89+
supportsSingleNodeMultiWriter: true,
90+
},
91+
{
92+
name: "ROX + RWOP with SINGLE_NODE_MULTI_WRITER capable driver",
93+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany, v1.ReadWriteOncePod},
94+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
95+
expectError: true,
96+
supportsSingleNodeMultiWriter: true,
97+
},
98+
{
99+
name: "RWO + RWOP with SINGLE_NODE_MULTI_WRITER capable driver",
100+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadWriteOncePod},
101+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
102+
expectError: true,
103+
supportsSingleNodeMultiWriter: true,
104+
},
105+
{
106+
name: "ROX with SINGLE_NODE_MULTI_WRITER capable driver",
107+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany},
108+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY,
109+
supportsSingleNodeMultiWriter: true,
110+
},
111+
{
112+
name: "RWO with SINGLE_NODE_MULTI_WRITER capable driver",
113+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
114+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_MULTI_WRITER,
115+
supportsSingleNodeMultiWriter: true,
116+
},
117+
{
118+
name: "RWOP with SINGLE_NODE_MULTI_WRITER capable driver",
119+
pvAccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod},
120+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_SINGLE_NODE_SINGLE_WRITER,
121+
supportsSingleNodeMultiWriter: true,
122+
},
123+
{
124+
name: "empty with SINGLE_NODE_MULTI_WRITER capable driver",
125+
pvAccessModes: []v1.PersistentVolumeAccessMode{},
126+
expectedCSIAccessMode: csi.VolumeCapability_AccessMode_UNKNOWN,
127+
expectError: true,
128+
supportsSingleNodeMultiWriter: true,
129+
},
130+
}
131+
132+
for _, test := range tests {
133+
t.Run(test.name, func(t *testing.T) {
134+
csiAccessMode, err := ToCSIAccessMode(test.pvAccessModes, test.supportsSingleNodeMultiWriter)
135+
136+
if err == nil && test.expectError {
137+
t.Errorf("test %s: expected error, got none", test.name)
138+
}
139+
if err != nil && !test.expectError {
140+
t.Errorf("test %s: got error: %s", test.name, err)
141+
}
142+
if !test.expectError && csiAccessMode != test.expectedCSIAccessMode {
143+
t.Errorf("test %s: unexpected access mode: %+v", test.name, csiAccessMode)
144+
}
145+
})
146+
}
147+
}

0 commit comments

Comments
 (0)