Skip to content

Commit

Permalink
Merge pull request #47274 from wongma7/accessmodes-provision
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue (batch tested with PRs 46929, 47391, 47399, 47428, 47274)

Don't provision for PVCs with AccessModes unsupported by plugin

Fail early in case the user actually expects e.g. RWM from AWS when in reality that isn't possible.
@eparis @gnufied 

edit: this needs release note because it's a breaking bugfix; will write one.

#46540
```release-note
Fix dynamic provisioning of PVs with inaccurate AccessModes by refusing to provision when PVCs ask for AccessModes that can't be satisfied by the PVs' underlying volume plugin
```
  • Loading branch information
Kubernetes Submit Queue authored Jun 13, 2017
2 parents 3db93e4 + 5e788a6 commit 38837b0
Show file tree
Hide file tree
Showing 17 changed files with 79 additions and 21 deletions.
21 changes: 2 additions & 19 deletions pkg/controller/volume/persistentvolume/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions pkg/controller/volume/persistentvolume/index_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}
Expand All @@ -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)
}

Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/aws_ebs/aws_ebs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/azure_dd/azure_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/azure_file/azure_provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/cinder/cinder.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions pkg/volume/flocker/flocker_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/gce_pd/gce_pd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/glusterfs/glusterfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/photon_pd/photon_pd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/portworx/portworx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/quobyte/quobyte.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/rbd/rbd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/volume/scaleio/sio_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
3 changes: 3 additions & 0 deletions pkg/volume/storageos/storageos.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 20 additions & 0 deletions pkg/volume/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
4 changes: 4 additions & 0 deletions pkg/volume/vsphere_volume/vsphere_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 38837b0

Please sign in to comment.