Add storageClass.mountOptions and use it in all applicable plugins
This commit is contained in:
@@ -54,6 +54,11 @@ type StorageClass struct {
|
|||||||
// PersistentVolumes of this storage class are created with
|
// PersistentVolumes of this storage class are created with
|
||||||
// +optional
|
// +optional
|
||||||
ReclaimPolicy *api.PersistentVolumeReclaimPolicy
|
ReclaimPolicy *api.PersistentVolumeReclaimPolicy
|
||||||
|
|
||||||
|
// mountOptions are the mount options that dynamically provisioned
|
||||||
|
// PersistentVolumes of this storage class are created with
|
||||||
|
// +optional
|
||||||
|
MountOptions []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|||||||
@@ -807,12 +807,13 @@ const operationDelete = "Delete"
|
|||||||
const operationRecycle = "Recycle"
|
const operationRecycle = "Recycle"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
classGold string = "gold"
|
classGold string = "gold"
|
||||||
classSilver string = "silver"
|
classSilver string = "silver"
|
||||||
classEmpty string = ""
|
classEmpty string = ""
|
||||||
classNonExisting string = "non-existing"
|
classNonExisting string = "non-existing"
|
||||||
classExternal string = "external"
|
classExternal string = "external"
|
||||||
classUnknownInternal string = "unknown-internal"
|
classUnknownInternal string = "unknown-internal"
|
||||||
|
classUnsupportedMountOptions string = "unsupported-mountoptions"
|
||||||
)
|
)
|
||||||
|
|
||||||
// wrapTestWithPluginCalls returns a testCall that:
|
// wrapTestWithPluginCalls returns a testCall that:
|
||||||
|
|||||||
@@ -81,6 +81,18 @@ var storageClasses = []*storage.StorageClass{
|
|||||||
Parameters: class1Parameters,
|
Parameters: class1Parameters,
|
||||||
ReclaimPolicy: &deleteReclaimPolicy,
|
ReclaimPolicy: &deleteReclaimPolicy,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
TypeMeta: metav1.TypeMeta{
|
||||||
|
Kind: "StorageClass",
|
||||||
|
},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "unsupported-mountoptions",
|
||||||
|
},
|
||||||
|
Provisioner: mockPluginName,
|
||||||
|
Parameters: class1Parameters,
|
||||||
|
ReclaimPolicy: &deleteReclaimPolicy,
|
||||||
|
MountOptions: []string{"foo"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// call to storageClass 1, returning an error
|
// call to storageClass 1, returning an error
|
||||||
@@ -392,6 +404,17 @@ func TestProvisionSync(t *testing.T) {
|
|||||||
testSyncClaim,
|
testSyncClaim,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
// No provisioning + warning event with unsupported storageClass.mountOptions
|
||||||
|
"11-20 - unsupported storageClass.mountOptions",
|
||||||
|
novolumes,
|
||||||
|
novolumes,
|
||||||
|
newClaimArray("claim11-20", "uid11-20", "1Gi", "", v1.ClaimPending, &classUnsupportedMountOptions),
|
||||||
|
newClaimArray("claim11-20", "uid11-20", "1Gi", "", v1.ClaimPending, &classUnsupportedMountOptions, annStorageProvisioner),
|
||||||
|
// Expect event to be prefixed with "Mount options" because saving PV will fail anyway
|
||||||
|
[]string{"Warning ProvisioningFailed Mount options"},
|
||||||
|
noerrors, wrapTestWithProvisionCalls([]provisionCall{}, testSyncClaim),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
runSyncTests(t, tests, storageClasses)
|
runSyncTests(t, tests, storageClasses)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1320,6 +1320,7 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(claimObj interfa
|
|||||||
|
|
||||||
options := vol.VolumeOptions{
|
options := vol.VolumeOptions{
|
||||||
PersistentVolumeReclaimPolicy: *storageClass.ReclaimPolicy,
|
PersistentVolumeReclaimPolicy: *storageClass.ReclaimPolicy,
|
||||||
|
MountOptions: storageClass.MountOptions,
|
||||||
CloudTags: &tags,
|
CloudTags: &tags,
|
||||||
ClusterName: ctrl.clusterName,
|
ClusterName: ctrl.clusterName,
|
||||||
PVName: pvName,
|
PVName: pvName,
|
||||||
@@ -1327,6 +1328,15 @@ func (ctrl *PersistentVolumeController) provisionClaimOperation(claimObj interfa
|
|||||||
Parameters: storageClass.Parameters,
|
Parameters: storageClass.Parameters,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refuse to provision if the plugin doesn't support mount options, creation
|
||||||
|
// of PV would be rejected by validation anyway
|
||||||
|
if !plugin.SupportsMountOption() && len(options.MountOptions) > 0 {
|
||||||
|
strerr := fmt.Sprintf("Mount options are not supported by the provisioner but StorageClass %q has mount options %v", storageClass.Name, options.MountOptions)
|
||||||
|
glog.V(2).Infof("Mount options are not supported by the provisioner but claim %q's StorageClass %q has mount options %v", claimToClaimKey(claim), storageClass.Name, options.MountOptions)
|
||||||
|
ctrl.eventRecorder.Event(claim, v1.EventTypeWarning, events.ProvisioningFailed, strerr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Provision the volume
|
// Provision the volume
|
||||||
provisioner, err := plugin.NewProvisioner(options)
|
provisioner, err := plugin.NewProvisioner(options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -465,6 +465,7 @@ func (c *awsElasticBlockStoreProvisioner) Provision() (*v1.PersistentVolume, err
|
|||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: c.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ func (p *azureDiskProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
FSType: &fsType,
|
FSType: &fsType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: p.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return pv, nil
|
return pv, nil
|
||||||
|
|||||||
@@ -196,6 +196,7 @@ func (a *azureFileProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
SecretNamespace: &secretNamespace,
|
SecretNamespace: &secretNamespace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: a.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return pv, nil
|
return pv, nil
|
||||||
|
|||||||
@@ -499,6 +499,7 @@ func (c *cinderVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
ReadOnly: false,
|
ReadOnly: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: c.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ func (c *gcePersistentDiskProvisioner) Provision() (*v1.PersistentVolume, error)
|
|||||||
FSType: fstype,
|
FSType: fstype,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: c.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
if len(c.options.PVC.Spec.AccessModes) == 0 {
|
||||||
|
|||||||
@@ -711,6 +711,7 @@ func (p *glusterfsVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
if len(pv.Spec.AccessModes) == 0 {
|
if len(pv.Spec.AccessModes) == 0 {
|
||||||
pv.Spec.AccessModes = p.plugin.GetAccessModes()
|
pv.Spec.AccessModes = p.plugin.GetAccessModes()
|
||||||
}
|
}
|
||||||
|
pv.Spec.MountOptions = p.options.MountOptions
|
||||||
|
|
||||||
gidStr := strconv.FormatInt(int64(gid), 10)
|
gidStr := strconv.FormatInt(int64(gid), 10)
|
||||||
|
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ func (p *photonPersistentDiskProvisioner) Provision() (*v1.PersistentVolume, err
|
|||||||
FSType: fstype,
|
FSType: fstype,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: p.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if len(p.options.PVC.Spec.AccessModes) == 0 {
|
if len(p.options.PVC.Spec.AccessModes) == 0 {
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ type VolumeOptions struct {
|
|||||||
|
|
||||||
// Reclamation policy for a persistent volume
|
// Reclamation policy for a persistent volume
|
||||||
PersistentVolumeReclaimPolicy v1.PersistentVolumeReclaimPolicy
|
PersistentVolumeReclaimPolicy v1.PersistentVolumeReclaimPolicy
|
||||||
|
// Mount options for a persistent volume
|
||||||
|
MountOptions []string
|
||||||
// Suggested PV.Name of the PersistentVolume to provision.
|
// Suggested PV.Name of the PersistentVolume to provision.
|
||||||
// This is a generated name guaranteed to be unique in Kubernetes cluster.
|
// This is a generated name guaranteed to be unique in Kubernetes cluster.
|
||||||
// If you choose not to use it as volume name, ensure uniqueness by either
|
// If you choose not to use it as volume name, ensure uniqueness by either
|
||||||
|
|||||||
@@ -420,6 +420,7 @@ func (provisioner *quobyteVolumeProvisioner) Provision() (*v1.PersistentVolume,
|
|||||||
pv.Spec.Capacity = v1.ResourceList{
|
pv.Spec.Capacity = v1.ResourceList{
|
||||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
|
||||||
}
|
}
|
||||||
|
pv.Spec.MountOptions = provisioner.options.MountOptions
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -383,6 +383,7 @@ func (r *rbdVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
pv.Spec.Capacity = v1.ResourceList{
|
pv.Spec.Capacity = v1.ResourceList{
|
||||||
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", sizeMB)),
|
v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dMi", sizeMB)),
|
||||||
}
|
}
|
||||||
|
pv.Spec.MountOptions = r.options.MountOptions
|
||||||
return pv, nil
|
return pv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ func (v *vsphereVolumeProvisioner) Provision() (*v1.PersistentVolume, error) {
|
|||||||
StoragePolicyID: volSpec.StoragePolicyID,
|
StoragePolicyID: volSpec.StoragePolicyID,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MountOptions: v.options.MountOptions,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if len(v.options.PVC.Spec.AccessModes) == 0 {
|
if len(v.options.PVC.Spec.AccessModes) == 0 {
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ type StorageClass struct {
|
|||||||
// created with this reclaimPolicy. Defaults to Delete.
|
// created with this reclaimPolicy. Defaults to Delete.
|
||||||
// +optional
|
// +optional
|
||||||
ReclaimPolicy *v1.PersistentVolumeReclaimPolicy `json:"reclaimPolicy,omitempty" protobuf:"bytes,4,opt,name=reclaimPolicy,casttype=k8s.io/api/core/v1.PersistentVolumeReclaimPolicy"`
|
ReclaimPolicy *v1.PersistentVolumeReclaimPolicy `json:"reclaimPolicy,omitempty" protobuf:"bytes,4,opt,name=reclaimPolicy,casttype=k8s.io/api/core/v1.PersistentVolumeReclaimPolicy"`
|
||||||
|
|
||||||
|
// Dynamically provisioned PersistentVolumes of this storage class are
|
||||||
|
// created with these mountOptions, e.g. ["ro", "soft"]. Not validated -
|
||||||
|
// mount of the PVs will simply fail if one is invalid.
|
||||||
|
// +optional
|
||||||
|
MountOptions []string `json:"mountOptions,omitempty" protobuf:"bytes,5,opt,name=mountOptions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|||||||
@@ -49,6 +49,12 @@ type StorageClass struct {
|
|||||||
// created with this reclaimPolicy. Defaults to Delete.
|
// created with this reclaimPolicy. Defaults to Delete.
|
||||||
// +optional
|
// +optional
|
||||||
ReclaimPolicy *v1.PersistentVolumeReclaimPolicy `json:"reclaimPolicy,omitempty" protobuf:"bytes,4,opt,name=reclaimPolicy,casttype=k8s.io/api/core/v1.PersistentVolumeReclaimPolicy"`
|
ReclaimPolicy *v1.PersistentVolumeReclaimPolicy `json:"reclaimPolicy,omitempty" protobuf:"bytes,4,opt,name=reclaimPolicy,casttype=k8s.io/api/core/v1.PersistentVolumeReclaimPolicy"`
|
||||||
|
|
||||||
|
// Dynamically provisioned PersistentVolumes of this storage class are
|
||||||
|
// created with these mountOptions, e.g. ["ro", "soft"]. Not validated -
|
||||||
|
// mount of the PVs will simply fail if one is invalid.
|
||||||
|
// +optional
|
||||||
|
MountOptions []string `json:"mountOptions,omitempty" protobuf:"bytes,5,opt,name=mountOptions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||||
|
|||||||
Reference in New Issue
Block a user