Add validation options for PersistentVolumeClaims

These options provide an extensible way of configuring how PVCs are
validated
This commit is contained in:
Chris Henzie 2021-06-08 13:30:55 -07:00
parent 9ba0eed7c5
commit dba8ee229e
3 changed files with 60 additions and 19 deletions

View File

@ -1608,16 +1608,17 @@ func validateEphemeralVolumeSource(ephemeral *core.EphemeralVolumeSource, fldPat
if ephemeral.VolumeClaimTemplate == nil {
allErrs = append(allErrs, field.Required(fldPath.Child("volumeClaimTemplate"), ""))
} else {
allErrs = append(allErrs, ValidatePersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, fldPath.Child("volumeClaimTemplate"))...)
opts := ValidationOptionsForPersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, nil)
allErrs = append(allErrs, ValidatePersistentVolumeClaimTemplate(ephemeral.VolumeClaimTemplate, fldPath.Child("volumeClaimTemplate"), opts)...)
}
return allErrs
}
// ValidatePersistentVolumeClaimTemplate verifies that the embedded object meta and spec are valid.
// Checking of the object data is very minimal because only labels and annotations are used.
func ValidatePersistentVolumeClaimTemplate(claimTemplate *core.PersistentVolumeClaimTemplate, fldPath *field.Path) field.ErrorList {
func ValidatePersistentVolumeClaimTemplate(claimTemplate *core.PersistentVolumeClaimTemplate, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := validatePersistentVolumeClaimTemplateObjectMeta(&claimTemplate.ObjectMeta, fldPath.Child("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&claimTemplate.Spec, fldPath.Child("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&claimTemplate.Spec, fldPath.Child("spec"), opts)...)
return allErrs
}
@ -1973,15 +1974,27 @@ func ValidatePersistentVolumeStatusUpdate(newPv, oldPv *core.PersistentVolume) f
return allErrs
}
// PersistentVolumeClaimSpecValidationOptions contains the different settings for PersistentVolumeClaim validation
type PersistentVolumeClaimSpecValidationOptions struct {
}
func ValidationOptionsForPersistentVolumeClaim(pvc, oldPvc *core.PersistentVolumeClaim) PersistentVolumeClaimSpecValidationOptions {
return PersistentVolumeClaimSpecValidationOptions{}
}
func ValidationOptionsForPersistentVolumeClaimTemplate(claimTemplate, oldClaimTemplate *core.PersistentVolumeClaimTemplate) PersistentVolumeClaimSpecValidationOptions {
return PersistentVolumeClaimSpecValidationOptions{}
}
// ValidatePersistentVolumeClaim validates a PersistentVolumeClaim
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim) field.ErrorList {
func ValidatePersistentVolumeClaim(pvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := ValidateObjectMeta(&pvc.ObjectMeta, true, ValidatePersistentVolumeName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"))...)
allErrs = append(allErrs, ValidatePersistentVolumeClaimSpec(&pvc.Spec, field.NewPath("spec"), opts)...)
return allErrs
}
// ValidatePersistentVolumeClaimSpec validates a PersistentVolumeClaimSpec
func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path) field.ErrorList {
func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fldPath *field.Path, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := field.ErrorList{}
if len(spec.AccessModes) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("accessModes"), "at least 1 access mode is required"))
@ -2032,9 +2045,9 @@ func ValidatePersistentVolumeClaimSpec(spec *core.PersistentVolumeClaimSpec, fld
}
// ValidatePersistentVolumeClaimUpdate validates an update to a PersistentVolumeClaim
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim) field.ErrorList {
func ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc *core.PersistentVolumeClaim, opts PersistentVolumeClaimSpecValidationOptions) field.ErrorList {
allErrs := ValidateObjectMetaUpdate(&newPvc.ObjectMeta, &oldPvc.ObjectMeta, field.NewPath("metadata"))
allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc)...)
allErrs = append(allErrs, ValidatePersistentVolumeClaim(newPvc, opts)...)
newPvcClone := newPvc.DeepCopy()
oldPvcClone := oldPvc.DeepCopy()

View File

