mirror of
https://github.com/agent-substrate/substrate.git
synced 2026-11-12 10:35:06 +08:00
This mostly eliminates the need to hand-write validation code. This PR is a long series of commits which add DV for most of Actor and all of Atespace. Here is a map to the commits: * The first few take a dep on a new Kubernetes tag, import the code into third_party, and apply a single patch. Because we have different Go modules for tools, I had to do it twice. When that patch lands, we can revert these commits, but that won't be until the 1.38 cycle in a few months. * The next commits slowly add DV support, so a human can review each of them in a reasonable amount of time. The emphasis is on great test cases. * I made a bad choice early on as to where to generate the code into, so I moved it. Rebasing on that was exceedingly hard, so I left it as a move. * This required changing update/go-generate -> update/codegen -- we need to get the ordering of tools right, which `go generate` does not guarantee. * Then I added a "middle" layer called "ServiceImpl" between the RPC and storage layers. This allows things like workflow to call the same business logic as the RPC layer, including validation. Lots of test fixes. * Then I finished the Create() and Update() paths for Actor. Those represent the "right" (or closest to) way to implement resources, and tests for validation. I strongly encourage reviewers to read it commit-by-commit. Rebasing this is VERY tedious, so the sooner it lands or dies completely, the better. Then we can start converting the rest. `./hack/run-tool.sh validation-gen --docs` will produce some docs on the tool and the available tags. @laoj2 @juli4n @EItanya @HavenXia @lalitc375 @yongruilin @jpbetz FYI
4578 lines
212 KiB
Diff
4578 lines
212 KiB
Diff
diff --git a/pkg/apis/admissionregistration/v1/zz_generated.validations.go b/pkg/apis/admissionregistration/v1/zz_generated.validations.go
|
||
index c2562dc0936b0..b4b638ed6458a 100644
|
||
--- a/pkg/apis/admissionregistration/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/admissionregistration/v1/zz_generated.validations.go
|
||
@@ -453,7 +453,7 @@ func Validate_ValidatingAdmissionPolicyStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -501,3 +501,8 @@ func Validate_ValidatingWebhookConfiguration(
|
||
// field admissionregistrationv1.ValidatingWebhookConfiguration.Webhooks has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/admissionregistration/v1alpha1/zz_generated.validations.go b/pkg/apis/admissionregistration/v1alpha1/zz_generated.validations.go
|
||
index 93f66e798e3ab..a08534553e18a 100644
|
||
--- a/pkg/apis/admissionregistration/v1alpha1/zz_generated.validations.go
|
||
+++ b/pkg/apis/admissionregistration/v1alpha1/zz_generated.validations.go
|
||
@@ -389,7 +389,7 @@ func Validate_ValidatingAdmissionPolicyStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -403,3 +403,8 @@ func Validate_ValidatingAdmissionPolicyStatus(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/admissionregistration/v1beta1/zz_generated.validations.go b/pkg/apis/admissionregistration/v1beta1/zz_generated.validations.go
|
||
index 3c1b50fa66d94..6d6d394f124b4 100644
|
||
--- a/pkg/apis/admissionregistration/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/admissionregistration/v1beta1/zz_generated.validations.go
|
||
@@ -453,7 +453,7 @@ func Validate_ValidatingAdmissionPolicyStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -501,3 +501,8 @@ func Validate_ValidatingWebhookConfiguration(
|
||
// field admissionregistrationv1beta1.ValidatingWebhookConfiguration.Webhooks has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/batch/v1/zz_generated.validations.go b/pkg/apis/batch/v1/zz_generated.validations.go
|
||
index 0a830fdeb62b3..8f0b35bb016f5 100644
|
||
--- a/pkg/apis/batch/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/batch/v1/zz_generated.validations.go
|
||
@@ -403,7 +403,7 @@ func Validate_JobSchedulingConfiguration(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.WorkloadPodGroupResourceClaim, b *schedulingv1alpha3.WorkloadPodGroupResourceClaim) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, v1alpha3.Validate_WorkloadPodGroupResourceClaim); len(e) != 0 {
|
||
+ }, deepEqualImpl_, v1alpha3.Validate_WorkloadPodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -583,3 +583,8 @@ func Validate_JobTemplateSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/certificates/v1/zz_generated.validations.go b/pkg/apis/certificates/v1/zz_generated.validations.go
|
||
index 35af1f3579770..bb98173b9a2c2 100644
|
||
--- a/pkg/apis/certificates/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/certificates/v1/zz_generated.validations.go
|
||
@@ -392,7 +392,7 @@ func Validate_PodCertificateRequestStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -410,3 +410,8 @@ func Validate_PodCertificateRequestStatus(
|
||
// field certificatesv1.PodCertificateRequestStatus.NotAfter has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/certificates/v1beta1/zz_generated.validations.go b/pkg/apis/certificates/v1beta1/zz_generated.validations.go
|
||
index 9af5455cf75c3..d64104e9a2e97 100644
|
||
--- a/pkg/apis/certificates/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/certificates/v1beta1/zz_generated.validations.go
|
||
@@ -392,7 +392,7 @@ func Validate_PodCertificateRequestStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -410,3 +410,8 @@ func Validate_PodCertificateRequestStatus(
|
||
// field certificatesv1beta1.PodCertificateRequestStatus.NotAfter has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/core/v1/zz_generated.validations.go b/pkg/apis/core/v1/zz_generated.validations.go
|
||
index cd8ad94525e30..8e0d53348368e 100644
|
||
--- a/pkg/apis/core/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/core/v1/zz_generated.validations.go
|
||
@@ -855,7 +855,7 @@ func Validate_NodeAllocatableResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *corev1.NodeAllocatableMappedResources, b *corev1.NodeAllocatableMappedResources) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_NodeAllocatableMappedResources); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_NodeAllocatableMappedResources); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -897,7 +897,7 @@ func Validate_NodeAllocatableResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *corev1.NodeAllocatableOverheadResources, b *corev1.NodeAllocatableOverheadResources) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_NodeAllocatableOverheadResources); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_NodeAllocatableOverheadResources); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -985,7 +985,8 @@ func Validate_NodeSpec(
|
||
if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||
earlyReturn = true
|
||
}
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -1390,7 +1391,7 @@ func Validate_PodSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *corev1.EvictionResponder, b *corev1.EvictionResponder) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_EvictionResponder); len(e) != 0 {
|
||
+ func(a *corev1.EvictionResponder, b *corev1.EvictionResponder) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_EvictionResponder); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1462,7 +1463,7 @@ func Validate_PodStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *corev1.NodeAllocatableResourceClaimStatus, b *corev1.NodeAllocatableResourceClaimStatus) bool {
|
||
return a.ResourceClaimName == b.ResourceClaimName
|
||
- }, validate.SemanticDeepEqual, Validate_NodeAllocatableResourceClaimStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_NodeAllocatableResourceClaimStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1500,7 +1501,7 @@ func Validate_PodStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *corev1.PodVolumeHealth, b *corev1.PodVolumeHealth) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodVolumeHealth); len(e) != 0 {
|
||
+ func(a *corev1.PodVolumeHealth, b *corev1.PodVolumeHealth) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodVolumeHealth); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2250,3 +2251,8 @@ func Validate_VolumeHealthStatusType(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/flowcontrol/v1/zz_generated.validations.go b/pkg/apis/flowcontrol/v1/zz_generated.validations.go
|
||
index d8484235332a3..8b590e900f5e2 100644
|
||
--- a/pkg/apis/flowcontrol/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/flowcontrol/v1/zz_generated.validations.go
|
||
@@ -307,7 +307,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1.PriorityLevelConfigurationSpec) flowcontrolv1.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1.ExemptPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -338,7 +338,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1.PriorityLevelConfigurationSpec) flowcontrolv1.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1.LimitedPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -454,3 +454,8 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/flowcontrol/v1beta1/zz_generated.validations.go b/pkg/apis/flowcontrol/v1beta1/zz_generated.validations.go
|
||
index b5bf044c847af..1079f7f7b4ae7 100644
|
||
--- a/pkg/apis/flowcontrol/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/flowcontrol/v1beta1/zz_generated.validations.go
|
||
@@ -309,7 +309,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta1.PriorityLevelConfigurationSpec) flowcontrolv1beta1.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta1.ExemptPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -340,7 +340,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta1.PriorityLevelConfigurationSpec) flowcontrolv1beta1.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta1.LimitedPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -456,3 +456,8 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/flowcontrol/v1beta2/zz_generated.validations.go b/pkg/apis/flowcontrol/v1beta2/zz_generated.validations.go
|
||
index c598fd02c48cb..56b84e881aa46 100644
|
||
--- a/pkg/apis/flowcontrol/v1beta2/zz_generated.validations.go
|
||
+++ b/pkg/apis/flowcontrol/v1beta2/zz_generated.validations.go
|
||
@@ -309,7 +309,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta2.PriorityLevelConfigurationSpec) flowcontrolv1beta2.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta2.ExemptPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -340,7 +340,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta2.PriorityLevelConfigurationSpec) flowcontrolv1beta2.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta2.LimitedPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -456,3 +456,8 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/flowcontrol/v1beta3/zz_generated.validations.go b/pkg/apis/flowcontrol/v1beta3/zz_generated.validations.go
|
||
index b7dece4e10418..213f621973b55 100644
|
||
--- a/pkg/apis/flowcontrol/v1beta3/zz_generated.validations.go
|
||
+++ b/pkg/apis/flowcontrol/v1beta3/zz_generated.validations.go
|
||
@@ -309,7 +309,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta3.PriorityLevelConfigurationSpec) flowcontrolv1beta3.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta3.ExemptPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -340,7 +340,7 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
},
|
||
func(obj *flowcontrolv1beta3.PriorityLevelConfigurationSpec) flowcontrolv1beta3.PriorityLevelEnablement {
|
||
return obj.Type
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *flowcontrolv1beta3.LimitedPriorityLevelConfiguration) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenPointer(ctx, op, fldPath, obj, oldObj).MarkBeta()...)
|
||
@@ -456,3 +456,8 @@ func Validate_PriorityLevelConfigurationSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/lifecycle/v1alpha1/zz_generated.validations.go b/pkg/apis/lifecycle/v1alpha1/zz_generated.validations.go
|
||
index dbc18b7e8c986..2335b86d38d80 100644
|
||
--- a/pkg/apis/lifecycle/v1alpha1/zz_generated.validations.go
|
||
+++ b/pkg/apis/lifecycle/v1alpha1/zz_generated.validations.go
|
||
@@ -552,7 +552,7 @@ func Validate_EvictionRequestStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -732,7 +732,7 @@ func Validate_EvictionStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -858,7 +858,7 @@ func Validate_EvictionStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *lifecyclev1alpha1.TargetResponder, b *lifecyclev1alpha1.TargetResponder) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_TargetResponder); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_TargetResponder); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -904,7 +904,7 @@ func Validate_EvictionStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *lifecyclev1alpha1.ResponderStatus, b *lifecyclev1alpha1.ResponderStatus) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_ResponderStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_ResponderStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1374,3 +1374,8 @@ func Validate_TargetResponder(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/lifecycle/validation/validation.go b/pkg/apis/lifecycle/validation/validation.go
|
||
index bf3f2e29df13b..28bb04bfb9709 100644
|
||
--- a/pkg/apis/lifecycle/validation/validation.go
|
||
+++ b/pkg/apis/lifecycle/validation/validation.go
|
||
@@ -23,6 +23,7 @@ import (
|
||
"strings"
|
||
"time"
|
||
|
||
+ "k8s.io/apimachinery/pkg/api/equality"
|
||
"k8s.io/apimachinery/pkg/api/meta"
|
||
"k8s.io/apimachinery/pkg/api/operation"
|
||
"k8s.io/apimachinery/pkg/api/validate"
|
||
@@ -487,7 +488,7 @@ func ValidateEvictionStatusResponder(status, oldStatus *lifecycle.ResponderStatu
|
||
oldDefaultedStatus = *oldStatus // +k8s:verify-mutation:reason=clone
|
||
}
|
||
|
||
- if oldStatus != nil && !validate.SemanticDeepEqual(status, oldStatus) && opts.responderState != lifecycle.ResponderStateActive {
|
||
+ if oldStatus != nil && !equality.Semantic.DeepEqual(status, oldStatus) && opts.responderState != lifecycle.ResponderStateActive {
|
||
// immutable; changes to the ResponderStatus are only allowed by the active responder or during initialization
|
||
return append(allErrs, field.Invalid(fldPath, status, validation.FieldImmutableErrorMsg).WithOrigin("immutable"))
|
||
}
|
||
diff --git a/pkg/apis/lifecycle/validation/validation_test.go b/pkg/apis/lifecycle/validation/validation_test.go
|
||
index 0c92b20367be6..0f962d44a5a54 100644
|
||
--- a/pkg/apis/lifecycle/validation/validation_test.go
|
||
+++ b/pkg/apis/lifecycle/validation/validation_test.go
|
||
@@ -23,7 +23,7 @@ import (
|
||
"time"
|
||
|
||
"github.com/google/go-cmp/cmp"
|
||
- "k8s.io/apimachinery/pkg/api/validate"
|
||
+ "k8s.io/apimachinery/pkg/api/equality"
|
||
"k8s.io/apimachinery/pkg/api/validation"
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||
@@ -143,7 +143,7 @@ func TestValidateEvictionRequestStatusUpdate(t *testing.T) {
|
||
}
|
||
for name, tc := range successCases {
|
||
t.Run(name, func(t *testing.T) {
|
||
- if validate.SemanticDeepEqual(tc.oldInput, tc.input) {
|
||
+ if equality.Semantic.DeepEqual(tc.oldInput, tc.input) {
|
||
t.Errorf("Expected oldInput and input to differ")
|
||
}
|
||
oldEvictionRequest := mkValidEvictionRequest()
|
||
@@ -812,7 +812,7 @@ func TestValidateEvictionStatusUpdate(t *testing.T) {
|
||
}
|
||
for name, tc := range successCases {
|
||
t.Run(name, func(t *testing.T) {
|
||
- if !tc.explicitEmptyUpdate && validate.SemanticDeepEqual(tc.oldInput, tc.input) {
|
||
+ if !tc.explicitEmptyUpdate && equality.Semantic.DeepEqual(tc.oldInput, tc.input) {
|
||
t.Errorf("Expected oldInput and input to differ")
|
||
}
|
||
// Ensure that the validation doesn't depend on the order of list elements.
|
||
diff --git a/pkg/apis/policy/v1/zz_generated.validations.go b/pkg/apis/policy/v1/zz_generated.validations.go
|
||
index f153234e54e76..df2d2e74fe77f 100644
|
||
--- a/pkg/apis/policy/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/policy/v1/zz_generated.validations.go
|
||
@@ -155,7 +155,7 @@ func Validate_PodDisruptionBudgetStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -169,3 +169,8 @@ func Validate_PodDisruptionBudgetStatus(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/policy/v1beta1/zz_generated.validations.go b/pkg/apis/policy/v1beta1/zz_generated.validations.go
|
||
index def9d2dd2d8a1..369d266ce91c1 100644
|
||
--- a/pkg/apis/policy/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/policy/v1beta1/zz_generated.validations.go
|
||
@@ -155,7 +155,7 @@ func Validate_PodDisruptionBudgetStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -169,3 +169,8 @@ func Validate_PodDisruptionBudgetStatus(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go
|
||
index 5353b062bb69e..716aa0f343c96 100644
|
||
--- a/pkg/apis/resource/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/resource/v1/zz_generated.validations.go
|
||
@@ -190,7 +190,7 @@ func Validate_AllocatedDeviceStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -386,7 +386,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_DeviceAttribute); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -434,7 +434,7 @@ func Validate_Device(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1.DeviceCounterConsumption, b *resourcev1.DeviceCounterConsumption) bool {
|
||
return a.CounterSet == b.CounterSet
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -570,7 +570,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1108,7 +1108,7 @@ func Validate_DeviceClaim(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1.DeviceRequest, b *resourcev1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest); len(e) != 0 {
|
||
+ func(a *resourcev1.DeviceRequest, b *resourcev1.DeviceRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1867,7 +1867,7 @@ func Validate_DeviceRequest(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1.DeviceSubRequest, b *resourcev1.DeviceSubRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceSubRequest); len(e) != 0 {
|
||
+ func(a *resourcev1.DeviceSubRequest, b *resourcev1.DeviceSubRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceSubRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2506,7 +2506,7 @@ func Validate_DeviceTaintRuleStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3440,7 +3440,7 @@ func Validate_ResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1.AllocatedDeviceStatus, b *resourcev1.AllocatedDeviceStatus) bool {
|
||
return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID))
|
||
- }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3674,7 +3674,7 @@ func Validate_ResourceSliceSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1.CounterSet, b *resourcev1.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet); len(e) != 0 {
|
||
+ func(a *resourcev1.CounterSet, b *resourcev1.CounterSet) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CounterSet); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3777,3 +3777,8 @@ func Validate_SkipNodeOperation(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/resource/v1alpha3/zz_generated.validations.go b/pkg/apis/resource/v1alpha3/zz_generated.validations.go
|
||
index 26768c1f0834f..ec13b717c6ee4 100644
|
||
--- a/pkg/apis/resource/v1alpha3/zz_generated.validations.go
|
||
+++ b/pkg/apis/resource/v1alpha3/zz_generated.validations.go
|
||
@@ -277,7 +277,7 @@ func Validate_DeviceTaintRuleStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -776,7 +776,7 @@ func Validate_PoolStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1alpha3.PartitionTypeStatus, b *resourcev1alpha3.PartitionTypeStatus) bool {
|
||
return a.Attribute == b.Attribute && a.Type == b.Type
|
||
- }, validate.SemanticDeepEqual, Validate_PartitionTypeStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PartitionTypeStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1165,7 +1165,7 @@ func Validate_ResourcePoolStatusRequestStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1413,3 +1413,8 @@ func Validate_ShareableSummaryStatus(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go
|
||
index 62ab9a8bfc35c..c78f404aeddd8 100644
|
||
--- a/pkg/apis/resource/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go
|
||
@@ -175,7 +175,7 @@ func Validate_AllocatedDeviceStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -296,7 +296,7 @@ func Validate_BasicDevice(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_DeviceAttribute); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -344,7 +344,7 @@ func Validate_BasicDevice(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta1.DeviceCounterConsumption, b *resourcev1beta1.DeviceCounterConsumption) bool {
|
||
return a.CounterSet == b.CounterSet
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -480,7 +480,7 @@ func Validate_BasicDevice(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1132,7 +1132,7 @@ func Validate_DeviceClaim(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1beta1.DeviceRequest, b *resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest); len(e) != 0 {
|
||
+ func(a *resourcev1beta1.DeviceRequest, b *resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1931,7 +1931,7 @@ func Validate_DeviceRequest(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta1.DeviceSubRequest, b *resourcev1beta1.DeviceSubRequest) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceSubRequest); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceSubRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3271,7 +3271,7 @@ func Validate_ResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta1.AllocatedDeviceStatus, b *resourcev1beta1.AllocatedDeviceStatus) bool {
|
||
return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID))
|
||
- }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3505,7 +3505,7 @@ func Validate_ResourceSliceSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1beta1.CounterSet, b *resourcev1beta1.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet); len(e) != 0 {
|
||
+ func(a *resourcev1beta1.CounterSet, b *resourcev1beta1.CounterSet) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CounterSet); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3608,3 +3608,8 @@ func Validate_SkipNodeOperation(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go
|
||
index 5858ddd09b4f4..c4a76b3e8f07f 100644
|
||
--- a/pkg/apis/resource/v1beta2/zz_generated.validations.go
|
||
+++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go
|
||
@@ -190,7 +190,7 @@ func Validate_AllocatedDeviceStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -386,7 +386,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_DeviceAttribute); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -434,7 +434,7 @@ func Validate_Device(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta2.DeviceCounterConsumption, b *resourcev1beta2.DeviceCounterConsumption) bool {
|
||
return a.CounterSet == b.CounterSet
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -570,7 +570,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1108,7 +1108,7 @@ func Validate_DeviceClaim(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1beta2.DeviceRequest, b *resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest); len(e) != 0 {
|
||
+ func(a *resourcev1beta2.DeviceRequest, b *resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1871,7 +1871,7 @@ func Validate_DeviceRequest(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta2.DeviceSubRequest, b *resourcev1beta2.DeviceSubRequest) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceSubRequest); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceSubRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2510,7 +2510,7 @@ func Validate_DeviceTaintRuleStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3444,7 +3444,7 @@ func Validate_ResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *resourcev1beta2.AllocatedDeviceStatus, b *resourcev1beta2.AllocatedDeviceStatus) bool {
|
||
return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID))
|
||
- }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3678,7 +3678,7 @@ func Validate_ResourceSliceSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *resourcev1beta2.CounterSet, b *resourcev1beta2.CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet); len(e) != 0 {
|
||
+ func(a *resourcev1beta2.CounterSet, b *resourcev1beta2.CounterSet) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CounterSet); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3781,3 +3781,8 @@ func Validate_SkipNodeOperation(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/scheduling/v1alpha3/zz_generated.validations.go b/pkg/apis/scheduling/v1alpha3/zz_generated.validations.go
|
||
index 797660524af64..ae573cfc6b99b 100644
|
||
--- a/pkg/apis/scheduling/v1alpha3/zz_generated.validations.go
|
||
+++ b/pkg/apis/scheduling/v1alpha3/zz_generated.validations.go
|
||
@@ -752,7 +752,7 @@ func Validate_CompositePodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1047,7 +1047,7 @@ func Validate_CompositePodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.PodGroupTemplate, b *schedulingv1alpha3.PodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1100,7 +1100,7 @@ func Validate_CompositePodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.CompositePodGroupTemplate, b *schedulingv1alpha3.CompositePodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1816,7 +1816,7 @@ func Validate_PodGroupSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.PodGroupResourceClaim, b *schedulingv1alpha3.PodGroupResourceClaim) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2010,7 +2010,7 @@ func Validate_PodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2056,7 +2056,7 @@ func Validate_PodGroupStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.PodGroupResourceClaimStatus, b *schedulingv1alpha3.PodGroupResourceClaimStatus) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2210,7 +2210,7 @@ func Validate_PodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.PodGroupResourceClaim, b *schedulingv1alpha3.PodGroupResourceClaim) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3171,7 +3171,7 @@ func Validate_WorkloadSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.PodGroupTemplate, b *schedulingv1alpha3.PodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3231,7 +3231,7 @@ func Validate_WorkloadSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1alpha3.CompositePodGroupTemplate, b *schedulingv1alpha3.CompositePodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3245,3 +3245,8 @@ func Validate_WorkloadSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/scheduling/v1beta1/zz_generated.validations.go b/pkg/apis/scheduling/v1beta1/zz_generated.validations.go
|
||
index 6914741407314..ccc3d7ff6a927 100644
|
||
--- a/pkg/apis/scheduling/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/scheduling/v1beta1/zz_generated.validations.go
|
||
@@ -632,7 +632,7 @@ func Validate_CompositePodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.PodGroupTemplate, b *schedulingv1beta1.PodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -685,7 +685,7 @@ func Validate_CompositePodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.CompositePodGroupTemplate, b *schedulingv1beta1.CompositePodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1401,7 +1401,7 @@ func Validate_PodGroupSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.PodGroupResourceClaim, b *schedulingv1beta1.PodGroupResourceClaim) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1595,7 +1595,7 @@ func Validate_PodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1641,7 +1641,7 @@ func Validate_PodGroupStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.PodGroupResourceClaimStatus, b *schedulingv1beta1.PodGroupResourceClaimStatus) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1795,7 +1795,7 @@ func Validate_PodGroupTemplate(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.PodGroupResourceClaim, b *schedulingv1beta1.PodGroupResourceClaim) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2379,7 +2379,7 @@ func Validate_WorkloadSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.PodGroupTemplate, b *schedulingv1beta1.PodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2439,7 +2439,7 @@ func Validate_WorkloadSpec(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *schedulingv1beta1.CompositePodGroupTemplate, b *schedulingv1beta1.CompositePodGroupTemplate) bool {
|
||
return a.Name == b.Name
|
||
- }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2453,3 +2453,8 @@ func Validate_WorkloadSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/storagemigration/v1/zz_generated.validations.go b/pkg/apis/storagemigration/v1/zz_generated.validations.go
|
||
index 3462185867b85..480bbb54c4f61 100644
|
||
--- a/pkg/apis/storagemigration/v1/zz_generated.validations.go
|
||
+++ b/pkg/apis/storagemigration/v1/zz_generated.validations.go
|
||
@@ -148,7 +148,7 @@ func Validate_StorageVersionMigrationStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -163,3 +163,8 @@ func Validate_StorageVersionMigrationStatus(
|
||
// field storagemigrationv1.StorageVersionMigrationStatus.ResourceVersion has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/pkg/apis/storagemigration/v1beta1/zz_generated.validations.go b/pkg/apis/storagemigration/v1beta1/zz_generated.validations.go
|
||
index 0f4a1e3feedd3..b76128cb3ba9f 100644
|
||
--- a/pkg/apis/storagemigration/v1beta1/zz_generated.validations.go
|
||
+++ b/pkg/apis/storagemigration/v1beta1/zz_generated.validations.go
|
||
@@ -148,7 +148,7 @@ func Validate_StorageVersionMigrationStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -163,3 +163,8 @@ func Validate_StorageVersionMigrationStatus(
|
||
// field storagemigrationv1beta1.StorageVersionMigrationStatus.ResourceVersion has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/api/resource/v1/zz_generated.validations.go b/staging/src/k8s.io/api/resource/v1/zz_generated.validations.go
|
||
index b771d5b92a349..2c77f7412ac17 100644
|
||
--- a/staging/src/k8s.io/api/resource/v1/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/api/resource/v1/zz_generated.validations.go
|
||
@@ -104,7 +104,7 @@ func Validate_AllocatedDeviceStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -300,7 +300,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_DeviceAttribute); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_DeviceAttribute); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -348,7 +348,7 @@ func Validate_Device(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *DeviceCounterConsumption, b *DeviceCounterConsumption) bool {
|
||
return a.CounterSet == b.CounterSet
|
||
- }, validate.SemanticDeepEqual, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_DeviceCounterConsumption); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -484,7 +484,7 @@ func Validate_Device(
|
||
return // do not proceed
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_NodeAllocatableResource); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1022,7 +1022,7 @@ func Validate_DeviceClaim(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *DeviceRequest, b *DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest); len(e) != 0 {
|
||
+ func(a *DeviceRequest, b *DeviceRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1711,7 +1711,7 @@ func Validate_DeviceRequest(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *DeviceSubRequest, b *DeviceSubRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceSubRequest); len(e) != 0 {
|
||
+ func(a *DeviceSubRequest, b *DeviceSubRequest) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_DeviceSubRequest); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2273,7 +2273,7 @@ func Validate_DeviceTaintRuleStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *metav1.Condition, b *metav1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3119,7 +3119,7 @@ func Validate_ResourceClaimStatus(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *AllocatedDeviceStatus, b *AllocatedDeviceStatus) bool {
|
||
return a.Driver == b.Driver && a.Device == b.Device && a.Pool == b.Pool && ((a.ShareID == nil && b.ShareID == nil) || (a.ShareID != nil && b.ShareID != nil && *a.ShareID == *b.ShareID))
|
||
- }, validate.SemanticDeepEqual, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_AllocatedDeviceStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3243,7 +3243,7 @@ func Validate_ResourceSliceSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *CounterSet, b *CounterSet) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CounterSet); len(e) != 0 {
|
||
+ func(a *CounterSet, b *CounterSet) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CounterSet); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3346,3 +3346,8 @@ func Validate_SkipNodeOperation(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/api/scheduling/v1alpha3/zz_generated.validations.go b/staging/src/k8s.io/api/scheduling/v1alpha3/zz_generated.validations.go
|
||
index 3202dbe2bea07..b7e1aa6954d65 100644
|
||
--- a/staging/src/k8s.io/api/scheduling/v1alpha3/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/api/scheduling/v1alpha3/zz_generated.validations.go
|
||
@@ -696,7 +696,7 @@ func Validate_CompositePodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -985,7 +985,7 @@ func Validate_CompositePodGroupTemplate(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *PodGroupTemplate, b *PodGroupTemplate) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ func(a *PodGroupTemplate, b *PodGroupTemplate) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1032,7 +1032,7 @@ func Validate_CompositePodGroupTemplate(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *CompositePodGroupTemplate, b *CompositePodGroupTemplate) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ func(a *CompositePodGroupTemplate, b *CompositePodGroupTemplate) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1744,7 +1744,7 @@ func Validate_PodGroupSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *PodGroupResourceClaim, b *PodGroupResourceClaim) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ func(a *PodGroupResourceClaim, b *PodGroupResourceClaim) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1938,7 +1938,7 @@ func Validate_PodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, validate.SemanticDeepEqual, validation.Validate_Condition); len(e) != 0 {
|
||
+ func(a *v1.Condition, b *v1.Condition) bool { return a.Type == b.Type }, deepEqualImpl_, validation.Validate_Condition); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -1980,7 +1980,7 @@ func Validate_PodGroupStatus(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *PodGroupResourceClaimStatus, b *PodGroupResourceClaimStatus) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
+ func(a *PodGroupResourceClaimStatus, b *PodGroupResourceClaimStatus) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodGroupResourceClaimStatus); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -2130,7 +2130,7 @@ func Validate_PodGroupTemplate(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *PodGroupResourceClaim, b *PodGroupResourceClaim) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
+ func(a *PodGroupResourceClaim, b *PodGroupResourceClaim) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodGroupResourceClaim); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3298,7 +3298,7 @@ func Validate_WorkloadSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *PodGroupTemplate, b *PodGroupTemplate) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_PodGroupTemplate); len(e) != 0 {
|
||
+ func(a *PodGroupTemplate, b *PodGroupTemplate) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_PodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3352,7 +3352,7 @@ func Validate_WorkloadSpec(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *CompositePodGroupTemplate, b *CompositePodGroupTemplate) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
+ func(a *CompositePodGroupTemplate, b *CompositePodGroupTemplate) bool { return a.Name == b.Name }, deepEqualImpl_, Validate_CompositePodGroupTemplate); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -3366,3 +3366,8 @@ func Validate_WorkloadSpec(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validate/each.go b/staging/src/k8s.io/apimachinery/pkg/api/validate/each.go
|
||
index 2930c21836cc7..25eef87562098 100644
|
||
--- a/staging/src/k8s.io/apimachinery/pkg/api/validate/each.go
|
||
+++ b/staging/src/k8s.io/apimachinery/pkg/api/validate/each.go
|
||
@@ -18,6 +18,7 @@ package validate
|
||
|
||
import (
|
||
"context"
|
||
+ "reflect"
|
||
"slices"
|
||
"sort"
|
||
|
||
@@ -163,6 +164,36 @@ func EachMapVal[K ~string, V any](ctx context.Context, op operation.Operation, f
|
||
return errs
|
||
}
|
||
|
||
+// EachPtrMapVal validates each value in newMap (which is a map of pointers)
|
||
+// using the specified validation function, passing the corresponding old value
|
||
+// from oldMap if the key exists in oldMap.
|
||
+// For update operations, it implements validation ratcheting by skipping validation
|
||
+// when the old value exists and the equiv function confirms the values are equivalent.
|
||
+//
|
||
+// The equiv function will never be called with nil arguments.
|
||
+//
|
||
+// If equiv is nil, value-based ratcheting is disabled and all values will be validated.
|
||
+func EachPtrMapVal[K ~string, V any](ctx context.Context, op operation.Operation, fldPath *field.Path, newMap, oldMap map[K]*V,
|
||
+ equiv MatchFunc[*V], validator ValidateFunc[*V]) field.ErrorList {
|
||
+ var errs field.ErrorList
|
||
+ for key, val := range newMap {
|
||
+ if val == nil {
|
||
+ // Ignore nil items; they are supposed to have been checked by PtrMapNoNils.
|
||
+ continue
|
||
+ }
|
||
+
|
||
+ old := oldMap[key]
|
||
+
|
||
+ // If the operation is an update, for validation ratcheting, skip re-validating if the old
|
||
+ // value is found and the equiv function confirms the values are equivalent.
|
||
+ if op.Type == operation.Update && old != nil && equiv != nil && equiv(val, old) {
|
||
+ continue
|
||
+ }
|
||
+ errs = append(errs, validator(ctx, op, fldPath.Key(string(key)), val, old)...)
|
||
+ }
|
||
+ return errs
|
||
+}
|
||
+
|
||
// EachMapKey validates each element of newMap with the specified
|
||
// validation function.
|
||
func EachMapKey[K ~string, T any](ctx context.Context, op operation.Operation, fldPath *field.Path, newMap, oldMap map[K]T,
|
||
@@ -261,10 +292,22 @@ func PtrSliceUnique[T any](_ context.Context, _ operation.Operation, fldPath *fi
|
||
// arguments of type interface{}/any. The wrapper satisfies the type
|
||
// constraints of MatchFunc while leveraging the underlying semantic equality
|
||
// logic. It can be used by any other function that needs to call DeepEqual.
|
||
+//
|
||
+// Deprecated: Callers should use equality.Semantic.DeepEqual directly.
|
||
func SemanticDeepEqual[T any](a, b T) bool {
|
||
return equality.Semantic.DeepEqual(a, b)
|
||
}
|
||
|
||
+// ReflectDeepEqual is a MatchFunc that uses reflect.DeepEqual to compare two
|
||
+// values.
|
||
+// This wrapper is needed because MatchFunc requires a function that takes two
|
||
+// arguments of specific type T, while reflect.DeepEqual takes arguments of
|
||
+// type interface{}/any. It can be used by any other function that needs to
|
||
+// call DeepEqual.
|
||
+func ReflectDeepEqual[T any](a, b T) bool {
|
||
+ return reflect.DeepEqual(a, b)
|
||
+}
|
||
+
|
||
// DirectEqual is a MatchFunc that dereferences two pointers and uses the ==
|
||
// operator to compare the values. If both pointers are nil, it returns true.
|
||
// If one pointer is nil and the other is not, it returns false.
|
||
@@ -290,3 +333,14 @@ func PtrSliceNoNils[T any](_ context.Context, _ operation.Operation, fldPath *fi
|
||
}
|
||
return
|
||
}
|
||
+
|
||
+// PtrMapNoNils returns a Required error for each nil element in a map of
|
||
+// pointers.
|
||
+func PtrMapNoNils[K ~string, V any](_ context.Context, _ operation.Operation, fldPath *field.Path, newMap, _ map[K]*V) (errs field.ErrorList) {
|
||
+ for key, val := range newMap {
|
||
+ if val == nil {
|
||
+ errs = append(errs, field.Required(fldPath.Key(string(key)), ""))
|
||
+ }
|
||
+ }
|
||
+ return
|
||
+}
|
||
diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validate/each_test.go b/staging/src/k8s.io/apimachinery/pkg/api/validate/each_test.go
|
||
index 3b98fcb0460bd..a31e8863cc183 100644
|
||
--- a/staging/src/k8s.io/apimachinery/pkg/api/validate/each_test.go
|
||
+++ b/staging/src/k8s.io/apimachinery/pkg/api/validate/each_test.go
|
||
@@ -137,7 +137,7 @@ func TestEachValSliceValRatcheting(t *testing.T) {
|
||
[]TestStruct{
|
||
{11, "a"}, {13, "c"}, {12, "b"},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
nil,
|
||
)
|
||
testEachValSliceValRatcheting(t, "ComparableStruct less data in new, exist in old",
|
||
@@ -181,7 +181,7 @@ func TestEachValSliceValRatcheting(t *testing.T) {
|
||
[]NonComparableStruct{
|
||
{I: 11, S: []string{"a"}}, {I: 13, S: []string{"c"}}, {I: 12, S: []string{"b"}},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
nil,
|
||
)
|
||
testEachValSliceValRatcheting(t, "NonComparableStructWithKey same data different order",
|
||
@@ -194,7 +194,7 @@ func TestEachValSliceValRatcheting(t *testing.T) {
|
||
MatchFunc[*NonComparableStructWithKey](func(a, b *NonComparableStructWithKey) bool {
|
||
return a.Key == b.Key
|
||
}),
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
)
|
||
|
||
}
|
||
@@ -276,7 +276,7 @@ func TestEachMapValRatcheting(t *testing.T) {
|
||
"three": {I: 13, S: []string{"c"}},
|
||
"two": {I: 12, S: []string{"b"}},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
0,
|
||
)
|
||
testEachMapValRatcheting(t, "non comparable value, less data in new, exist in old",
|
||
@@ -289,7 +289,7 @@ func TestEachMapValRatcheting(t *testing.T) {
|
||
"one": {I: 11, S: []string{"a"}},
|
||
"three": {I: 13, S: []string{"c"}},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
0,
|
||
)
|
||
testEachMapValRatcheting(t, "non comparable value, new data, not exist in old",
|
||
@@ -304,7 +304,7 @@ func TestEachMapValRatcheting(t *testing.T) {
|
||
"two": {I: 12, S: []string{"b"}},
|
||
"four": {I: 14, S: []string{"d"}},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
1,
|
||
)
|
||
testEachMapValRatcheting(t, "struct with pointer field, same value different pointer",
|
||
@@ -316,7 +316,7 @@ func TestEachMapValRatcheting(t *testing.T) {
|
||
"one": {I: 11, P: new(1)},
|
||
"two": {I: 12, P: new(2)},
|
||
},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
0,
|
||
)
|
||
testEachMapValRatcheting(t, "nil map to empty map",
|
||
@@ -364,6 +364,179 @@ func testEachMapValRatcheting[K ~string, V any](t *testing.T, name string, old,
|
||
})
|
||
}
|
||
|
||
+func TestEachPtrMapVal(t *testing.T) {
|
||
+ testEachPtrMapVal(t, "valid", map[string]*int{"one": new(11), "two": new(12), "three": new(13)})
|
||
+ testEachPtrMapVal(t, "valid", map[string]*string{"A": new("a"), "B": new("b"), "C": new("c")})
|
||
+ testEachPtrMapVal(t, "valid", map[string]*TestStruct{"one": {11, "a"}, "two": {12, "b"}, "three": {13, "c"}})
|
||
+
|
||
+ testEachPtrMapVal(t, "empty", map[string]*int{})
|
||
+ testEachPtrMapVal(t, "empty", map[string]*string{})
|
||
+ testEachPtrMapVal(t, "empty", map[string]*TestStruct{})
|
||
+
|
||
+ testEachPtrMapVal[int](t, "nil", nil)
|
||
+ testEachPtrMapVal[string](t, "nil", nil)
|
||
+ testEachPtrMapVal[TestStruct](t, "nil", nil)
|
||
+
|
||
+ t.Run("nil element ignored", func(t *testing.T) {
|
||
+ calls := 0
|
||
+ vfn := func(ctx context.Context, op operation.Operation, fldPath *field.Path, newVal, oldVal *int) field.ErrorList {
|
||
+ calls++
|
||
+ return nil
|
||
+ }
|
||
+ input := map[string]*int{"a": new(1), "b": nil, "c": new(3)}
|
||
+ errs := EachPtrMapVal(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, nil, vfn)
|
||
+ if len(errs) != 0 {
|
||
+ t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
||
+ }
|
||
+ if calls != 2 {
|
||
+ t.Errorf("expected 2 calls, got %d", calls)
|
||
+ }
|
||
+ })
|
||
+}
|
||
+
|
||
+func testEachPtrMapVal[T any](t *testing.T, name string, input map[string]*T) {
|
||
+ t.Helper()
|
||
+ var zero T
|
||
+ t.Run(fmt.Sprintf("%s(*%T)", name, zero), func(t *testing.T) {
|
||
+ calls := 0
|
||
+ vfn := func(ctx context.Context, op operation.Operation, fldPath *field.Path, newVal, oldVal *T) field.ErrorList {
|
||
+ if oldVal != nil {
|
||
+ t.Errorf("expected nil oldVal, got %v", *oldVal)
|
||
+ }
|
||
+ calls++
|
||
+ return nil
|
||
+ }
|
||
+ errs := EachPtrMapVal(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, nil, vfn)
|
||
+ if len(errs) != 0 {
|
||
+ t.Errorf("expected 0 errors, got %d: %v", len(errs), errs)
|
||
+ }
|
||
+ if calls != len(input) {
|
||
+ t.Errorf("expected %d calls, got %d", len(input), calls)
|
||
+ }
|
||
+ })
|
||
+}
|
||
+
|
||
+func TestEachPtrMapValRatcheting(t *testing.T) {
|
||
+ testEachPtrMapValRatcheting(t, "primitive same data",
|
||
+ map[string]*int{"one": new(11), "two": new(12), "three": new(13)},
|
||
+ map[string]*int{"one": new(11), "three": new(13), "two": new(12)},
|
||
+ DirectEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "primitive changed value, exists in old",
|
||
+ map[string]*int{"one": new(11), "two": new(12)}, // old
|
||
+ map[string]*int{"one": new(11), "two": new(99)}, // new
|
||
+ DirectEqual,
|
||
+ 1,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "primitive less data in new, exist in old",
|
||
+ map[string]*int{"one": new(11), "two": new(12), "three": new(13)},
|
||
+ map[string]*int{"one": new(11), "three": new(13)},
|
||
+ DirectEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "primitive new data, not exist in old",
|
||
+ map[string]*int{"one": new(11), "two": new(12)},
|
||
+ map[string]*int{"one": new(11), "two": new(12), "three": new(13)},
|
||
+ DirectEqual,
|
||
+ 1,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "non comparable value, same data",
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "two": {I: 12, S: []string{"b"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ },
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ "two": {I: 12, S: []string{"b"}},
|
||
+ },
|
||
+ ReflectDeepEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "non comparable value, less data in new, exist in old",
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "two": {I: 12, S: []string{"b"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ },
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ },
|
||
+ ReflectDeepEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "non comparable value, new data, not exist in old",
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "two": {I: 12, S: []string{"b"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ },
|
||
+ map[string]*NonComparableStruct{
|
||
+ "one": {I: 11, S: []string{"a"}},
|
||
+ "three": {I: 13, S: []string{"c"}},
|
||
+ "two": {I: 12, S: []string{"b"}},
|
||
+ "four": {I: 14, S: []string{"d"}},
|
||
+ },
|
||
+ ReflectDeepEqual,
|
||
+ 1,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "struct with pointer field, same value different pointer",
|
||
+ map[string]*NonComparableStructWithPtr{
|
||
+ "one": {I: 11, P: new(1)},
|
||
+ "two": {I: 12, P: new(2)},
|
||
+ },
|
||
+ map[string]*NonComparableStructWithPtr{
|
||
+ "one": {I: 11, P: new(1)},
|
||
+ "two": {I: 12, P: new(2)},
|
||
+ },
|
||
+ ReflectDeepEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "nil map to empty map",
|
||
+ nil,
|
||
+ map[string]*int{},
|
||
+ DirectEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "nil map to non-empty map",
|
||
+ nil,
|
||
+ map[string]*int{"one": new(1)},
|
||
+ DirectEqual,
|
||
+ 1,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "empty map to nil map",
|
||
+ map[string]*int{},
|
||
+ nil,
|
||
+ DirectEqual,
|
||
+ 0,
|
||
+ )
|
||
+ testEachPtrMapValRatcheting(t, "non-empty map to nil map",
|
||
+ map[string]*int{"one": new(1)},
|
||
+ nil,
|
||
+ DirectEqual,
|
||
+ 0,
|
||
+ )
|
||
+}
|
||
+
|
||
+func testEachPtrMapValRatcheting[K ~string, V any](t *testing.T, name string, old, new map[K]*V, equiv MatchFunc[*V], wantCalls int) {
|
||
+ t.Helper()
|
||
+ var zero V
|
||
+ t.Run(fmt.Sprintf("%s(*%T)", name, zero), func(t *testing.T) {
|
||
+ calls := 0
|
||
+ vfn := func(ctx context.Context, op operation.Operation, fldPath *field.Path, newVal, oldVal *V) field.ErrorList {
|
||
+ calls++
|
||
+ return nil
|
||
+ }
|
||
+ _ = EachPtrMapVal(context.Background(), operation.Operation{Type: operation.Update}, field.NewPath("test"), new, old, equiv, vfn)
|
||
+ if calls != wantCalls {
|
||
+ t.Errorf("expected %d calls, got %d", wantCalls, calls)
|
||
+ }
|
||
+ })
|
||
+}
|
||
+
|
||
type StringType string
|
||
|
||
func TestEachMapKey(t *testing.T) {
|
||
@@ -455,7 +628,7 @@ func testValSliceUnique[T comparable](t *testing.T, name string, input []T, want
|
||
}
|
||
})
|
||
t.Run(fmt.Sprintf("%s(reflect)", name), func(t *testing.T) {
|
||
- errs := ValSliceUnique(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, SemanticDeepEqual)
|
||
+ errs := ValSliceUnique(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, ReflectDeepEqual)
|
||
if len(errs) != wantErrs {
|
||
t.Errorf("expected %d errors, got %d: %s", wantErrs, len(errs), fmtErrs(errs))
|
||
}
|
||
@@ -483,7 +656,7 @@ func testValSliceUniqueByReflect[T any](t *testing.T, name string, input []T, wa
|
||
t.Helper()
|
||
var zero T
|
||
t.Run(fmt.Sprintf("%s(%T)", name, zero), func(t *testing.T) {
|
||
- errs := ValSliceUnique(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, SemanticDeepEqual)
|
||
+ errs := ValSliceUnique(context.Background(), operation.Operation{}, field.NewPath("test"), input, nil, ReflectDeepEqual)
|
||
if len(errs) != wantErrs {
|
||
t.Errorf("expected %d errors, got %d: %s", wantErrs, len(errs), fmtErrs(errs))
|
||
}
|
||
@@ -573,7 +746,7 @@ func TestEachPtrSliceValRatcheting(t *testing.T) {
|
||
testEachPtrSliceValRatcheting(t, "ComparableStruct same data different order",
|
||
[]*TestStruct{{11, "a"}, {12, "b"}, {13, "c"}},
|
||
[]*TestStruct{{11, "a"}, {13, "c"}, {12, "b"}},
|
||
- SemanticDeepEqual,
|
||
+ ReflectDeepEqual,
|
||
nil,
|
||
)
|
||
}
|
||
@@ -650,3 +823,30 @@ func TestPtrSliceNoNils(t *testing.T) {
|
||
})
|
||
}
|
||
}
|
||
+
|
||
+func TestPtrMapNoNils(t *testing.T) {
|
||
+ tests := []struct {
|
||
+ name string
|
||
+ input map[string]*int
|
||
+ wantErrs int
|
||
+ }{
|
||
+ {"nil", nil, 0},
|
||
+ {"empty", map[string]*int{}, 0},
|
||
+ {"no_nil", map[string]*int{"a": new(1), "b": new(2)}, 0},
|
||
+ {"has_nil", map[string]*int{"a": new(1), "b": nil, "c": new(3), "d": nil}, 2},
|
||
+ }
|
||
+
|
||
+ for _, tt := range tests {
|
||
+ t.Run(tt.name, func(t *testing.T) {
|
||
+ errs := PtrMapNoNils(context.Background(), operation.Operation{}, field.NewPath("test"), tt.input, nil)
|
||
+ if len(errs) != tt.wantErrs {
|
||
+ t.Errorf("expected %d errors, got %d: %v", tt.wantErrs, len(errs), errs)
|
||
+ }
|
||
+ for _, err := range errs {
|
||
+ if err.Type != field.ErrorTypeRequired {
|
||
+ t.Errorf("expected Required error, got %v", err.Type)
|
||
+ }
|
||
+ }
|
||
+ })
|
||
+ }
|
||
+}
|
||
diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validate/item_test.go b/staging/src/k8s.io/apimachinery/pkg/api/validate/item_test.go
|
||
index 2efd68c0d2b0a..180b8da62c0cd 100644
|
||
--- a/staging/src/k8s.io/apimachinery/pkg/api/validate/item_test.go
|
||
+++ b/staging/src/k8s.io/apimachinery/pkg/api/validate/item_test.go
|
||
@@ -157,7 +157,7 @@ func TestValSliceItem(t *testing.T) {
|
||
op := operation.Operation{Type: operation.Update}
|
||
fp := field.NewPath("")
|
||
|
||
- got := ValSliceItem(ctx, op, fp, tc.new, tc.old, tc.match, SemanticDeepEqual, tc.validator)
|
||
+ got := ValSliceItem(ctx, op, fp, tc.new, tc.old, tc.match, ReflectDeepEqual, tc.validator)
|
||
|
||
if !reflect.DeepEqual(got, tc.expected) {
|
||
t.Errorf("got %v want %v", got, tc.expected)
|
||
@@ -227,7 +227,7 @@ func TestPtrSliceItem(t *testing.T) {
|
||
op := operation.Operation{Type: operation.Update}
|
||
fp := field.NewPath("")
|
||
|
||
- got := PtrSliceItem(ctx, op, fp, tc.new, tc.old, tc.match, SemanticDeepEqual, tc.validator)
|
||
+ got := PtrSliceItem(ctx, op, fp, tc.new, tc.old, tc.match, ReflectDeepEqual, tc.validator)
|
||
|
||
if !reflect.DeepEqual(got, tc.expected) {
|
||
t.Errorf("got %v want %v", got, tc.expected)
|
||
diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validate/update.go b/staging/src/k8s.io/apimachinery/pkg/api/validate/update.go
|
||
index c258e6d112c65..cf4db5b5294f1 100644
|
||
--- a/staging/src/k8s.io/apimachinery/pkg/api/validate/update.go
|
||
+++ b/staging/src/k8s.io/apimachinery/pkg/api/validate/update.go
|
||
@@ -42,8 +42,8 @@ const (
|
||
NoRemoveItem
|
||
)
|
||
|
||
-// UpdateValueByCompare verifies update constraints for comparable value types.
|
||
-func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||
+// UpdateValue verifies update constraints for value types.
|
||
+func UpdateValue[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, equiv MatchFunc[T], constraints ...UpdateConstraint) field.ErrorList {
|
||
// nil oldValue means no prior value to compare against (eg: a new item at +k8s:eachVal scope) -> no transition to check.
|
||
if op.Type != operation.Update || oldValue == nil {
|
||
return nil
|
||
@@ -51,22 +51,24 @@ func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operatio
|
||
|
||
var errs field.ErrorList
|
||
var zero T
|
||
+ valueIsZero := equiv(*value, zero)
|
||
+ oldValueIsZero := equiv(*oldValue, zero)
|
||
|
||
for _, constraint := range constraints {
|
||
switch constraint {
|
||
case NoSet:
|
||
- if *oldValue == zero && *value != zero {
|
||
+ if oldValueIsZero && !valueIsZero {
|
||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be set once created").WithOrigin("update"))
|
||
}
|
||
case NoUnset:
|
||
- if *oldValue != zero && *value == zero {
|
||
+ if !oldValueIsZero && valueIsZero {
|
||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be cleared once set").WithOrigin("update"))
|
||
}
|
||
case NoModify:
|
||
// Rely on validation ratcheting to detect that the value has changed.
|
||
// This check only verifies that the field was set in both the old and
|
||
// new objects, confirming it was a modification, not a set/unset.
|
||
- if *oldValue != zero && *value != zero {
|
||
+ if !oldValueIsZero && !valueIsZero {
|
||
errs = append(errs, field.Invalid(fldPath, nil, "field cannot be modified once set").WithOrigin("update"))
|
||
}
|
||
}
|
||
@@ -75,6 +77,13 @@ func UpdateValueByCompare[T comparable](_ context.Context, op operation.Operatio
|
||
return errs
|
||
}
|
||
|
||
+// UpdateValueByCompare verifies update constraints for comparable value types.
|
||
+//
|
||
+// Deprecated: Use UpdateValue instead.
|
||
+func UpdateValueByCompare[T comparable](ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||
+ return UpdateValue(ctx, op, fldPath, value, oldValue, func(a, b T) bool { return a == b }, constraints...)
|
||
+}
|
||
+
|
||
// UpdatePointer verifies update constraints for pointer types.
|
||
func UpdatePointer[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||
if op.Type != operation.Update {
|
||
@@ -107,38 +116,10 @@ func UpdatePointer[T any](_ context.Context, op operation.Operation, fldPath *fi
|
||
}
|
||
|
||
// UpdateValueByReflect verifies update constraints for non-comparable value types using reflection.
|
||
-func UpdateValueByReflect[T any](_ context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||
- // nil oldValue means no prior value to compare against (eg: a new item at +k8s:eachVal scope) -> no transition to check.
|
||
- if op.Type != operation.Update || oldValue == nil {
|
||
- return nil
|
||
- }
|
||
-
|
||
- var errs field.ErrorList
|
||
- var zero T
|
||
- valueIsZero := equality.Semantic.DeepEqual(*value, zero)
|
||
- oldValueIsZero := equality.Semantic.DeepEqual(*oldValue, zero)
|
||
-
|
||
- for _, constraint := range constraints {
|
||
- switch constraint {
|
||
- case NoSet:
|
||
- if oldValueIsZero && !valueIsZero {
|
||
- errs = append(errs, field.Invalid(fldPath, nil, "field cannot be set once created").WithOrigin("update"))
|
||
- }
|
||
- case NoUnset:
|
||
- if !oldValueIsZero && valueIsZero {
|
||
- errs = append(errs, field.Invalid(fldPath, nil, "field cannot be cleared once set").WithOrigin("update"))
|
||
- }
|
||
- case NoModify:
|
||
- // Rely on validation ratcheting to detect that the value has changed.
|
||
- // This check only verifies that the field was set in both the old and
|
||
- // new objects, confirming it was a modification, not a set/unset.
|
||
- if !oldValueIsZero && !valueIsZero {
|
||
- errs = append(errs, field.Invalid(fldPath, nil, "field cannot be modified once set").WithOrigin("update"))
|
||
- }
|
||
- }
|
||
- }
|
||
-
|
||
- return errs
|
||
+//
|
||
+// Deprecated: Use UpdateValue instead.
|
||
+func UpdateValueByReflect[T any](ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *T, constraints ...UpdateConstraint) field.ErrorList {
|
||
+ return UpdateValue(ctx, op, fldPath, value, oldValue, func(a, b T) bool { return equality.Semantic.DeepEqual(a, b) }, constraints...)
|
||
}
|
||
|
||
// UpdateStruct verifies update constraints for non-pointer struct types.
|
||
diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validate/update_test.go b/staging/src/k8s.io/apimachinery/pkg/api/validate/update_test.go
|
||
index f9e13e0e99dd6..1978ced8d72ab 100644
|
||
--- a/staging/src/k8s.io/apimachinery/pkg/api/validate/update_test.go
|
||
+++ b/staging/src/k8s.io/apimachinery/pkg/api/validate/update_test.go
|
||
@@ -138,10 +138,153 @@ func TestUpdateValue(t *testing.T) {
|
||
},
|
||
}
|
||
|
||
+ for _, tt := range tests {
|
||
+ t.Run(tt.name, func(t *testing.T) {
|
||
+ op := operation.Operation{Type: tt.op}
|
||
+ errs := UpdateValue(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, func(a, b string) bool { return a == b }, tt.constraints...)
|
||
+ if len(errs) != tt.wantErrs {
|
||
+ t.Errorf("UpdateValue() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||
+ }
|
||
+ for i, msg := range tt.wantMsgs {
|
||
+ if i >= len(errs) {
|
||
+ t.Errorf("Expected error message %q not found", msg)
|
||
+ continue
|
||
+ }
|
||
+ if errs[i].Detail != msg {
|
||
+ t.Errorf("UpdateValue() error message = %q, want %q", errs[i].Detail, msg)
|
||
+ }
|
||
+ }
|
||
+ })
|
||
+ }
|
||
+}
|
||
+
|
||
+func TestUpdateValueByCompare(t *testing.T) {
|
||
+ tests := []struct {
|
||
+ name string
|
||
+ op operation.Type
|
||
+ value string
|
||
+ oldValue string
|
||
+ constraints []UpdateConstraint
|
||
+ wantErrs int
|
||
+ wantMsgs []string
|
||
+ }{
|
||
+ {
|
||
+ name: "NoSet - unset to set transition (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: "value",
|
||
+ oldValue: "",
|
||
+ constraints: []UpdateConstraint{NoSet},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be set once created"},
|
||
+ },
|
||
+ {
|
||
+ name: "NoUnset - set to unset transition (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: "",
|
||
+ oldValue: "value",
|
||
+ constraints: []UpdateConstraint{NoUnset},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be cleared once set"},
|
||
+ },
|
||
+ {
|
||
+ name: "NoModify - set to different value (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: "value2",
|
||
+ oldValue: "value1",
|
||
+ constraints: []UpdateConstraint{NoModify},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be modified once set"},
|
||
+ },
|
||
+ }
|
||
+
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
op := operation.Operation{Type: tt.op}
|
||
errs := UpdateValueByCompare(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, tt.constraints...)
|
||
+ if len(errs) != tt.wantErrs {
|
||
+ t.Errorf("UpdateValueByCompare() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||
+ }
|
||
+ for i, msg := range tt.wantMsgs {
|
||
+ if i >= len(errs) {
|
||
+ t.Errorf("Expected error message %q not found", msg)
|
||
+ continue
|
||
+ }
|
||
+ if errs[i].Detail != msg {
|
||
+ t.Errorf("UpdateValueByCompare() error message = %q, want %q", errs[i].Detail, msg)
|
||
+ }
|
||
+ }
|
||
+ })
|
||
+ }
|
||
+}
|
||
+
|
||
+func TestUpdateValue_CustomMatchFunc(t *testing.T) {
|
||
+ type NonComparableStruct struct {
|
||
+ Name string
|
||
+ Tags []string
|
||
+ }
|
||
+
|
||
+ customEquiv := func(a, b NonComparableStruct) bool {
|
||
+ return a.Name == b.Name && (len(a.Tags) == 0 && len(b.Tags) == 0 || (len(a.Tags) == len(b.Tags) && a.Tags[0] == b.Tags[0]))
|
||
+ }
|
||
+
|
||
+ tests := []struct {
|
||
+ name string
|
||
+ op operation.Type
|
||
+ value NonComparableStruct
|
||
+ oldValue NonComparableStruct
|
||
+ constraints []UpdateConstraint
|
||
+ wantErrs int
|
||
+ wantMsgs []string
|
||
+ }{
|
||
+ {
|
||
+ name: "NoSet - unset to set (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||
+ oldValue: NonComparableStruct{},
|
||
+ constraints: []UpdateConstraint{NoSet},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be set once created"},
|
||
+ },
|
||
+ {
|
||
+ name: "NoUnset - set to unset (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: NonComparableStruct{},
|
||
+ oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||
+ constraints: []UpdateConstraint{NoUnset},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be cleared once set"},
|
||
+ },
|
||
+ {
|
||
+ name: "NoModify - zero to non-zero transition (allowed)",
|
||
+ op: operation.Update,
|
||
+ value: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||
+ oldValue: NonComparableStruct{},
|
||
+ constraints: []UpdateConstraint{NoModify},
|
||
+ wantErrs: 0,
|
||
+ },
|
||
+ {
|
||
+ name: "NoModify - non-zero to zero transition (allowed)",
|
||
+ op: operation.Update,
|
||
+ value: NonComparableStruct{},
|
||
+ oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||
+ constraints: []UpdateConstraint{NoModify},
|
||
+ wantErrs: 0,
|
||
+ },
|
||
+ {
|
||
+ name: "NoModify - non-zero to non-zero transition (forbidden)",
|
||
+ op: operation.Update,
|
||
+ value: NonComparableStruct{Name: "foo", Tags: []string{"b"}},
|
||
+ oldValue: NonComparableStruct{Name: "foo", Tags: []string{"a"}},
|
||
+ constraints: []UpdateConstraint{NoModify},
|
||
+ wantErrs: 1,
|
||
+ wantMsgs: []string{"field cannot be modified once set"},
|
||
+ },
|
||
+ }
|
||
+
|
||
+ for _, tt := range tests {
|
||
+ t.Run(tt.name, func(t *testing.T) {
|
||
+ op := operation.Operation{Type: tt.op}
|
||
+ errs := UpdateValue(context.TODO(), op, field.NewPath("test"), &tt.value, &tt.oldValue, customEquiv, tt.constraints...)
|
||
if len(errs) != tt.wantErrs {
|
||
t.Errorf("UpdateValue() returned %d errors, want %d: %v", len(errs), tt.wantErrs, errs)
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/doc.go
|
||
index 25f869adadfb6..a8017edc07723 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/doc.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/doc.go
|
||
@@ -39,6 +39,16 @@ type Struct struct {
|
||
MapTypedefField map[string]StringType `json:"mapTypedefField"`
|
||
|
||
UnvalidatedMapField map[string]string `json:"UnvalidatedMapField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrField[*]"
|
||
+ MapPtrField map[string]*string `json:"mapPtrField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrTypedefField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrTypedefField[*]"
|
||
+ MapPtrTypedefField map[string]*StringType `json:"mapPtrTypedefField"`
|
||
+
|
||
+ UnvalidatedMapPtrField map[string]*string `json:"UnvalidatedMapPtrField"`
|
||
}
|
||
|
||
// +k8s:validateFalse="type StringType"
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/testdata/validate-false.json b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/testdata/validate-false.json
|
||
index 5076a6fcff727..b7758a6448905 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/testdata/validate-false.json
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_primitive/testdata/validate-false.json
|
||
@@ -12,6 +12,26 @@
|
||
"mapField[岯Ȉ\u0026\u003c沲3]": [
|
||
"field Struct.MapField[*]"
|
||
],
|
||
+ "mapPtrField": [
|
||
+ "field Struct.MapPtrField"
|
||
+ ],
|
||
+ "mapPtrField[`ȝ懿沇]": [
|
||
+ "field Struct.MapPtrField[*]"
|
||
+ ],
|
||
+ "mapPtrField[ǝ祇Fæƭō]": [
|
||
+ "field Struct.MapPtrField[*]"
|
||
+ ],
|
||
+ "mapPtrTypedefField": [
|
||
+ "field Struct.MapPtrTypedefField"
|
||
+ ],
|
||
+ "mapPtrTypedefField[ʈ劉j蕓ư銩6ij憏歅%ɜ¤0Ȼ]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type StringType"
|
||
+ ],
|
||
+ "mapPtrTypedefField[剛ň=Z?ǿ\u003c]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type StringType"
|
||
+ ],
|
||
"mapTypedefField": [
|
||
"field Struct.MapTypedefField"
|
||
],
|
||
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 e2520a6146e80..a4c8f92c3abda 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
|
||
@@ -146,5 +146,87 @@ func Validate_Struct(
|
||
}
|
||
|
||
// field Struct.UnvalidatedMapField has no validation
|
||
+
|
||
+ { // field Struct.MapPtrField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*string,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, string](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*string {
|
||
+ return oldObj.MapPtrField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrField"), obj.MapPtrField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field Struct.MapPtrTypedefField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*StringType,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, StringType](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrTypedefField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrTypedefField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, Validate_StringType); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*StringType {
|
||
+ return oldObj.MapPtrTypedefField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrTypedefField"), obj.MapPtrTypedefField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ // field Struct.UnvalidatedMapPtrField has no validation
|
||
return errs
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/doc.go
|
||
index 4e39a1de8e8ab..2f771fb1deae0 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/doc.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/doc.go
|
||
@@ -38,7 +38,17 @@ type Struct struct {
|
||
// +k8s:eachVal=+k8s:validateFalse="field Struct.MapTypedefField[*]"
|
||
MapTypedefField map[string]OtherTypedefStruct `json:"mapTypedefField"`
|
||
|
||
- UnvalidatedMapField map[string]string `json:"UnvalidatedMapField"`
|
||
+ UnvalidatedMapField map[string]UnvalidatedStruct `json:"UnvalidatedMapField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrField[*]"
|
||
+ MapPtrField map[string]*OtherStruct `json:"mapPtrField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrTypedefField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrTypedefField[*]"
|
||
+ MapPtrTypedefField map[string]*OtherTypedefStruct `json:"mapPtrTypedefField"`
|
||
+
|
||
+ UnvalidatedMapPtrField map[string]*UnvalidatedStruct `json:"UnvalidatedMapPtrField"`
|
||
}
|
||
|
||
// +k8s:validateFalse="type OtherStruct"
|
||
@@ -46,3 +56,5 @@ type OtherStruct struct{}
|
||
|
||
// +k8s:validateFalse="type OtherTypedefStruct"
|
||
type OtherTypedefStruct OtherStruct
|
||
+
|
||
+type UnvalidatedStruct struct{}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/testdata/validate-false.json b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/testdata/validate-false.json
|
||
index ec038cbc5e682..2253b92760894 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/testdata/validate-false.json
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/map_of_struct/testdata/validate-false.json
|
||
@@ -14,6 +14,28 @@
|
||
"field Struct.MapField[*]",
|
||
"type OtherStruct"
|
||
],
|
||
+ "mapPtrField": [
|
||
+ "field Struct.MapPtrField"
|
||
+ ],
|
||
+ "mapPtrField[\u003eóDz岋笨Gń條]": [
|
||
+ "field Struct.MapPtrField[*]",
|
||
+ "type OtherStruct"
|
||
+ ],
|
||
+ "mapPtrField[]": [
|
||
+ "field Struct.MapPtrField[*]",
|
||
+ "type OtherStruct"
|
||
+ ],
|
||
+ "mapPtrTypedefField": [
|
||
+ "field Struct.MapPtrTypedefField"
|
||
+ ],
|
||
+ "mapPtrTypedefField[v$蛷)讻IeYF墄[C_睿ȉǫ蹟t´]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type OtherTypedefStruct"
|
||
+ ],
|
||
+ "mapPtrTypedefField[ș$0đ\u003eƯ]ɹȽ東]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type OtherTypedefStruct"
|
||
+ ],
|
||
"mapTypedefField": [
|
||
"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 0b45bc834b4bd..ebe69b0254344 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
|
||
@@ -163,5 +163,91 @@ func Validate_Struct(
|
||
}
|
||
|
||
// field Struct.UnvalidatedMapField has no validation
|
||
+
|
||
+ { // field Struct.MapPtrField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*OtherStruct,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, OtherStruct](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, Validate_OtherStruct); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*OtherStruct {
|
||
+ return oldObj.MapPtrField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrField"), obj.MapPtrField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field Struct.MapPtrTypedefField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*OtherTypedefStruct,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, OtherTypedefStruct](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrTypedefField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrTypedefField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, Validate_OtherTypedefStruct); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*OtherTypedefStruct {
|
||
+ return oldObj.MapPtrTypedefField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrTypedefField"), obj.MapPtrTypedefField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ // field Struct.UnvalidatedMapPtrField has no validation
|
||
return errs
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/doc.go
|
||
index d21f559c00da1..a4b62cc13e9e0 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/doc.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/doc.go
|
||
@@ -29,20 +29,28 @@ var localSchemeBuilder = testscheme.New()
|
||
// Note: no validation here
|
||
type UnvalidatedType map[string]string
|
||
|
||
+// Note: no validation here
|
||
+type UnvalidatedPtrType map[string]*string
|
||
+
|
||
// +k8s:validateFalse="type MapType"
|
||
// +k8s:eachVal=+k8s:validateFalse="type MapType[*]"
|
||
type MapType map[string]string
|
||
|
||
-// Note: no validation here
|
||
-type UnvalidatedPtrType map[string]*string
|
||
-
|
||
-// +k8s:validateFalse="type StringType"
|
||
-type StringType string
|
||
+// +k8s:validateFalse="type MapPtrType"
|
||
+// +k8s:eachVal=+k8s:validateFalse="type MapPtrType[*]"
|
||
+type MapPtrType map[string]*string
|
||
|
||
// +k8s:validateFalse="type MapTypedefType"
|
||
// +k8s:eachVal=+k8s:validateFalse="type MapTypedefType[*]"
|
||
type MapTypedefType map[string]StringType
|
||
|
||
+// +k8s:validateFalse="type MapPtrTypedefType"
|
||
+// +k8s:eachVal=+k8s:validateFalse="type MapPtrTypedefType[*]"
|
||
+type MapPtrTypedefType map[string]*StringType
|
||
+
|
||
+// +k8s:validateFalse="type StringType"
|
||
+type StringType string
|
||
+
|
||
// +k8s:validateFalse="type Struct"
|
||
type Struct struct {
|
||
TypeMeta int
|
||
@@ -54,4 +62,16 @@ type Struct struct {
|
||
// +k8s:validateFalse="field Struct.MapTypedefField"
|
||
// +k8s:eachVal=+k8s:validateFalse="field Struct.MapTypedefField[*]"
|
||
MapTypedefField MapTypedefType `json:"mapTypedefField"`
|
||
+
|
||
+ UnvalidatedMapField UnvalidatedType `json:"UnvalidatedMapField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrField[*]"
|
||
+ MapPtrField MapPtrType `json:"mapPtrField"`
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.MapPtrTypedefField"
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPtrTypedefField[*]"
|
||
+ MapPtrTypedefField MapPtrTypedefType `json:"mapPtrTypedefField"`
|
||
+
|
||
+ UnvalidatedMapPtrField UnvalidatedPtrType `json:"UnvalidatedMapPtrField"`
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/testdata/validate-false.json b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/testdata/validate-false.json
|
||
index c4b147c06f9f2..65a321116f417 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/testdata/validate-false.json
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/maps/typedef_to_map/testdata/validate-false.json
|
||
@@ -15,6 +15,32 @@
|
||
"field Struct.MapField[*]",
|
||
"type MapType[*]"
|
||
],
|
||
+ "mapPtrField": [
|
||
+ "field Struct.MapPtrField",
|
||
+ "type MapPtrType"
|
||
+ ],
|
||
+ "mapPtrField[`ȝ懿沇]": [
|
||
+ "field Struct.MapPtrField[*]",
|
||
+ "type MapPtrType[*]"
|
||
+ ],
|
||
+ "mapPtrField[ǝ祇Fæƭō]": [
|
||
+ "field Struct.MapPtrField[*]",
|
||
+ "type MapPtrType[*]"
|
||
+ ],
|
||
+ "mapPtrTypedefField": [
|
||
+ "field Struct.MapPtrTypedefField",
|
||
+ "type MapPtrTypedefType"
|
||
+ ],
|
||
+ "mapPtrTypedefField[ʈ劉j蕓ư銩6ij憏歅%ɜ¤0Ȼ]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type MapPtrTypedefType[*]",
|
||
+ "type StringType"
|
||
+ ],
|
||
+ "mapPtrTypedefField[剛ň=Z?ǿ\u003c]": [
|
||
+ "field Struct.MapPtrTypedefField[*]",
|
||
+ "type MapPtrTypedefType[*]",
|
||
+ "type StringType"
|
||
+ ],
|
||
"mapTypedefField": [
|
||
"field Struct.MapTypedefField",
|
||
"type MapTypedefType"
|
||
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 a5da56f4c5832..15a29c153d037 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
|
||
@@ -56,6 +56,49 @@ func RegisterValidations(scheme *testscheme.Scheme) error {
|
||
return nil
|
||
}
|
||
|
||
+// Validate_MapPtrType validates an instance of MapPtrType according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_MapPtrType(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj MapPtrType) (errs field.ErrorList) {
|
||
+
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapPtrType"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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 MapPtrType[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+
|
||
+ return errs
|
||
+}
|
||
+
|
||
+// Validate_MapPtrTypedefType validates an instance of MapPtrTypedefType according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_MapPtrTypedefType(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj MapPtrTypedefType) (errs field.ErrorList) {
|
||
+
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type MapPtrTypedefType"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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 MapPtrTypedefType[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, Validate_StringType); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+
|
||
+ return errs
|
||
+}
|
||
+
|
||
// Validate_MapType validates an instance of MapType according
|
||
// to declarative validation rules in the API schema.
|
||
func Validate_MapType(
|
||
@@ -188,5 +231,88 @@ func Validate_Struct(
|
||
errs = append(errs, fn(fldPath.Child("mapTypedefField"), obj.MapTypedefField, oldVal, oldObj != nil)...)
|
||
}
|
||
|
||
+ // field Struct.UnvalidatedMapField has no validation
|
||
+
|
||
+ { // field Struct.MapPtrField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj MapPtrType,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, string](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // call the type's validation function
|
||
+ errs = append(errs, Validate_MapPtrType(ctx, op, fldPath, obj, oldObj)...)
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) MapPtrType {
|
||
+ return oldObj.MapPtrField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrField"), obj.MapPtrField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field Struct.MapPtrTypedefField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj MapPtrTypedefType,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, StringType](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapPtrTypedefField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPtrTypedefField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // call the type's validation function
|
||
+ errs = append(errs, Validate_MapPtrTypedefType(ctx, op, fldPath, obj, oldObj)...)
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) MapPtrTypedefType {
|
||
+ return oldObj.MapPtrTypedefField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPtrTypedefField"), obj.MapPtrTypedefField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ // field Struct.UnvalidatedMapPtrField has no validation
|
||
return errs
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc.go
|
||
new file mode 100644
|
||
index 0000000000000..74beb7963a20c
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc.go
|
||
@@ -0,0 +1,54 @@
|
||
+/*
|
||
+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.
|
||
+*/
|
||
+
|
||
+// +k8s:validation-gen=TypesWithField=TypeMeta
|
||
+// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||
+// +k8s:validation-gen-deep-equal-func=k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func.CustomDeepEqual
|
||
+
|
||
+// This is a test package.
|
||
+// +k8s:validation-gen-nolint
|
||
+package deepequalfunc
|
||
+
|
||
+import (
|
||
+ "reflect"
|
||
+
|
||
+ "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||
+)
|
||
+
|
||
+var localSchemeBuilder = testscheme.New()
|
||
+
|
||
+var CustomDeepEqualCalls int
|
||
+
|
||
+func CustomDeepEqual(a, b any) bool {
|
||
+ CustomDeepEqualCalls++
|
||
+ return reflect.DeepEqual(a, b)
|
||
+}
|
||
+
|
||
+type Struct struct {
|
||
+ TypeMeta int
|
||
+
|
||
+ // +k8s:validateFalse="field Struct.NonComparableField"
|
||
+ NonComparableField NonComparableStruct `json:"nonComparableField"`
|
||
+
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapField[*]"
|
||
+ MapField map[string]NonComparableStruct `json:"mapField"`
|
||
+}
|
||
+
|
||
+// +k8s:validateFalse="type NonComparableStruct"
|
||
+type NonComparableStruct struct {
|
||
+ // Ptr makes it not direct-comparable
|
||
+ Ptr *int `json:"ptr"`
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc_test.go
|
||
new file mode 100644
|
||
index 0000000000000..595e71d26c1bd
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/doc_test.go
|
||
@@ -0,0 +1,71 @@
|
||
+/*
|
||
+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.
|
||
+*/
|
||
+
|
||
+package deepequalfunc
|
||
+
|
||
+import (
|
||
+ "testing"
|
||
+)
|
||
+
|
||
+func TestDeepEqualFunc(t *testing.T) {
|
||
+ st := localSchemeBuilder.Test(t)
|
||
+
|
||
+ // Create
|
||
+ val1 := 10
|
||
+ obj := &Struct{
|
||
+ NonComparableField: NonComparableStruct{Ptr: &val1},
|
||
+ MapField: map[string]NonComparableStruct{
|
||
+ "a": {Ptr: &val1},
|
||
+ },
|
||
+ }
|
||
+ st.Value(obj).ExpectValidateFalseByPath(map[string][]string{
|
||
+ "nonComparableField": {"field Struct.NonComparableField", "type NonComparableStruct"},
|
||
+ "mapField[a]": {"field Struct.MapField[*]", "type NonComparableStruct"},
|
||
+ })
|
||
+
|
||
+ // Update with same values should ratchet (skip validation) using CustomDeepEqual
|
||
+ CustomDeepEqualCalls = 0
|
||
+ oldObj := &Struct{
|
||
+ NonComparableField: NonComparableStruct{Ptr: &val1},
|
||
+ MapField: map[string]NonComparableStruct{
|
||
+ "a": {Ptr: &val1},
|
||
+ },
|
||
+ }
|
||
+ newObj := &Struct{
|
||
+ NonComparableField: NonComparableStruct{Ptr: &val1},
|
||
+ MapField: map[string]NonComparableStruct{
|
||
+ "a": {Ptr: &val1},
|
||
+ },
|
||
+ }
|
||
+ st.Value(newObj).OldValue(oldObj).ExpectValid()
|
||
+ if CustomDeepEqualCalls == 0 {
|
||
+ t.Errorf("expected CustomDeepEqual to be called during update ratcheting, got %d calls", CustomDeepEqualCalls)
|
||
+ }
|
||
+
|
||
+ // Update with a new map key added: old key "a" ratchets via deepEqualImpl_ -> CustomDeepEqual,
|
||
+ // only new key "b" fails.
|
||
+ val2 := 20
|
||
+ updateObj := &Struct{
|
||
+ NonComparableField: NonComparableStruct{Ptr: &val1},
|
||
+ MapField: map[string]NonComparableStruct{
|
||
+ "a": {Ptr: &val1},
|
||
+ "b": {Ptr: &val2},
|
||
+ },
|
||
+ }
|
||
+ st.Value(updateObj).OldValue(oldObj).ExpectValidateFalseByPath(map[string][]string{
|
||
+ "mapField[b]": {"field Struct.MapField[*]", "type NonComparableStruct"},
|
||
+ })
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/zz_generated.validations.go
|
||
new file mode 100644
|
||
index 0000000000000..2ec646bb0e067
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/deep_equal_func/zz_generated.validations.go
|
||
@@ -0,0 +1,143 @@
|
||
+//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 deepequalfunc
|
||
+
|
||
+import (
|
||
+ context "context"
|
||
+ fmt "fmt"
|
||
+
|
||
+ 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 {
|
||
+ // type Struct
|
||
+ scheme.AddValidationFunc(
|
||
+ (*Struct)(nil),
|
||
+ func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||
+ switch op.Request.SubresourcePath() {
|
||
+ case "/":
|
||
+ return Validate_Struct(
|
||
+ ctx, op, nil, /* fldPath */
|
||
+ obj.(*Struct),
|
||
+ safe.Cast[*Struct](oldObj))
|
||
+ }
|
||
+ return field.ErrorList{
|
||
+ field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
|
||
+ }
|
||
+ })
|
||
+ return nil
|
||
+}
|
||
+
|
||
+// Validate_NonComparableStruct validates an instance of NonComparableStruct according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_NonComparableStruct(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj *NonComparableStruct) (errs field.ErrorList) {
|
||
+
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type NonComparableStruct"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+
|
||
+ // field NonComparableStruct.Ptr has no validation
|
||
+ return errs
|
||
+}
|
||
+
|
||
+// Validate_Struct validates an instance of Struct according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_Struct(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj *Struct) (errs field.ErrorList) {
|
||
+
|
||
+ // field Struct.TypeMeta has no validation
|
||
+
|
||
+ { // field Struct.NonComparableField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj *NonComparableStruct,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if CustomDeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.NonComparableField"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // call the type's validation function
|
||
+ errs = append(errs, Validate_NonComparableStruct(ctx, op, fldPath, obj, oldObj)...)
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) *NonComparableStruct {
|
||
+ return &oldObj.NonComparableField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("nonComparableField"), &obj.NonComparableField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field Struct.MapField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]NonComparableStruct,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if CustomDeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_,
|
||
+ func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStruct) field.ErrorList {
|
||
+ return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.MapField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_NonComparableStruct); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]NonComparableStruct {
|
||
+ return oldObj.MapField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapField"), obj.MapField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ return errs
|
||
+}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return CustomDeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/list/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/list/zz_generated.validations.go
|
||
index eb7d8cd0c62dd..9b6ecf48a5b03 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/list/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/list/zz_generated.validations.go
|
||
@@ -133,7 +133,7 @@ func Validate_ItemList(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *Item, b *Item) bool { return a.Key == b.Key }, validate.SemanticDeepEqual, Validate_Item); len(e) != 0 {
|
||
+ func(a *Item, b *Item) bool { return a.Key == b.Key }, deepEqualImpl_, Validate_Item); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -371,18 +371,18 @@ func Validate_StructSlice(
|
||
}
|
||
}
|
||
// call field-attached validations
|
||
- if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, nil,
|
||
+ if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, nil,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field SetSliceNonComparableField[*]")
|
||
}); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
- if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, nil, Validate_NonComparableStruct); len(e) != 0 {
|
||
+ if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, nil, Validate_NonComparableStruct); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -440,7 +440,7 @@ func Validate_StructSlice(
|
||
}
|
||
// call field-attached validations
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *NonComparableStructWithKey, b *NonComparableStructWithKey) bool { return a.Key == b.Key }, validate.SemanticDeepEqual,
|
||
+ func(a *NonComparableStructWithKey, b *NonComparableStructWithKey) bool { return a.Key == b.Key }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStructWithKey) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field MapSliceNonComparableField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -453,7 +453,7 @@ func Validate_StructSlice(
|
||
}
|
||
// iterate the list and call the type's validation function
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *NonComparableStructWithKey, b *NonComparableStructWithKey) bool { return a.Key == b.Key }, validate.SemanticDeepEqual, Validate_NonComparableStructWithKey); len(e) != 0 {
|
||
+ func(a *NonComparableStructWithKey, b *NonComparableStructWithKey) bool { return a.Key == b.Key }, deepEqualImpl_, Validate_NonComparableStructWithKey); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -480,7 +480,7 @@ func Validate_StructSlice(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *PtrKeyStruct, b *PtrKeyStruct) bool {
|
||
return ((a.Key == nil && b.Key == nil) || (a.Key != nil && b.Key != nil && *a.Key == *b.Key))
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *PtrKeyStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field MapSlicePtrKeyField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -497,7 +497,7 @@ func Validate_StructSlice(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *PtrKeyStruct, b *PtrKeyStruct) bool {
|
||
return ((a.Key == nil && b.Key == nil) || (a.Key != nil && b.Key != nil && *a.Key == *b.Key))
|
||
- }, validate.SemanticDeepEqual, Validate_PtrKeyStruct); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_PtrKeyStruct); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -524,7 +524,7 @@ func Validate_StructSlice(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *MixedKeyStruct, b *MixedKeyStruct) bool {
|
||
return ((a.Key1 == nil && b.Key1 == nil) || (a.Key1 != nil && b.Key1 != nil && *a.Key1 == *b.Key1)) && a.Key2 == b.Key2
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MixedKeyStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field MapSliceMixedKeyField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -541,7 +541,7 @@ func Validate_StructSlice(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *MixedKeyStruct, b *MixedKeyStruct) bool {
|
||
return ((a.Key1 == nil && b.Key1 == nil) || (a.Key1 != nil && b.Key1 != nil && *a.Key1 == *b.Key1)) && a.Key2 == b.Key2
|
||
- }, validate.SemanticDeepEqual, Validate_MixedKeyStruct); len(e) != 0 {
|
||
+ }, deepEqualImpl_, Validate_MixedKeyStruct); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -555,3 +555,8 @@ func Validate_StructSlice(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/maps/eachval/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/maps/eachval/zz_generated.validations.go
|
||
index e9364e922abcb..183797afbb0c9 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/maps/eachval/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/ratcheting/maps/eachval/zz_generated.validations.go
|
||
@@ -157,7 +157,7 @@ func Validate_StructWithMaps(
|
||
}
|
||
}
|
||
// call field-attached validations
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual,
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field MapTest.MapNonComparableStructField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -174,3 +174,8 @@ func Validate_StructWithMaps(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
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 8dea5b21bba98..0c9ae1e747285 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
|
||
@@ -193,7 +193,7 @@ func Validate_T1(
|
||
}
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T5); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T5); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -217,7 +217,7 @@ func Validate_T1(
|
||
}
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T6); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T6); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -250,7 +250,7 @@ func Validate_T2(
|
||
}
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T1); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T1); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -318,7 +318,7 @@ func Validate_T4(
|
||
}
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T3); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T3); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -355,7 +355,7 @@ func Validate_T5(
|
||
errs = append(errs, e...)
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T5); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T5); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -412,7 +412,7 @@ func Validate_T6(
|
||
}
|
||
}
|
||
// iterate the map and call the value type's validation function
|
||
- if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, Validate_T6); len(e) != 0 {
|
||
+ if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, Validate_T6); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -426,3 +426,8 @@ func Validate_T6(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc.go
|
||
new file mode 100644
|
||
index 0000000000000..dfa903970c25e
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc.go
|
||
@@ -0,0 +1,41 @@
|
||
+/*
|
||
+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.
|
||
+*/
|
||
+
|
||
+// +k8s:validation-gen=TypesWithField=TypeMeta
|
||
+// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||
+
|
||
+// This is a test package.
|
||
+// +k8s:validation-gen-nolint
|
||
+package mapofpointers
|
||
+
|
||
+import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||
+
|
||
+var localSchemeBuilder = testscheme.New()
|
||
+
|
||
+type Struct struct {
|
||
+ TypeMeta int
|
||
+
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapField[*]"
|
||
+ MapField map[string]*OtherStruct `json:"mapField"`
|
||
+
|
||
+ // +k8s:eachVal=+k8s:validateFalse="field Struct.MapPrimitiveField[*]"
|
||
+ MapPrimitiveField map[string]*string `json:"mapPrimitiveField"`
|
||
+}
|
||
+
|
||
+// +k8s:validateFalse="type OtherStruct"
|
||
+type OtherStruct struct {
|
||
+ Value string `json:"value"`
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc_test.go
|
||
new file mode 100644
|
||
index 0000000000000..b9e93fe5666a8
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/doc_test.go
|
||
@@ -0,0 +1,52 @@
|
||
+/*
|
||
+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.
|
||
+*/
|
||
+
|
||
+package mapofpointers
|
||
+
|
||
+import (
|
||
+ "testing"
|
||
+
|
||
+ "k8s.io/apimachinery/pkg/util/validation/field"
|
||
+)
|
||
+
|
||
+func Test(t *testing.T) {
|
||
+ st := localSchemeBuilder.Test(t)
|
||
+
|
||
+ // 1. Zero values should be valid
|
||
+ st.Value(&Struct{
|
||
+ // All zero values.
|
||
+ }).ExpectValid()
|
||
+
|
||
+ // 2. Non-nil elements trigger validation errors
|
||
+ st.Value(&Struct{
|
||
+ MapField: map[string]*OtherStruct{"k1": {}, "k2": {}},
|
||
+ MapPrimitiveField: map[string]*string{"k1": new("a"), "k2": new("b")},
|
||
+ }).ExpectValidateFalseByPath(map[string][]string{
|
||
+ "mapField[k1]": {"field Struct.MapField[*]", "type OtherStruct"},
|
||
+ "mapField[k2]": {"field Struct.MapField[*]", "type OtherStruct"},
|
||
+ "mapPrimitiveField[k1]": {"field Struct.MapPrimitiveField[*]"},
|
||
+ "mapPrimitiveField[k2]": {"field Struct.MapPrimitiveField[*]"},
|
||
+ })
|
||
+
|
||
+ // 3. Nil elements trigger Required errors from PtrMapNoNils
|
||
+ st.Value(&Struct{
|
||
+ MapField: map[string]*OtherStruct{"k1": nil},
|
||
+ MapPrimitiveField: map[string]*string{"k1": nil},
|
||
+ }).ExpectMatches(field.ErrorMatcher{}.ByType().ByField(), field.ErrorList{
|
||
+ field.Required(field.NewPath("mapField").Key("k1"), ""),
|
||
+ field.Required(field.NewPath("mapPrimitiveField").Key("k1"), ""),
|
||
+ })
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/zz_generated.validations.go
|
||
new file mode 100644
|
||
index 0000000000000..641eb0e3fc9b9
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/eachval/map_of_pointers/zz_generated.validations.go
|
||
@@ -0,0 +1,156 @@
|
||
+//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 mapofpointers
|
||
+
|
||
+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 {
|
||
+ // type Struct
|
||
+ scheme.AddValidationFunc(
|
||
+ (*Struct)(nil),
|
||
+ func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||
+ switch op.Request.SubresourcePath() {
|
||
+ case "/":
|
||
+ return Validate_Struct(
|
||
+ ctx, op, nil, /* fldPath */
|
||
+ obj.(*Struct),
|
||
+ safe.Cast[*Struct](oldObj))
|
||
+ }
|
||
+ return field.ErrorList{
|
||
+ field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath())),
|
||
+ }
|
||
+ })
|
||
+ return nil
|
||
+}
|
||
+
|
||
+// Validate_OtherStruct validates an instance of OtherStruct according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_OtherStruct(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj *OtherStruct) (errs field.ErrorList) {
|
||
+
|
||
+ if e := validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "type OtherStruct"); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+
|
||
+ // field OtherStruct.Value has no validation
|
||
+ return errs
|
||
+}
|
||
+
|
||
+// Validate_Struct validates an instance of Struct according
|
||
+// to declarative validation rules in the API schema.
|
||
+func Validate_Struct(
|
||
+ ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
+ obj, oldObj *Struct) (errs field.ErrorList) {
|
||
+
|
||
+ // field Struct.TypeMeta has no validation
|
||
+
|
||
+ { // field Struct.MapField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*OtherStruct,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, OtherStruct](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ // iterate the map and call the value type's validation function
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, Validate_OtherStruct); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*OtherStruct {
|
||
+ return oldObj.MapField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapField"), obj.MapField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field Struct.MapPrimitiveField
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*string,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, string](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ if e := validate.EachPtrMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual,
|
||
+ 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.MapPrimitiveField[*]")
|
||
+ }); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *Struct) map[string]*string {
|
||
+ return oldObj.MapPrimitiveField
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("mapPrimitiveField"), obj.MapPrimitiveField, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ return errs
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/multiple_keys/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/multiple_keys/zz_generated.validations.go
|
||
index 85cc57c4600f8..78c8f3462e128 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/multiple_keys/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/multiple_keys/zz_generated.validations.go
|
||
@@ -168,7 +168,7 @@ func Validate_Struct(
|
||
if e := validate.ValSliceItem(ctx, op, fldPath, obj, oldObj,
|
||
func(item *PtrItem) bool {
|
||
return item.StringKey != nil && *item.StringKey == "target-ptr" && item.IntKey == 42 && item.BoolKey == true
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *PtrItem) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "item PtrItems[stringKey=target-ptr,intKey=42,boolKey=true]")
|
||
}); len(e) != 0 {
|
||
@@ -207,7 +207,7 @@ func Validate_Struct(
|
||
if e := validate.ValSliceItem(ctx, op, fldPath, obj, oldObj,
|
||
func(item *MixedPtrItem) bool {
|
||
return item.StringPtrKey != nil && *item.StringPtrKey == "target-ptr" && item.StringKey == "target"
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MixedPtrItem) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "item MixedPtrItems")
|
||
}); len(e) != 0 {
|
||
@@ -225,3 +225,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/single_key/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/single_key/zz_generated.validations.go
|
||
index 870b19161765c..9556a0689460c 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/single_key/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/item/single_key/zz_generated.validations.go
|
||
@@ -303,7 +303,7 @@ func Validate_Struct(
|
||
}
|
||
func() { // cohort = "{"key": "target-ptr"}"
|
||
if e := validate.ValSliceItem(ctx, op, fldPath, obj, oldObj,
|
||
- func(item *PtrKeyItem) bool { return item.Key != nil && *item.Key == "target-ptr" }, validate.SemanticDeepEqual,
|
||
+ func(item *PtrKeyItem) bool { return item.Key != nil && *item.Key == "target-ptr" }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *PtrKeyItem) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "item PtrKeyItems[key=target-ptr]")
|
||
}); len(e) != 0 {
|
||
@@ -410,3 +410,8 @@ func Validate_StructWithNestedTypedef(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/levels/simple/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/levels/simple/zz_generated.validations.go
|
||
index 40c4c8a1d6ca0..c176df16c859d 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/levels/simple/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/levels/simple/zz_generated.validations.go
|
||
@@ -219,7 +219,8 @@ func Validate_SpecialValidationStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify).MarkAlpha().MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -248,7 +249,8 @@ func Validate_SpecialValidationStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkBeta().MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify).MarkBeta().MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
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 8e9c0e99c504f..bd1654a64c5f1 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
|
||
@@ -245,7 +245,7 @@ func Validate_Struct(
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
func(a *NonComparableStruct, b *NonComparableStruct) bool {
|
||
return a.Key1Field == b.Key1Field && a.Key2Field == b.Key2Field
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListNonComparableField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -290,7 +290,7 @@ func Validate_Struct(
|
||
if e := validate.ValSliceItem(ctx, op, fldPath, obj, oldObj,
|
||
func(item *PtrKeyStruct) bool {
|
||
return item.Key1Field != nil && *item.Key1Field == "target-ptr" && item.Key2Field == 42
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *PtrKeyStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "item ListPtrKeyField[key1Field=target-ptr,key2Field=42]")
|
||
}); len(e) != 0 {
|
||
@@ -329,7 +329,7 @@ func Validate_Struct(
|
||
if e := validate.ValSliceItem(ctx, op, fldPath, obj, oldObj,
|
||
func(item *MixedPtrKeyStruct) bool {
|
||
return item.StringPtrKey != nil && *item.StringPtrKey == "target-ptr" && item.StringKey == "target"
|
||
- }, validate.SemanticDeepEqual,
|
||
+ }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *MixedPtrKeyStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "item ListMixedPtrKeyField")
|
||
}); len(e) != 0 {
|
||
@@ -347,3 +347,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
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 79c4099dfb3e0..fb48c34721b80 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
|
||
@@ -227,7 +227,7 @@ func Validate_Struct(
|
||
}
|
||
// call field-attached validations
|
||
if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj,
|
||
- func(a *NonComparableStruct, b *NonComparableStruct) bool { return a.KeyField == b.KeyField }, validate.SemanticDeepEqual,
|
||
+ func(a *NonComparableStruct, b *NonComparableStruct) bool { return a.KeyField == b.KeyField }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *NonComparableStruct) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "field Struct.ListNonComparableField[*]")
|
||
}); len(e) != 0 {
|
||
@@ -277,3 +277,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listset/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listset/zz_generated.validations.go
|
||
index 8a8b743054e9d..77b155ffdda58 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listset/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/listset/zz_generated.validations.go
|
||
@@ -183,7 +183,7 @@ func Validate_ImmutableStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, nil, validate.Immutable).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, nil, validate.Immutable).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -191,7 +191,7 @@ func Validate_ImmutableStruct(
|
||
return // do not proceed
|
||
}
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -278,7 +278,7 @@ func Validate_ImmutableStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, nil, validate.Immutable).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, nil, validate.Immutable).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -286,7 +286,7 @@ func Validate_ImmutableStruct(
|
||
return // do not proceed
|
||
}
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -397,7 +397,7 @@ func Validate_Struct(
|
||
}
|
||
// call field-attached validations
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -422,7 +422,7 @@ func Validate_Struct(
|
||
}
|
||
// call field-attached validations
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -436,3 +436,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/mode/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/mode/zz_generated.validations.go
|
||
index 00cd01fb9ceae..d3968299c2140 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/mode/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/mode/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"
|
||
@@ -195,7 +196,7 @@ func Validate_Collections(
|
||
|
||
if e := validate.Discriminated(ctx, op, fldPath, obj, oldObj, "listField",
|
||
func(obj *Collections) []string { return obj.ListField },
|
||
- func(obj *Collections) string { return obj.D1 }, validate.SemanticDeepEqual,
|
||
+ func(obj *Collections) string { return obj.D1 }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj []string) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenSlice(ctx, op, fldPath, obj, oldObj)...)
|
||
@@ -222,7 +223,7 @@ func Validate_Collections(
|
||
}
|
||
if e := validate.Discriminated(ctx, op, fldPath, obj, oldObj, "mapField",
|
||
func(obj *Collections) map[string]string { return obj.MapField },
|
||
- func(obj *Collections) string { return obj.D1 }, validate.SemanticDeepEqual,
|
||
+ func(obj *Collections) string { return obj.D1 }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
errs := field.ErrorList{}
|
||
errs = append(errs, validate.ForbiddenMap(ctx, op, fldPath, obj, oldObj)...)
|
||
@@ -531,3 +532,8 @@ func Validate_StrictUnion(
|
||
// field StrictUnion.FieldB has no validation
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/monotonic/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/monotonic/zz_generated.validations.go
|
||
index ef8c3bc1ef004..120b2c9072d41 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/monotonic/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/monotonic/zz_generated.validations.go
|
||
@@ -258,7 +258,8 @@ func Validate_Struct(
|
||
if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
earlyReturn = true
|
||
}
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a int, b int) bool { return a == b }, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/options/update/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/options/update/zz_generated.validations.go
|
||
index 2134450e65b06..b96320a866dcd 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/options/update/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/options/update/zz_generated.validations.go
|
||
@@ -136,7 +136,8 @@ func Validate_Struct(
|
||
earlyReturn := false
|
||
if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", true,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||
- return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||
+ return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify)
|
||
}).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
@@ -168,7 +169,8 @@ func Validate_Struct(
|
||
earlyReturn := false
|
||
if e := validate.IfOption(ctx, op, fldPath, obj, oldObj, "FeatureX", false,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||
- return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||
+ return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify)
|
||
}).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/list/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/list/zz_generated.validations.go
|
||
index b9d799f3e64e2..de5a185c66a68 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/list/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/list/zz_generated.validations.go
|
||
@@ -79,7 +79,7 @@ func Validate_Struct(
|
||
// call field-attached validations
|
||
func() { // cohort = "labels"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "labels",
|
||
- func(o *v1.ObjectMeta) map[string]string { return o.Labels }, validate.SemanticDeepEqual,
|
||
+ func(o *v1.ObjectMeta) map[string]string { return o.Labels }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
return validate.EachMapKey(ctx, op, fldPath, obj, oldObj,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||
@@ -91,7 +91,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "ownerReferences"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "ownerReferences",
|
||
- func(o *v1.ObjectMeta) []v1.OwnerReference { return o.OwnerReferences }, validate.SemanticDeepEqual,
|
||
+ func(o *v1.ObjectMeta) []v1.OwnerReference { return o.OwnerReferences }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj []v1.OwnerReference) field.ErrorList {
|
||
return validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj,
|
||
func(a *v1.OwnerReference, b *v1.OwnerReference) bool { return a.UID == b.UID })
|
||
@@ -101,7 +101,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "finalizers"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "finalizers",
|
||
- func(o *v1.ObjectMeta) []string { return o.Finalizers }, validate.SemanticDeepEqual,
|
||
+ func(o *v1.ObjectMeta) []string { return o.Finalizers }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj []string) field.ErrorList {
|
||
return validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual)
|
||
}); len(e) != 0 {
|
||
@@ -119,3 +119,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
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 e9c369fdf7722..d9cf158b01398 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
|
||
@@ -112,7 +112,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "sliceField"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "sliceField",
|
||
- func(o *OtherStruct) []string { return o.SliceField }, validate.SemanticDeepEqual,
|
||
+ func(o *OtherStruct) []string { return o.SliceField }, deepEqualImpl_,
|
||
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.SliceField")
|
||
}); len(e) != 0 {
|
||
@@ -121,7 +121,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "mapField"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "mapField",
|
||
- func(o *OtherStruct) map[string]string { return o.MapField }, validate.SemanticDeepEqual,
|
||
+ func(o *OtherStruct) map[string]string { return o.MapField }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructField.MapField")
|
||
}); len(e) != 0 {
|
||
@@ -185,7 +185,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "sliceField"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "sliceField",
|
||
- func(o *OtherStruct) []string { return o.SliceField }, validate.SemanticDeepEqual,
|
||
+ func(o *OtherStruct) []string { return o.SliceField }, deepEqualImpl_,
|
||
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.SliceField")
|
||
}); len(e) != 0 {
|
||
@@ -194,7 +194,7 @@ func Validate_Struct(
|
||
}()
|
||
func() { // cohort = "mapField"
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "mapField",
|
||
- func(o *OtherStruct) map[string]string { return o.MapField }, validate.SemanticDeepEqual,
|
||
+ func(o *OtherStruct) map[string]string { return o.MapField }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield Struct.StructPtrField.MapField")
|
||
}); len(e) != 0 {
|
||
@@ -212,3 +212,8 @@ func Validate_Struct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shortcircuit/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shortcircuit/zz_generated.validations.go
|
||
index b21f8fbf63bcf..2c0282191a3d1 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shortcircuit/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/subfield/shortcircuit/zz_generated.validations.go
|
||
@@ -492,7 +492,7 @@ func Validate_ParentWithMaxItems(
|
||
func() { // cohort = "value"
|
||
earlyReturn := false
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||
- func(o *TargetWithMaxItems) []string { return o.Value }, validate.SemanticDeepEqual,
|
||
+ func(o *TargetWithMaxItems) []string { return o.Value }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj []string) field.ErrorList {
|
||
return validate.MaxItems(ctx, op, fldPath, obj, oldObj, 2)
|
||
}).MarkShortCircuit(); len(e) != 0 {
|
||
@@ -502,7 +502,7 @@ func Validate_ParentWithMaxItems(
|
||
return // do not proceed
|
||
}
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||
- func(o *TargetWithMaxItems) []string { return o.Value }, validate.SemanticDeepEqual,
|
||
+ func(o *TargetWithMaxItems) []string { return o.Value }, deepEqualImpl_,
|
||
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 ParentWithMaxItems.Field.Value")
|
||
}); len(e) != 0 {
|
||
@@ -546,7 +546,7 @@ func Validate_ParentWithMaxProperties(
|
||
func() { // cohort = "value"
|
||
earlyReturn := false
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||
- func(o *TargetWithMaxProperties) map[string]string { return o.Value }, validate.SemanticDeepEqual,
|
||
+ func(o *TargetWithMaxProperties) map[string]string { return o.Value }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
return validate.MaxProperties(ctx, op, fldPath, obj, oldObj, 2)
|
||
}).MarkShortCircuit(); len(e) != 0 {
|
||
@@ -556,7 +556,7 @@ func Validate_ParentWithMaxProperties(
|
||
return // do not proceed
|
||
}
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||
- func(o *TargetWithMaxProperties) map[string]string { return o.Value }, validate.SemanticDeepEqual,
|
||
+ func(o *TargetWithMaxProperties) map[string]string { return o.Value }, deepEqualImpl_,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj map[string]string) field.ErrorList {
|
||
return validate.FixedResult(ctx, op, fldPath, obj, oldObj, false, "subfield ParentWithMaxProperties.Field.Value")
|
||
}); len(e) != 0 {
|
||
@@ -1029,7 +1029,8 @@ func Validate_ParentWithUpdate(
|
||
if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "value",
|
||
func(o *TargetWithUpdate) *string { return &o.Value }, validate.DirectEqual,
|
||
func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList {
|
||
- return validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify)
|
||
+ return validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify)
|
||
}).MarkShortCircuit(); len(e) != 0 {
|
||
earlyReturn = true
|
||
}
|
||
@@ -1308,7 +1309,8 @@ func Validate_TargetWithUpdate(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -1326,3 +1328,8 @@ func Validate_TargetWithUpdate(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/lists/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/lists/zz_generated.validations.go
|
||
index 23dfb6e20603c..b0e1a4e3fb7f8 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/lists/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/lists/zz_generated.validations.go
|
||
@@ -426,7 +426,7 @@ func Validate_UpdateListStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.ValSliceUpdate(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, validate.NoAddItem, validate.NoRemoveItem).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.ValSliceUpdate(ctx, op, fldPath, obj, oldObj, deepEqualImpl_, validate.NoAddItem, validate.NoRemoveItem).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -434,7 +434,7 @@ func Validate_UpdateListStruct(
|
||
return // do not proceed
|
||
}
|
||
// lists with set semantics require unique values
|
||
- if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual); len(e) != 0 {
|
||
+ if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, deepEqualImpl_); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
}
|
||
return
|
||
@@ -627,3 +627,8 @@ func Validate_UpdateListStruct(
|
||
|
||
return errs
|
||
}
|
||
+
|
||
+// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.
|
||
+func deepEqualImpl_[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc.go
|
||
index 8737cf5e61f67..465888aea1f33 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc.go
|
||
@@ -52,6 +52,18 @@ type UpdateMapStruct struct {
|
||
|
||
// +k8s:eachVal=+k8s:update=NoModify
|
||
EachValNoModifyMap map[string]MapItem `json:"eachValNoModifyMap"`
|
||
+
|
||
+ // Pointer Map NoSet
|
||
+ // +k8s:update=NoSet
|
||
+ PointerMapNoSet map[string]*string `json:"pointerMapNoSet"`
|
||
+
|
||
+ // Pointer Map NoAdd
|
||
+ // +k8s:update=NoAddItem
|
||
+ PointerMapNoAdd map[string]*string `json:"pointerMapNoAdd"`
|
||
+
|
||
+ // Pointer Map NoRemove
|
||
+ // +k8s:update=NoRemoveItem
|
||
+ PointerMapNoRemove map[string]*MapItem `json:"pointerMapNoRemove"`
|
||
}
|
||
|
||
type MapItem struct {
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc_test.go
|
||
index bb5e84ded9b00..3917898a1c416 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc_test.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/doc_test.go
|
||
@@ -129,4 +129,60 @@ func TestUpdateMapTags(t *testing.T) {
|
||
field.Invalid(field.NewPath("eachValNoModifyMap").Key("a"), nil, "cannot be modified").WithOrigin("update"),
|
||
})
|
||
}
|
||
+
|
||
+ // Pointer Map NoSet
|
||
+ {
|
||
+ old := base
|
||
+ old.PointerMapNoSet = nil
|
||
+ cur := base
|
||
+ cur.PointerMapNoSet = map[string]*string{"a": new("1")}
|
||
+
|
||
+ st.Value(&cur).OldValue(&old).ExpectMatches(matcher, field.ErrorList{
|
||
+ field.Invalid(field.NewPath("pointerMapNoSet"), nil, "field cannot be set once created").WithOrigin("update"),
|
||
+ })
|
||
+
|
||
+ // nil -> nil is allowed
|
||
+ st.Value(&old).OldValue(&old).ExpectValid()
|
||
+ }
|
||
+
|
||
+ // Pointer Map NoAddItem
|
||
+ {
|
||
+ old := base
|
||
+ old.PointerMapNoAdd = map[string]*string{"a": new("1")}
|
||
+ cur := base
|
||
+ cur.PointerMapNoAdd = map[string]*string{"a": new("1"), "b": new("2")}
|
||
+
|
||
+ st.Value(&cur).OldValue(&old).ExpectMatches(matcher, field.ErrorList{
|
||
+ field.Forbidden(field.NewPath("pointerMapNoAdd").Key("b"), "item may not be added").WithOrigin("update"),
|
||
+ })
|
||
+ }
|
||
+
|
||
+ // Pointer Map NoRemoveItem
|
||
+ {
|
||
+ old := base
|
||
+ old.PointerMapNoRemove = map[string]*MapItem{
|
||
+ "a": {Name: "a", Value: "1"},
|
||
+ "b": {Name: "b", Value: "2"},
|
||
+ }
|
||
+ cur := base
|
||
+ cur.PointerMapNoRemove = map[string]*MapItem{
|
||
+ "a": {Name: "a", Value: "1"},
|
||
+ }
|
||
+
|
||
+ st.Value(&cur).OldValue(&old).ExpectMatches(matcher, field.ErrorList{
|
||
+ field.Forbidden(field.NewPath("pointerMapNoRemove").Key("b"), "item may not be removed").WithOrigin("update"),
|
||
+ })
|
||
+ }
|
||
+
|
||
+ // Nil element in pointer map on update should trigger Required error from PtrMapNoNils
|
||
+ {
|
||
+ old := base
|
||
+ old.PointerMapNoAdd = map[string]*string{"a": new("1")}
|
||
+ cur := base
|
||
+ cur.PointerMapNoAdd = map[string]*string{"a": nil}
|
||
+
|
||
+ st.Value(&cur).OldValue(&old).ExpectMatches(matcher, field.ErrorList{
|
||
+ field.Required(field.NewPath("pointerMapNoAdd").Key("a"), ""),
|
||
+ })
|
||
+ }
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/zz_generated.validations.go
|
||
index 38588d3eb9fbc..e471ab147021d 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/maps/zz_generated.validations.go
|
||
@@ -270,5 +270,104 @@ func Validate_UpdateMapStruct(
|
||
errs = append(errs, fn(fldPath.Child("eachValNoModifyMap"), obj.EachValNoModifyMap, oldVal, oldObj != nil)...)
|
||
}
|
||
|
||
+ { // field UpdateMapStruct.PointerMapNoSet
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*string,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, string](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if e := validate.UpdateMap(ctx, op, fldPath, obj, oldObj, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *UpdateMapStruct) map[string]*string {
|
||
+ return oldObj.PointerMapNoSet
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("pointerMapNoSet"), obj.PointerMapNoSet, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field UpdateMapStruct.PointerMapNoAdd
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*string,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, string](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if e := validate.UpdateMap(ctx, op, fldPath, obj, oldObj, validate.NoAddItem).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *UpdateMapStruct) map[string]*string {
|
||
+ return oldObj.PointerMapNoAdd
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("pointerMapNoAdd"), obj.PointerMapNoAdd, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
+ { // field UpdateMapStruct.PointerMapNoRemove
|
||
+ fn := func(
|
||
+ fldPath *field.Path,
|
||
+ obj, oldObj map[string]*MapItem,
|
||
+ oldValueCorrelated bool) (errs field.ErrorList) {
|
||
+ // don't revalidate unchanged data
|
||
+ if oldValueCorrelated && op.Type == operation.Update {
|
||
+ if equality.Semantic.DeepEqual(obj, oldObj) {
|
||
+ return nil
|
||
+ }
|
||
+ }
|
||
+ // call field-attached validations
|
||
+ earlyReturn := false
|
||
+ if e := validate.PtrMapNoNils[string, MapItem](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if e := validate.UpdateMap(ctx, op, fldPath, obj, oldObj, validate.NoRemoveItem).MarkShortCircuit(); len(e) != 0 {
|
||
+ errs = append(errs, e...)
|
||
+ earlyReturn = true
|
||
+ }
|
||
+ if earlyReturn {
|
||
+ return // do not proceed
|
||
+ }
|
||
+ return
|
||
+ }
|
||
+ oldVal := safe.Field(oldObj,
|
||
+ func(oldObj *UpdateMapStruct) map[string]*MapItem {
|
||
+ return oldObj.PointerMapNoRemove
|
||
+ })
|
||
+ errs = append(errs, fn(fldPath.Child("pointerMapNoRemove"), obj.PointerMapNoRemove, oldVal, oldObj != nil)...)
|
||
+ }
|
||
+
|
||
return errs
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/primitives/zz_generated.validations.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/primitives/zz_generated.validations.go
|
||
index 888cb51b17a87..61e82e9fdecb6 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/primitives/zz_generated.validations.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/output_tests/tags/update/primitives/zz_generated.validations.go
|
||
@@ -77,7 +77,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -106,7 +107,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoUnset).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -135,7 +137,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -164,7 +167,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoSet, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -193,7 +197,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -222,7 +227,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a int, b int) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -251,7 +257,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a int32, b int32) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -280,7 +287,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a int64, b int64) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -309,7 +317,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a uint, b uint) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -338,7 +347,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a bool, b bool) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -367,7 +377,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a float32, b float32) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -396,7 +407,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a float64, b float64) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -425,7 +437,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a byte, b byte) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -715,7 +728,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a CustomString, b CustomString) bool { return a == b }, validate.NoModify).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
@@ -744,7 +758,8 @@ func Validate_UpdateTestStruct(
|
||
}
|
||
// call field-attached validations
|
||
earlyReturn := false
|
||
- if e := validate.UpdateValueByCompare(ctx, op, fldPath, obj, oldObj, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||
+ if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj,
|
||
+ func(a CustomInt, b CustomInt) bool { return a == b }, validate.NoSet).MarkShortCircuit(); len(e) != 0 {
|
||
errs = append(errs, e...)
|
||
earlyReturn = true
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/targets.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/targets.go
|
||
index 29a676b040ddd..90692c7f40266 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/targets.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/targets.go
|
||
@@ -54,6 +54,9 @@ const (
|
||
// (e.g. in tests), or set to "nil" to disable scheme registration for this
|
||
// package.
|
||
schemeRegistryTagName = "k8s:validation-gen-scheme-registry"
|
||
+ // Defines the deep-equal function used for equivalence and ratcheting checks.
|
||
+ // Defaults to "k8s.io/apimachinery/pkg/api/equality.Semantic.DeepEqual".
|
||
+ deepEqualFuncTagName = "k8s:validation-gen-deep-equal-func"
|
||
// If set, generate go test files for test fixtures. Supported values: "validateFalse".
|
||
testFixtureTagName = "k8s:validation-gen-test-fixture"
|
||
|
||
@@ -69,10 +72,11 @@ const (
|
||
)
|
||
|
||
var (
|
||
- runtimePkg = "k8s.io/apimachinery/pkg/runtime"
|
||
- schemeType = types.Name{Package: runtimePkg, Name: "Scheme"}
|
||
- metav1Pkg = "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
- listMetaType = types.Name{Package: metav1Pkg, Name: "ListMeta"}
|
||
+ runtimePkg = "k8s.io/apimachinery/pkg/runtime"
|
||
+ schemeType = types.Name{Package: runtimePkg, Name: "Scheme"}
|
||
+ defaultDeepEqualFunc = types.Name{Package: "k8s.io/apimachinery/pkg/api/equality", Name: "Semantic.DeepEqual"}
|
||
+ metav1Pkg = "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
+ listMetaType = types.Name{Package: metav1Pkg, Name: "ListMeta"}
|
||
)
|
||
|
||
// extractAndParseTag extracts all the values for a given tag, according to the
|
||
@@ -157,6 +161,48 @@ func registerScheme(pkg *types.Package) bool {
|
||
return ok
|
||
}
|
||
|
||
+func deepEqualFuncTag(pkg *types.Package) types.Name {
|
||
+ // TODO: convert to extractAndParseTag() and update all callers to use quoted values
|
||
+ tags, err := gengo.ExtractFunctionStyleCommentTags("+", []string{deepEqualFuncTagName}, pkg.Comments)
|
||
+ if err != nil {
|
||
+ klog.Fatalf("Failed to extract deep equal func tags: %v", err)
|
||
+ }
|
||
+ values, found := tags[deepEqualFuncTagName]
|
||
+ if !found || len(values) == 0 {
|
||
+ return defaultDeepEqualFunc
|
||
+ }
|
||
+ if len(values) > 1 {
|
||
+ panic(fmt.Sprintf("Package %q contains more than one usage of %q", pkg.Path, deepEqualFuncTagName))
|
||
+ }
|
||
+ val := values[0].Value
|
||
+ if val == "" {
|
||
+ return defaultDeepEqualFunc
|
||
+ }
|
||
+ return parseDeepEqualFunc(val)
|
||
+}
|
||
+
|
||
+func parseDeepEqualFunc(val string) types.Name {
|
||
+ lastSlash := strings.LastIndex(val, "/")
|
||
+ if lastSlash == -1 {
|
||
+ dot := strings.Index(val, ".")
|
||
+ if dot == -1 {
|
||
+ return types.Name{Name: val}
|
||
+ }
|
||
+ return types.Name{
|
||
+ Package: val[:dot],
|
||
+ Name: val[dot+1:],
|
||
+ }
|
||
+ }
|
||
+ dot := lastSlash + strings.Index(val[lastSlash:], ".")
|
||
+ if dot == lastSlash {
|
||
+ return types.Name{Package: val}
|
||
+ }
|
||
+ return types.Name{
|
||
+ Package: val[:dot],
|
||
+ Name: val[dot+1:],
|
||
+ }
|
||
+}
|
||
+
|
||
func isSubresourceTag(t *types.Type) (string, bool) {
|
||
var comments []string
|
||
comments = append(comments, t.SecondClosestCommentLines...)
|
||
@@ -358,6 +404,7 @@ func GetTargets(context *generator.Context, args *Args) []generator.Target {
|
||
pkg := context.Universe[input]
|
||
|
||
schemeRegistry, registerThisPkg := schemeRegistryTag(pkg)
|
||
+ deepEqualFunc := deepEqualFuncTag(pkg)
|
||
|
||
criteria, found := validationTypeMatch(pkg, idOpts)
|
||
if !found {
|
||
@@ -485,7 +532,7 @@ func GetTargets(context *generator.Context, args *Args) []generator.Target {
|
||
|
||
GeneratorsFunc: func(c *generator.Context) (generators []generator.Generator) {
|
||
generators = []generator.Generator{
|
||
- NewGenValidations(args.OutputFile, pkg.Path, typesPkg.Path, rootTypes, td, inputToCanonicalPkg, schemeRegistry, registerThisPkg),
|
||
+ NewGenValidations(args.OutputFile, pkg.Path, typesPkg.Path, rootTypes, td, inputToCanonicalPkg, schemeRegistry, registerThisPkg, deepEqualFunc),
|
||
}
|
||
testFixtureTags := testFixtureTag(pkg)
|
||
if testFixtureTags.Len() > 0 {
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/targets_test.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/targets_test.go
|
||
new file mode 100644
|
||
index 0000000000000..ff67234ed3261
|
||
--- /dev/null
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/targets_test.go
|
||
@@ -0,0 +1,64 @@
|
||
+/*
|
||
+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.
|
||
+*/
|
||
+
|
||
+package main
|
||
+
|
||
+import (
|
||
+ "testing"
|
||
+
|
||
+ "k8s.io/gengo/v2/types"
|
||
+)
|
||
+
|
||
+func TestParseDeepEqualFunc(t *testing.T) {
|
||
+ testCases := []struct {
|
||
+ name string
|
||
+ input string
|
||
+ expected types.Name
|
||
+ }{{
|
||
+ name: "fully qualified with struct field",
|
||
+ input: "k8s.io/apimachinery/pkg/api/equality.Semantic.DeepEqual",
|
||
+ expected: types.Name{Package: "k8s.io/apimachinery/pkg/api/equality", Name: "Semantic.DeepEqual"},
|
||
+ }, {
|
||
+ name: "fully qualified package function",
|
||
+ input: "k8s.io/code-generator/cmd/validation-gen/testscheme.CustomEqual",
|
||
+ expected: types.Name{Package: "k8s.io/code-generator/cmd/validation-gen/testscheme", Name: "CustomEqual"},
|
||
+ }, {
|
||
+ name: "single level package function",
|
||
+ input: "testscheme.CustomEqual",
|
||
+ expected: types.Name{Package: "testscheme", Name: "CustomEqual"},
|
||
+ }, {
|
||
+ name: "single level package struct field",
|
||
+ input: "testscheme.Semantic.DeepEqual",
|
||
+ expected: types.Name{Package: "testscheme", Name: "Semantic.DeepEqual"},
|
||
+ }, {
|
||
+ name: "package-local function",
|
||
+ input: "CustomDeepEqual",
|
||
+ expected: types.Name{Package: "", Name: "CustomDeepEqual"},
|
||
+ }, {
|
||
+ name: "empty input",
|
||
+ input: "",
|
||
+ expected: types.Name{Package: "", Name: ""},
|
||
+ }}
|
||
+
|
||
+ for _, tc := range testCases {
|
||
+ t.Run(tc.name, func(t *testing.T) {
|
||
+ result := parseDeepEqualFunc(tc.input)
|
||
+ if want, got := tc.expected, result; got != want {
|
||
+ t.Errorf("expected %v, got %v", 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 16725be28ccb7..239d03a4a34cf 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
|
||
@@ -56,8 +56,6 @@ var (
|
||
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.
|
||
@@ -71,10 +69,12 @@ type genValidations struct {
|
||
imports namer.ImportTracker
|
||
schemeRegistry types.Name
|
||
emitRegisterFunc bool
|
||
+ deepEqualFunc types.Name
|
||
+ usedDeepEqualImpl bool
|
||
}
|
||
|
||
// NewGenValidations creates a new generator for the specified package.
|
||
-func NewGenValidations(outputFilename, outputPackage, inputPackage string, rootTypes []*types.Type, discovered *typeDiscoverer, inputToCanonicalPkg map[string]string, schemeRegistry types.Name, emitRegisterFunc bool) generator.Generator {
|
||
+func NewGenValidations(outputFilename, outputPackage, inputPackage string, rootTypes []*types.Type, discovered *typeDiscoverer, inputToCanonicalPkg map[string]string, schemeRegistry types.Name, emitRegisterFunc bool, deepEqualFunc types.Name) generator.Generator {
|
||
return &genValidations{
|
||
GoGenerator: generator.GoGenerator{
|
||
OutputFilename: outputFilename,
|
||
@@ -87,6 +87,7 @@ func NewGenValidations(outputFilename, outputPackage, inputPackage string, rootT
|
||
imports: generator.NewImportTrackerForPackage(outputPackage),
|
||
schemeRegistry: schemeRegistry,
|
||
emitRegisterFunc: emitRegisterFunc,
|
||
+ deepEqualFunc: deepEqualFunc,
|
||
}
|
||
}
|
||
|
||
@@ -169,6 +170,26 @@ func (g *genValidations) GenerateType(c *generator.Context, t *types.Type, w io.
|
||
return nil
|
||
}
|
||
|
||
+func (g *genValidations) Finalize(c *generator.Context, w io.Writer) error {
|
||
+ if g.usedDeepEqualImpl {
|
||
+ sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||
+ sw.Do("\n", nil)
|
||
+ sw.Do("// deepEqualImpl_ is a validate.MatchFunc which allows the implementation of deep-equality to be defined at codegen time.\n", nil)
|
||
+ sw.Do("func deepEqualImpl_[T any](a, b T) bool {\n", nil)
|
||
+ if g.deepEqualFunc.Package == "" {
|
||
+ sw.Do(" return $.$(a, b)\n", g.deepEqualFunc.Name)
|
||
+ } else {
|
||
+ targs := generator.Args{
|
||
+ "deepEqualFunc": c.Universe.Type(g.deepEqualFunc),
|
||
+ }
|
||
+ sw.Do(" return $.deepEqualFunc|raw$(a, b)\n", targs)
|
||
+ }
|
||
+ sw.Do("}\n", nil)
|
||
+ return sw.Error()
|
||
+ }
|
||
+ return nil
|
||
+}
|
||
+
|
||
// typeDiscoverer contains fields necessary to build graphs of types.
|
||
type typeDiscoverer struct {
|
||
initialized bool
|
||
@@ -670,8 +691,6 @@ func (td *typeDiscoverer) verifySupportedType(t *types.Type) error {
|
||
}
|
||
elem := util.NativeType(t.Elem)
|
||
switch elem.Kind {
|
||
- case types.Pointer:
|
||
- return fmt.Errorf("maps of pointers are not supported")
|
||
case types.Map:
|
||
return fmt.Errorf("maps of maps are not supported")
|
||
}
|
||
@@ -1289,23 +1308,30 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||
// need to be executed, but if no fields changed they can be
|
||
// skipped).
|
||
//
|
||
- // For the same reasons, in the specific case of lists of pointers,
|
||
+ // For the same reasons, in the specific case of lists and maps of pointers,
|
||
// we do not emit nil-checks inside type-specific validation
|
||
// functions. If we solve for the duplicative ratcheting, then we
|
||
// should also solve for nil-checks.
|
||
|
||
- if nt := util.NativeType(fld.childType); nt.Kind == types.Slice && nt.Elem.Kind == types.Pointer {
|
||
- hasFieldValidations := len(fld.fieldValidations.Functions) > 0
|
||
- hasPropagation := fld.node != nil && g.hasValidations(fld.node)
|
||
- hasKeyIterations := len(fld.fieldKeyIterations.Functions) > 0 && hasPropagation
|
||
- hasValIterations := len(fld.fieldValIterations.Functions) > 0 && hasPropagation
|
||
-
|
||
- if hasFieldValidations || hasPropagation || hasKeyIterations || hasValIterations {
|
||
- // Prepend the nil-check so it runs before any other
|
||
- // validations. This is important because the other
|
||
- // validations may assume that the slice has no nil values.
|
||
- nilCheck := validators.PtrSliceNoNils(nt.Elem.Elem.Name)
|
||
- fld.fieldValidations.Functions = append([]validators.FunctionGen{nilCheck}, fld.fieldValidations.Functions...)
|
||
+ if nt := util.NativeType(fld.childType); nt.Kind == types.Slice || nt.Kind == types.Map {
|
||
+ if nt.Elem.Kind == types.Pointer {
|
||
+ hasFieldValidations := len(fld.fieldValidations.Functions) > 0
|
||
+ hasPropagation := fld.node != nil && g.hasValidations(fld.node)
|
||
+ hasKeyIterations := len(fld.fieldKeyIterations.Functions) > 0 && hasPropagation
|
||
+ hasValIterations := len(fld.fieldValIterations.Functions) > 0 && hasPropagation
|
||
+ if hasFieldValidations || hasPropagation || hasKeyIterations || hasValIterations {
|
||
+ // Prepend the nil-check so it runs before any other
|
||
+ // validations. This is important because the other
|
||
+ // validations may assume that the map or slice has no nil
|
||
+ // values.
|
||
+ var nilCheck validators.FunctionGen
|
||
+ if nt.Kind == types.Slice {
|
||
+ nilCheck = validators.PtrSliceNoNils(nt.Elem.Elem.Name)
|
||
+ } else {
|
||
+ nilCheck = validators.PtrMapNoNils(nt.Key.Name, nt.Elem.Elem.Name)
|
||
+ }
|
||
+ fld.fieldValidations.Functions = append([]validators.FunctionGen{nilCheck}, fld.fieldValidations.Functions...)
|
||
+ }
|
||
}
|
||
}
|
||
|
||
@@ -1314,7 +1340,7 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||
if !validations.Empty() {
|
||
emitComments(validations.Comments, bufsw)
|
||
if len(validations.Functions) > 0 {
|
||
- emitRatchetingCheck(c, fld.childType, bufsw)
|
||
+ g.emitRatchetingCheck(c, fld.childType, bufsw)
|
||
fldRatchetingChecked = true
|
||
bufsw.Do("// call field-attached validations\n", nil)
|
||
g.emitCallsToValidators(c, validations.Functions, bufsw)
|
||
@@ -1332,7 +1358,7 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||
// (transitively) then we don't need to do anything.
|
||
if g.hasValidations(fld.node) {
|
||
if !fldRatchetingChecked {
|
||
- emitRatchetingCheck(c, fld.childType, bufsw)
|
||
+ g.emitRatchetingCheck(c, fld.childType, bufsw)
|
||
fldRatchetingChecked = true
|
||
}
|
||
g.emitCallToOtherTypeFunc(c, fld.node, bufsw)
|
||
@@ -1347,7 +1373,7 @@ func (g *genValidations) emitValidationForChild(c *generator.Context, thisChild
|
||
emitComments(iterations.Comments, bufsw)
|
||
if len(iterations.Functions) > 0 {
|
||
if !fldRatchetingChecked {
|
||
- emitRatchetingCheck(c, fld.childType, bufsw)
|
||
+ g.emitRatchetingCheck(c, fld.childType, bufsw)
|
||
fldRatchetingChecked = true
|
||
}
|
||
g.emitCallsToValidators(c, iterations.Functions, bufsw)
|
||
@@ -1448,7 +1474,7 @@ func (g *genValidations) emitCallToOtherTypeFunc(c *generator.Context, node *typ
|
||
}
|
||
|
||
// emitRatchetingCheck emits an equivalence check for default ratcheting.
|
||
-func emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.SnippetWriter) {
|
||
+func (g *genValidations) emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.SnippetWriter) {
|
||
// Emit equivalence check for default ratcheting.
|
||
targs := generator.Args{
|
||
"operation": mkSymbolArgs(c, operationPkgSymbols),
|
||
@@ -1467,8 +1493,12 @@ func emitRatchetingCheck(c *generator.Context, t *types.Type, sw *generator.Snip
|
||
// - *obj == *oldObj : compare values
|
||
sw.Do(" if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {\n", targs)
|
||
} else {
|
||
- targs["equality"] = mkSymbolArgs(c, equalityPkgSymbols)
|
||
- sw.Do(" if $.equality.Semantic|raw$.DeepEqual(obj, oldObj) {\n", targs)
|
||
+ if g.deepEqualFunc.Package == "" {
|
||
+ sw.Do(" if $.$(obj, oldObj) {\n", g.deepEqualFunc.Name)
|
||
+ } else {
|
||
+ targs["deepEqualFunc"] = c.Universe.Type(g.deepEqualFunc)
|
||
+ sw.Do(" if $.deepEqualFunc|raw$(obj, oldObj) {\n", targs)
|
||
+ }
|
||
}
|
||
sw.Do(" return nil\n", nil)
|
||
sw.Do(" }\n", nil)
|
||
@@ -1755,9 +1785,23 @@ func (g *genValidations) toGolangSourceDataLiteral(sw *generator.SnippetWriter,
|
||
case *types.Member:
|
||
sw.Do("obj."+v.Name, nil)
|
||
case validators.Identifier:
|
||
- sw.Do("$.|raw$", c.Universe.Type(types.Name(v)))
|
||
+ if types.Name(v) == validators.DeepEqualImpl {
|
||
+ g.usedDeepEqualImpl = true
|
||
+ }
|
||
+ if v.Package == "" {
|
||
+ sw.Do(v.Name, nil)
|
||
+ } else {
|
||
+ sw.Do("$.|raw$", c.Universe.Type(types.Name(v)))
|
||
+ }
|
||
case *validators.Identifier:
|
||
- sw.Do("$.|raw$", c.Universe.Type(types.Name(*v)))
|
||
+ if types.Name(*v) == validators.DeepEqualImpl {
|
||
+ g.usedDeepEqualImpl = true
|
||
+ }
|
||
+ if v.Package == "" {
|
||
+ sw.Do(v.Name, nil)
|
||
+ } else {
|
||
+ sw.Do("$.|raw$", c.Universe.Type(types.Name(*v)))
|
||
+ }
|
||
case validators.PrivateVar:
|
||
sw.Do("$.|private$", c.Universe.Type(types.Name(v)))
|
||
case *validators.PrivateVar:
|
||
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 e5e5fc30a54b2..75709c87d33fb 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
|
||
@@ -70,6 +70,7 @@ var (
|
||
validateEachValSliceVal = types.Name{Package: libValidationPkg, Name: "EachValSliceVal"}
|
||
validateEachPtrSliceVal = types.Name{Package: libValidationPkg, Name: "EachPtrSliceVal"}
|
||
validateEachMapVal = types.Name{Package: libValidationPkg, Name: "EachMapVal"}
|
||
+ validateEachPtrMapVal = types.Name{Package: libValidationPkg, Name: "EachPtrMapVal"}
|
||
)
|
||
|
||
func (evtv eachValTagValidator) GetValidations(context Context, tag codetags.Tag) (Validations, error) {
|
||
@@ -240,10 +241,16 @@ func (evtv eachValTagValidator) getMapValidations(t *types.Type, validations Val
|
||
if util.IsDirectComparable(util.NonPointer(util.NativeType(nt.Elem))) {
|
||
equivArg = Identifier(validateDirectEqual)
|
||
}
|
||
+
|
||
+ validateFunc := validateEachMapVal
|
||
+ if nt.Elem.Kind == types.Pointer {
|
||
+ validateFunc = validateEachPtrMapVal
|
||
+ }
|
||
+
|
||
wrapped := WrapFunctions(validations, func(vfn FunctionGen, _ DeferredScope) FunctionGen {
|
||
comm := vfn.Comments
|
||
vfn.Comments = nil
|
||
- return Function(eachValTagName, vfn.Flags, validateEachMapVal, equivArg, WrapperFunction{Function: vfn, ObjType: nt.Elem, PathFragment: "[*]"}).WithComments(comm...)
|
||
+ return Function(eachValTagName, vfn.Flags, validateFunc, equivArg, WrapperFunction{Function: vfn, ObjType: nt.Elem, PathFragment: "[*]"}).WithComments(comm...)
|
||
})
|
||
return Validations{
|
||
Functions: wrapped.Functions,
|
||
@@ -387,6 +394,7 @@ func (ektv eachKeyTagValidator) Docs() TagDoc {
|
||
|
||
var (
|
||
validatePtrSliceNoNils = types.Name{Package: libValidationPkg, Name: "PtrSliceNoNils"}
|
||
+ validatePtrMapNoNils = types.Name{Package: libValidationPkg, Name: "PtrMapNoNils"}
|
||
)
|
||
|
||
// PtrSliceNoNils returns a synthetic validation that rejects nil elements of a
|
||
@@ -394,3 +402,9 @@ var (
|
||
func PtrSliceNoNils(elemType types.Name) FunctionGen {
|
||
return Function("PtrSliceNoNils", ShortCircuit, validatePtrSliceNoNils).WithTypeArgs(elemType)
|
||
}
|
||
+
|
||
+// PtrMapNoNils returns a synthetic validation that rejects nil elements of a
|
||
+// pointer map. It is not tag-driven; the generator injects it for map[K]*V fields.
|
||
+func PtrMapNoNils(keyType, elemType types.Name) FunctionGen {
|
||
+ return Function("PtrMapNoNils", ShortCircuit, validatePtrMapNoNils).WithTypeArgs(keyType, elemType)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/list.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/list.go
|
||
index adb25196f9a84..16103556a3560 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/list.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/list.go
|
||
@@ -408,7 +408,7 @@ func (cutv customUniqueTagValidator) Docs() TagDoc {
|
||
var (
|
||
validateValSliceUnique = types.Name{Package: libValidationPkg, Name: "ValSliceUnique"}
|
||
validatePtrSliceUnique = types.Name{Package: libValidationPkg, Name: "PtrSliceUnique"}
|
||
- validateSemanticDeepEqual = types.Name{Package: libValidationPkg, Name: "SemanticDeepEqual"}
|
||
+ validateSemanticDeepEqual = DeepEqualImpl
|
||
validateDirectEqual = types.Name{Package: libValidationPkg, Name: "DirectEqual"}
|
||
)
|
||
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/update.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/update.go
|
||
index d883e20c1caaf..f0cfd3264db4d 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/update.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/update.go
|
||
@@ -199,13 +199,12 @@ func (utc updateTagCollector) Docs() TagDoc {
|
||
}
|
||
|
||
var (
|
||
- updateValueValidator = types.Name{Package: libValidationPkg, Name: "UpdateValueByCompare"}
|
||
- updatePointerValidator = types.Name{Package: libValidationPkg, Name: "UpdatePointer"}
|
||
- updateValueByReflectValidator = types.Name{Package: libValidationPkg, Name: "UpdateValueByReflect"}
|
||
- updateStructValidator = types.Name{Package: libValidationPkg, Name: "UpdateStruct"}
|
||
- valSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "ValSliceUpdate"}
|
||
- ptrSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "PtrSliceUpdate"}
|
||
- updateMapValidator = types.Name{Package: libValidationPkg, Name: "UpdateMap"}
|
||
+ updateValueValidator = types.Name{Package: libValidationPkg, Name: "UpdateValue"}
|
||
+ updatePointerValidator = types.Name{Package: libValidationPkg, Name: "UpdatePointer"}
|
||
+ updateStructValidator = types.Name{Package: libValidationPkg, Name: "UpdateStruct"}
|
||
+ valSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "ValSliceUpdate"}
|
||
+ ptrSliceUpdateValidator = types.Name{Package: libValidationPkg, Name: "PtrSliceUpdate"}
|
||
+ updateMapValidator = types.Name{Package: libValidationPkg, Name: "UpdateMap"}
|
||
|
||
// Constraint constants that will be used as arguments
|
||
noSetConstraint = types.Name{Package: libValidationPkg, Name: "NoSet"}
|
||
@@ -314,9 +313,8 @@ func generateMapValidation(constraints []validate.UpdateConstraint) Validations
|
||
return Validations{Functions: []FunctionGen{fn}}
|
||
}
|
||
|
||
-// emitScalarUpdate emits a call to
|
||
-// UpdateValueByCompare/UpdatePointer/UpdateStruct/UpdateValueByReflect based
|
||
-// on the Go kind of context.Type.
|
||
+// emitScalarUpdate emits a call to UpdateValue/UpdatePointer/UpdateStruct
|
||
+// based on the Go kind of context.Type.
|
||
// Used for both field scope (scalar/pointer/struct) and list/map element
|
||
// scope via +k8s:eachVal.
|
||
func emitScalarUpdate(context Context, constraints []validate.UpdateConstraint) Validations {
|
||
@@ -327,21 +325,33 @@ func emitScalarUpdate(context Context, constraints []validate.UpdateConstraint)
|
||
isComparable := util.IsDirectComparable(t)
|
||
|
||
var validatorFunc types.Name
|
||
+ var args []any
|
||
if isPointer {
|
||
validatorFunc = updatePointerValidator
|
||
+ args = constraintIdentifierArgs(constraints)
|
||
} else if isStruct {
|
||
validatorFunc = updateStructValidator
|
||
- } else if isComparable {
|
||
- validatorFunc = updateValueValidator
|
||
+ args = constraintIdentifierArgs(constraints)
|
||
} else {
|
||
- validatorFunc = updateValueByReflectValidator
|
||
+ validatorFunc = updateValueValidator
|
||
+ var matchArg any
|
||
+ if isComparable {
|
||
+ matchArg = FunctionLiteral{
|
||
+ Parameters: []ParamResult{{"a", context.Type}, {"b", context.Type}},
|
||
+ Results: []ParamResult{{"", types.Bool}},
|
||
+ Body: "return a == b",
|
||
+ }
|
||
+ } else {
|
||
+ matchArg = Identifier(validateSemanticDeepEqual)
|
||
+ }
|
||
+ args = append([]any{matchArg}, constraintIdentifierArgs(constraints)...)
|
||
}
|
||
|
||
// Use ShortCircuit flag so these run in the same group as +k8s:optional.
|
||
// Scalar/pointer/struct fields only accept NoSet/NoUnset/NoModify
|
||
// (validateConstraintsForType rejects the rest), all of which emit
|
||
// field.Invalid at the field path.
|
||
- fn := Function(updateTagName, ShortCircuit, validatorFunc, constraintIdentifierArgs(constraints)...).
|
||
+ fn := Function(updateTagName, ShortCircuit, validatorFunc, args...).
|
||
WithEmits(Emission{field.ErrorTypeInvalid, "update", ""})
|
||
return Validations{Functions: []FunctionGen{fn}}
|
||
}
|
||
diff --git a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go
|
||
index 196c94bbeb1f1..235897dddc26c 100644
|
||
--- a/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go
|
||
+++ b/staging/src/k8s.io/code-generator/cmd/validation-gen/validators/validators.go
|
||
@@ -26,6 +26,10 @@ import (
|
||
"k8s.io/gengo/v2/types"
|
||
)
|
||
|
||
+var (
|
||
+ DeepEqualImpl = types.Name{Name: "deepEqualImpl_"}
|
||
+)
|
||
+
|
||
// TagValidator describes a single validation tag and how to use it. To be
|
||
// findable by validation-gen, a TagValidator must be registered - see
|
||
// RegisterTagValidator.
|
||
diff --git a/staging/src/k8s.io/code-generator/pkg/apidefinitions/activation_test.go b/staging/src/k8s.io/code-generator/pkg/apidefinitions/activation_test.go
|
||
index fa7c8e29f6408..c4d35d8d5f227 100644
|
||
--- a/staging/src/k8s.io/code-generator/pkg/apidefinitions/activation_test.go
|
||
+++ b/staging/src/k8s.io/code-generator/pkg/apidefinitions/activation_test.go
|
||
@@ -178,6 +178,7 @@ func TestIdentify(t *testing.T) {
|
||
"+k8s:defaulter-gen-input=k8s.io/api/foo/v1",
|
||
"+k8s:validation-gen=TypesWithField=TypeMeta",
|
||
"+k8s:validation-gen-input=k8s.io/api/foo/v1",
|
||
+ "+k8s:validation-gen-deep-equal-func=foo",
|
||
"+k8s:validation-gen-nolint",
|
||
"+k8s:validation-gen-scheme-registry=foo",
|
||
"+k8s:validation-gen-test-fixture=bar",
|
||
diff --git a/staging/src/k8s.io/code-generator/pkg/apidefinitions/lint_test.go b/staging/src/k8s.io/code-generator/pkg/apidefinitions/lint_test.go
|
||
index a30549a216844..5c11d765bb261 100644
|
||
--- a/staging/src/k8s.io/code-generator/pkg/apidefinitions/lint_test.go
|
||
+++ b/staging/src/k8s.io/code-generator/pkg/apidefinitions/lint_test.go
|
||
@@ -49,6 +49,7 @@ func TestLintRules(t *testing.T) {
|
||
"+k8s:defaulter-gen-input=k8s.io/api/foo/v1",
|
||
"+k8s:validation-gen=TypesWithField=TypeMeta",
|
||
"+k8s:validation-gen-input=k8s.io/api/foo/v1",
|
||
+ "+k8s:validation-gen-deep-equal-func=foo",
|
||
"+k8s:validation-gen-nolint",
|
||
"+k8s:validation-gen-scheme-registry=foo",
|
||
"+k8s:validation-gen-test-fixture=bar",
|
||
diff --git a/staging/src/k8s.io/code-generator/pkg/apidefinitions/spec.go b/staging/src/k8s.io/code-generator/pkg/apidefinitions/spec.go
|
||
index 58c5da6cb01a1..96289c97bb434 100644
|
||
--- a/staging/src/k8s.io/code-generator/pkg/apidefinitions/spec.go
|
||
+++ b/staging/src/k8s.io/code-generator/pkg/apidefinitions/spec.go
|
||
@@ -96,6 +96,7 @@ var (
|
||
InputTag: "k8s:validation-gen-input",
|
||
ValueMode: TypeFilterList,
|
||
AuxTags: []string{
|
||
+ "k8s:validation-gen-deep-equal-func",
|
||
"k8s:validation-gen-nolint",
|
||
"k8s:validation-gen-scheme-registry",
|
||
"k8s:validation-gen-test-fixture",
|
||
diff --git a/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1alpha1/validation.go b/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1alpha1/validation.go
|
||
index 6c0dbe3bc8ecb..d2aae78012279 100644
|
||
--- a/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1alpha1/validation.go
|
||
+++ b/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1alpha1/validation.go
|
||
@@ -20,6 +20,7 @@ import (
|
||
"context"
|
||
|
||
resourceapi "k8s.io/api/resource/v1"
|
||
+ "k8s.io/apimachinery/pkg/api/equality"
|
||
"k8s.io/apimachinery/pkg/api/operation"
|
||
"k8s.io/apimachinery/pkg/api/validate"
|
||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||
@@ -29,7 +30,7 @@ func ValidateCustom_Device_Attributes(
|
||
ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
obj, oldObj map[resourceapi.QualifiedName]resourceapi.DeviceAttribute,
|
||
) field.ErrorList {
|
||
- return validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, resourceapi.Validate_DeviceAttribute)
|
||
+ return validate.EachMapVal(ctx, op, fldPath, obj, oldObj, semanticDeepEqual, resourceapi.Validate_DeviceAttribute)
|
||
}
|
||
|
||
func ValidateCustom_Device_NetworkData(
|
||
@@ -38,3 +39,12 @@ func ValidateCustom_Device_NetworkData(
|
||
) field.ErrorList {
|
||
return resourceapi.Validate_NetworkDeviceData(ctx, op, fldPath, obj, oldObj)
|
||
}
|
||
+
|
||
+// semanticDeepEqual is a MatchFunc that uses equality.Semantic.DeepEqual to
|
||
+// compare two values.
|
||
+// This wrapper is needed because MatchFunc requires a function that takes two
|
||
+// arguments of specific type T, while equality.Semantic.DeepEqual takes
|
||
+// arguments of type interface{}/any.
|
||
+func semanticDeepEqual[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|
||
diff --git a/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1beta1/validation.go b/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1beta1/validation.go
|
||
index d4df1c48f5e37..00cad29f34893 100644
|
||
--- a/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1beta1/validation.go
|
||
+++ b/staging/src/k8s.io/dynamic-resource-allocation/api/metadata/v1beta1/validation.go
|
||
@@ -20,6 +20,7 @@ import (
|
||
"context"
|
||
|
||
resourceapi "k8s.io/api/resource/v1"
|
||
+ "k8s.io/apimachinery/pkg/api/equality"
|
||
"k8s.io/apimachinery/pkg/api/operation"
|
||
"k8s.io/apimachinery/pkg/api/validate"
|
||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||
@@ -29,7 +30,7 @@ func ValidateCustom_Device_Attributes(
|
||
ctx context.Context, op operation.Operation, fldPath *field.Path,
|
||
obj, oldObj map[resourceapi.QualifiedName]resourceapi.DeviceAttribute,
|
||
) field.ErrorList {
|
||
- return validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.SemanticDeepEqual, resourceapi.Validate_DeviceAttribute)
|
||
+ return validate.EachMapVal(ctx, op, fldPath, obj, oldObj, semanticDeepEqual, resourceapi.Validate_DeviceAttribute)
|
||
}
|
||
|
||
func ValidateCustom_Device_NetworkData(
|
||
@@ -38,3 +39,12 @@ func ValidateCustom_Device_NetworkData(
|
||
) field.ErrorList {
|
||
return resourceapi.Validate_NetworkDeviceData(ctx, op, fldPath, obj, oldObj)
|
||
}
|
||
+
|
||
+// semanticDeepEqual is a MatchFunc that uses equality.Semantic.DeepEqual to
|
||
+// compare two values.
|
||
+// This wrapper is needed because MatchFunc requires a function that takes two
|
||
+// arguments of specific type T, while equality.Semantic.DeepEqual takes
|
||
+// arguments of type interface{}/any.
|
||
+func semanticDeepEqual[T any](a, b T) bool {
|
||
+ return equality.Semantic.DeepEqual(a, b)
|
||
+}
|