diff --git a/pkg/controller/volume/persistentvolume/index.go b/pkg/controller/volume/persistentvolume/index.go index eee6b97d280..1dcac700ea4 100644 --- a/pkg/controller/volume/persistentvolume/index.go +++ b/pkg/controller/volume/persistentvolume/index.go @@ -25,6 +25,7 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/kubernetes/pkg/api/v1" v1helper "k8s.io/kubernetes/pkg/api/v1/helper" + "k8s.io/kubernetes/pkg/volume" ) // persistentVolumeOrderedIndex is a cache.Store that keeps persistent volumes @@ -206,7 +207,7 @@ func (pvIndex *persistentVolumeOrderedIndex) allPossibleMatchingAccessModes(requ keys := pvIndex.store.ListIndexFuncValues("accessmodes") for _, key := range keys { indexedModes := v1helper.GetAccessModesFromString(key) - if containedInAll(indexedModes, requestedModes) { + if volume.AccessModesContainedInAll(indexedModes, requestedModes) { matchedModes = append(matchedModes, indexedModes) } } @@ -218,24 +219,6 @@ func (pvIndex *persistentVolumeOrderedIndex) allPossibleMatchingAccessModes(requ return matchedModes } -func contains(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool { - for _, m := range modes { - if m == mode { - return true - } - } - return false -} - -func containedInAll(indexedModes []v1.PersistentVolumeAccessMode, requestedModes []v1.PersistentVolumeAccessMode) bool { - for _, mode := range requestedModes { - if !contains(indexedModes, mode) { - return false - } - } - return true -} - // byAccessModes is used to order access modes by size, with the fewest modes first type byAccessModes struct { modes [][]v1.PersistentVolumeAccessMode diff --git a/pkg/controller/volume/persistentvolume/index_test.go b/pkg/controller/volume/persistentvolume/index_test.go index 75c8109f452..caeefe5cc60 100644 --- a/pkg/controller/volume/persistentvolume/index_test.go +++ b/pkg/controller/volume/persistentvolume/index_test.go @@ -26,6 +26,7 @@ import ( "k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1/ref" + "k8s.io/kubernetes/pkg/volume" ) func makePVC(size string, modfn func(*v1.PersistentVolumeClaim)) *v1.PersistentVolumeClaim { @@ -257,7 +258,7 @@ func TestAllPossibleAccessModes(t *testing.T) { t.Errorf("Expected 3 arrays of modes that match RWO, but got %v", len(possibleModes)) } for _, m := range possibleModes { - if !contains(m, v1.ReadWriteOnce) { + if !volume.AccessModesContains(m, v1.ReadWriteOnce) { t.Errorf("AccessModes does not contain %s", v1.ReadWriteOnce) } } @@ -266,7 +267,7 @@ func TestAllPossibleAccessModes(t *testing.T) { if len(possibleModes) != 1 { t.Errorf("Expected 1 array of modes that match RWX, but got %v", len(possibleModes)) } - if !contains(possibleModes[0], v1.ReadWriteMany) { + if !volume.AccessModesContains(possibleModes[0], v1.ReadWriteMany) { t.Errorf("AccessModes does not contain %s", v1.ReadWriteOnce) } diff --git a/pkg/volume/aws_ebs/aws_ebs.go b/pkg/volume/aws_ebs/aws_ebs.go index 6a046d7654b..d0c3d3a541d 100644 --- a/pkg/volume/aws_ebs/aws_ebs.go +++ b/pkg/volume/aws_ebs/aws_ebs.go @@ -430,6 +430,10 @@ type awsElasticBlockStoreProvisioner struct { var _ volume.Provisioner = &awsElasticBlockStoreProvisioner{} func (c *awsElasticBlockStoreProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } + volumeID, sizeGB, labels, err := c.manager.CreateVolume(c) if err != nil { glog.Errorf("Provision failed: %v", err) diff --git a/pkg/volume/azure_dd/azure_provision.go b/pkg/volume/azure_dd/azure_provision.go index 71679b5fcf8..0b63b5b73da 100644 --- a/pkg/volume/azure_dd/azure_provision.go +++ b/pkg/volume/azure_dd/azure_provision.go @@ -107,6 +107,10 @@ type azureDiskProvisioner struct { var _ volume.Provisioner = &azureDiskProvisioner{} func (a *azureDiskProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(a.plugin.GetAccessModes(), a.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", a.options.PVC.Spec.AccessModes, a.plugin.GetAccessModes()) + } + var sku, location, account string // maxLength = 79 - (4 for ".vhd") = 75 diff --git a/pkg/volume/azure_file/azure_provision.go b/pkg/volume/azure_file/azure_provision.go index 1020b60cc84..11fab8f1c03 100644 --- a/pkg/volume/azure_file/azure_provision.go +++ b/pkg/volume/azure_file/azure_provision.go @@ -132,6 +132,10 @@ type azureFileProvisioner struct { var _ volume.Provisioner = &azureFileProvisioner{} func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(a.plugin.GetAccessModes(), a.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", a.options.PVC.Spec.AccessModes, a.plugin.GetAccessModes()) + } + var sku, location, account string name := volume.GenerateVolumeName(a.options.ClusterName, a.options.PVName, 75) diff --git a/pkg/volume/cinder/cinder.go b/pkg/volume/cinder/cinder.go index fc6fbee01a1..92a3e5710f8 100644 --- a/pkg/volume/cinder/cinder.go +++ b/pkg/volume/cinder/cinder.go @@ -482,6 +482,10 @@ type cinderVolumeProvisioner struct { var _ volume.Provisioner = &cinderVolumeProvisioner{} func (c *cinderVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } + volumeID, sizeGB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/flocker/flocker_volume.go b/pkg/volume/flocker/flocker_volume.go index 703ae6f6815..aa183c497c8 100644 --- a/pkg/volume/flocker/flocker_volume.go +++ b/pkg/volume/flocker/flocker_volume.go @@ -55,6 +55,9 @@ type flockerVolumeProvisioner struct { var _ volume.Provisioner = &flockerVolumeProvisioner{} func (c *flockerVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } if len(c.options.Parameters) > 0 { return nil, fmt.Errorf("Provisioning failed: Specified at least one unsupported parameter") diff --git a/pkg/volume/gce_pd/gce_pd.go b/pkg/volume/gce_pd/gce_pd.go index 9abb1982a3d..685d82d7f2d 100644 --- a/pkg/volume/gce_pd/gce_pd.go +++ b/pkg/volume/gce_pd/gce_pd.go @@ -375,6 +375,10 @@ type gcePersistentDiskProvisioner struct { var _ volume.Provisioner = &gcePersistentDiskProvisioner{} func (c *gcePersistentDiskProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } + volumeID, sizeGB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/glusterfs/glusterfs.go b/pkg/volume/glusterfs/glusterfs.go index dc790731c8f..dd6d52d1336 100644 --- a/pkg/volume/glusterfs/glusterfs.go +++ b/pkg/volume/glusterfs/glusterfs.go @@ -788,6 +788,10 @@ func (d *glusterfsVolumeDeleter) Delete() error { } func (p *glusterfsVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes()) + } + var err error if p.options.PVC.Spec.Selector != nil { glog.V(4).Infof("glusterfs: not able to parse your claim Selector") diff --git a/pkg/volume/photon_pd/photon_pd.go b/pkg/volume/photon_pd/photon_pd.go index e5cd8ca09be..7733de663c8 100644 --- a/pkg/volume/photon_pd/photon_pd.go +++ b/pkg/volume/photon_pd/photon_pd.go @@ -343,6 +343,10 @@ func (plugin *photonPersistentDiskPlugin) newProvisionerInternal(options volume. } func (p *photonPersistentDiskProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes()) + } + pdID, sizeGB, fstype, err := p.manager.CreateVolume(p) if err != nil { return nil, err diff --git a/pkg/volume/portworx/portworx.go b/pkg/volume/portworx/portworx.go index 9d5a6123aa4..9aa93bb8494 100644 --- a/pkg/volume/portworx/portworx.go +++ b/pkg/volume/portworx/portworx.go @@ -351,6 +351,10 @@ type portworxVolumeProvisioner struct { var _ volume.Provisioner = &portworxVolumeProvisioner{} func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } + volumeID, sizeGB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err diff --git a/pkg/volume/quobyte/quobyte.go b/pkg/volume/quobyte/quobyte.go index 9afc91903b8..4aa2d99379a 100644 --- a/pkg/volume/quobyte/quobyte.go +++ b/pkg/volume/quobyte/quobyte.go @@ -356,6 +356,10 @@ type quobyteVolumeProvisioner struct { } func (provisioner *quobyteVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(provisioner.plugin.GetAccessModes(), provisioner.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", provisioner.options.PVC.Spec.AccessModes, provisioner.plugin.GetAccessModes()) + } + if provisioner.options.PVC.Spec.Selector != nil { return nil, fmt.Errorf("claim Selector is not supported") } diff --git a/pkg/volume/rbd/rbd.go b/pkg/volume/rbd/rbd.go index 4532c311b68..3163cb8b597 100644 --- a/pkg/volume/rbd/rbd.go +++ b/pkg/volume/rbd/rbd.go @@ -255,6 +255,10 @@ type rbdVolumeProvisioner struct { } func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(r.plugin.GetAccessModes(), r.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", r.options.PVC.Spec.AccessModes, r.plugin.GetAccessModes()) + } + if r.options.PVC.Spec.Selector != nil { return nil, fmt.Errorf("claim Selector is not supported") } diff --git a/pkg/volume/scaleio/sio_volume.go b/pkg/volume/scaleio/sio_volume.go index 979ca57d843..c0bd76bc44c 100644 --- a/pkg/volume/scaleio/sio_volume.go +++ b/pkg/volume/scaleio/sio_volume.go @@ -236,6 +236,10 @@ var _ volume.Provisioner = &sioVolume{} func (v *sioVolume) Provision() (*api.PersistentVolume, error) { glog.V(4).Info(log("attempting to dynamically provision pvc %v", v.options.PVName)) + if !volume.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes()) + } + // setup volume attrributes name := v.generateVolName() capacity := v.options.PVC.Spec.Resources.Requests[api.ResourceName(api.ResourceStorage)] diff --git a/pkg/volume/storageos/storageos.go b/pkg/volume/storageos/storageos.go index 092354b2b05..0a5e3519a19 100644 --- a/pkg/volume/storageos/storageos.go +++ b/pkg/volume/storageos/storageos.go @@ -553,6 +553,9 @@ type storageosProvisioner struct { var _ volume.Provisioner = &storageosProvisioner{} func (c *storageosProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) + } var adminSecretName, adminSecretNamespace string diff --git a/pkg/volume/util.go b/pkg/volume/util.go index aac4c9e543c..2e56103621f 100644 --- a/pkg/volume/util.go +++ b/pkg/volume/util.go @@ -434,3 +434,23 @@ func ValidateZone(zone string) error { } return nil } + +// AccessModesContains returns whether the requested mode is contained by modes +func AccessModesContains(modes []v1.PersistentVolumeAccessMode, mode v1.PersistentVolumeAccessMode) bool { + for _, m := range modes { + if m == mode { + return true + } + } + return false +} + +// AccessModesContainedInAll returns whether all of the requested modes are contained by modes +func AccessModesContainedInAll(indexedModes []v1.PersistentVolumeAccessMode, requestedModes []v1.PersistentVolumeAccessMode) bool { + for _, mode := range requestedModes { + if !AccessModesContains(indexedModes, mode) { + return false + } + } + return true +} diff --git a/pkg/volume/vsphere_volume/vsphere_volume.go b/pkg/volume/vsphere_volume/vsphere_volume.go index ce513832cd2..2c6bf74be8e 100644 --- a/pkg/volume/vsphere_volume/vsphere_volume.go +++ b/pkg/volume/vsphere_volume/vsphere_volume.go @@ -345,6 +345,10 @@ func (plugin *vsphereVolumePlugin) newProvisionerInternal(options volume.VolumeO } func (v *vsphereVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { + if !volume.AccessModesContainedInAll(v.plugin.GetAccessModes(), v.options.PVC.Spec.AccessModes) { + return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", v.options.PVC.Spec.AccessModes, v.plugin.GetAccessModes()) + } + volSpec, err := v.manager.CreateVolume(v) if err != nil { return nil, err