mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-10 12:32:03 +00:00
refactor: factor out object correlation
so it is reusable by CEL validators
This commit is contained in:
parent
30cf9ed567
commit
471e3ab828
@ -60,7 +60,11 @@ func (r *RatchetingSchemaValidator) Validate(new interface{}) *validate.Result {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *RatchetingSchemaValidator) ValidateUpdate(new, old interface{}) *validate.Result {
|
func (r *RatchetingSchemaValidator) ValidateUpdate(new, old interface{}) *validate.Result {
|
||||||
return newRatchetingValueValidator(new, old, r.schemaArgs).Validate(new)
|
return newRatchetingValueValidator(&CorrelatedObject{
|
||||||
|
oldValue: old,
|
||||||
|
value: new,
|
||||||
|
schema: r.schema,
|
||||||
|
}, r.schemaArgs).Validate(new)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ratchetingValueValidator represents an invocation of SchemaValidator.ValidateUpdate
|
// ratchetingValueValidator represents an invocation of SchemaValidator.ValidateUpdate
|
||||||
@ -80,13 +84,18 @@ type ratchetingValueValidator struct {
|
|||||||
// schemaArgs provides the arguments to use in the temporary SchemaValidator
|
// schemaArgs provides the arguments to use in the temporary SchemaValidator
|
||||||
// that is created during a call to Validate.
|
// that is created during a call to Validate.
|
||||||
schemaArgs
|
schemaArgs
|
||||||
|
correlation *CorrelatedObject
|
||||||
|
}
|
||||||
|
|
||||||
|
type CorrelatedObject struct {
|
||||||
// Currently correlated old value during traversal of the schema/object
|
// Currently correlated old value during traversal of the schema/object
|
||||||
oldValue interface{}
|
oldValue interface{}
|
||||||
|
|
||||||
// Value being validated
|
// Value being validated
|
||||||
value interface{}
|
value interface{}
|
||||||
|
|
||||||
|
schema *spec.Schema
|
||||||
|
|
||||||
// Scratch space below, may change during validation
|
// Scratch space below, may change during validation
|
||||||
|
|
||||||
// Cached comparison result of DeepEqual of `value` and `thunk.oldValue`
|
// Cached comparison result of DeepEqual of `value` and `thunk.oldValue`
|
||||||
@ -105,15 +114,13 @@ type ratchetingValueValidator struct {
|
|||||||
//
|
//
|
||||||
// It should be expected to have an entry for either all of the children, or
|
// It should be expected to have an entry for either all of the children, or
|
||||||
// none of them.
|
// none of them.
|
||||||
children map[interface{}]*ratchetingValueValidator
|
children map[interface{}]*CorrelatedObject
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRatchetingValueValidator(newValue, oldValue interface{}, args schemaArgs) *ratchetingValueValidator {
|
func newRatchetingValueValidator(correlation *CorrelatedObject, args schemaArgs) *ratchetingValueValidator {
|
||||||
return &ratchetingValueValidator{
|
return &ratchetingValueValidator{
|
||||||
oldValue: oldValue,
|
|
||||||
value: newValue,
|
|
||||||
schemaArgs: args,
|
schemaArgs: args,
|
||||||
children: map[interface{}]*ratchetingValueValidator{},
|
correlation: correlation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,14 +159,14 @@ func (r *ratchetingValueValidator) Validate(new interface{}) *validate.Result {
|
|||||||
|
|
||||||
s := validate.NewSchemaValidator(r.schema, r.root, r.path, r.knownFormats, opts...)
|
s := validate.NewSchemaValidator(r.schema, r.root, r.path, r.knownFormats, opts...)
|
||||||
|
|
||||||
res := s.Validate(r.value)
|
res := s.Validate(r.correlation.value)
|
||||||
|
|
||||||
if res.IsValid() {
|
if res.IsValid() {
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current ratcheting rule is to ratchet errors if DeepEqual(old, new) is true.
|
// Current ratcheting rule is to ratchet errors if DeepEqual(old, new) is true.
|
||||||
if r.CachedDeepEqual() {
|
if r.correlation.CachedDeepEqual() {
|
||||||
newRes := &validate.Result{}
|
newRes := &validate.Result{}
|
||||||
newRes.MergeAsWarnings(res)
|
newRes.MergeAsWarnings(res)
|
||||||
return newRes
|
return newRes
|
||||||
@ -177,8 +184,8 @@ func (r *ratchetingValueValidator) Validate(new interface{}) *validate.Result {
|
|||||||
// If the old value cannot be correlated, then default validation is used.
|
// If the old value cannot be correlated, then default validation is used.
|
||||||
func (r *ratchetingValueValidator) SubPropertyValidator(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...validate.Option) validate.ValueValidator {
|
func (r *ratchetingValueValidator) SubPropertyValidator(field string, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...validate.Option) validate.ValueValidator {
|
||||||
// Find correlated old value
|
// Find correlated old value
|
||||||
oldAsMap, okOld := r.oldValue.(map[string]interface{})
|
oldAsMap, okOld := r.correlation.oldValue.(map[string]interface{})
|
||||||
newAsMap, okNew := r.value.(map[string]interface{})
|
newAsMap, okNew := r.correlation.value.(map[string]interface{})
|
||||||
if !okOld || !okNew {
|
if !okOld || !okNew {
|
||||||
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
||||||
}
|
}
|
||||||
@ -189,15 +196,19 @@ func (r *ratchetingValueValidator) SubPropertyValidator(field string, schema *sp
|
|||||||
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
childNode := newRatchetingValueValidator(newValueForField, oldValueForField, schemaArgs{
|
childNode := &CorrelatedObject{
|
||||||
|
oldValue: oldValueForField,
|
||||||
|
value: newValueForField,
|
||||||
|
schema: schema,
|
||||||
|
}
|
||||||
|
r.correlation.children[field] = childNode
|
||||||
|
return newRatchetingValueValidator(childNode, schemaArgs{
|
||||||
schema: schema,
|
schema: schema,
|
||||||
root: rootSchema,
|
root: rootSchema,
|
||||||
path: root,
|
path: root,
|
||||||
knownFormats: formats,
|
knownFormats: formats,
|
||||||
options: options,
|
options: options,
|
||||||
})
|
})
|
||||||
r.children[field] = childNode
|
|
||||||
return childNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubIndexValidator overrides the standard validator constructor for sub-indicies by
|
// SubIndexValidator overrides the standard validator constructor for sub-indicies by
|
||||||
@ -208,27 +219,30 @@ func (r *ratchetingValueValidator) SubPropertyValidator(field string, schema *sp
|
|||||||
//
|
//
|
||||||
// If the old value cannot be correlated, then default validation is used.
|
// If the old value cannot be correlated, then default validation is used.
|
||||||
func (r *ratchetingValueValidator) SubIndexValidator(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...validate.Option) validate.ValueValidator {
|
func (r *ratchetingValueValidator) SubIndexValidator(index int, schema *spec.Schema, rootSchema interface{}, root string, formats strfmt.Registry, options ...validate.Option) validate.ValueValidator {
|
||||||
oldValueForIndex := r.correlateOldValueForChildAtNewIndex(index)
|
oldValueForIndex := r.correlation.correlateOldValueForChildAtNewIndex(index)
|
||||||
if oldValueForIndex == nil {
|
if oldValueForIndex == nil {
|
||||||
// If correlation fails, default to non-ratcheting logic
|
// If correlation fails, default to non-ratcheting logic
|
||||||
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
asList, ok := r.value.([]interface{})
|
asList, ok := r.correlation.value.([]interface{})
|
||||||
if !ok || len(asList) <= index {
|
if !ok || len(asList) <= index {
|
||||||
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
return validate.NewSchemaValidator(schema, rootSchema, root, formats, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
childNode := newRatchetingValueValidator(asList[index], oldValueForIndex, schemaArgs{
|
childNode := &CorrelatedObject{
|
||||||
|
oldValue: oldValueForIndex,
|
||||||
|
value: asList[index],
|
||||||
|
schema: schema,
|
||||||
|
}
|
||||||
|
r.correlation.children[index] = childNode
|
||||||
|
return newRatchetingValueValidator(childNode, schemaArgs{
|
||||||
schema: schema,
|
schema: schema,
|
||||||
root: rootSchema,
|
root: rootSchema,
|
||||||
path: root,
|
path: root,
|
||||||
knownFormats: formats,
|
knownFormats: formats,
|
||||||
options: options,
|
options: options,
|
||||||
})
|
})
|
||||||
|
|
||||||
r.children[index] = childNode
|
|
||||||
return childNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If oldValue is not a list, returns nil
|
// If oldValue is not a list, returns nil
|
||||||
@ -237,7 +251,7 @@ func (r *ratchetingValueValidator) SubIndexValidator(index int, schema *spec.Sch
|
|||||||
//
|
//
|
||||||
// If listType is map, creates a map representation of the list using the designated
|
// If listType is map, creates a map representation of the list using the designated
|
||||||
// map-keys and caches it for future calls.
|
// map-keys and caches it for future calls.
|
||||||
func (r *ratchetingValueValidator) correlateOldValueForChildAtNewIndex(index int) any {
|
func (r *CorrelatedObject) correlateOldValueForChildAtNewIndex(index int) any {
|
||||||
oldAsList, ok := r.oldValue.([]interface{})
|
oldAsList, ok := r.oldValue.([]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -295,7 +309,7 @@ func (r *ratchetingValueValidator) correlateOldValueForChildAtNewIndex(index int
|
|||||||
// If a lazy computation could not be found for all children possibly due
|
// If a lazy computation could not be found for all children possibly due
|
||||||
// to validation logic short circuiting and skipping the children, then
|
// to validation logic short circuiting and skipping the children, then
|
||||||
// this function simply defers to reflect.DeepEqual.
|
// this function simply defers to reflect.DeepEqual.
|
||||||
func (r *ratchetingValueValidator) CachedDeepEqual() (res bool) {
|
func (r *CorrelatedObject) CachedDeepEqual() (res bool) {
|
||||||
if r.comparisonResult != nil {
|
if r.comparisonResult != nil {
|
||||||
return *r.comparisonResult
|
return *r.comparisonResult
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user