@ -922,12 +922,14 @@ func TestAlphaVolumeSnapshotDataSource(t *testing.T) {
}
for _, tc := range successTestCases {
if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) != 0 {
opts := PersistentVolumeClaimSpecValidationOptions{}
if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec"), opts); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}
for _, tc := range failedTestCases {
if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec")); len(errs) == 0 {
opts := PersistentVolumeClaimSpecValidationOptions{}
if errs := ValidatePersistentVolumeClaimSpec(&tc, field.NewPath("spec"), opts); len(errs) == 0 {
t.Errorf("expected failure: %v", errs)
}
}
@ -1323,7 +1325,8 @@ func testValidatePVC(t *testing.T, ephemeral bool) {
opts := PodValidationOptions{}
_, errs = ValidateVolumes(volumes, nil, field.NewPath(""), opts)
} else {
errs = ValidatePersistentVolumeClaim(scenario.claim)
opts := ValidationOptionsForPersistentVolumeClaim(scenario.claim, nil)
errs = ValidatePersistentVolumeClaim(scenario.claim, opts)
}
if len(errs) == 0 && scenario.isExpectedFailure {
t.Error("Unexpected success for scenario")
@ -1826,7 +1829,8 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ExpandPersistentVolumes, scenario.enableResize)()
scenario.oldClaim.ResourceVersion = "1"
scenario.newClaim.ResourceVersion = "1"
errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim)
opts := ValidationOptionsForPersistentVolumeClaim(scenario.newClaim, scenario.oldClaim)
errs := ValidatePersistentVolumeClaimUpdate(scenario.newClaim, scenario.oldClaim, opts)
if len(errs) == 0 && scenario.isExpectedFailure {
t.Errorf("Unexpected success for scenario: %s", name)
}
@ -1837,6 +1841,22 @@ func TestValidatePersistentVolumeClaimUpdate(t *testing.T) {
}
}
func TestValidationOptionsForPersistentVolumeClaim(t *testing.T) {
expectedValidationOpts := PersistentVolumeClaimSpecValidationOptions{}
opts := ValidationOptionsForPersistentVolumeClaim(nil, nil)
if opts != expectedValidationOpts {
t.Errorf("Expected opts: %+v, received: %+v", opts, expectedValidationOpts)
}
}
func TestValidationOptionsForPersistentVolumeClaimTemplate(t *testing.T) {
expectedValidationOpts := PersistentVolumeClaimSpecValidationOptions{}
opts := ValidationOptionsForPersistentVolumeClaimTemplate(nil, nil)
if opts != expectedValidationOpts {
t.Errorf("Expected opts: %+v, received: %+v", opts, expectedValidationOpts)
}
}
func TestValidateKeyToPath(t *testing.T) {
testCases := []struct {
kp core.KeyToPath
@ -4321,7 +4341,8 @@ func TestPVCVolumeMode(t *testing.T) {
"valid nil value": createTestVolModePVC(nil),
}
for k, v := range successCasesPVC {
if errs := ValidatePersistentVolumeClaim(v); len(errs) != 0 {
opts := ValidationOptionsForPersistentVolumeClaim(v, nil)
if errs := ValidatePersistentVolumeClaim(v, opts); len(errs) != 0 {
t.Errorf("expected success for %s", k)
}
}
@ -4332,7 +4353,8 @@ func TestPVCVolumeMode(t *testing.T) {
"empty value": createTestVolModePVC(&empty),
}
for k, v := range errorCasesPVC {
if errs := ValidatePersistentVolumeClaim(v); len(errs) == 0 {
opts := ValidationOptionsForPersistentVolumeClaim(v, nil)
if errs := ValidatePersistentVolumeClaim(v, opts); len(errs) == 0 {
t.Errorf("expected failure for %s", k)
}
}
@ -16585,12 +16607,14 @@ func TestAlphaVolumePVCDataSource(t *testing.T) {
for _, tc := range testCases {
if tc.expectedFail {
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) == 0 {
opts := PersistentVolumeClaimSpecValidationOptions{}
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) == 0 {
t.Errorf("expected failure: %v", errs)
}
} else {
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec")); len(errs) != 0 {
opts := PersistentVolumeClaimSpecValidationOptions{}
if errs := ValidatePersistentVolumeClaimSpec(&tc.claimSpec, field.NewPath("spec"), opts); len(errs) != 0 {
t.Errorf("expected success: %v", errs)
}
}

View File

@ -72,7 +72,8 @@ func (persistentvolumeclaimStrategy) PrepareForCreate(ctx context.Context, obj r
func (persistentvolumeclaimStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
pvc := obj.(*api.PersistentVolumeClaim)
return validation.ValidatePersistentVolumeClaim(pvc)
opts := validation.ValidationOptionsForPersistentVolumeClaim(pvc, nil)
return validation.ValidatePersistentVolumeClaim(pvc, opts)
}
// WarningsOnCreate returns warnings for the creation of the given object.
@ -98,8 +99,11 @@ func (persistentvolumeclaimStrategy) PrepareForUpdate(ctx context.Context, obj,
}
func (persistentvolumeclaimStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
errorList := validation.ValidatePersistentVolumeClaim(obj.(*api.PersistentVolumeClaim))
return append(errorList, validation.ValidatePersistentVolumeClaimUpdate(obj.(*api.PersistentVolumeClaim), old.(*api.PersistentVolumeClaim))...)
newPvc := obj.(*api.PersistentVolumeClaim)
oldPvc := old.(*api.PersistentVolumeClaim)
opts := validation.ValidationOptionsForPersistentVolumeClaim(newPvc, oldPvc)
errorList := validation.ValidatePersistentVolumeClaim(newPvc, opts)
return append(errorList, validation.ValidatePersistentVolumeClaimUpdate(newPvc, oldPvc, opts)...)
}
// WarningsOnUpdate returns warnings for the given update.