diff --git a/pkg/validation/validate/object_validator.go b/pkg/validation/validate/object_validator.go index f0417beb0..0f73ace8c 100644 --- a/pkg/validation/validate/object_validator.go +++ b/pkg/validation/validate/object_validator.go @@ -99,7 +99,7 @@ func (o *objectValidator) Validate(data interface{}) *Result { // Cases: properties which are not regular properties and have not been matched by the PatternProperties validator if o.AdditionalProperties != nil && o.AdditionalProperties.Schema != nil { // AdditionalProperties as Schema - res.Merge(NewSchemaValidator(o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)) + res.Merge(o.Options.NewValidatorForField(key, o.AdditionalProperties.Schema, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)) } else if regularProperty && !(matched || succeededOnce) { // TODO: this is dead code since regularProperty=false here res.AddErrors(errors.FailedAllPatternProperties(o.Path, o.In, key)) @@ -121,7 +121,7 @@ func (o *objectValidator) Validate(data interface{}) *Result { // Recursively validates each property against its schema if v, ok := val[pName]; ok { - r := NewSchemaValidator(&pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v) + r := o.Options.NewValidatorForField(pName, &pSchema, o.Root, rName, o.KnownFormats, o.Options.Options()...).Validate(v) res.Merge(r) } } @@ -144,7 +144,7 @@ func (o *objectValidator) Validate(data interface{}) *Result { if !regularProperty && (matched /*|| succeededOnce*/) { for _, pName := range patterns { if v, ok := o.PatternProperties[pName]; ok { - res.Merge(NewSchemaValidator(&v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)) + res.Merge(o.Options.NewValidatorForField(key, &v, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...).Validate(value)) } } } @@ -163,7 +163,7 @@ func (o *objectValidator) validatePatternProperty(key string, value interface{}, if match, _ := regexp.MatchString(k, key); match { patterns = append(patterns, k) matched = true - validator := NewSchemaValidator(&sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...) + validator := o.Options.NewValidatorForField(key, &sch, o.Root, o.Path+"."+key, o.KnownFormats, o.Options.Options()...) res := validator.Validate(value) result.Merge(res) diff --git a/pkg/validation/validate/object_validator_test.go b/pkg/validation/validate/object_validator_test.go index 4d344b9ba..2657bc6f7 100644 --- a/pkg/validation/validate/object_validator_test.go +++ b/pkg/validation/validate/object_validator_test.go @@ -27,7 +27,7 @@ func itemsFixture() map[string]interface{} { } } -func expectAllValid(t *testing.T, ov valueValidator, dataValid, dataInvalid map[string]interface{}) { +func expectAllValid(t *testing.T, ov ValueValidator, dataValid, dataInvalid map[string]interface{}) { res := ov.Validate(dataValid) assert.Equal(t, 0, len(res.Errors)) @@ -35,7 +35,7 @@ func expectAllValid(t *testing.T, ov valueValidator, dataValid, dataInvalid map[ assert.Equal(t, 0, len(res.Errors)) } -func expectOnlyInvalid(t *testing.T, ov valueValidator, dataValid, dataInvalid map[string]interface{}) { +func expectOnlyInvalid(t *testing.T, ov ValueValidator, dataValid, dataInvalid map[string]interface{}) { res := ov.Validate(dataValid) assert.Equal(t, 0, len(res.Errors)) diff --git a/pkg/validation/validate/schema.go b/pkg/validation/validate/schema.go index 778c01948..511eee1d0 100644 --- a/pkg/validation/validate/schema.go +++ b/pkg/validation/validate/schema.go @@ -35,7 +35,7 @@ type SchemaValidator struct { Path string in string Schema *spec.Schema - validators []valueValidator + validators []ValueValidator Root interface{} KnownFormats strfmt.Registry Options SchemaValidatorOptions @@ -78,7 +78,15 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string for _, o := range options { o(&s.Options) } - s.validators = []valueValidator{ + + if s.Options.NewValidatorForIndex == nil { + s.Options.NewValidatorForIndex = s.NewValidatorForIndex + } + if s.Options.NewValidatorForField == nil { + s.Options.NewValidatorForField = s.NewValidatorForField + } + + s.validators = []ValueValidator{ s.typeValidator(), s.schemaPropsValidator(), s.stringValidator(), @@ -91,6 +99,14 @@ func NewSchemaValidator(schema *spec.Schema, rootSchema interface{}, root string return &s } +func (s *SchemaValidator) NewValidatorForField(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator { + return NewSchemaValidator(schema, rootSchema, root, formats, opts...) +} + +func (s *SchemaValidator) NewValidatorForIndex(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator { + return NewSchemaValidator(schema, rootSchema, root, formats, opts...) +} + // SetPath sets the path for this schema validator func (s *SchemaValidator) SetPath(path string) { s.Path = path @@ -174,11 +190,11 @@ func (s *SchemaValidator) Validate(data interface{}) *Result { return result } -func (s *SchemaValidator) typeValidator() valueValidator { +func (s *SchemaValidator) typeValidator() ValueValidator { return &typeValidator{Type: s.Schema.Type, Nullable: s.Schema.Nullable, Format: s.Schema.Format, In: s.in, Path: s.Path} } -func (s *SchemaValidator) commonValidator() valueValidator { +func (s *SchemaValidator) commonValidator() ValueValidator { return &basicCommonValidator{ Path: s.Path, In: s.in, @@ -186,7 +202,7 @@ func (s *SchemaValidator) commonValidator() valueValidator { } } -func (s *SchemaValidator) sliceValidator() valueValidator { +func (s *SchemaValidator) sliceValidator() ValueValidator { return &schemaSliceValidator{ Path: s.Path, In: s.in, @@ -201,7 +217,7 @@ func (s *SchemaValidator) sliceValidator() valueValidator { } } -func (s *SchemaValidator) numberValidator() valueValidator { +func (s *SchemaValidator) numberValidator() ValueValidator { return &numberValidator{ Path: s.Path, In: s.in, @@ -214,7 +230,7 @@ func (s *SchemaValidator) numberValidator() valueValidator { } } -func (s *SchemaValidator) stringValidator() valueValidator { +func (s *SchemaValidator) stringValidator() ValueValidator { return &stringValidator{ Path: s.Path, In: s.in, @@ -224,7 +240,7 @@ func (s *SchemaValidator) stringValidator() valueValidator { } } -func (s *SchemaValidator) formatValidator() valueValidator { +func (s *SchemaValidator) formatValidator() ValueValidator { return &formatValidator{ Path: s.Path, In: s.in, @@ -233,12 +249,12 @@ func (s *SchemaValidator) formatValidator() valueValidator { } } -func (s *SchemaValidator) schemaPropsValidator() valueValidator { +func (s *SchemaValidator) schemaPropsValidator() ValueValidator { sch := s.Schema return newSchemaPropsValidator(s.Path, s.in, sch.AllOf, sch.OneOf, sch.AnyOf, sch.Not, sch.Dependencies, s.Root, s.KnownFormats, s.Options.Options()...) } -func (s *SchemaValidator) objectValidator() valueValidator { +func (s *SchemaValidator) objectValidator() ValueValidator { return &objectValidator{ Path: s.Path, In: s.in, diff --git a/pkg/validation/validate/schema_option.go b/pkg/validation/validate/schema_option.go index 34991102b..f3637bcfd 100644 --- a/pkg/validation/validate/schema_option.go +++ b/pkg/validation/validate/schema_option.go @@ -14,9 +14,16 @@ package validate +import ( + "k8s.io/kube-openapi/pkg/validation/spec" + "k8s.io/kube-openapi/pkg/validation/strfmt" +) + // SchemaValidatorOptions defines optional rules for schema validation type SchemaValidatorOptions struct { validationRulesEnabled bool + NewValidatorForIndex func(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator + NewValidatorForField func(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, opts ...Option) ValueValidator } // Option sets optional rules for schema validation diff --git a/pkg/validation/validate/slice_validator.go b/pkg/validation/validate/slice_validator.go index 3d1fc7218..38083f7ca 100644 --- a/pkg/validation/validate/slice_validator.go +++ b/pkg/validation/validate/slice_validator.go @@ -54,9 +54,8 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result { size := val.Len() if s.Items != nil && s.Items.Schema != nil { - validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats, s.Options.Options()...) for i := 0; i < size; i++ { - validator.SetPath(fmt.Sprintf("%s[%d]", s.Path, i)) + validator := s.Options.NewValidatorForIndex(i, s.Items.Schema, s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...) value := val.Index(i) result.Merge(validator.Validate(value.Interface())) } @@ -66,7 +65,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result { if s.Items != nil && len(s.Items.Schemas) > 0 { itemsSize = len(s.Items.Schemas) for i := 0; i < itemsSize; i++ { - validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...) + validator := s.Options.NewValidatorForIndex(i, &s.Items.Schemas[i], s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...) if val.Len() <= i { break } @@ -79,7 +78,7 @@ func (s *schemaSliceValidator) Validate(data interface{}) *Result { } if s.AdditionalItems.Schema != nil { for i := itemsSize; i < size-itemsSize+1; i++ { - validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...) + validator := s.Options.NewValidatorForIndex(i, s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s[%d]", s.Path, i), s.KnownFormats, s.Options.Options()...) result.Merge(validator.Validate(val.Index(i).Interface())) } } diff --git a/pkg/validation/validate/validator.go b/pkg/validation/validate/validator.go index 38a6b31c3..abfc30f6b 100644 --- a/pkg/validation/validate/validator.go +++ b/pkg/validation/validate/validator.go @@ -21,8 +21,8 @@ import ( "k8s.io/kube-openapi/pkg/validation/spec" ) -// valueValidator validates the values it applies to. -type valueValidator interface { +// ValueValidator validates the values it applies to. +type ValueValidator interface { // SetPath sets the exact path of the validator prior to calling Validate. // The exact path contains the map keys and array indices to locate the // value to be validated from the root data element.