diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 4ad6c85a62..456090e9bc 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -277,6 +277,44 @@ func makeVolumeName(prefix, pvcUID string, volumeNameUUIDLength int) (string, er return fmt.Sprintf("%s-%s", prefix, strings.Replace(string(pvcUID), "-", "", -1)[0:volumeNameUUIDLength]), nil } +func getVolumeCapabilities(pvc *v1.PersistentVolumeClaim) []*csi.VolumeCapability { + if len(pvc.Spec.AccessModes) == 0 { + return []*csi.VolumeCapability{ + &csi.VolumeCapability{ + AccessType: accessType, + AccessMode: &csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + }, + } + } + + m := map[v1.PersistentVolumeAccessMode]bool{} + for _, mode := range pvc.Spec.AccessModes { + m[mode] = true + } + + var caps []*csi.VolumeCapability + + if m[v1.ReadWriteMany] { + caps = append(caps, &csi.VolumeCapability{ + AccessType: accessType, + AccessMode: &csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}, + }) + } + if m[v1.ReadWriteOnce] { + caps = append(caps, &csi.VolumeCapability{ + AccessType: accessType, + AccessMode: &csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + }) + } + if m[v1.ReadOnlyMany] { + caps = append(caps, &csi.VolumeCapability{ + AccessType: accessType, + AccessMode: &csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY}, + }) + } + return caps +} + func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) { if options.PVC.Spec.Selector != nil { return nil, fmt.Errorf("claim Selector is not supported") @@ -292,20 +330,16 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis return nil, err } + capabilities := getVolumeCapabilities(options.PVC) + capacity := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] volSizeBytes := capacity.Value() // Create a CSI CreateVolumeRequest and Response req := csi.CreateVolumeRequest{ - - Name: pvName, - Parameters: options.Parameters, - VolumeCapabilities: []*csi.VolumeCapability{ - { - AccessType: accessType, - AccessMode: accessMode, - }, - }, + Name: pvName, + Parameters: options.Parameters, + VolumeCapabilities: capabilities, CapacityRange: &csi.CapacityRange{ RequiredBytes: int64(volSizeBytes), }, diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go index 25ac5b6d6c..f06c61d935 100644 --- a/pkg/controller/controller_test.go +++ b/pkg/controller/controller_test.go @@ -651,6 +651,101 @@ func TestGetSecretReference(t *testing.T) { } } +func createVolumeCapabilities(modes []csi.VolumeCapability_AccessMode) []*csi.VolumeCapability { + var res []*csi.VolumeCapability + for i, _ := range modes { + res = append(res, &csi.VolumeCapability{ + AccessType: &csi.VolumeCapability_Mount{ + Mount: &csi.VolumeCapability_MountVolume{}, + }, + AccessMode: &modes[i], + }) + } + return res +} + +func TestGetVolumeCapabilities(t *testing.T) { + tests := []struct { + name string + modes []v1.PersistentVolumeAccessMode + expectedCapabilities []*csi.VolumeCapability + }{ + { + name: "default", + modes: []v1.PersistentVolumeAccessMode{}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + }), + }, + { + name: "RWX", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}, + }), + }, + { + name: "RWO", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + }), + }, + { + name: "ROX", + modes: []v1.PersistentVolumeAccessMode{v1.ReadOnlyMany}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY}, + }), + }, + { + name: "RWX+RWO+ROX", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany, v1.ReadWriteOnce, v1.ReadOnlyMany}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}, + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY}, + }), + }, + { + name: "RWX+RWO", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany, v1.ReadWriteOnce}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}, + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + }), + }, + { + name: "RWX+ROX", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteMany, v1.ReadOnlyMany}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER}, + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY}, + }), + }, + { + name: "RWO+ROX", + modes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce, v1.ReadOnlyMany}, + expectedCapabilities: createVolumeCapabilities([]csi.VolumeCapability_AccessMode{ + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER}, + csi.VolumeCapability_AccessMode{Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_READER_ONLY}, + }), + }, + } + for _, test := range tests { + pvc := &v1.PersistentVolumeClaim{ + Spec: v1.PersistentVolumeClaimSpec{ + AccessModes: test.modes, + }, + } + cap := getVolumeCapabilities(pvc) + + if !reflect.DeepEqual(cap, test.expectedCapabilities) { + t.Errorf("test %s: unexpected VolumeCapability: %+v", test.name, cap) + } + } +} + func TestProvision(t *testing.T) { var requestedBytes int64 = 100