mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-11-06 05:17:08 +00:00
Add validate.UpdateValue, deprecate the By* forms
This is the last place that calls semantic DeepEqual directly.
This commit is contained in:
1 parent
b8d76348fa
commit
bb954fb651
9 files changed
+231
-74
No files matched your search
+2
-1
@@ -985,7 +985,8 @@ func Validate_NodeSpec(
|
||||
if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ const (
|
||||
NoRemoveItem
|
||||
)
|
||||
|
||||
// UpdateValueByCompare verifies update constraints for comparable value types.
|
||||
func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||||
// UpdateValue verifies update constraints for value types.
|
||||
func UpdateValue[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, equiv MatchFunc[T], constraints ...UpdateConstraint) field.ErrorList {
|
||||
// nil oldValue means no prior value to compare against (eg: a new item at +k8s:eachVal scope) -> no transition to check.
|
||||
if op.Type != operation.Update || oldValue == nil {
|
||||
return nil
|
||||
@@ -51,22 +51,24 @@ func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operatio
|
||||
|
||||
var errs field.ErrorList
|
||||
var zero T
|
||||
valueIsZero := equiv(*value, zero)
|
||||
oldValueIsZero := equiv(*oldValue, zero)
|
||||
|
||||
for _, constraint := range constraints {
|
||||
switch constraint {
|
||||
case NoSet:
|
||||
if *oldValue == zero && *value != zero {
|
||||
if oldValueIsZero && !valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be set once created").WithOrigin("update"))
|
||||
}
|
||||
case NoUnset:
|
||||
if *oldValue != zero && *value == zero {
|
||||
if !oldValueIsZero && valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be cleared once set").WithOrigin("update"))
|
||||
}
|
||||
case NoModify:
|
||||
// Rely on validation ratcheting to detect that the value has changed.
|
||||
// This check only verifies that the field was set in both the old and
|
||||
// new objects, confirming it was a modification, not a set/unset.
|
||||
if *oldValue != zero && *value != zero {
|
||||
if !oldValueIsZero && !valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be modified once set").WithOrigin("update"))
|
||||
}
|
||||
}
|
||||
@@ -75,6 +77,13 @@ func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operatio
|
||||
return errs
|
||||
}
|
||||
|
||||
// UpdateValueByCompare verifies update constraints for comparable value types.
|
||||
//
|
||||
// Deprecated: Use UpdateValue instead.
|
||||
func UpdateValueByCompare[T comparable](ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||||
return UpdateValue(ctx, op, fldPath, value, oldValue, func(a, b T) bool { return a == b }, constraints...)
|
||||
}
|
||||
|
||||
// UpdatePointer verifies update constraints for pointer types.
|
||||
func UpdatePointer[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||||
if op.Type != operation.Update {
|
||||
@@ -107,38 +116,10 @@ func UpdatePointer[T any](_ context.Context, op operation.Operation, fldPath *fi
|
||||
}
|
||||
|
||||
// UpdateValueByReflect verifies update constraints for non-comparable value types using reflection.
|
||||
func UpdateValueByReflect[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||||
// nil oldValue means no prior value to compare against (eg: a new item at +k8s:eachVal scope) -> no transition to check.
|
||||
if op.Type != operation.Update || oldValue == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var errs field.ErrorList
|
||||
var zero T
|
||||
valueIsZero := equality.Semantic.DeepEqual(*value, zero)
|
||||
oldValueIsZero := equality.Semantic.DeepEqual(*oldValue, zero)
|
||||
|
||||
for _, constraint := range constraints {
|
||||
switch constraint {
|
||||
case NoSet:
|
||||
if oldValueIsZero && !valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be set once created").WithOrigin("update"))
|
||||
}
|
||||
case NoUnset:
|
||||
if !oldValueIsZero && valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be cleared once set").WithOrigin("update"))
|
||||
}
|
||||
case NoModify:
|
||||
// Rely on validation ratcheting to detect that the value has changed.
|
||||
// This check only verifies that the field was set in both the old and
|
||||
// new objects, confirming it was a modification, not a set/unset.
|
||||
if !oldValueIsZero && !valueIsZero {
|
||||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be modified once set").WithOrigin("update"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return errs
|
||||
//
|
||||
// Deprecated: Use UpdateValue instead.
|
||||
func UpdateValueByReflect[T any](ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||||
return UpdateValue(ctx, op, fldPath, value, oldValue, func(a, b T) bool { return equality.Semantic.DeepEqual(a, b) }, constraints...)
|
||||
}
|
||||
|
||||
// UpdateStruct verifies update constraints for non-pointer struct types.
|
||||
|
||||
@@ -138,10 +138,153 @@ func TestUpdateValue(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
op := operation.Operation{Type: tt.op}
|
||||
errs := UpdateValue(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, func(a, b string) bool { return a == b }, tt.constraints...)
|
||||
if len(errs) != tt.wantErrs {
|
||||
t.Errorf("UpdateValue() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||||
}
|
||||
for i, msg := range tt.wantMsgs {
|
||||
if i >= len(errs) {
|
||||
t.Errorf("Expected error message %q not found", msg)
|
||||
continue
|
||||
}
|
||||
if errs[i].Detail != msg {
|
||||
t.Errorf("UpdateValue() error message = %q, want %q", errs[i].Detail, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateValueByCompare(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
op operation.Type
|
||||
value string
|
||||
oldValue string
|
||||
constraints []UpdateConstraint
|
||||
wantErrs int
|
||||
wantMsgs []string
|
||||
}{
|
||||
{
|
||||
name: "NoSet - unset to set transition (forbidden)",
|
||||
op: operation.Update,
|
||||
value: "value",
|
||||
oldValue: "",
|
||||
constraints: []UpdateConstraint{NoSet},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be set once created"},
|
||||
},
|
||||
{
|
||||
name: "NoUnset - set to unset transition (forbidden)",
|
||||
op: operation.Update,
|
||||
value: "",
|
||||
oldValue: "value",
|
||||
constraints: []UpdateConstraint{NoUnset},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be cleared once set"},
|
||||
},
|
||||
{
|
||||
name: "NoModify - set to different value (forbidden)",
|
||||
op: operation.Update,
|
||||
value: "value2",
|
||||
oldValue: "value1",
|
||||
constraints: []UpdateConstraint{NoModify},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be modified once set"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
op := operation.Operation{Type: tt.op}
|
||||
errs := UpdateValueByCompare(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, tt.constraints...)
|
||||
if len(errs) != tt.wantErrs {
|
||||
t.Errorf("UpdateValueByCompare() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||||
}
|
||||
for i, msg := range tt.wantMsgs {
|
||||
if i >= len(errs) {
|
||||
t.Errorf("Expected error message %q not found", msg)
|
||||
continue
|
||||
}
|
||||
if errs[i].Detail != msg {
|
||||
t.Errorf("UpdateValueByCompare() error message = %q, want %q", errs[i].Detail, msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateValue_CustomMatchFunc(t *testing.T) {
|
||||
type NonComparableStruct struct {
|
||||
Name string
|
||||
Tags []string
|
||||
}
|
||||
|
||||
customEquiv := func(a, b NonComparableStruct) bool {
|
||||
return a.Name == b.Name && (len(a.Tags) == 0 && len(b.Tags) == 0 || (len(a.Tags) == len(b.Tags) && a.Tags[0] == b.Tags[0]))
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
op operation.Type
|
||||
value NonComparableStruct
|
||||
oldValue NonComparableStruct
|
||||
constraints []UpdateConstraint
|
||||
wantErrs int
|
||||
wantMsgs []string
|
||||
}{
|
||||
{
|
||||
name: "NoSet - unset to set (forbidden)",
|
||||
op: operation.Update,
|
||||
value: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||||
oldValue: NonComparableStruct{},
|
||||
constraints: []UpdateConstraint{NoSet},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be set once created"},
|
||||
},
|
||||
{
|
||||
name: "NoUnset - set to unset (forbidden)",
|
||||
op: operation.Update,
|
||||
value: NonComparableStruct{},
|
||||
oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||||
constraints: []UpdateConstraint{NoUnset},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be cleared once set"},
|
||||
},
|
||||
{
|
||||
name: "NoModify - zero to non-zero transition (allowed)",
|
||||
op: operation.Update,
|
||||
value: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||||
oldValue: NonComparableStruct{},
|
||||
constraints: []UpdateConstraint{NoModify},
|
||||
wantErrs: 0,
|
||||
},
|
||||
{
|
||||
name: "NoModify - non-zero to zero transition (allowed)",
|
||||
op: operation.Update,
|
||||
value: NonComparableStruct{},
|
||||
oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||||
constraints: []UpdateConstraint{NoModify},
|
||||
wantErrs: 0,
|
||||
},
|
||||
{
|
||||
name: "NoModify - non-zero to non-zero transition (forbidden)",
|
||||
op: operation.Update,
|
||||
value: NonComparableStruct{Name: "foo", Tags: []string{"b"}},
|
||||
oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||||
constraints: []UpdateConstraint{NoModify},
|
||||
wantErrs: 1,
|
||||
wantMsgs: []string{"field cannot be modified once set"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
op := operation.Operation{Type: tt.op}
|
||||
errs := UpdateValue(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, customEquiv, tt.constraints...)
|
||||
if len(errs) != tt.wantErrs {
|
||||
t.Errorf("UpdateValue() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||||
}
|
||||
|
||||
+4
-2
@@ -219,7 +219,8 @@ func Validate_SpecialValidationStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -248,7 +249,8 @@ func Validate_SpecialValidationStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkBeta().MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify).MarkBeta().MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
|
||||
+2
-1
@@ -258,7 +258,8 @@ func Validate_Struct(
|
||||
if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a int, b int) bool { return a == b }, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
|
||||
+4
-2
@@ -136,7 +136,8 @@ func Validate_Struct(
|
||||
earlyReturn := false
|
||||
if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", true,
|
||||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||||
return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify)
|
||||
}).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
@@ -168,7 +169,8 @@ func Validate_Struct(
|
||||
earlyReturn := false
|
||||
if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", false,
|
||||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||||
return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify)
|
||||
}).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
|
||||
+4
-2
@@ -1029,7 +1029,8 @@ func Validate_ParentWithUpdate(
|
||||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||||
func(o *TargetWithUpdate) *string { return &o.Value }, validate.DirectEqual,
|
||||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||||
return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||||
return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify)
|
||||
}).MarkShortCircuit(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -1308,7 +1309,8 @@ func Validate_TargetWithUpdate(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
|
||||
+30
-15
@@ -77,7 +77,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -106,7 +107,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -135,7 +137,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -164,7 +167,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoSet, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -193,7 +197,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -222,7 +227,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a int, b int) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -251,7 +257,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a int32, b int32) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -280,7 +287,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a int64, b int64) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -309,7 +317,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a uint, b uint) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -338,7 +347,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a bool, b bool) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -367,7 +377,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a float32, b float32) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -396,7 +407,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a float64, b float64) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -425,7 +437,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a byte, b byte) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -715,7 +728,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a CustomString, b CustomString) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
@@ -744,7 +758,8 @@ func Validate_UpdateTestStruct(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||||
if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||||
func(a CustomInt, b CustomInt) bool { return a == b }, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
|
||||
@@ -199,13 +199,12 @@ func (utc updateTagCollector) Docs() TagDoc {
|
||||
}
|
||||
|
||||
var (
|
||||
updateValueValidator = types.Name{Package: libValidationPkg, Name: "UpdateValueByCompare"}
|
||||
updatePointerValidator = types.Name{Package: libValidationPkg, Name: "UpdatePointer"}
|
||||
updateValueByReflectValidator = types.Name{Package: libValidationPkg, Name: "UpdateValueByReflect"}
|
||||
updateStructValidator = types.Name{Package: libValidationPkg, Name: "UpdateStruct"}
|
||||
valSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "ValSliceUpdate"}
|
||||
ptrSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "PtrSliceUpdate"}
|
||||
updateMapValidator = types.Name{Package: libValidationPkg, Name: "UpdateMap"}
|
||||
updateValueValidator = types.Name{Package: libValidationPkg, Name: "UpdateValue"}
|
||||
updatePointerValidator = types.Name{Package: libValidationPkg, Name: "UpdatePointer"}
|
||||
updateStructValidator = types.Name{Package: libValidationPkg, Name: "UpdateStruct"}
|
||||
valSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "ValSliceUpdate"}
|
||||
ptrSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "PtrSliceUpdate"}
|
||||
updateMapValidator = types.Name{Package: libValidationPkg, Name: "UpdateMap"}
|
||||
|
||||
// Constraint constants that will be used as arguments
|
||||
noSetConstraint = types.Name{Package: libValidationPkg, Name: "NoSet"}
|
||||
@@ -314,9 +313,8 @@ func generateMapValidation(constraints []validate.UpdateConstraint) Validations
|
||||
return Validations{Functions: []FunctionGen{fn}}
|
||||
}
|
||||
|
||||
// emitScalarUpdate emits a call to
|
||||
// UpdateValueByCompare/UpdatePointer/UpdateStruct/UpdateValueByReflect based
|
||||
// on the Go kind of context.Type.
|
||||
// emitScalarUpdate emits a call to UpdateValue/UpdatePointer/UpdateStruct
|
||||
// based on the Go kind of context.Type.
|
||||
// Used for both field scope (scalar/pointer/struct) and list/map element
|
||||
// scope via +k8s:eachVal.
|
||||
func emitScalarUpdate(context Context, constraints []validate.UpdateConstraint) Validations {
|
||||
@@ -327,21 +325,33 @@ func emitScalarUpdate(context Context, constraints []validate.UpdateConstraint)
|
||||
isComparable := util.IsDirectComparable(t)
|
||||
|
||||
var validatorFunc types.Name
|
||||
var args []any
|
||||
if isPointer {
|
||||
validatorFunc = updatePointerValidator
|
||||
args = constraintIdentifierArgs(constraints)
|
||||
} else if isStruct {
|
||||
validatorFunc = updateStructValidator
|
||||
} else if isComparable {
|
||||
validatorFunc = updateValueValidator
|
||||
args = constraintIdentifierArgs(constraints)
|
||||
} else {
|
||||
validatorFunc = updateValueByReflectValidator
|
||||
validatorFunc = updateValueValidator
|
||||
var matchArg any
|
||||
if isComparable {
|
||||
matchArg = FunctionLiteral{
|
||||
Parameters: []ParamResult{{"a", context.Type}, {"b", context.Type}},
|
||||
Results: []ParamResult{{"", types.Bool}},
|
||||
Body: "return a == b",
|
||||
}
|
||||
} else {
|
||||
matchArg = Identifier(validateSemanticDeepEqual)
|
||||
}
|
||||
args = append([]any{matchArg}, constraintIdentifierArgs(constraints)...)
|
||||
}
|
||||
|
||||
// Use ShortCircuit flag so these run in the same group as +k8s:optional.
|
||||
// Scalar/pointer/struct fields only accept NoSet/NoUnset/NoModify
|
||||
// (validateConstraintsForType rejects the rest), all of which emit
|
||||
// field.Invalid at the field path.
|
||||
fn := Function(updateTagName, ShortCircuit, validatorFunc, constraintIdentifierArgs(constraints)...).
|
||||
fn := Function(updateTagName, ShortCircuit, validatorFunc, args...).
|
||||
WithEmits(Emission{field.ErrorTypeInvalid, "update", ""})
|
||||
return Validations{Functions: []FunctionGen{fn}}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user