mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-05-28 04:04:39 -04:00
validation-gen: add tests for nilable optional fields with defaults
Cover *Struct, []string, and map[string]string with non-zero defaults in the nonzero_defaults package. Each previously failed code generation and now emits the expected RequiredPointer/RequiredSlice/RequiredMap validator.
This commit is contained in:
parent
dd66e21dad
commit
6bf5c1bf6c
3 changed files with 113 additions and 0 deletions
|
|
@ -51,4 +51,20 @@ type Struct struct {
|
|||
// +k8s:optional
|
||||
// +default=true
|
||||
BoolPtrField *bool `json:"boolPtrField"`
|
||||
|
||||
// +k8s:optional
|
||||
// +default={"name": "x"}
|
||||
StructPtrField *Submarker `json:"structPtrField"`
|
||||
|
||||
// +k8s:optional
|
||||
// +default=["foo"]
|
||||
SliceField []string `json:"sliceField"`
|
||||
|
||||
// +k8s:optional
|
||||
// +default={"k": "v"}
|
||||
MapField map[string]string `json:"mapField"`
|
||||
}
|
||||
|
||||
type Submarker struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ func Test(t *testing.T) {
|
|||
field.Required(field.NewPath("intPtrField"), ""),
|
||||
field.Required(field.NewPath("boolField"), ""),
|
||||
field.Required(field.NewPath("boolPtrField"), ""),
|
||||
field.Required(field.NewPath("structPtrField"), ""),
|
||||
field.Required(field.NewPath("sliceField"), ""),
|
||||
field.Required(field.NewPath("mapField"), ""),
|
||||
})
|
||||
|
||||
st.Value(&Struct{
|
||||
|
|
@ -44,5 +47,8 @@ func Test(t *testing.T) {
|
|||
IntPtrField: ptr.To(0),
|
||||
BoolField: true,
|
||||
BoolPtrField: ptr.To(false),
|
||||
StructPtrField: &Submarker{Name: "x"},
|
||||
SliceField: []string{"foo"},
|
||||
MapField: map[string]string{"k": "v"},
|
||||
}).ExpectValid()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -243,5 +244,95 @@ func Validate_Struct(
|
|||
errs = append(errs, fn(fldPath.Child("boolPtrField"), obj.BoolPtrField, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field Struct.StructPtrField
|
||||
fn := func(
|
||||
fldPath *field.Path,
|
||||
obj, oldObj *Submarker,
|
||||
oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update {
|
||||
if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
// optional fields with default values are effectively required
|
||||
if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}
|
||||
oldVal := safe.Field(oldObj,
|
||||
func(oldObj *Struct) *Submarker {
|
||||
return oldObj.StructPtrField
|
||||
})
|
||||
errs = append(errs, fn(fldPath.Child("structPtrField"), obj.StructPtrField, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field Struct.SliceField
|
||||
fn := func(
|
||||
fldPath *field.Path,
|
||||
obj, oldObj []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
|
||||
// optional fields with default values are effectively required
|
||||
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}
|
||||
oldVal := safe.Field(oldObj,
|
||||
func(oldObj *Struct) []string {
|
||||
return oldObj.SliceField
|
||||
})
|
||||
errs = append(errs, fn(fldPath.Child("sliceField"), obj.SliceField, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field Struct.MapField
|
||||
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
|
||||
// optional fields with default values are effectively required
|
||||
if e := validate.RequiredMap(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}
|
||||
oldVal := safe.Field(oldObj,
|
||||
func(oldObj *Struct) map[string]string {
|
||||
return oldObj.MapField
|
||||
})
|
||||
errs = append(errs, fn(fldPath.Child("mapField"), obj.MapField, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue