diff --git a/pkg/apis/apps/v1beta1/zz_generated.validations.go b/pkg/apis/apps/v1beta1/zz_generated.validations.go index 269fcad7424..d3e13a4f9d9 100644 --- a/pkg/apis/apps/v1beta1/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta1/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/apps/v1beta2/zz_generated.validations.go b/pkg/apis/apps/v1beta2/zz_generated.validations.go index 85e3e6147b3..3c16d10b674 100644 --- a/pkg/apis/apps/v1beta2/zz_generated.validations.go +++ b/pkg/apis/apps/v1beta2/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *appsv1beta2.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/autoscaling/v1/zz_generated.validations.go b/pkg/apis/autoscaling/v1/zz_generated.validations.go index dfe00d930a6..1c71f7f80e5 100644 --- a/pkg/apis/autoscaling/v1/zz_generated.validations.go +++ b/pkg/apis/autoscaling/v1/zz_generated.validations.go @@ -68,6 +68,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *autoscalingv1.ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/pkg/apis/core/v1/zz_generated.validations.go b/pkg/apis/core/v1/zz_generated.validations.go index 5ea67206150..bdde796f66b 100644 --- a/pkg/apis/core/v1/zz_generated.validations.go +++ b/pkg/apis/core/v1/zz_generated.validations.go @@ -26,6 +26,7 @@ import ( fmt "fmt" corev1 "k8s.io/api/core/v1" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -77,6 +78,9 @@ func Validate_ReplicationControllerList(ctx context.Context, op operation.Operat // field corev1.ReplicationControllerList.Items errs = append(errs, func(fldPath *field.Path, obj, oldObj []corev1.ReplicationController) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_ReplicationController)...) return }(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerList) []corev1.ReplicationController { return oldObj.Items }))...) @@ -88,6 +92,9 @@ func Validate_ReplicationControllerSpec(ctx context.Context, op operation.Operat // field corev1.ReplicationControllerSpec.Replicas errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -101,6 +108,9 @@ func Validate_ReplicationControllerSpec(ctx context.Context, op operation.Operat errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("minReadySeconds"), &obj.MinReadySeconds, safe.Field(oldObj, func(oldObj *corev1.ReplicationControllerSpec) *int32 { return &oldObj.MinReadySeconds }))...) diff --git a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go index 16bd37c4e04..6d2a1666ae3 100644 --- a/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go +++ b/staging/src/k8s.io/api/extensions/v1beta1/zz_generated.validations.go @@ -67,6 +67,9 @@ func Validate_ScaleSpec(ctx context.Context, op operation.Operation, fldPath *fi errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 0)...) return }(fldPath.Child("replicas"), &obj.Replicas, safe.Field(oldObj, func(oldObj *ScaleSpec) *int32 { return &oldObj.Replicas }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go index 8daed8e2db8..94892383e82 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_field_validations/zz_generated.validations.go @@ -65,6 +65,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -72,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -80,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T3")...) return }(fldPath.Child("t3"), &obj.T3, safe.Field(oldObj, func(oldObj *T1) *T3 { return &oldObj.T3 }))...) @@ -91,6 +100,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) @@ -102,6 +114,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T4.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T4) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go index e0e380f4a99..5788f6f579f 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/all_types_match/with_type_validations/zz_generated.validations.go @@ -56,6 +56,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E1")...) return errs @@ -63,11 +66,17 @@ func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1")...) // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go index 1798bfc0736..97ce08e259a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/cross_pkg/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -56,6 +57,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -64,6 +68,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT1Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT1Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -72,6 +79,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT2")...) errs = append(errs, primitives.Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -80,6 +90,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT2Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT2Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -88,6 +101,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT3")...) return }(fldPath.Child("primitivest3"), &obj.PrimitivesT3, safe.Field(oldObj, func(oldObj *T1) *primitives.T3 { return &oldObj.PrimitivesT3 }))...) @@ -95,6 +111,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PrimitivesT3Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.PrimitivesT3Ptr")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -103,6 +122,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE1")...) errs = append(errs, typedefs.Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -111,6 +133,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE1Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE1Ptr")...) errs = append(errs, typedefs.Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -119,6 +144,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE2")...) errs = append(errs, typedefs.Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -127,6 +155,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE2Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE2Ptr")...) errs = append(errs, typedefs.Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -135,6 +166,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE3")...) errs = append(errs, typedefs.Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -143,6 +177,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE3Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE3Ptr")...) errs = append(errs, typedefs.Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -151,6 +188,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E4) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE4")...) errs = append(errs, typedefs.Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -159,6 +199,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.TypedefsE4Ptr errs = append(errs, func(fldPath *field.Path, obj, oldObj *typedefs.E4) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.TypedefsE4Ptr")...) errs = append(errs, typedefs.Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -167,6 +210,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherString errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherString")...) return }(fldPath.Child("otherString"), &obj.OtherString, safe.Field(oldObj, func(oldObj *T1) *other.StringType { return &oldObj.OtherString }))...) @@ -174,6 +220,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStringPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherStringPtr")...) return }(fldPath.Child("otherStringPtr"), obj.OtherStringPtr, safe.Field(oldObj, func(oldObj *T1) *other.StringType { return oldObj.OtherStringPtr }))...) @@ -181,6 +230,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherInt errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherInt")...) return }(fldPath.Child("otherInt"), &obj.OtherInt, safe.Field(oldObj, func(oldObj *T1) *other.IntType { return &oldObj.OtherInt }))...) @@ -188,6 +240,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherIntPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherIntPtr")...) return }(fldPath.Child("otherIntPtr"), obj.OtherIntPtr, safe.Field(oldObj, func(oldObj *T1) *other.IntType { return oldObj.OtherIntPtr }))...) @@ -195,6 +250,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherStruct")...) return }(fldPath.Child("otherStruct"), &obj.OtherStruct, safe.Field(oldObj, func(oldObj *T1) *other.StructType { return &oldObj.OtherStruct }))...) @@ -202,6 +260,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.OtherStructPtr errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.OtherStructPtr")...) return }(fldPath.Child("otherStructPtr"), obj.OtherStructPtr, safe.Field(oldObj, func(oldObj *T1) *other.StructType { return oldObj.OtherStructPtr }))...) @@ -209,6 +270,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.SliceOfOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj []other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StructType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct values") @@ -219,6 +283,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.ListMapOfOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj []other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.ListMapOfOtherStruct")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a other.StructType, b other.StructType) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StructType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.SliceOfOtherStruct values") @@ -229,6 +296,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.MapOfOtherStringToOtherStruct errs = append(errs, func(fldPath *field.Path, obj, oldObj map[other.StringType]other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *other.StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.MapOfOtherStringToOtherStruct keys") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go index 45155dede7e..496025b23f2 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/elide_no_validations/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_HasFieldVal(ctx context.Context, op operation.Operation, fldPath * // field HasFieldVal.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field HasFieldVal.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *HasFieldVal) *string { return &oldObj.S }))...) @@ -60,6 +63,9 @@ func Validate_HasFieldVal(ctx context.Context, op operation.Operation, fldPath * func Validate_HasTypeVal(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *HasTypeVal) (errs field.ErrorList) { // type HasTypeVal + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type HasTypeVal")...) // field HasTypeVal.S has no validation @@ -88,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.HasNoValFieldVal errs = append(errs, func(fldPath *field.Path, obj, oldObj *HasNoVal) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.HasNoValFieldVal")...) return }(fldPath.Child("hasNoValFieldVal"), &obj.HasNoValFieldVal, safe.Field(oldObj, func(oldObj *T1) *HasNoVal { return &oldObj.HasNoValFieldVal }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go index 9e8977f0f53..26dd31341a2 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/embedded/zz_generated.validations.go @@ -71,6 +71,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.IntField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T2.IntField")...) return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *T2) *int { return &oldObj.IntField }))...) @@ -82,6 +85,9 @@ func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T3.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T3.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *T3) *string { return &oldObj.StringField }))...) @@ -89,6 +95,9 @@ func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T3.IntField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T3.IntField")...) return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *T3) *int { return &oldObj.IntField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go index f74ab88224e..f21259adc77 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/keys/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -56,6 +60,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField(keys)") })...) @@ -66,6 +73,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[UnvalidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UnvalidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField(keys)") })...) @@ -76,6 +86,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapValidatedTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[ValidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapValidatedTypedefField(keys)") })...) @@ -87,6 +100,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj UnvalidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypeField(keys)") })...) @@ -97,6 +113,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ValidatedMapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ValidatedMapTypeField(keys)") })...) @@ -110,6 +129,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { // type ValidatedMapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ValidatedMapType(keys)") })...) @@ -120,6 +142,9 @@ func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldP func Validate_ValidatedStringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) (errs field.ErrorList) { // type ValidatedStringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "ValidatedStringType")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go index c78aeb19e35..efdf874ae89 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -56,6 +60,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -63,6 +70,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -73,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go index 3d3e0db5c2a..5d47ce0b36b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherStruct")...) return errs @@ -56,6 +60,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) (errs field.ErrorList) { // type OtherTypedefStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherTypedefStruct")...) return errs @@ -63,6 +70,9 @@ func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fl func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -70,6 +80,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go index 876a5c944b7..5d4ecc6f6df 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/multiple_validations/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -56,6 +60,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField(keys) #1") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go index b49a264142c..6695b015d11 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType[*]") @@ -59,6 +63,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { // type MapTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType[*]") @@ -70,6 +77,9 @@ func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPat func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -77,6 +87,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -84,6 +97,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") @@ -95,6 +111,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField")...) errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go index a0a1f693cde..c8bf791b803 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/multiple_tags/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1 #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1 #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1 #3")...) @@ -59,6 +62,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S false #3")...) @@ -69,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2 false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2 false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2 false #3")...) @@ -82,6 +91,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { // type T2 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T2 false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T2 false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type T2 true")...) @@ -89,6 +101,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S false #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S false #2")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T2.S true")...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go index 1f35c9c8833..60f41b8c713 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_field_validations/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E1")...) return errs @@ -60,6 +63,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -67,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -75,6 +84,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T3")...) return }(fldPath.Child("t3"), &obj.T3, safe.Field(oldObj, func(oldObj *T1) *T3 { return &oldObj.T3 }))...) @@ -82,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -90,6 +105,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E2")...) return }(fldPath.Child("e2"), &obj.E2, safe.Field(oldObj, func(oldObj *T1) *E2 { return &oldObj.E2 }))...) @@ -101,6 +119,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go index f3d8938b40d..9716b26ce97 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/one_type_match/with_type_validations/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1")...) // field T1.TypeMeta has no validation diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go index 4d788478fe4..124a93e6ece 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/structs/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -99,6 +100,9 @@ func Validate_T00(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T01) (errs field.ErrorList) { // type T01 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01, no flags")...) // field T01.TypeMeta has no validation @@ -106,6 +110,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01.S, no flags")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T01) *string { return &oldObj.S }))...) @@ -113,6 +120,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01.PS, no flags")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T01) *string { return oldObj.PS }))...) @@ -120,6 +130,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01.T, no flags")...) errs = append(errs, Validate_Tother(ctx, op, fldPath, obj, oldObj)...) return @@ -128,6 +141,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T01.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T01.PT, no flags")...) errs = append(errs, Validate_Tother(ctx, op, fldPath, obj, oldObj)...) return @@ -138,6 +154,9 @@ func Validate_T01(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T02) (errs field.ErrorList) { // type T02 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -148,6 +167,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.S, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -158,6 +180,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.PS, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -168,6 +193,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.T, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -179,6 +207,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T02.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T02.PT, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -192,6 +223,9 @@ func Validate_T02(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T03) (errs field.ErrorList) { // type T03 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -203,6 +237,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.S, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -214,6 +251,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.PS, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -225,6 +265,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.T, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -237,6 +280,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa // field T03.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "T03.PT, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -251,6 +297,9 @@ func Validate_T03(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *TMultiple) (errs field.ErrorList) { // type TMultiple + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -268,6 +317,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.S, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -285,6 +337,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.PS, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -302,6 +357,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.T errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.T, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -320,6 +378,9 @@ func Validate_TMultiple(ctx context.Context, op operation.Operation, fldPath *fi // field TMultiple.PT errs = append(errs, func(fldPath *field.Path, obj, oldObj *Tother) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "TMultiple.PT, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -342,6 +403,9 @@ func Validate_Tother(ctx context.Context, op operation.Operation, fldPath *field // field Tother.OS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Tother, no flags")...) return }(fldPath.Child("os"), &obj.OS, safe.Field(oldObj, func(oldObj *Tother) *string { return &oldObj.OS }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go index dc3be29b6a6..11fc97154af 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ordering/typedefs/zz_generated.validations.go @@ -70,6 +70,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E01(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E01) (errs field.ErrorList) { // type E01 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "E01, no flags")...) return errs @@ -77,6 +80,9 @@ func Validate_E01(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_E02(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E02) (errs field.ErrorList) { // type E02 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "E02, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -87,6 +93,9 @@ func Validate_E02(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_E03(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E03) (errs field.ErrorList) { // type E03 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "E03, ShortCircuit"); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -98,6 +107,9 @@ func Validate_E03(ctx context.Context, op operation.Operation, fldPath *field.Pa func Validate_EMultiple(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *EMultiple) (errs field.ErrorList) { // type EMultiple + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "EMultiple, ShortCircuit 1"); len(e) != 0 { errs = append(errs, e...) return // do not proceed diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go index 316f8baa337..b353d293a2e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/pointers/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PS")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T1) *string { return oldObj.PS }))...) @@ -60,6 +64,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PI errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PI")...) return }(fldPath.Child("pi"), obj.PI, safe.Field(oldObj, func(oldObj *T1) *int { return oldObj.PI }))...) @@ -67,6 +74,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PB errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PB")...) return }(fldPath.Child("pb"), obj.PB, safe.Field(oldObj, func(oldObj *T1) *bool { return oldObj.PB }))...) @@ -74,6 +84,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PF errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PF")...) return }(fldPath.Child("pf"), obj.PF, safe.Field(oldObj, func(oldObj *T1) *float64 { return oldObj.PF }))...) @@ -81,6 +94,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PT2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -97,6 +113,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PS errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.PS")...) return }(fldPath.Child("ps"), obj.PS, safe.Field(oldObj, func(oldObj *T2) *string { return oldObj.PS }))...) @@ -104,6 +123,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PI errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.PI")...) return }(fldPath.Child("pi"), obj.PI, safe.Field(oldObj, func(oldObj *T2) *int { return oldObj.PI }))...) @@ -111,6 +133,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PB errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.PB")...) return }(fldPath.Child("pb"), obj.PB, safe.Field(oldObj, func(oldObj *T2) *bool { return oldObj.PB }))...) @@ -118,6 +143,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PF errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.PF")...) return }(fldPath.Child("pf"), obj.PF, safe.Field(oldObj, func(oldObj *T2) *float64 { return oldObj.PF }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go index ceaf93bbe87..84d3819e15d 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/primitives/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) @@ -60,6 +63,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.I errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.I")...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *T1) *int { return &oldObj.I }))...) @@ -67,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.B")...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *T1) *bool { return &oldObj.B }))...) @@ -74,6 +83,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.F")...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *T1) *float64 { return &oldObj.F }))...) @@ -81,6 +93,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -106,6 +121,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) @@ -113,6 +131,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.I errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.I")...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *T2) *int { return &oldObj.I }))...) @@ -120,6 +141,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.B")...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *T2) *bool { return &oldObj.B }))...) @@ -127,6 +151,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.F")...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *T2) *float64 { return &oldObj.F }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go index e1bdaffb676..16de705f710 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/public_private/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.Public errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.Public")...) return }(fldPath.Child("public"), &obj.Public, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.Public }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go new file mode 100644 index 00000000000..b5514658ec9 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc.go @@ -0,0 +1,144 @@ +/* +Copyright 2024 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme + +// This is a test package. +package defaultbehavior + +import "k8s.io/code-generator/cmd/validation-gen/testscheme" + +var localSchemeBuilder = testscheme.New() + +type StructPrimitive struct { + TypeMeta int + + // +k8s:validateFalse="field intField" + IntField int `json:"intField"` + + // +k8s:optional + // +k8s:validateFalse="field intPtrField" + IntPtrField *int `json:"intPtrField"` +} + +type StructSlice struct { + TypeMeta int + + // +k8s:validateFalse="field sliceField" + SliceField []S `json:"sliceField"` + + // +k8s:validateFalse="field typedefSliceField" + TypeDefSliceField MySlice `json:"typedefSliceField"` +} + +type StructMap struct { + TypeMeta int + + // +k8s:validateFalse="field mapKeyField" + MapKeyField map[S]string `json:"mapKeyField"` + + // +k8s:validateFalse="field mapValueField" + MapValueField map[string]S `json:"mapValueField"` + + // +k8s:validateFalse="field aliasMapKeyTypeField" + AliasMapKeyTypeField AliasMapKeyType `json:"aliasMapKeyTypeField"` + + // +k8s:validateFalse="field aliasMapValueTypeField" + AliasMapValueTypeField AliasMapValueType `json:"aliasMapValueTypeField"` +} + +type StructStruct struct { + TypeMeta int + + // +k8s:validateFalse="field directComparableStructField" + DirectComparableStructField DirectComparableStruct `json:"directComparableStructField"` + + // +k8s:validateFalse="field nonDirectComparableStructField" + NonDirectComparableStructField NonDirectComparableStruct `json:"nonDirectComparableStructField"` + + // +k8s:validateFalse="field directComparableStructPtrField" + DirectComparableStructPtr *DirectComparableStruct `json:"directComparableStructPtrField"` + + // +k8s:validateFalse="field nonDirectComparableStructPtrField" + NonDirectComparableStructPtr *NonDirectComparableStruct `json:"nonDirectComparableStructPtrField"` + + // +k8s:validateFalse="field DirectComparableStruct" + DirectComparableStruct + + // +k8s:validateFalse="field NonDirectComparableStruct" + NonDirectComparableStruct +} + +type StructEmbedded struct { + TypeMeta int + // +k8s:validateFalse="field DirectComparableStruct" + DirectComparableStruct `json:"directComparableStruct"` + + // +k8s:validateFalse="field NonDirectComparableStruct" + NonDirectComparableStruct `json:"nonDirectComparableStruct"` + + // +k8s:validateFalse="field NestedDirectComparableStructField" + NestedDirectComparableStructField NestedDirectComparableStruct `json:"nestedDirectComparableStructField"` + + // +k8s:validateFalse="field NestedNonDirectComparableStructField" + NestedNonDirectComparableStructField NestedNonDirectComparableStruct `json:"nestedNonDirectComparableStructField"` +} + +// +k8s:validateFalse="type TypeDefStruct" +type TypeDefStruct struct{} + +// +k8s:validateFalse="type MySlice" +type MySlice []int + +// +k8s:validateFalse="type S" +type S string + +// +k8s:validateFalse="type MapKeyType" +type AliasMapKeyType MapKeyType + +// +k8s:validateFalse="type MapValueType" +type AliasMapValueType MapValueType + +// no validation +type MapKeyType map[S]string + +// no validation +type MapValueType map[string]S + +// +k8s:validateFalse="type DirectComparableStruct" +type DirectComparableStruct struct { + // +k8s:validateFalse="field intField" + IntField int `json:"intField"` +} + +// +k8s:validateFalse="type NonDirectComparableStruct" +type NonDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + IntPtrField *int `json:"intPtrField"` +} + +// +k8s:validateFalse="type NestedDirectComparableStruct" +type NestedDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + DirectComparableStructField DirectComparableStruct `json:"directComparableStructField"` +} + +// +k8s:validateFalse="type NestedNonDirectComparableStruct" +type NestedNonDirectComparableStruct struct { + // +k8s:validateFalse="field intField" + NonDirectComparableStructField NonDirectComparableStruct `json:"nonDirectComparableStructField"` +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go new file mode 100644 index 00000000000..972049ed2c0 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/doc_test.go @@ -0,0 +1,209 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package defaultbehavior + +import ( + "testing" + + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" +) + +func Test_StructPrimitive(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("intField"), "", ""), + field.Invalid(field.NewPath("intPtrField"), "", ""), + }) + st.Value(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), + }).OldValue(&StructPrimitive{ + IntField: 1, + IntPtrField: ptr.To(1), // Different pointers but value unchanged. + }).ExpectValid() +} + +func Test_StructSlice(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("sliceField"), "", ""), + field.Invalid(field.NewPath("sliceField[0]"), "", ""), + field.Invalid(field.NewPath("typedefSliceField"), "", ""), + }) + + st.Value(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).OldValue(&StructSlice{ + SliceField: []S{""}, + TypeDefSliceField: MySlice{1}, + }).ExpectValid() +} + +func Test_StructMap(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("mapKeyField"), "", ""), + field.Invalid(field.NewPath("mapValueField"), "", ""), + field.Invalid(field.NewPath("mapValueField[k]"), "", ""), + field.Invalid(field.NewPath("aliasMapKeyTypeField"), "", ""), + field.Invalid(field.NewPath("aliasMapValueTypeField"), "", ""), + field.Invalid(field.NewPath("aliasMapValueTypeField[k]"), "", ""), + }) + + st.Value(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).OldValue(&StructMap{ + MapKeyField: map[S]string{S("k"): "v"}, + MapValueField: map[string]S{"k": "v"}, + AliasMapKeyTypeField: AliasMapKeyType{"k": "v"}, + AliasMapValueTypeField: AliasMapValueType{"k": "v"}, + }).ExpectValid() +} + +func Test_StructStruct(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("directComparableStructField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("directComparableStructField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructField").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("directComparableStructPtrField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructPtrField"), "", ""), + field.Invalid(field.NewPath("directComparableStructPtrField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStructPtrField").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("DirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("NonDirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("DirectComparableStruct").Child("intField"), "", ""), + field.Invalid(field.NewPath("NonDirectComparableStruct").Child("intPtrField"), "", ""), + }) + + st.Value(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).OldValue(&StructStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + DirectComparableStructPtr: &DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStructPtr: &NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectValid() +} + +func Test_StructEmbedded(t *testing.T) { + st := localSchemeBuilder.Test(t) + st.Value(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{ + field.Invalid(field.NewPath("directComparableStruct"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStruct"), "", ""), + field.Invalid(field.NewPath("directComparableStruct").Child("intField"), "", ""), + field.Invalid(field.NewPath("nonDirectComparableStruct").Child("intPtrField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField").Child("directComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedDirectComparableStructField").Child("directComparableStructField").Child("intField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField").Child("nonDirectComparableStructField"), "", ""), + field.Invalid(field.NewPath("nestedNonDirectComparableStructField").Child("nonDirectComparableStructField").Child("intPtrField"), "", ""), + }) + + st.Value(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + NestedDirectComparableStructField: NestedDirectComparableStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + }, + NestedNonDirectComparableStructField: NestedNonDirectComparableStruct{ + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }, + }).OldValue(&StructEmbedded{ + DirectComparableStruct: DirectComparableStruct{ + IntField: 1, + }, + NonDirectComparableStruct: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + NestedDirectComparableStructField: NestedDirectComparableStruct{ + DirectComparableStructField: DirectComparableStruct{ + IntField: 1, + }, + }, + NestedNonDirectComparableStructField: NestedNonDirectComparableStruct{ + NonDirectComparableStructField: NonDirectComparableStruct{ + IntPtrField: ptr.To(1), + }, + }, + }).ExpectValid() +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go new file mode 100644 index 00000000000..26a3efd54d2 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/default_behavior/zz_generated.validations.go @@ -0,0 +1,438 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by validation-gen. DO NOT EDIT. + +package defaultbehavior + +import ( + context "context" + fmt "fmt" + + equality "k8s.io/apimachinery/pkg/api/equality" + operation "k8s.io/apimachinery/pkg/api/operation" + safe "k8s.io/apimachinery/pkg/api/safe" + validate "k8s.io/apimachinery/pkg/api/validate" + field "k8s.io/apimachinery/pkg/util/validation/field" + testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme" +) + +func init() { localSchemeBuilder.Register(RegisterValidations) } + +// RegisterValidations adds validation functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterValidations(scheme *testscheme.Scheme) error { + scheme.AddValidationFunc((*StructEmbedded)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructEmbedded(ctx, op, nil /* fldPath */, obj.(*StructEmbedded), safe.Cast[*StructEmbedded](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructMap)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructMap(ctx, op, nil /* fldPath */, obj.(*StructMap), safe.Cast[*StructMap](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructPrimitive)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructPrimitive(ctx, op, nil /* fldPath */, obj.(*StructPrimitive), safe.Cast[*StructPrimitive](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructSlice(ctx, op, nil /* fldPath */, obj.(*StructSlice), safe.Cast[*StructSlice](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + scheme.AddValidationFunc((*StructStruct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList { + switch op.Request.SubresourcePath() { + case "/": + return Validate_StructStruct(ctx, op, nil /* fldPath */, obj.(*StructStruct), safe.Cast[*StructStruct](oldObj)) + } + return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))} + }) + return nil +} + +func Validate_AliasMapKeyType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj AliasMapKeyType) (errs field.ErrorList) { + // type AliasMapKeyType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapKeyType")...) + errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, Validate_S)...) + + return errs +} + +func Validate_AliasMapValueType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj AliasMapValueType) (errs field.ErrorList) { + // type AliasMapValueType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapValueType")...) + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_S)...) + + return errs +} + +func Validate_DirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + // type DirectComparableStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type DirectComparableStruct")...) + + // field DirectComparableStruct.IntField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + return + }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *DirectComparableStruct) *int { return &oldObj.IntField }))...) + + return errs +} + +func Validate_MySlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MySlice) (errs field.ErrorList) { + // type MySlice + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MySlice")...) + + return errs +} + +func Validate_NestedDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NestedDirectComparableStruct) (errs field.ErrorList) { + // type NestedDirectComparableStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NestedDirectComparableStruct")...) + + // field NestedDirectComparableStruct.DirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructField"), &obj.DirectComparableStructField, safe.Field(oldObj, func(oldObj *NestedDirectComparableStruct) *DirectComparableStruct { + return &oldObj.DirectComparableStructField + }))...) + + return errs +} + +func Validate_NestedNonDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NestedNonDirectComparableStruct) (errs field.ErrorList) { + // type NestedNonDirectComparableStruct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NestedNonDirectComparableStruct")...) + + // field NestedNonDirectComparableStruct.NonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructField"), &obj.NonDirectComparableStructField, safe.Field(oldObj, func(oldObj *NestedNonDirectComparableStruct) *NonDirectComparableStruct { + return &oldObj.NonDirectComparableStructField + }))...) + + return errs +} + +func Validate_NonDirectComparableStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + // type NonDirectComparableStruct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NonDirectComparableStruct")...) + + // field NonDirectComparableStruct.IntPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + return + }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *NonDirectComparableStruct) *int { return oldObj.IntPtrField }))...) + + return errs +} + +func Validate_S(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *S) (errs field.ErrorList) { + // type S + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type S")...) + + return errs +} + +func Validate_StructEmbedded(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructEmbedded) (errs field.ErrorList) { + // field StructEmbedded.TypeMeta has no validation + + // field StructEmbedded.DirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field DirectComparableStruct")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStruct"), &obj.DirectComparableStruct, safe.Field(oldObj, func(oldObj *StructEmbedded) *DirectComparableStruct { return &oldObj.DirectComparableStruct }))...) + + // field StructEmbedded.NonDirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NonDirectComparableStruct")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStruct"), &obj.NonDirectComparableStruct, safe.Field(oldObj, func(oldObj *StructEmbedded) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStruct }))...) + + // field StructEmbedded.NestedDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NestedDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NestedDirectComparableStructField")...) + errs = append(errs, Validate_NestedDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nestedDirectComparableStructField"), &obj.NestedDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructEmbedded) *NestedDirectComparableStruct { + return &oldObj.NestedDirectComparableStructField + }))...) + + // field StructEmbedded.NestedNonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NestedNonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NestedNonDirectComparableStructField")...) + errs = append(errs, Validate_NestedNonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nestedNonDirectComparableStructField"), &obj.NestedNonDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructEmbedded) *NestedNonDirectComparableStruct { + return &oldObj.NestedNonDirectComparableStructField + }))...) + + return errs +} + +func Validate_StructMap(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructMap) (errs field.ErrorList) { + // field StructMap.TypeMeta has no validation + + // field StructMap.MapKeyField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[S]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field mapKeyField")...) + errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, Validate_S)...) + return + }(fldPath.Child("mapKeyField"), obj.MapKeyField, safe.Field(oldObj, func(oldObj *StructMap) map[S]string { return oldObj.MapKeyField }))...) + + // field StructMap.MapValueField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj map[string]S) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field mapValueField")...) + errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_S)...) + return + }(fldPath.Child("mapValueField"), obj.MapValueField, safe.Field(oldObj, func(oldObj *StructMap) map[string]S { return oldObj.MapValueField }))...) + + // field StructMap.AliasMapKeyTypeField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj AliasMapKeyType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field aliasMapKeyTypeField")...) + errs = append(errs, Validate_AliasMapKeyType(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("aliasMapKeyTypeField"), obj.AliasMapKeyTypeField, safe.Field(oldObj, func(oldObj *StructMap) AliasMapKeyType { return oldObj.AliasMapKeyTypeField }))...) + + // field StructMap.AliasMapValueTypeField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj AliasMapValueType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field aliasMapValueTypeField")...) + errs = append(errs, Validate_AliasMapValueType(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("aliasMapValueTypeField"), obj.AliasMapValueTypeField, safe.Field(oldObj, func(oldObj *StructMap) AliasMapValueType { return oldObj.AliasMapValueTypeField }))...) + + return errs +} + +func Validate_StructPrimitive(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructPrimitive) (errs field.ErrorList) { + // field StructPrimitive.TypeMeta has no validation + + // field StructPrimitive.IntField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intField")...) + return + }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *StructPrimitive) *int { return &oldObj.IntField }))...) + + // field StructPrimitive.IntPtrField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + return // do not proceed + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field intPtrField")...) + return + }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *StructPrimitive) *int { return oldObj.IntPtrField }))...) + + return errs +} + +func Validate_StructSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructSlice) (errs field.ErrorList) { + // field StructSlice.TypeMeta has no validation + + // field StructSlice.SliceField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj []S) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field sliceField")...) + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_S)...) + return + }(fldPath.Child("sliceField"), obj.SliceField, safe.Field(oldObj, func(oldObj *StructSlice) []S { return oldObj.SliceField }))...) + + // field StructSlice.TypeDefSliceField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj MySlice) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field typedefSliceField")...) + errs = append(errs, Validate_MySlice(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("typedefSliceField"), obj.TypeDefSliceField, safe.Field(oldObj, func(oldObj *StructSlice) MySlice { return oldObj.TypeDefSliceField }))...) + + return errs +} + +func Validate_StructStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StructStruct) (errs field.ErrorList) { + // field StructStruct.TypeMeta has no validation + + // field StructStruct.DirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field directComparableStructField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructField"), &obj.DirectComparableStructField, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return &oldObj.DirectComparableStructField }))...) + + // field StructStruct.NonDirectComparableStructField + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field nonDirectComparableStructField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructField"), &obj.NonDirectComparableStructField, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStructField }))...) + + // field StructStruct.DirectComparableStructPtr + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field directComparableStructPtrField")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("directComparableStructPtrField"), obj.DirectComparableStructPtr, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return oldObj.DirectComparableStructPtr }))...) + + // field StructStruct.NonDirectComparableStructPtr + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field nonDirectComparableStructPtrField")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(fldPath.Child("nonDirectComparableStructPtrField"), obj.NonDirectComparableStructPtr, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return oldObj.NonDirectComparableStructPtr }))...) + + // field StructStruct.DirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *DirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field DirectComparableStruct")...) + errs = append(errs, Validate_DirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("DirectComparableStruct") }), &obj.DirectComparableStruct, safe.Field(oldObj, func(oldObj *StructStruct) *DirectComparableStruct { return &oldObj.DirectComparableStruct }))...) + + // field StructStruct.NonDirectComparableStruct + errs = append(errs, + func(fldPath *field.Path, obj, oldObj *NonDirectComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } + errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field NonDirectComparableStruct")...) + errs = append(errs, Validate_NonDirectComparableStruct(ctx, op, fldPath, obj, oldObj)...) + return + }(safe.Value(fldPath, func() *field.Path { return fldPath.Child("NonDirectComparableStruct") }), &obj.NonDirectComparableStruct, safe.Field(oldObj, func(oldObj *StructStruct) *NonDirectComparableStruct { return &oldObj.NonDirectComparableStruct }))...) + + return errs +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go index 0a512f519df..1fcb13d2fc0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/maps/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -90,6 +91,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.MT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_T1)...) return }(fldPath.Child("mt1"), obj.MT1, safe.Field(oldObj, func(oldObj *T2) map[string]T1 { return oldObj.MT1 }))...) @@ -99,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T3")...) // field T3.T4 @@ -115,6 +122,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.MT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]T3) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_T3)...) return }(fldPath.Child("mt3"), obj.MT3, safe.Field(oldObj, func(oldObj *T4) map[string]T3 { return oldObj.MT3 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go index f126983098b..1ebdaf5c959 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/pointers/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -65,6 +66,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -82,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -96,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -106,6 +116,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -116,6 +129,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.PT3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -128,6 +144,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T3")...) // field T3.I has no validation diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go index 1e60142ba07..fb6802c71c8 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/recursive/slices/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -90,6 +91,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T2.ST1 errs = append(errs, func(fldPath *field.Path, obj, oldObj []T1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_T1)...) return }(fldPath.Child("st1"), obj.ST1, safe.Field(oldObj, func(oldObj *T2) []T1 { return oldObj.ST1 }))...) @@ -99,6 +103,9 @@ func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T3) (errs field.ErrorList) { // type T3 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T3")...) // field T3.T4 @@ -115,6 +122,9 @@ func Validate_T4(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T4.ST3 errs = append(errs, func(fldPath *field.Path, obj, oldObj []T3) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_T3)...) return }(fldPath.Child("st3"), obj.ST3, safe.Field(oldObj, func(oldObj *T4) []T3 { return oldObj.ST3 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go index 78c2e2fe6f8..6144e21a4e4 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/multiple_validations/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct #2")...) @@ -57,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField #1")...) errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField #2")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go index 00f2e9649f6..0f70452efc5 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -56,6 +60,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -63,6 +70,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -73,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go index 51e32a725ae..77351fab73a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/slice_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherStruct")...) return errs @@ -56,6 +60,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) (errs field.ErrorList) { // type OtherTypedefStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherTypedefStruct")...) return errs @@ -63,6 +70,9 @@ func Validate_OtherTypedefStruct(ctx context.Context, op operation.Operation, fl func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -70,6 +80,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") @@ -92,6 +108,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UnvalidatedListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_OtherStruct)...) return }(fldPath.Child("UnvalidatedListField"), obj.UnvalidatedListField, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.UnvalidatedListField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go index cf3c1535431..73c4b2e3ab9 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/slices/typedef_to_slice/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { // type ListType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType[*]") @@ -59,6 +63,9 @@ func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *fie func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { // type ListTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType[*]") @@ -70,6 +77,9 @@ func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPa func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -77,6 +87,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -84,6 +97,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") @@ -95,6 +111,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go index 20b7530bed5..05603483916 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachkey/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapField(keys)") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[UnvalidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *UnvalidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapTypedefField(keys)") })...) @@ -71,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapValidatedTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[ValidatedStringType]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapValidatedTypedefField(keys)") })...) @@ -81,6 +91,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj UnvalidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.MapTypeField(keys)") })...) @@ -90,6 +103,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ValidatedMapTypeField errs = append(errs, func(fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "Struct.ValidatedMapTypeField(keys)") })...) @@ -102,6 +118,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ValidatedMapType) (errs field.ErrorList) { // type ValidatedMapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "ValidatedMapType(keys)") })...) @@ -111,6 +130,9 @@ func Validate_ValidatedMapType(ctx context.Context, op operation.Operation, fldP func Validate_ValidatedStringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ValidatedStringType) (errs field.ErrorList) { // type ValidatedStringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "ValidatedStringType")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go index 7e8f79fe17e..fc030f63ed0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go index 91dbe96de17..604838a8768 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go index 1e9dfc11098..922665ca515 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_primitive/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []StringType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go index e3166b543df..f8c03528e9e 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/slice_of_struct/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherTypedefStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go index 3f034ef725e..a7b73881763 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_map/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType[*]") })...) @@ -58,6 +62,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { // type MapTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapTypedefType[*]") })...) @@ -68,6 +75,9 @@ func Validate_MapTypedefType(ctx context.Context, op operation.Operation, fldPat func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -75,6 +85,9 @@ func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *f func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) { // type Struct + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type Struct")...) // field Struct.TypeMeta has no validation @@ -82,6 +95,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]") })...) @@ -92,6 +108,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go index 5796574cbd2..df48f76ed5b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/typedef_to_slice/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { // type ListType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListType[*]") })...) @@ -58,6 +62,9 @@ func Validate_ListType(ctx context.Context, op operation.Operation, fldPath *fie func Validate_ListTypedefType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { // type ListTypedefType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type ListTypedefType[*]") })...) @@ -71,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListField[*]") })...) @@ -81,6 +91,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj ListTypedefType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListTypedefField[*]") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go index 4fb6bcffbc9..b6aff0a3255 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/immutable/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_ImmutableType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ImmutableType) (errs field.ErrorList) { // type ImmutableType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return errs @@ -60,6 +64,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) @@ -67,6 +74,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("stringPtrField"), obj.StringPtrField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.StringPtrField }))...) @@ -74,6 +84,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *ComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("structField"), &obj.StructField, safe.Field(oldObj, func(oldObj *Struct) *ComparableStruct { return &oldObj.StructField }))...) @@ -81,6 +94,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *ComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("structPtrField"), obj.StructPtrField, safe.Field(oldObj, func(oldObj *Struct) *ComparableStruct { return oldObj.StructPtrField }))...) @@ -88,6 +104,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.NonComparableStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *NonComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("noncomparableStructField"), &obj.NonComparableStructField, safe.Field(oldObj, func(oldObj *Struct) *NonComparableStruct { return &oldObj.NonComparableStructField }))...) @@ -95,6 +114,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.NonComparableStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *NonComparableStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("noncomparableStructPtrField"), obj.NonComparableStructPtrField, safe.Field(oldObj, func(oldObj *Struct) *NonComparableStruct { return oldObj.NonComparableStructPtrField }))...) @@ -102,6 +124,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("sliceField"), obj.SliceField, safe.Field(oldObj, func(oldObj *Struct) []string { return oldObj.SliceField }))...) @@ -109,6 +134,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.ImmutableByReflect(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("mapField"), obj.MapField, safe.Field(oldObj, func(oldObj *Struct) map[string]string { return oldObj.MapField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go index eebdcd83c76..c6805afd2cc 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/multiple_keys/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.Key1Field == b.Key1Field && a.Key2Field == b.Key2Field }, validate.ImmutableByReflect)...) @@ -62,6 +66,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherTypedefStruct, b OtherTypedefStruct) bool { return a.Key1Field == b.Key1Field && a.Key2Field == b.Key2Field }, validate.ImmutableByReflect)...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go index 501e4599546..807d77de92b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listmap/single_key/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.KeyField == b.KeyField }, validate.ImmutableByReflect)...) return }(fldPath.Child("listField"), obj.ListField, safe.Field(oldObj, func(oldObj *Struct) []OtherStruct { return oldObj.ListField }))...) @@ -60,6 +64,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherTypedefStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherTypedefStruct, b OtherTypedefStruct) bool { return a.KeyField == b.KeyField }, validate.ImmutableByReflect)...) return }(fldPath.Child("listTypedefField"), obj.ListTypedefField, safe.Field(oldObj, func(oldObj *Struct) []OtherTypedefStruct { return oldObj.ListTypedefField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go index d084889683e..46d416e2f28 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/doc_test.go @@ -46,6 +46,16 @@ func Test(t *testing.T) { field.Invalid(field.NewPath("typedefField"), 0, content.MinError(1)), field.Invalid(field.NewPath("typedefPtrField"), 0, content.MinError(1)), ) + // Test validation ratcheting + st.Value(&Struct{ + IntPtrField: ptr.To(0), + UintPtrField: ptr.To(uint(0)), + TypedefPtrField: ptr.To(IntType(0)), + }).OldValue(&Struct{ + IntPtrField: ptr.To(0), + UintPtrField: ptr.To(uint(0)), + TypedefPtrField: ptr.To(IntType(0)), + }).ExpectValid() st.Value(&Struct{ IntField: 1, diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go index c8172d6675e..21495665141 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/minimum/zz_generated.validations.go @@ -49,6 +49,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { // type IntType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return errs @@ -60,6 +63,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }))...) @@ -67,6 +73,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("intPtrField"), obj.IntPtrField, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IntPtrField }))...) @@ -74,6 +83,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int16Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int16) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int16Field"), &obj.Int16Field, safe.Field(oldObj, func(oldObj *Struct) *int16 { return &oldObj.Int16Field }))...) @@ -81,6 +93,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int32Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int32Field"), &obj.Int32Field, safe.Field(oldObj, func(oldObj *Struct) *int32 { return &oldObj.Int32Field }))...) @@ -88,6 +103,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Int64Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("int64Field"), &obj.Int64Field, safe.Field(oldObj, func(oldObj *Struct) *int64 { return &oldObj.Int64Field }))...) @@ -95,6 +113,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UintField errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uintField"), &obj.UintField, safe.Field(oldObj, func(oldObj *Struct) *uint { return &oldObj.UintField }))...) @@ -102,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.UintPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uintPtrField"), obj.UintPtrField, safe.Field(oldObj, func(oldObj *Struct) *uint { return oldObj.UintPtrField }))...) @@ -109,6 +133,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint16Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint16) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint16Field"), &obj.Uint16Field, safe.Field(oldObj, func(oldObj *Struct) *uint16 { return &oldObj.Uint16Field }))...) @@ -116,6 +143,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint32Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint32) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint32Field"), &obj.Uint32Field, safe.Field(oldObj, func(oldObj *Struct) *uint32 { return &oldObj.Uint32Field }))...) @@ -123,6 +153,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.Uint64Field errs = append(errs, func(fldPath *field.Path, obj, oldObj *uint64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) return }(fldPath.Child("uint64Field"), &obj.Uint64Field, safe.Field(oldObj, func(oldObj *Struct) *uint64 { return &oldObj.Uint64Field }))...) @@ -130,6 +163,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.TypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...) return @@ -138,6 +174,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.TypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Minimum(ctx, op, fldPath, obj, oldObj, 1)...) errs = append(errs, Validate_IntType(ctx, op, fldPath, obj, oldObj)...) return diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go index f303cf0cf28..6210190a545 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/opaque/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -77,6 +78,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_OtherString(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) (errs field.ErrorList) { // type OtherString + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherString")...) return errs @@ -84,11 +88,17 @@ func Validate_OtherString(ctx context.Context, op operation.Operation, fldPath * func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherStruct")...) // field OtherStruct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field OtherStruct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *OtherStruct) *string { return &oldObj.StringField }))...) @@ -102,6 +112,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.StructField")...) errs = append(errs, Validate_OtherStruct(ctx, op, fldPath, obj, oldObj)...) return @@ -110,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -122,6 +138,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.OpaqueStructField")...) return }(fldPath.Child("opaqueStructField"), &obj.OpaqueStructField, safe.Field(oldObj, func(oldObj *Struct) *OtherStruct { return &oldObj.OpaqueStructField }))...) @@ -129,6 +148,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OpaqueStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) return // do not proceed @@ -140,6 +162,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceOfStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfStructField vals") @@ -151,6 +176,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceOfOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfOpaqueStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.SliceOfOpaqueStructField vals") @@ -161,6 +189,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListMapOfStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfStructField vals") @@ -172,6 +203,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.ListMapOfOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj []OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfOpaqueStructField")...) errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a OtherStruct, b OtherStruct) bool { return a.StringField == b.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListMapOfOpaqueStructField vals") @@ -182,6 +216,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapOfStringToStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[OtherString]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapOfStringToStructField keys") })...) @@ -197,6 +234,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapOfStringToOpaqueStructField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[OtherString]OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapKey(ctx, op, fldPath, obj, oldObj, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherString) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapOfStringToOpaqueStructField keys") })...) @@ -212,6 +252,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field func Validate_TypedefMapOther(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj TypedefMapOther) (errs field.ErrorList) { // type TypedefMapOther + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type TypedefMapOther")...) return errs @@ -219,6 +262,9 @@ func Validate_TypedefMapOther(ctx context.Context, op operation.Operation, fldPa func Validate_TypedefSliceOther(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj TypedefSliceOther) (errs field.ErrorList) { // type TypedefSliceOther + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "type TypedefSliceOther")...) return errs diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go index 49829b71239..996057dcd70 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/nonzero_defaults/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -64,6 +67,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -75,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -86,6 +95,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -97,6 +109,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BoolField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -108,6 +123,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BoolPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go index afdc74af490..2cbb773d7b3 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zero_defaults/zz_generated.validations.go @@ -54,12 +54,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) // field Struct.StringPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -72,12 +78,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("intField"), &obj.IntField, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.IntField }))...) // field Struct.IntPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) @@ -90,12 +102,18 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { // optional value-type fields with zero-value defaults are purely documentation + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } return }(fldPath.Child("boolField"), &obj.BoolField, safe.Field(oldObj, func(oldObj *Struct) *bool { return &oldObj.BoolField }))...) // field Struct.BoolPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } // optional fields with default values are effectively required if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { errs = append(errs, e...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go index 78dd83d2321..9f187794952 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/optional/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { // type IntType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type IntType")...) return errs @@ -56,6 +60,9 @@ func Validate_IntType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { // type MapType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapType")...) return errs @@ -63,6 +70,9 @@ func Validate_MapType(ctx context.Context, op operation.Operation, fldPath *fiel func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { // type OtherStruct + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherStruct")...) return errs @@ -70,6 +80,9 @@ func Validate_OtherStruct(ctx context.Context, op operation.Operation, fldPath * func Validate_SliceType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj SliceType) (errs field.ErrorList) { // type SliceType + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type SliceType")...) return errs @@ -77,6 +90,9 @@ func Validate_SliceType(ctx context.Context, op operation.Operation, fldPath *fi func Validate_StringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { // type StringType + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type StringType")...) return errs @@ -88,6 +104,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -98,6 +117,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -108,6 +130,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -119,6 +144,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringTypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *StringType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -130,6 +158,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -140,6 +171,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -150,6 +184,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -161,6 +198,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IntTypedefPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *IntType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -172,6 +212,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.OtherStructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -183,6 +226,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceField errs = append(errs, func(fldPath *field.Path, obj, oldObj []string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -193,6 +239,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SliceTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj SliceType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -204,6 +253,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapField errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]string) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } @@ -214,6 +266,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.MapTypedefField errs = append(errs, func(fldPath *field.Path, obj, oldObj MapType) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj); len(e) != 0 { return // do not proceed } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go index b1df9d4d759..f5d5d438bc8 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/deep/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "structField", func(o *OtherStruct) *SmallStruct { return &o.StructField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SmallStruct) field.ErrorList { return validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *SmallStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "StructField.StructField") @@ -78,6 +82,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "structField", func(o *OtherStruct) *SmallStruct { return &o.StructField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *SmallStruct) field.ErrorList { return validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *SmallStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "StructPtrField.StructField") diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go index 07b08f3fcd8..56834253659 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/nonincluded/zz_generated.validations.go @@ -52,6 +52,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructType errs = append(errs, func(fldPath *field.Path, obj, oldObj *other.StructType) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *other.StructType) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.(other.StructType).StringField") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go index 13e6aa3f44d..00bc98497f3 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shallow/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -53,6 +54,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *OtherStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructField.StringField") })...) @@ -74,6 +78,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StructPtrField errs = append(errs, func(fldPath *field.Path, obj, oldObj *OtherStruct) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.Subfield(ctx, op, fldPath, obj, oldObj, "stringField", func(o *OtherStruct) *string { return &o.StringField }, func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructPtrField.StringField") })...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go index e09a2600158..48821544698 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/issubresource/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go index 7874ba13ecb..20f7445020c 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/root/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go index c800d54b283..3ca113b5f1a 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/supported_resources/subresource/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field T1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T1) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go index 564382d84c2..3bbc4edecca 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/doc_test.go @@ -28,10 +28,18 @@ func Test(t *testing.T) { }).ExpectValidateFalseByPath(map[string][]string{ "stringField": {"field Struct.StringField"}, }) + // Test validation ratcheting + st.Value(&Struct{}).OldValue(&Struct{}).ExpectValid() st.Value(&Struct{ StringField: "abc", }).ExpectValidateFalseByPath(map[string][]string{ "stringField": {"field Struct.StringField"}, }) + // Test validation ratcheting + st.Value(&Struct{ + StringField: "abc", + }).OldValue(&Struct{ + StringField: "abc", + }).ExpectValid() } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go index 5183ed29095..81e19cd3c08 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_false/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go index 6e4b4712470..5a425c89556 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/validate_true/zz_generated.validations.go @@ -53,6 +53,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.StringField errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, true, "field Struct.StringField")...) return }(fldPath.Child("stringField"), &obj.StringField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.StringField }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go index 76b94a9e383..7707e9a9005 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/type_args/zz_generated.validations.go @@ -50,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E1")...) return errs @@ -61,6 +64,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.S1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*primitives.T1](ctx, op, fldPath, obj, oldObj, false, "T1.S1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -69,6 +75,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PS1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *primitives.T1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*primitives.T1](ctx, op, fldPath, obj, oldObj, false, "PT1.PS1")...) errs = append(errs, primitives.Validate_T1(ctx, op, fldPath, obj, oldObj)...) return @@ -77,6 +86,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*E1](ctx, op, fldPath, obj, oldObj, false, "T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -85,6 +97,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*E1](ctx, op, fldPath, obj, oldObj, true, "T1.PE1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -93,6 +108,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.I1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*int](ctx, op, fldPath, obj, oldObj, false, "T1.I1")...) return }(fldPath.Child("i1"), &obj.I1, safe.Field(oldObj, func(oldObj *T1) *int { return &oldObj.I1 }))...) @@ -100,6 +118,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PI1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult[*int](ctx, op, fldPath, obj, oldObj, true, "T1.PI1")...) return }(fldPath.Child("pi1"), obj.PI1, safe.Field(oldObj, func(oldObj *T1) *int { return oldObj.PI1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go index 5b7fc13c04d..90ace869022 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/typedefs/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -49,6 +50,9 @@ func RegisterValidations(scheme *testscheme.Scheme) error { func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { // type E1 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E1")...) return errs @@ -56,6 +60,9 @@ func Validate_E1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E2) (errs field.ErrorList) { // type E2 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E2")...) return errs @@ -63,6 +70,9 @@ func Validate_E2(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E3(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E3) (errs field.ErrorList) { // type E3 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E3")...) return errs @@ -70,11 +80,17 @@ func Validate_E3(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_E4(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *E4) (errs field.ErrorList) { // type E4 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type E4")...) // field E4.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *E4) *string { return &oldObj.S }))...) @@ -84,6 +100,9 @@ func Validate_E4(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T1) (errs field.ErrorList) { // type T1 + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T1")...) // field T1.TypeMeta has no validation @@ -91,6 +110,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -99,6 +121,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE1 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E1) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PE1")...) errs = append(errs, Validate_E1(ctx, op, fldPath, obj, oldObj)...) return @@ -107,6 +132,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E2")...) errs = append(errs, Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -115,6 +143,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PE2")...) errs = append(errs, Validate_E2(ctx, op, fldPath, obj, oldObj)...) return @@ -123,6 +154,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E3")...) errs = append(errs, Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -131,6 +165,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE3 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E3) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PE3")...) errs = append(errs, Validate_E3(ctx, op, fldPath, obj, oldObj)...) return @@ -139,6 +176,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.E4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E4) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.E4")...) errs = append(errs, Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -147,6 +187,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PE4 errs = append(errs, func(fldPath *field.Path, obj, oldObj *E4) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PE4")...) errs = append(errs, Validate_E4(ctx, op, fldPath, obj, oldObj)...) return @@ -155,6 +198,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.T2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.T2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -163,6 +209,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.PT2 errs = append(errs, func(fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T1.PT2")...) errs = append(errs, Validate_T2(ctx, op, fldPath, obj, oldObj)...) return @@ -173,11 +222,17 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat func Validate_T2(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *T2) (errs field.ErrorList) { // type T2 + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type T2")...) // field T2.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field T2.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *T2) *string { return &oldObj.S }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go index 0fba0ff9c40..f586748d8f5 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/lists/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -58,6 +59,9 @@ func Validate_M1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field M1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "M1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *M1) *string { return &oldObj.S }))...) @@ -69,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.LM1 errs = append(errs, func(fldPath *field.Path, obj, oldObj []M1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, Validate_M1)...) return }(fldPath.Child("lm1"), obj.LM1, safe.Field(oldObj, func(oldObj *T1) []M1 { return oldObj.LM1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go index 8038e0df057..7b8e86a35a4 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/maps/zz_generated.validations.go @@ -25,6 +25,7 @@ import ( context "context" fmt "fmt" + equality "k8s.io/apimachinery/pkg/api/equality" operation "k8s.io/apimachinery/pkg/api/operation" safe "k8s.io/apimachinery/pkg/api/safe" validate "k8s.io/apimachinery/pkg/api/validate" @@ -58,6 +59,9 @@ func Validate_M1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field M1.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "M1.S")...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *M1) *string { return &oldObj.S }))...) @@ -69,6 +73,9 @@ func Validate_T1(ctx context.Context, op operation.Operation, fldPath *field.Pat // field T1.MSM1 errs = append(errs, func(fldPath *field.Path, obj, oldObj map[string]M1) (errs field.ErrorList) { + if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) { + return nil // no changes + } errs = append(errs, validate.EachMapVal(ctx, op, fldPath, obj, oldObj, Validate_M1)...) return }(fldPath.Child("msm1"), obj.MSM1, safe.Field(oldObj, func(oldObj *T1) map[string]M1 { return oldObj.MSM1 }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go index 5541b49597e..2b92cfb9ace 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitive_pointers/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.SP errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("sp"), obj.SP, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.SP }))...) @@ -58,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.IP errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("ip"), obj.IP, safe.Field(oldObj, func(oldObj *Struct) *int { return oldObj.IP }))...) @@ -65,6 +71,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.BP errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("bp"), obj.BP, safe.Field(oldObj, func(oldObj *Struct) *bool { return oldObj.BP }))...) @@ -72,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.FP errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("fp"), obj.FP, safe.Field(oldObj, func(oldObj *Struct) *float64 { return oldObj.FP }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go index d3a973b1e1d..3bad101b188 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/update_validations/primitives/zz_generated.validations.go @@ -51,6 +51,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.S errs = append(errs, func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("s"), &obj.S, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.S }))...) @@ -58,6 +61,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.I errs = append(errs, func(fldPath *field.Path, obj, oldObj *int) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("i"), &obj.I, safe.Field(oldObj, func(oldObj *Struct) *int { return &oldObj.I }))...) @@ -65,6 +71,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.B errs = append(errs, func(fldPath *field.Path, obj, oldObj *bool) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("b"), &obj.B, safe.Field(oldObj, func(oldObj *Struct) *bool { return &oldObj.B }))...) @@ -72,6 +81,9 @@ func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field // field Struct.F errs = append(errs, func(fldPath *field.Path, obj, oldObj *float64) (errs field.ErrorList) { + if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) { + return nil // no changes + } errs = append(errs, validate.ImmutableByCompare(ctx, op, fldPath, obj, oldObj)...) return }(fldPath.Child("f"), &obj.F, safe.Field(oldObj, func(oldObj *Struct) *float64 { return &oldObj.F }))...) diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go new file mode 100644 index 00000000000..25165586610 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util.go @@ -0,0 +1,116 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "k8s.io/gengo/v2/parser/tags" + "k8s.io/gengo/v2/types" +) + +// GetMemberByJSON returns the child member of the type that has the given JSON +// name. It returns nil if no such member exists. +func GetMemberByJSON(t *types.Type, jsonName string) *types.Member { + for i := range t.Members { + if jsonTag, ok := tags.LookupJSON(t.Members[i]); ok { + if jsonTag.Name == jsonName { + return &t.Members[i] + } + } + } + return nil +} + +// IsNilableType returns true if the argument type can be compared to nil. +func IsNilableType(t *types.Type) bool { + t = NativeType(t) + + switch t.Kind { + case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable + return true + } + return false +} + +// NativeType returns the Go native type of the argument type, with any +// intermediate typedefs removed. Go itself already flattens typedefs, but this +// handles it in the unlikely event that we ever fix that. +// +// Examples: +// * Trivial: +// - given `int`, returns `int` +// - given `*int`, returns `*int` +// - given `[]int`, returns `[]int` +// +// * Typedefs +// - given `type X int; X`, returns `int` +// - given `type X int; []X`, returns `[]X` +// +// * Typedefs and pointers: +// - given `type X int; *X`, returns `*int` +// - given `type X *int; *X`, returns `**int` +// - given `type X []int; X`, returns `[]int` +// - given `type X []int; *X`, returns `*[]int` +func NativeType(t *types.Type) *types.Type { + ptrs := 0 + for { + switch t.Kind { + case types.Alias: + t = t.Underlying + case types.Pointer: + ptrs++ + t = t.Elem + default: + goto done + } + } +done: + for range ptrs { + t = types.PointerTo(t) + } + return t +} + +// NonPointer returns the value-type of a possibly pointer type. If type is not +// a pointer, it returns the input type. +func NonPointer(t *types.Type) *types.Type { + for t.Kind == types.Pointer { + t = t.Elem + } + return t +} + +// IsDirectComparable returns true if the type is safe to compare using "==". +// It is similar to gengo.IsComparable, but it doesn't consider Pointers to be +// comparable (we don't want shallow compare). +func IsDirectComparable(t *types.Type) bool { + switch t.Kind { + case types.Builtin: + return true + case types.Struct: + for _, f := range t.Members { + if !IsDirectComparable(f.Type) { + return false + } + } + return true + case types.Array: + return IsDirectComparable(t.Elem) + case types.Alias: + return IsDirectComparable(t.Underlying) + } + return false +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go new file mode 100644 index 00000000000..5ff3ae6cb8c --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/util/util_test.go @@ -0,0 +1,454 @@ +/* +Copyright 2025 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "reflect" + "testing" + + "k8s.io/gengo/v2/types" +) + +func TestGetMemberByJSON(t *testing.T) { + tests := []struct { + name string + t *types.Type + jsonTag string + want *types.Member + wantBool bool + }{{ + name: "exact match", + t: &types.Type{ + Members: []types.Member{ + {Name: "Field0", Tags: `json:"field0"`}, + {Name: "Field1", Tags: `json:"field1"`}, + {Name: "Field2", Tags: `json:"field2"`}, + }, + }, + jsonTag: "field1", + want: &types.Member{Name: "Field1", Tags: `json:"field1"`}, + wantBool: true, + }, { + name: "no match", + t: &types.Type{ + Members: []types.Member{ + {Name: "Field0", Tags: `json:"field0"`}, + {Name: "Field1", Tags: `json:"field1"`}, + }, + }, + jsonTag: "field2", + want: nil, + wantBool: false, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := GetMemberByJSON(tt.t, tt.jsonTag) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("GetMemberByJSON() got %v, want %v", got, tt.want) + } + }) + } +} + +func TestIsNilableType(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + + tests := []struct { + name string + t *types.Type + want bool + }{{ + name: "pointer", + t: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + want: true, + }, { + name: "alias to pointer", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + }, + want: true, + }, { + name: "map", + t: &types.Type{ + Kind: types.Map, + }, + want: true, + }, { + name: "alias to map", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Map, + }, + }, + want: true, + }, { + name: "slice", + t: &types.Type{ + Kind: types.Slice, + }, + want: true, + }, { + name: "alias to slice", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Slice, + }, + }, + want: true, + }, { + name: "interface", + t: &types.Type{ + Kind: types.Interface, + }, + want: true, + }, { + name: "alias to interface", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Interface, + }, + }, + want: true, + }, { + name: "struct", + t: &types.Type{ + Kind: types.Struct, + }, + want: false, + }, { + name: "alias to struct", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Struct, + }, + }, + want: false, + }, { + name: "builtin", + t: &types.Type{ + Kind: types.Builtin, + }, + want: false, + }, { + name: "alias to builtin", + t: &types.Type{ + Kind: types.Alias, + Underlying: &types.Type{ + Kind: types.Builtin, + }, + }, + want: false, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsNilableType(tt.t); got != tt.want { + t.Errorf("IsNilableType() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestNativeType(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + pStruct := &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + } + + tests := []struct { + name string + t *types.Type + want *types.Type + }{{ + name: "struct", + t: tStruct, + want: tStruct, + }, { + name: "pointer to struct", + t: pStruct, + want: pStruct, + }, { + name: "alias to struct", + t: &types.Type{ + Name: types.Name{Name: "Alias"}, + Kind: types.Alias, + Underlying: tStruct, + }, + want: tStruct, + }, { + name: "pointer to alias to struct", + t: &types.Type{ + Name: types.Name{Name: "*Alias"}, + Kind: types.Pointer, + Elem: &types.Type{ + Kind: types.Alias, + Underlying: tStruct, + }, + }, + want: pStruct, + }, { + name: "alias of pointer to struct", + t: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: pStruct, + }, + want: pStruct, + }, { + name: "pointer to alias of pointer to struct", + t: &types.Type{ + Name: types.Name{Name: "*AliasP"}, + Kind: types.Pointer, + Elem: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: pStruct, + }, + }, + want: &types.Type{ + Name: types.Name{Name: "**MyStruct"}, + Kind: types.Pointer, + Elem: pStruct, + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if want, got := tt.want.String(), NativeType(tt.t).String(); want != got { + t.Errorf("NativeType() = %q, want %q", got, tt.want) + } + }) + } +} + +func TestNonPointer(t *testing.T) { + tStruct := &types.Type{ + Name: types.Name{Name: "MyStruct"}, + Kind: types.Struct, + } + + tests := []struct { + name string + t *types.Type + want *types.Type + }{{ + name: "value", + t: tStruct, + want: tStruct, + }, { + name: "pointer", + t: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + want: tStruct, + }, { + name: "pointer pointer", + t: &types.Type{ + Name: types.Name{Name: "**MyStruct"}, + Kind: types.Pointer, + Elem: &types.Type{ + Kind: types.Pointer, + Elem: tStruct, + }, + }, + want: tStruct, + }, { + name: "pointer alias pointer", + t: &types.Type{ + Name: types.Name{Name: "*AliasP"}, + Kind: types.Pointer, + Elem: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + }, + }, + want: &types.Type{ + Name: types.Name{Name: "AliasP"}, + Kind: types.Alias, + Underlying: &types.Type{ + Name: types.Name{Name: "*MyStruct"}, + Kind: types.Pointer, + Elem: tStruct, + }, + }, + }} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := NonPointer(tt.t); !reflect.DeepEqual(got, tt.want) { + t.Errorf("NonPointer() = %v, want %v", got, tt.want) + } + }) + } +} + +// gengo has `PointerTo()` but not the rest, so keep this here for consistency. +func ptrTo(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "*" + t.Name.String(), + }, + Kind: types.Pointer, + Elem: t, + } +} + +func sliceOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "[]" + t.Name.String(), + }, + Kind: types.Slice, + Elem: t, + } +} + +func mapOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "map[string]" + t.Name.String(), + }, + Kind: types.Map, + Key: types.String, + Elem: t, + } +} + +func arrayOf(t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "[2]" + t.Name.String(), + }, + Kind: types.Array, + Len: 2, + Elem: t, + } +} + +func aliasOf(name string, t *types.Type) *types.Type { + return &types.Type{ + Name: types.Name{ + Package: "", + Name: "Alias_" + name, + }, + Kind: types.Alias, + Underlying: t, + } +} + +func TestIsDirectComparable(t *testing.T) { + cases := []struct { + in *types.Type + expect bool + }{ + { + in: types.String, + expect: true, + }, { + in: ptrTo(types.String), + expect: false, + }, { + in: sliceOf(types.String), + expect: false, + }, { + in: mapOf(types.String), + expect: false, + }, { + in: aliasOf("s", types.String), + expect: true, + }, { + in: &types.Type{ + Name: types.Name{ + Package: "", + Name: "struct_comparable_member", + }, + Kind: types.Struct, + Members: []types.Member{ + { + Name: "s", + Type: types.String, + }, + }, + }, + expect: true, + }, { + in: &types.Type{ + Name: types.Name{ + Package: "", + Name: "struct_uncomparable_member", + }, + Kind: types.Struct, + Members: []types.Member{ + { + Name: "s", + Type: ptrTo(types.String), + }, + }, + }, + expect: false, + }, { + in: arrayOf(types.String), + expect: true, + }, { + in: arrayOf(aliasOf("s", types.String)), + expect: true, + }, { + in: arrayOf(ptrTo(types.String)), + expect: false, + }, { + in: arrayOf(mapOf(types.String)), + expect: false, + }, + } + + for _, tc := range cases { + if got, want := IsDirectComparable(tc.in), tc.expect; got != want { + t.Errorf("%q: expected %v, got %v", tc.in, want, got) + } + } +} diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go index dc8f966fd5a..38ef3b40323 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validation.go @@ -29,6 +29,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/code-generator/cmd/validation-gen/util" "k8s.io/code-generator/cmd/validation-gen/validators" "k8s.io/gengo/v2/generator" "k8s.io/gengo/v2/namer" @@ -52,9 +53,11 @@ var ( safePkg = "k8s.io/apimachinery/pkg/api/safe" safePkgSymbols = mkPkgNames(safePkg, "Field", "Cast", "Value") operationPkg = "k8s.io/apimachinery/pkg/api/operation" - operationPkgSymbols = mkPkgNames(operationPkg, "Operation", "MatchesSubresource") + operationPkgSymbols = mkPkgNames(operationPkg, "Operation", "MatchesSubresource", "Update") contextPkg = "context" contextPkgSymbols = mkPkgNames(contextPkg, "Context") + equalityPkg = "k8s.io/apimachinery/pkg/api/equality" + equalityPkgSymbols = mkPkgNames(equalityPkg, "Semantic") ) // genValidations produces a file with autogenerated validations. @@ -219,14 +222,6 @@ func (td *typeDiscoverer) DiscoverType(t *types.Type) error { return nil } -// unalias returns the unaliased type. -func unalias(t *types.Type) *types.Type { - for t.Kind == types.Alias { - t = t.Underlying - } - return t -} - // discoverType walks the given type recursively and returns a typeNode // representing it. This does not distinguish between discovering a type // definition and discovering a field of a struct. The first time it @@ -255,7 +250,7 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ return nil, fmt.Errorf("field %s (%s): typedefs to pointers are not supported", fldPath.String(), t) } case types.Pointer: - pointee := unalias(t.Elem) + pointee := util.NativeType(t.Elem) switch pointee.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): pointers to pointers are not supported", fldPath.String(), t) @@ -267,23 +262,23 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ case types.Array: return nil, fmt.Errorf("field %s (%s): fixed-size arrays are not supported", fldPath.String(), t) case types.Slice: - elem := unalias(t.Elem) + elem := util.NativeType(t.Elem) switch elem.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): lists of pointers are not supported", fldPath.String(), t) case types.Slice: - if unalias(elem.Elem) != types.Byte { + if util.NativeType(elem.Elem) != types.Byte { return nil, fmt.Errorf("field %s (%s): lists of lists are not supported", fldPath.String(), t) } case types.Map: return nil, fmt.Errorf("field %s (%s): lists of maps are not supported", fldPath.String(), t) } case types.Map: - key := unalias(t.Key) + key := util.NativeType(t.Key) if key != types.String { return nil, fmt.Errorf("field %s (%s): maps with non-string keys are not supported", fldPath.String(), t) } - elem := unalias(t.Elem) + elem := util.NativeType(t.Elem) switch elem.Kind { case types.Pointer: return nil, fmt.Errorf("field %s (%s): maps of pointers are not supported", fldPath.String(), t) @@ -416,7 +411,7 @@ func (td *typeDiscoverer) discoverType(t *types.Type, fldPath *field.Path) (*typ // system, while preventing validation on them since that functionality is not yet // implemented. Once validation support for map-of-slices is added, this check should // be removed. - if unalias(t).Kind == types.Map && unalias(t).Elem.Kind == types.Slice { + if util.NativeType(t).Kind == types.Map && util.NativeType(t).Elem.Kind == types.Slice { return nil, fmt.Errorf("field %s: validation for map of slices is not supported", fldPath) } klog.V(5).InfoS("found type-attached validations", "n", validations.Len(), "type", t) @@ -595,7 +590,7 @@ func (td *typeDiscoverer) discoverStruct(thisNode *typeNode, fldPath *field.Path // system, while preventing validation on them since that functionality is not yet // implemented. Once validation support for map-of-slices is added, this check should // be removed. - if unalias(childType).Kind == types.Map && unalias(childType).Elem.Kind == types.Slice { + if util.NativeType(childType).Kind == types.Map && util.NativeType(childType).Elem.Kind == types.Slice { return fmt.Errorf("field %s: validation for map of slices is not supported", childPath) } child.fieldValidations.Add(validations) @@ -838,7 +833,7 @@ func (g *genValidations) emitRegisterFunction(c *generator.Context, schemeRegist "safe": mkSymbolArgs(c, safePkgSymbols), "context": mkSymbolArgs(c, contextPkgSymbols), } - if !isNilableType(rootType) { + if !util.IsNilableType(rootType) { targs["typePfx"] = "*" } @@ -905,7 +900,7 @@ func (g *genValidations) emitValidationFunction(c *generator.Context, t *types.T "context": mkSymbolArgs(c, contextPkgSymbols), "objTypePfx": "*", } - if isNilableType(t) { + if util.IsNilableType(t) { targs["objTypePfx"] = "" } @@ -958,6 +953,7 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild } sw.Do("// type $.inType|raw$\n", targs) emitComments(validations.Comments, sw) + emitRatchetingCheck(c, thisNode.valueType, sw) emitCallsToValidators(c, validations.Functions, sw) if thisNode.valueType.Kind == types.Alias { underlyingNode := thisNode.underlying.node @@ -1010,9 +1006,14 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild bufsw := sw.Dup(buf) validations := fld.fieldValidations + + // fldRatchetingChecked is used to avoid emitting the ratcheting check multiple times. + fldRatchetingChecked := false if !validations.Empty() { emitComments(validations.Comments, bufsw) + emitRatchetingCheck(c, fld.childType, bufsw) emitCallsToValidators(c, validations.Functions, bufsw) + fldRatchetingChecked = true } // If the node is nil, this must be a type in a package we are not @@ -1028,6 +1029,10 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild // validations, call its validation function. if validations := fld.fieldValIterations; g.hasValidations(fld.node.elem.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // Descend into this field. @@ -1037,12 +1042,20 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild // validations, call its validation function. if validations := fld.fieldKeyIterations; g.hasValidations(fld.node.key.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // If this field is a map and the value-type has // validations, call its validation function. if validations := fld.fieldValIterations; g.hasValidations(fld.node.elem.node) && !validations.Empty() { emitComments(validations.Comments, bufsw) + if !fldRatchetingChecked { + emitRatchetingCheck(c, fld.childType, bufsw) + fldRatchetingChecked = true + } emitCallsToValidators(c, validations.Functions, bufsw) } // Descend into this field. @@ -1120,6 +1133,27 @@ func (g *genValidations) emitCallToOtherTypeFunc(c *generator.Context, node *typ sw.Do("errs = append(errs, $.funcName|raw$(ctx, op, fldPath, obj, oldObj)...)\n", targs) } +// emitRachetingCheck emits a equivalence check for default ratcheting. +func emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.SnippetWriter) { + // Emit equivalence check for default ratcheting. + // TODO: Need to have a follow-up PR to handle the default behavior + // for slices and maps. + targs := generator.Args{ + "operation": mkSymbolArgs(c, operationPkgSymbols), + } + // If the type is a builtin, we can use a simpler equality check when they are not nil. + if util.IsDirectComparable(util.NonPointer(util.NativeType(t))) { + _, exprPfx, _ := getLeafTypeAndPrefixes(t) + targs["exprPfx"] = exprPfx + sw.Do("if op.Type == $.operation.Update|raw$ && (obj == oldObj || (obj != nil && oldObj != nil && $.exprPfx$obj == $.exprPfx$oldObj)) {\n", targs) + } else { + targs["equality"] = mkSymbolArgs(c, equalityPkgSymbols) + sw.Do("if op.Type == $.operation.Update|raw$ && $.equality.Semantic|raw$.DeepEqual(obj, oldObj) {\n", targs) + } + sw.Do(" return nil // no changes\n", nil) + sw.Do("}\n", nil) +} + // emitCallsToValidators emits calls to a list of validation functions for // a single field or type. validations is a list of functions to call, with // arguments. @@ -1334,7 +1368,7 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context "objType": v.ObjType, "objTypePfx": "*", } - if isNilableType(v.ObjType) { + if util.IsNilableType(v.ObjType) { targs["objTypePfx"] = "" } @@ -1431,16 +1465,6 @@ func toGolangSourceDataLiteral(sw *generator.SnippetWriter, c *generator.Context } } -// isNilableType returns true if the argument type can be compared to nil. -func isNilableType(t *types.Type) bool { - t = unalias(t) - switch t.Kind { - case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable - return true - } - return false -} - // getLeafTypeAndPrefixes returns the "leaf value type" for a given type, as // well as type and expression prefix strings for the input type. The type // prefix can be prepended to the given type's name to produce the nilable form @@ -1466,7 +1490,7 @@ func getLeafTypeAndPrefixes(inType *types.Type) (*types.Type, string, string) { nPtrs++ leafType = leafType.Elem } - if !isNilableType(leafType) { + if !util.IsNilableType(leafType) { typePfx = "*" if nPtrs == 0 { exprPfx = "&" diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go index 145262edc61..93cea458e84 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/common.go @@ -17,7 +17,6 @@ limitations under the License. package validators import ( - "k8s.io/gengo/v2/parser/tags" "k8s.io/gengo/v2/types" ) @@ -27,75 +26,6 @@ const ( libValidationPkg = "k8s.io/apimachinery/pkg/api/validate" ) -func getMemberByJSON(t *types.Type, jsonName string) *types.Member { - for i := range t.Members { - if jsonTag, ok := tags.LookupJSON(t.Members[i]); ok { - if jsonTag.Name == jsonName { - return &t.Members[i] - } - } - } - return nil -} - -// isNilableType returns true if the argument type can be compared to nil. -func isNilableType(t *types.Type) bool { - for t.Kind == types.Alias { - t = t.Underlying - } - switch t.Kind { - case types.Pointer, types.Map, types.Slice, types.Interface: // Note: Arrays are not nilable - return true - } - return false -} - -// nativeType returns the Go native type of the argument type, with any -// intermediate typedefs removed. Go itself already flattens typedefs, but this -// handles it in the unlikely event that we ever fix that. -// -// Examples: -// * Trivial: -// - given `int`, returns `int` -// - given `*int`, returns `*int` -// - given `[]int`, returns `[]int` -// -// * Typedefs -// - given `type X int; X`, returns `int` -// - given `type X int; []X`, returns `[]X` -// -// * Typedefs and pointers: -// - given `type X int; *X`, returns `*int` -// - given `type X *int; *X`, returns `**int` -// - given `type X []int; X`, returns `[]int` -// - given `type X []int; *X`, returns `*[]int` -func nativeType(t *types.Type) *types.Type { - ptrs := 0 - for { - if t.Kind == types.Alias { - t = t.Underlying - } else if t.Kind == types.Pointer { - ptrs++ - t = t.Elem - } else { - break - } - } - for range ptrs { - t = types.PointerTo(t) - } - return t -} - -// nonPointer returns the value-type of a possibly pointer type. If type is not -// a pointer, it returns the input type. -func nonPointer(t *types.Type) *types.Type { - for t.Kind == types.Pointer { - t = t.Elem - } - return t -} - // rootTypeString returns a string representation of the relationship between // src and dst types, for use in error messages. func rootTypeString(src, dst *types.Type) string { diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go index c3e20de6fbb..d17e574ddc0 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/each.go @@ -24,6 +24,8 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -78,7 +80,7 @@ func (listTypeTagValidator) ValidScopes() sets.Set[Scope] { func (lttv listTypeTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Slice && t.Kind != types.Array { return Validations{}, fmt.Errorf("can only be used on list types") } @@ -88,7 +90,7 @@ func (lttv listTypeTagValidator) GetValidations(context Context, tag codetags.Ta // Allowed but no special handling. case "map": // NOTE: maps of pointers are not supported, so we should never see a pointer here. - if nativeType(t.Elem).Kind != types.Struct { + if util.NativeType(t.Elem).Kind != types.Struct { return Validations{}, fmt.Errorf("only lists of structs can be list-maps") } @@ -138,19 +140,19 @@ func (listMapKeyTagValidator) ValidScopes() sets.Set[Scope] { func (lmktv listMapKeyTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Slice && t.Kind != types.Array { return Validations{}, fmt.Errorf("can only be used on list types") } // NOTE: lists of pointers are not supported, so we should never see a pointer here. - if nativeType(t.Elem).Kind != types.Struct { + if util.NativeType(t.Elem).Kind != types.Struct { return Validations{}, fmt.Errorf("only lists of structs can be list-maps") } var fieldName string - if memb := getMemberByJSON(nativeType(t.Elem), tag.Value); memb == nil { + if memb := util.GetMemberByJSON(util.NativeType(t.Elem), tag.Value); memb == nil { return Validations{}, fmt.Errorf("no field for JSON name %q", tag.Value) - } else if k := nativeType(memb.Type).Kind; k != types.Builtin { + } else if k := util.NativeType(memb.Type).Kind; k != types.Builtin { return Validations{}, fmt.Errorf("only primitive types can be list-map keys (%s)", k) } else { fieldName = memb.Name @@ -210,7 +212,7 @@ var ( func (evtv eachValTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists and maps are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) switch t.Kind { case types.Slice, types.Array, types.Map: default: @@ -350,7 +352,7 @@ var ( func (ektv eachKeyTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) { // NOTE: pointers to lists are not supported, so we should never see a pointer here. - t := nativeType(context.Type) + t := util.NativeType(context.Type) if t.Kind != types.Map { return Validations{}, fmt.Errorf("can only be used on map types") } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go index 519484e67e2..12be03c7924 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/immutable.go @@ -20,6 +20,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -52,7 +54,7 @@ var ( func (immutableTagValidator) GetValidations(context Context, _ codetags.Tag) (Validations, error) { var result Validations - if nonPointer(nativeType(context.Type)).Kind == types.Builtin { + if util.NonPointer(util.NativeType(context.Type)).Kind == types.Builtin { // This is a minor optimization to just compare primitive values when // possible. Slices and maps are not comparable, and structs might hold // pointer fields, which are directly comparable but not what we need. diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go index 47f52aa2473..57a79920070 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/limits.go @@ -23,6 +23,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -58,7 +60,7 @@ func (minimumTagValidator) GetValidations(context Context, tag codetags.Tag) (Va // This tag can apply to value and pointer fields, as well as typedefs // (which should never be pointers). We need to check the concrete type. - if t := nonPointer(nativeType(context.Type)); !types.IsInteger(t) { + if t := util.NonPointer(util.NativeType(context.Type)); !types.IsInteger(t) { return result, fmt.Errorf("can only be used on integer types (%s)", rootTypeString(context.Type, t)) } diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go index 11e05eef047..b52acdd2e17 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/required.go @@ -25,6 +25,8 @@ import ( "k8s.io/gengo/v2" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -91,7 +93,7 @@ func (rtv requirednessTagValidator) doRequired(context Context) (Validations, er // originally defined as a value-type or a pointer-type in the API. This // one does. Since Go doesn't do partial specialization of templates, we // do manual dispatch here. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{Functions: []FunctionGen{Function(requiredTagName, ShortCircuit, requiredSliceValidator)}}, nil case types.Map: @@ -145,7 +147,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er if hasDefault, zeroDefault, err := rtv.hasZeroDefault(context); err != nil { return Validations{}, err } else if hasDefault { - if !isNilableType(context.Type) && zeroDefault { + if !util.IsNilableType(context.Type) && zeroDefault { return Validations{Comments: []string{"optional value-type fields with zero-value defaults are purely documentation"}}, nil } validations, err := rtv.doRequired(context) @@ -162,7 +164,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // originally defined as a value-type or a pointer-type in the API. This // one does. Since Go doesn't do partial specialization of templates, we // do manual dispatch here. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{Functions: []FunctionGen{Function(optionalTagName, ShortCircuit|NonError, optionalSliceValidator)}}, nil case types.Map: @@ -183,7 +185,7 @@ func (rtv requirednessTagValidator) doOptional(context Context) (Validations, er // hasZeroDefault returns whether the field has a default value and whether // that default value is the zero value for the field's type. func (rtv requirednessTagValidator) hasZeroDefault(context Context) (bool, bool, error) { - t := nonPointer(nativeType(context.Type)) + t := util.NonPointer(util.NativeType(context.Type)) // This validator only applies to fields, so Member must be valid. tagsByName, err := gengo.ExtractFunctionStyleCommentTags("+", []string{defaultTagName}, context.Member.CommentLines) if err != nil { @@ -262,7 +264,7 @@ func (requirednessTagValidator) doForbidden(context Context) (Validations, error // optional check and short-circuit (but without error). Why? For // example, this prevents any further validation from trying to run on a // nil pointer. - switch nativeType(context.Type).Kind { + switch util.NativeType(context.Type).Kind { case types.Slice: return Validations{ Functions: []FunctionGen{ diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go index 7643dd364d2..f9f154fde6b 100644 --- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go +++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/subfield.go @@ -22,6 +22,8 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/gengo/v2/codetags" "k8s.io/gengo/v2/types" + + "k8s.io/code-generator/cmd/validation-gen/util" ) const ( @@ -58,12 +60,12 @@ func (stv subfieldTagValidator) GetValidations(context Context, tag codetags.Tag args := tag.Args // This tag can apply to value and pointer fields, as well as typedefs // (which should never be pointers). We need to check the concrete type. - t := nonPointer(nativeType(context.Type)) + t := util.NonPointer(util.NativeType(context.Type)) if t.Kind != types.Struct { return Validations{}, fmt.Errorf("can only be used on struct types") } subname := args[0].Value - submemb := getMemberByJSON(t, subname) + submemb := util.GetMemberByJSON(t, subname) if submemb == nil { return Validations{}, fmt.Errorf("no field for json name %q", subname) } @@ -83,12 +85,12 @@ func (stv subfieldTagValidator) GetValidations(context Context, tag codetags.Tag for _, vfn := range validations.Functions { nilableStructType := context.Type - if !isNilableType(nilableStructType) { + if !util.IsNilableType(nilableStructType) { nilableStructType = types.PointerTo(nilableStructType) } nilableFieldType := submemb.Type fieldExprPrefix := "" - if !isNilableType(nilableFieldType) { + if !util.IsNilableType(nilableFieldType) { nilableFieldType = types.PointerTo(nilableFieldType) fieldExprPrefix = "&" }