API changes for persistent local volumes.
Includes: - A new volume type, LocalVolumeSource. This only supports file-based local volumes for now. - New alpha annotation in PV: NodeAffinity - Validation + tests for specifying LocalVolumeSource and PV NodeAffinity - Alpha feature gate
This commit is contained in:
@@ -1101,6 +1101,14 @@ func validateScaleIOVolumeSource(sio *api.ScaleIOVolumeSource, fldPath *field.Pa
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if ls.Path == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("path"), ""))
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidatePersistentVolumeName checks that a name is appropriate for a
|
||||
// PersistentVolumeName object.
|
||||
var ValidatePersistentVolumeName = NameIsDNSSubdomain
|
||||
@@ -1110,7 +1118,8 @@ var supportedAccessModes = sets.NewString(string(api.ReadWriteOnce), string(api.
|
||||
var supportedReclaimPolicy = sets.NewString(string(api.PersistentVolumeReclaimDelete), string(api.PersistentVolumeReclaimRecycle), string(api.PersistentVolumeReclaimRetain))
|
||||
|
||||
func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
||||
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, field.NewPath("metadata"))
|
||||
metaPath := field.NewPath("metadata")
|
||||
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
|
||||
|
||||
specPath := field.NewPath("spec")
|
||||
if len(pv.Spec.AccessModes) == 0 {
|
||||
@@ -1139,6 +1148,9 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
||||
}
|
||||
}
|
||||
|
||||
nodeAffinitySpecified, errs := validateStorageNodeAffinityAnnotation(pv.ObjectMeta.Annotations, metaPath.Child("annotations"))
|
||||
allErrs = append(allErrs, errs...)
|
||||
|
||||
numVolumes := 0
|
||||
if pv.Spec.HostPath != nil {
|
||||
if numVolumes > 0 {
|
||||
@@ -1290,6 +1302,22 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
|
||||
allErrs = append(allErrs, validateScaleIOVolumeSource(pv.Spec.ScaleIO, specPath.Child("scaleIO"))...)
|
||||
}
|
||||
}
|
||||
if pv.Spec.Local != nil {
|
||||
if numVolumes > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("local"), "may not specify more than 1 volume type"))
|
||||
} else {
|
||||
numVolumes++
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.PersistentLocalVolumes) {
|
||||
allErrs = append(allErrs, field.Forbidden(specPath.Child("local"), "Local volumes are disabled by feature-gate"))
|
||||
}
|
||||
allErrs = append(allErrs, validateLocalVolumeSource(pv.Spec.Local, specPath.Child("local"))...)
|
||||
|
||||
// NodeAffinity is required
|
||||
if !nodeAffinitySpecified {
|
||||
allErrs = append(allErrs, field.Required(metaPath.Child("annotations"), "Local volume requires node affinity"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if numVolumes == 0 {
|
||||
allErrs = append(allErrs, field.Required(specPath, "must specify a volume type"))
|
||||
@@ -4044,3 +4072,32 @@ func sysctlIntersection(a []api.Sysctl, b []api.Sysctl) []string {
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// validateStorageNodeAffinityAnnotation tests that the serialized TopologyConstraints in PersistentVolume.Annotations has valid data
|
||||
func validateStorageNodeAffinityAnnotation(annotations map[string]string, fldPath *field.Path) (bool, field.ErrorList) {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
na, err := helper.GetStorageNodeAffinityFromAnnotation(annotations)
|
||||
if err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, api.AlphaStorageNodeAffinityAnnotation, err.Error()))
|
||||
return false, allErrs
|
||||
}
|
||||
if na == nil {
|
||||
return false, allErrs
|
||||
}
|
||||
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.PersistentLocalVolumes) {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath, "Storage node affinity is disabled by feature-gate"))
|
||||
}
|
||||
|
||||
policySpecified := false
|
||||
if na.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
allErrs = append(allErrs, ValidateNodeSelector(na.RequiredDuringSchedulingIgnoredDuringExecution, fldPath.Child("requiredDuringSchedulingIgnoredDuringExecution"))...)
|
||||
policySpecified = true
|
||||
}
|
||||
|
||||
if len(na.PreferredDuringSchedulingIgnoredDuringExecution) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("preferredDuringSchedulingIgnoredDuringExection"), "Storage node affinity does not support preferredDuringSchedulingIgnoredDuringExecution"))
|
||||
}
|
||||
return policySpecified, allErrs
|
||||
}
|
||||
|
Reference in New Issue
Block a user