Add validation options for PersistentVolumes

These options provide an extensible way of configuring how PVs are
validated
This commit is contained in:
Chris Henzie 2021-06-08 12:38:03 -07:00
parent d92f6c424d
commit 9ba0eed7c5
4 changed files with 45 additions and 17 deletions

View File

@ -1638,6 +1638,10 @@ var allowedPVCTemplateObjectMetaFields = map[string]bool{
"Labels": true,
}
// PersistentVolumeSpecValidationOptions contains the different settings for PeristentVolume validation
type PersistentVolumeSpecValidationOptions struct {
}
// ValidatePersistentVolumeName checks that a name is appropriate for a
// PersistentVolumeName object.
var ValidatePersistentVolumeName = apimachineryvalidation.NameIsDNSSubdomain
@ -1648,7 +1652,11 @@ var supportedReclaimPolicy = sets.NewString(string(core.PersistentVolumeReclaimD
var supportedVolumeModes = sets.NewString(string(core.PersistentVolumeBlock), string(core.PersistentVolumeFilesystem))
func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path) field.ErrorList {
func ValidationOptionsForPersistentVolume(pv, oldPv *core.PersistentVolume) PersistentVolumeSpecValidationOptions {
return PersistentVolumeSpecValidationOptions{}
}
func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName string, validateInlinePersistentVolumeSpec bool, fldPath *field.Path, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if validateInlinePersistentVolumeSpec {
@ -1922,17 +1930,17 @@ func ValidatePersistentVolumeSpec(pvSpec *core.PersistentVolumeSpec, pvName stri
return allErrs
}
func ValidatePersistentVolume(pv *core.PersistentVolume) field.ErrorList {
func ValidatePersistentVolume(pv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
metaPath := field.NewPath("metadata")
allErrs := ValidateObjectMeta(&pv.ObjectMeta, false, ValidatePersistentVolumeName, metaPath)
allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeSpec(&pv.Spec, pv.ObjectMeta.Name, false, field.NewPath("spec"), opts)...)
return allErrs
}
// ValidatePersistentVolumeUpdate tests to see if the update is legal for an end user to make.
// newPv is updated with fields that cannot be changed.
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume) field.ErrorList {
allErrs := ValidatePersistentVolume(newPv)
func ValidatePersistentVolumeUpdate(newPv, oldPv *core.PersistentVolume, opts PersistentVolumeSpecValidationOptions) field.ErrorList {
allErrs := ValidatePersistentVolume(newPv, opts)
// if oldPV does not have ControllerExpandSecretRef then allow it to be set
if (oldPv.Spec.CSI != nil && oldPv.Spec.CSI.ControllerExpandSecretRef == nil) &&

View File

@ -415,7 +415,8 @@ func TestValidatePersistentVolumes(t *testing.T) {
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
errs := ValidatePersistentVolume(scenario.volume)
opts := ValidationOptionsForPersistentVolume(scenario.volume, nil)
errs := ValidatePersistentVolume(scenario.volume, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -558,7 +559,8 @@ func TestValidatePersistentVolumeSpec(t *testing.T) {
},
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolumeSpec(scenario.pvSpec, "", scenario.isInlineSpec, field.NewPath("field"))
opts := PersistentVolumeSpecValidationOptions{}
errs := ValidatePersistentVolumeSpec(scenario.pvSpec, "", scenario.isInlineSpec, field.NewPath("field"), opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -655,7 +657,8 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
},
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolumeUpdate(scenario.newVolume, scenario.oldVolume)
opts := ValidationOptionsForPersistentVolume(scenario.newVolume, scenario.oldVolume)
errs := ValidatePersistentVolumeUpdate(scenario.newVolume, scenario.oldVolume, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -665,6 +668,14 @@ func TestValidatePersistentVolumeSourceUpdate(t *testing.T) {
}
}
func TestValidationOptionsForPersistentVolume(t *testing.T) {
expectedValidationOpts := PersistentVolumeSpecValidationOptions{}
opts := ValidationOptionsForPersistentVolume(nil, nil)
if opts != expectedValidationOpts {
t.Errorf("Expected opts: %+v, received: %+v", opts, expectedValidationOpts)
}
}
func getCSIVolumeWithSecret(pv *core.PersistentVolume, secret *core.SecretReference) *core.PersistentVolume {
pvCopy := pv.DeepCopy()
if secret != nil {
@ -729,7 +740,8 @@ func TestValidateLocalVolumes(t *testing.T) {
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolume(scenario.volume)
opts := ValidationOptionsForPersistentVolume(scenario.volume, nil)
errs := ValidatePersistentVolume(scenario.volume, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -808,7 +820,8 @@ func TestValidateVolumeNodeAffinityUpdate(t *testing.T) {
}
for name, scenario := range scenarios {
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
opts := ValidationOptionsForPersistentVolume(scenario.newPV, scenario.oldPV)
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -1393,8 +1406,9 @@ func TestAlphaPVVolumeModeUpdate(t *testing.T) {
for name, scenario := range scenarios {
t.Run(name, func(t *testing.T) {
opts := ValidationOptionsForPersistentVolume(scenario.newPV, scenario.oldPV)
// ensure we have a resource version specified for updates
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV)
errs := ValidatePersistentVolumeUpdate(scenario.newPV, scenario.oldPV, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -4337,7 +4351,8 @@ func TestPVVolumeMode(t *testing.T) {
"valid nil value": createTestVolModePV(nil),
}
for k, v := range successCasesPV {
if errs := ValidatePersistentVolume(v); len(errs) != 0 {
opts := ValidationOptionsForPersistentVolume(v, nil)
if errs := ValidatePersistentVolume(v, opts); len(errs) != 0 {
t.Errorf("expected success for %s", k)
}
}
@ -4348,7 +4363,8 @@ func TestPVVolumeMode(t *testing.T) {
"empty value": createTestVolModePV(&empty),
}
for k, v := range errorCasesPV {
if errs := ValidatePersistentVolume(v); len(errs) == 0 {
opts := ValidationOptionsForPersistentVolume(v, nil)
if errs := ValidatePersistentVolume(v, opts); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
}

View File

@ -192,7 +192,8 @@ func validateVolumeAttachmentSource(source *storage.VolumeAttachmentSource, fldP
allErrs = append(allErrs, field.Required(fldPath.Child("persistentVolumeName"), "must specify non empty persistentVolumeName"))
}
case source.InlineVolumeSpec != nil:
allErrs = append(allErrs, apivalidation.ValidatePersistentVolumeSpec(source.InlineVolumeSpec, "", true, fldPath.Child("inlineVolumeSpec"))...)
opts := apivalidation.PersistentVolumeSpecValidationOptions{}
allErrs = append(allErrs, apivalidation.ValidatePersistentVolumeSpec(source.InlineVolumeSpec, "", true, fldPath.Child("inlineVolumeSpec"), opts)...)
}
return allErrs
}

View File

@ -71,7 +71,8 @@ func (persistentvolumeStrategy) PrepareForCreate(ctx context.Context, obj runtim
func (persistentvolumeStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
persistentvolume := obj.(*api.PersistentVolume)
errorList := validation.ValidatePersistentVolume(persistentvolume)
opts := validation.ValidationOptionsForPersistentVolume(persistentvolume, nil)
errorList := validation.ValidatePersistentVolume(persistentvolume, opts)
return append(errorList, volumevalidation.ValidatePersistentVolume(persistentvolume)...)
}
@ -99,9 +100,11 @@ func (persistentvolumeStrategy) PrepareForUpdate(ctx context.Context, obj, old r
func (persistentvolumeStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
newPv := obj.(*api.PersistentVolume)
errorList := validation.ValidatePersistentVolume(newPv)
oldPv := old.(*api.PersistentVolume)
opts := validation.ValidationOptionsForPersistentVolume(newPv, oldPv)
errorList := validation.ValidatePersistentVolume(newPv, opts)
errorList = append(errorList, volumevalidation.ValidatePersistentVolume(newPv)...)
return append(errorList, validation.ValidatePersistentVolumeUpdate(newPv, old.(*api.PersistentVolume))...)
return append(errorList, validation.ValidatePersistentVolumeUpdate(newPv, oldPv, opts)...)
}
// WarningsOnUpdate returns warnings for the given update.