diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go index 4f065a18fb7..aba8f067417 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation.go @@ -122,10 +122,10 @@ func Compile(s *schema.Structural, declType *apiservercel.DeclType, perCallLimit metrics.Metrics.ObserveCompilation(time.Since(t)) }() - if len(s.Extensions.XValidations) == 0 { + if len(s.XValidations) == 0 { return nil, nil } - celRules := s.Extensions.XValidations + celRules := s.XValidations oldSelfEnvSet, optionalOldSelfEnvSet, err := prepareEnvSet(baseEnvSet, declType) if err != nil { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation_test.go index ac9149c2482..5f078e2f0df 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/compilation_test.go @@ -168,7 +168,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "integer", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "self >= oldSelf.value()", OptionalOldSelf: ptr.To(true)}, {Rule: "self >= oldSelf.orValue(1)", OptionalOldSelf: ptr.To(true)}, @@ -216,7 +216,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "self.i >= oldSelf.i.value()", OptionalOldSelf: ptr.To(true)}, {Rule: "self.s == oldSelf.s.value()", OptionalOldSelf: ptr.To(true)}, @@ -266,7 +266,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self.minReplicas < self.maxReplicas", @@ -285,7 +285,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "string", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self.startsWith('s')", @@ -307,7 +307,7 @@ func TestCelCompilation(t *testing.T) { ValueValidation: &schema.ValueValidation{ Format: "byte", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "string(self).endsWith('s')", @@ -326,7 +326,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "boolean", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self == true", @@ -345,7 +345,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "integer", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self > 0", @@ -364,7 +364,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "number", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self > 1.0", @@ -400,7 +400,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self.nestedObj.val == 10", @@ -436,7 +436,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self.nestedObj[0]) == 10", @@ -470,7 +470,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self[0][0]) == 10", @@ -511,7 +511,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self[0].nestedObj.val == 10", @@ -529,17 +529,17 @@ func TestCelCompilation(t *testing.T) { input: schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Bool: true, - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "boolean", - Nullable: false, - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Bool: true, + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "boolean", + Nullable: false, }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self) > 0", @@ -558,7 +558,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "number", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self) == 10", @@ -577,7 +577,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "integer", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self) == 10", @@ -623,7 +623,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self.__namespace__[0]) == 10", @@ -677,7 +677,7 @@ func TestCelCompilation(t *testing.T) { }, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "size(self.namespace[0]) == 10", @@ -704,7 +704,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "integer", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "self > 0"}, {Rule: "self >= oldSelf"}, @@ -722,7 +722,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "object", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: " \t"}, }, @@ -745,7 +745,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "object", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "42"}, }, @@ -761,7 +761,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "string", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self.startsWith('s')", @@ -780,7 +780,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "integer", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self == 5", @@ -799,7 +799,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "number", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: "self < 32.0", @@ -818,7 +818,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "object", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "fakeFunction('abc') == 'ABC'"}, }, @@ -835,7 +835,7 @@ func TestCelCompilation(t *testing.T) { Generic: schema.Generic{ Type: "object", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ {Rule: "fakeFunction('abc') == 'ABC'"}, }, @@ -917,7 +917,7 @@ func genArrayWithRule(arrayType, rule string) func(maxItems *int64) *schema.Stru ValueValidation: &schema.ValueValidation{ MaxItems: maxItems, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -947,7 +947,7 @@ func genArrayOfArraysWithRule(arrayType, rule string) func(maxItems *int64) *sch ValueValidation: &schema.ValueValidation{ MaxItems: maxItems, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -987,7 +987,7 @@ func genObjectArrayWithRule(rule string) func(maxItems *int64) *schema.Structura ValueValidation: &schema.ValueValidation{ MaxItems: maxItems, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1007,17 +1007,17 @@ func getMapArrayWithRule(mapType, rule string) func(maxItems *int64) *schema.Str Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: mapType, - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: mapType, + }, + }}, }, ValueValidation: &schema.ValueValidation{ MaxItems: maxItems, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1034,22 +1034,22 @@ func genMapWithRule(mapType, rule string) func(maxProperties *int64) *schema.Str return &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: passedType, - }, - ValueValidation: &schema.ValueValidation{ - Format: passedFormat, - }, - Extensions: schema.Extensions{ - XIntOrString: xIntString, - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: passedType, + }, + ValueValidation: &schema.ValueValidation{ + Format: passedFormat, + }, + Extensions: schema.Extensions{ + XIntOrString: xIntString, + }, + }}, ValueValidation: &schema.ValueValidation{ MaxProperties: maxProperties, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1069,7 +1069,7 @@ func genStringWithRule(rule string) func(maxLength *int64) *schema.Structural { ValueValidation: &schema.ValueValidation{ MaxLength: maxLength, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1098,7 +1098,7 @@ func genEnumWithRuleAndValues(rule string, values ...string) func(maxLength *int MaxLength: maxLength, Enum: enums, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1119,7 +1119,7 @@ func genBytesWithRule(rule string) func(maxLength *int64) *schema.Structural { MaxLength: maxLength, Format: "byte", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1135,19 +1135,19 @@ func genNestedSpecWithRule(rule string) func(maxLength *int64) *schema.Structura return &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, - ValueValidation: &schema.ValueValidation{ - MaxLength: maxLength, - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + }, + ValueValidation: &schema.ValueValidation{ + MaxLength: maxLength, + }, + }}, ValueValidation: &schema.ValueValidation{ MaxProperties: maxLength, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1167,28 +1167,28 @@ func genAllMaxNestedSpecWithRootRule(rule string) func(maxLength *int64) *schema Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "object", - }, - ValueValidation: &schema.ValueValidation{ - Required: []string{"required"}, - MaxProperties: maxLength, - }, - Properties: map[string]schema.Structural{ - "required": { - Generic: schema.Generic{ - Type: "string", - }, - }, - "optional": { - Generic: schema.Generic{ - Type: "string", - }, - }, - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "object", + }, + ValueValidation: &schema.ValueValidation{ + Required: []string{"required"}, + MaxProperties: maxLength, + }, + Properties: map[string]schema.Structural{ + "required": { + Generic: schema.Generic{ + Type: "string", + }, + }, + "optional": { + Generic: schema.Generic{ + Type: "string", + }, + }, + }, + }}, ValueValidation: &schema.ValueValidation{ MaxProperties: maxLength, }, @@ -1196,7 +1196,7 @@ func genAllMaxNestedSpecWithRootRule(rule string) func(maxLength *int64) *schema ValueValidation: &schema.ValueValidation{ MaxItems: maxLength, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1216,32 +1216,32 @@ func genOneMaxNestedSpecWithRootRule(rule string) func(maxLength *int64) *schema Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "object", - }, - ValueValidation: &schema.ValueValidation{ - Required: []string{"required"}, - }, - Properties: map[string]schema.Structural{ - "required": { - Generic: schema.Generic{ - Type: "string", - }, - }, - "optional": { - Generic: schema.Generic{ - Type: "string", - }, - }, - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "object", + }, + ValueValidation: &schema.ValueValidation{ + Required: []string{"required"}, + }, + Properties: map[string]schema.Structural{ + "required": { + Generic: schema.Generic{ + Type: "string", + }, + }, + "optional": { + Generic: schema.Generic{ + Type: "string", + }, + }, + }, + }}, ValueValidation: &schema.ValueValidation{ MaxProperties: maxLength, }, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, @@ -1292,12 +1292,12 @@ func genMapForMap() *schema.Structural { return &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "number", - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "number", + }, + }}, } } @@ -1305,13 +1305,13 @@ func genMapWithCustomItemRule(item *schema.Structural, rule string) func(maxProp return func(maxProperties *int64) *schema.Structural { return &schema.Structural{ Generic: schema.Generic{ - Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: item}, + Type: "object", }, + AdditionalProperties: &schema.StructuralOrBool{Structural: item}, ValueValidation: &schema.ValueValidation{ MaxProperties: maxProperties, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go index 0bc109a73f8..2228b0b8468 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/adaptor.go @@ -279,11 +279,18 @@ func nestedValueValidationToStructural(nvv *schema.NestedValueValidation) *Struc newProperties[k] = *nestedValueValidationToStructural(&v).Structural } + var newAdditionalProperties *schema.StructuralOrBool + if nvv.AdditionalProperties != nil { + newAdditionalProperties = &schema.StructuralOrBool{Structural: nestedValueValidationToStructural(nvv.AdditionalProperties).Structural} + } + return &Structural{ Structural: &schema.Structural{ - Items: newItems, - Properties: newProperties, - ValueValidation: &nvv.ValueValidation, + Items: newItems, + Properties: newProperties, + AdditionalProperties: newAdditionalProperties, + ValueValidation: &nvv.ValueValidation, + ValidationExtensions: nvv.ValidationExtensions, }, } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go index 6b49e67a404..f1c5ad9e9b8 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas.go @@ -49,9 +49,11 @@ func WithTypeAndObjectMeta(s *schema.Structural) *schema.Structural { return s } result := &schema.Structural{ - Generic: s.Generic, - Extensions: s.Extensions, - ValueValidation: s.ValueValidation, + AdditionalProperties: s.AdditionalProperties, + Generic: s.Generic, + Extensions: s.Extensions, + ValueValidation: s.ValueValidation, + ValidationExtensions: s.ValidationExtensions, } props := make(map[string]schema.Structural, len(s.Properties)) for k, prop := range s.Properties { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas_test.go index 04ae3a1a415..4665d21651c 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/model/schemas_test.go @@ -199,11 +199,11 @@ func testSchema() *schema.Structural { "flags": { Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "boolean", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "boolean", }, }, }, @@ -317,12 +317,12 @@ func TestEstimateMaxLengthJSON(t *testing.T) { InputSchema: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + }, + }}, }, // expected JSON is {"":"","":"",...} so our length should be (3000000 - 2) / 6 ExpectedMaxElements: 393215, @@ -382,12 +382,12 @@ func TestEstimateMaxLengthJSON(t *testing.T) { InputSchema: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, - }}, }, + AdditionalProperties: &schema.StructuralOrBool{Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + }, + }}, ValueValidation: &schema.ValueValidation{ Format: "string", MaxProperties: maxPtr(15), @@ -519,15 +519,15 @@ func genNestedSchema(depth int) *schema.Structural { generator = func(d int) schema.Structural { nodeTemplate := schema.Structural{ Generic: schema.Generic{ - Type: "object", - AdditionalProperties: &schema.StructuralOrBool{}, + Type: "object", }, + AdditionalProperties: &schema.StructuralOrBool{}, } if d == 1 { return nodeTemplate } else { mapType := generator(d - 1) - nodeTemplate.Generic.AdditionalProperties.Structural = &mapType + nodeTemplate.AdditionalProperties.Structural = &mapType return nodeTemplate } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go index 1041aed7ada..4c304a72843 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/validation_test.go @@ -2787,7 +2787,7 @@ func TestReasonAndFldPath(t *testing.T) { "field2": stringType, "field3": stringType, }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: apiextensions.ValidationRules{ { Rule: `self.field2 != "value2"`, @@ -2852,16 +2852,16 @@ func TestValidateFieldPath(t *testing.T) { "foo": { Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "object", - }, - Properties: map[string]schema.Structural{ - "subAdd": { - Generic: schema.Generic{ - Type: "number", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "object", + }, + Properties: map[string]schema.Structural{ + "subAdd": { + Generic: schema.Generic{ + Type: "number", }, }, }, @@ -4387,7 +4387,7 @@ func BenchmarkCELValidationWithAndWithoutOldSelfReference(b *testing.B) { ValueValidation: &schema.ValueValidation{ Format: "date-time", }, - Extensions: schema.Extensions{ + ValidationExtensions: schema.ValidationExtensions{ XValidations: []apiextensions.ValidationRule{ {Rule: rule}, }, @@ -4520,9 +4520,9 @@ func objectTypePtr(props map[string]schema.Structural) *schema.Structural { func mapType(valSchema *schema.Structural) schema.Structural { result := schema.Structural{ Generic: schema.Generic{ - Type: "object", - AdditionalProperties: &schema.StructuralOrBool{Bool: true, Structural: valSchema}, + Type: "object", }, + AdditionalProperties: &schema.StructuralOrBool{Bool: true, Structural: valSchema}, } return result } @@ -4541,7 +4541,7 @@ func intOrStringType() schema.Structural { } func withRule(s schema.Structural, rule string) schema.Structural { - s.Extensions.XValidations = apiextensions.ValidationRules{ + s.XValidations = apiextensions.ValidationRules{ { Rule: rule, }, @@ -4550,7 +4550,7 @@ func withRule(s schema.Structural, rule string) schema.Structural { } func withRuleMessageAndMessageExpression(s schema.Structural, rule, message, messageExpression string) schema.Structural { - s.Extensions.XValidations = apiextensions.ValidationRules{ + s.XValidations = apiextensions.ValidationRules{ { Rule: rule, Message: message, @@ -4561,7 +4561,7 @@ func withRuleMessageAndMessageExpression(s schema.Structural, rule, message, mes } func withReasonAndFldPath(s schema.Structural, rule, jsonPath string, reason *apiextensions.FieldValueErrorReason) schema.Structural { - s.Extensions.XValidations = apiextensions.ValidationRules{ + s.XValidations = apiextensions.ValidationRules{ { Rule: rule, FieldPath: jsonPath, @@ -4572,7 +4572,7 @@ func withReasonAndFldPath(s schema.Structural, rule, jsonPath string, reason *ap } func withRuleAndMessageExpression(s schema.Structural, rule, messageExpression string) schema.Structural { - s.Extensions.XValidations = apiextensions.ValidationRules{ + s.XValidations = apiextensions.ValidationRules{ { Rule: rule, MessageExpression: messageExpression, @@ -4582,7 +4582,7 @@ func withRuleAndMessageExpression(s schema.Structural, rule, messageExpression s } func withRulePtr(s *schema.Structural, rule string) *schema.Structural { - s.Extensions.XValidations = apiextensions.ValidationRules{ + s.XValidations = apiextensions.ValidationRules{ { Rule: rule, }, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/values_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/values_test.go index 2ad65a07538..96a1efabdc6 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/values_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/cel/values_test.go @@ -100,10 +100,10 @@ var ( mapSchema = schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Bool: true, - Structural: &stringSchema, - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Bool: true, + Structural: &stringSchema, }, } ) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go index 9ec23b3322d..78350c135fc 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/convert.go @@ -18,6 +18,7 @@ package schema import ( "fmt" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" ) @@ -62,10 +63,16 @@ func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) { return nil, err } + vx, err := newValidationExtensions(s) + if err != nil { + return nil, err + } + ss := &Structural{ - Generic: *g, - Extensions: *x, - ValueValidation: vv, + Generic: *g, + Extensions: *x, + ValueValidation: vv, + ValidationExtensions: *vx, } if s.Items != nil { @@ -91,6 +98,18 @@ func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) { } } + if s.AdditionalProperties != nil { + if s.AdditionalProperties.Schema != nil { + additionalPropertiesSchema, err := NewStructural(s.AdditionalProperties.Schema) + if err != nil { + return nil, err + } + ss.AdditionalProperties = &StructuralOrBool{Structural: additionalPropertiesSchema, Bool: true} + } else { + ss.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows} + } + } + return ss, nil } @@ -108,18 +127,6 @@ func newGenerics(s *apiextensions.JSONSchemaProps) (*Generic, error) { g.Default = JSON{interface{}(*s.Default)} } - if s.AdditionalProperties != nil { - if s.AdditionalProperties.Schema != nil { - ss, err := NewStructural(s.AdditionalProperties.Schema) - if err != nil { - return nil, err - } - g.AdditionalProperties = &StructuralOrBool{Structural: ss, Bool: true} - } else { - g.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows} - } - } - return g, nil } @@ -205,10 +212,16 @@ func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueVal return nil, err } + vx, err := newValidationExtensions(s) + if err != nil { + return nil, err + } + v := &NestedValueValidation{ - ValueValidation: *vv, - ForbiddenGenerics: *g, - ForbiddenExtensions: *x, + ValueValidation: *vv, + ValidationExtensions: *vx, + ForbiddenGenerics: *g, + ForbiddenExtensions: *x, } if s.Items != nil { @@ -232,6 +245,18 @@ func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueVal v.Properties[k] = *nvv } } + if s.AdditionalProperties != nil { + if s.AdditionalProperties.Schema != nil { + additionalPropertiesSchema, err := newNestedValueValidation(s.AdditionalProperties.Schema) + if err != nil { + return nil, err + } + v.AdditionalProperties = additionalPropertiesSchema + } else if s.AdditionalProperties.Allows { + v.AdditionalProperties = &NestedValueValidation{} + } + + } return v, nil } @@ -248,9 +273,6 @@ func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) { XListType: s.XListType, XMapType: s.XMapType, } - if err := apiextensionsv1.Convert_apiextensions_ValidationRules_To_v1_ValidationRules(&s.XValidations, &ret.XValidations, nil); err != nil { - return nil, err - } if s.XPreserveUnknownFields != nil { if !*s.XPreserveUnknownFields { @@ -262,6 +284,19 @@ func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) { return ret, nil } +func newValidationExtensions(s *apiextensions.JSONSchemaProps) (*ValidationExtensions, error) { + if s == nil { + return nil, nil + } + + ret := &ValidationExtensions{} + if err := apiextensionsv1.Convert_apiextensions_ValidationRules_To_v1_ValidationRules(&s.XValidations, &ret.XValidations, nil); err != nil { + return nil, err + } + + return ret, nil +} + // validateUnsupportedFields checks that those fields rejected by validation are actually unset. func validateUnsupportedFields(s *apiextensions.JSONSchemaProps) error { if len(s.ID) > 0 { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go index 3fd4369930b..696734fbb1e 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting/algorithm_test.go @@ -99,19 +99,17 @@ func TestDefault(t *testing.T) { }, }, "additionalProperties": { - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Properties: map[string]structuralschema.Structural{ - "a": { - Generic: structuralschema.Generic{ - Default: structuralschema.JSON{Object: "alpha"}, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Properties: map[string]structuralschema.Structural{ + "a": { + Generic: structuralschema.Generic{ + Default: structuralschema.JSON{Object: "alpha"}, }, - "b": { - Generic: structuralschema.Generic{ - Default: structuralschema.JSON{Object: "beta"}, - }, + }, + "b": { + Generic: structuralschema.Generic{ + Default: structuralschema.JSON{Object: "beta"}, }, }, }, @@ -182,34 +180,28 @@ func TestDefault(t *testing.T) { }, }, `{"a": "A"}`}, {"null in nullable object with additionalProperties", `{"a": null}`, &structuralschema.Structural{ - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Generic: structuralschema.Generic{ - Nullable: true, - Default: structuralschema.JSON{Object: "A"}, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Generic: structuralschema.Generic{ + Nullable: true, + Default: structuralschema.JSON{Object: "A"}, }, }, }, }, `{"a": null}`}, {"null in non-nullable object with additionalProperties", `{"a": null}`, &structuralschema.Structural{ - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Generic: structuralschema.Generic{ - Nullable: false, - Default: structuralschema.JSON{Object: "A"}, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Generic: structuralschema.Generic{ + Nullable: false, + Default: structuralschema.JSON{Object: "A"}, }, }, }, }, `{"a": "A"}`}, {"null unknown field", `{"a": null}`, &structuralschema.Structural{ - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Bool: true, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Bool: true, }, }, `{"a": null}`}, } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go index 23bffbfb190..df78ba77e61 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/kubeopenapi.go @@ -37,10 +37,16 @@ func (s *Structural) ToKubeOpenAPI() *spec.Schema { ret.Properties[k] = *v.ToKubeOpenAPI() } } + if s.AdditionalProperties != nil { + ret.AdditionalProperties = &spec.SchemaOrBool{ + Allows: s.AdditionalProperties.Bool, + Schema: s.AdditionalProperties.Structural.ToKubeOpenAPI(), + } + } s.Generic.toKubeOpenAPI(ret) s.Extensions.toKubeOpenAPI(ret) s.ValueValidation.toKubeOpenAPI(ret) - + s.ValidationExtensions.toKubeOpenAPI(ret) return ret } @@ -53,12 +59,6 @@ func (g *Generic) toKubeOpenAPI(ret *spec.Schema) { ret.Type = spec.StringOrArray{g.Type} } ret.Nullable = g.Nullable - if g.AdditionalProperties != nil { - ret.AdditionalProperties = &spec.SchemaOrBool{ - Allows: g.AdditionalProperties.Bool, - Schema: g.AdditionalProperties.Structural.ToKubeOpenAPI(), - } - } ret.Description = g.Description ret.Title = g.Title ret.Default = g.Default.Object @@ -87,6 +87,13 @@ func (x *Extensions) toKubeOpenAPI(ret *spec.Schema) { if x.XMapType != nil { ret.VendorExtensible.AddExtension("x-kubernetes-map-type", *x.XMapType) } +} + +func (x *ValidationExtensions) toKubeOpenAPI(ret *spec.Schema) { + if x == nil { + return + } + if len(x.XValidations) > 0 { ret.VendorExtensible.AddExtension("x-kubernetes-validations", x.XValidations) } @@ -138,6 +145,7 @@ func (vv *NestedValueValidation) toKubeOpenAPI() *spec.Schema { ret := &spec.Schema{} vv.ValueValidation.toKubeOpenAPI(ret) + vv.ValidationExtensions.toKubeOpenAPI(ret) if vv.Items != nil { ret.Items = &spec.SchemaOrArray{Schema: vv.Items.toKubeOpenAPI()} } @@ -149,6 +157,5 @@ func (vv *NestedValueValidation) toKubeOpenAPI() *spec.Schema { } vv.ForbiddenGenerics.toKubeOpenAPI(ret) // normally empty. Exception: int-or-string vv.ForbiddenExtensions.toKubeOpenAPI(ret) // shouldn't do anything - return ret } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype/validation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype/validation_test.go index 3b3dce9385d..1df07a1d0f4 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype/validation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/listtype/validation_test.go @@ -202,18 +202,18 @@ func TestValidateListSetsAndMaps(t *testing.T) { schema: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "array", + }, + Extensions: schema.Extensions{ + XListType: strPtr("set"), + }, + Items: &schema.Structural{ Generic: schema.Generic{ - Type: "array", - }, - Extensions: schema.Extensions{ - XListType: strPtr("set"), - }, - Items: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + Type: "string", }, }, }, @@ -387,11 +387,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -407,11 +407,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -427,12 +427,12 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - Nullable: true, - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + Nullable: true, }, }, }, @@ -485,11 +485,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -670,11 +670,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -691,11 +691,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -712,11 +712,11 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", }, }, }, @@ -733,12 +733,12 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - Nullable: true, - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + Nullable: true, }, }, }, @@ -856,12 +856,12 @@ func TestValidateListSetsAndMaps(t *testing.T) { Items: &schema.Structural{ Generic: schema.Generic{ Type: "object", - AdditionalProperties: &schema.StructuralOrBool{ - Structural: &schema.Structural{ - Generic: schema.Generic{ - Type: "string", - Nullable: true, - }, + }, + AdditionalProperties: &schema.StructuralOrBool{ + Structural: &schema.Structural{ + Generic: schema.Generic{ + Type: "string", + Nullable: true, }, }, }, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation_test.go index 4270bcd7c10..180497d33fd 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/validation_test.go @@ -171,11 +171,9 @@ func TestValidate(t *testing.T) { }, }, "additionalProperties": { - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Extensions: structuralschema.Extensions{XEmbeddedResource: true}, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Extensions: structuralschema.Extensions{XEmbeddedResource: true}, }, }, }, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning/algorithm_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning/algorithm_test.go index 6d06f9434de..30e1ded7f4f 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning/algorithm_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning/algorithm_test.go @@ -146,12 +146,12 @@ func TestPrune(t *testing.T) { Extensions: structuralschema.Extensions{XPreserveUnknownFields: true}, Generic: structuralschema.Generic{ Type: "object", - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Generic: structuralschema.Generic{Type: "object"}, - Properties: map[string]structuralschema.Structural{ - "specified": {Generic: structuralschema.Generic{Type: "object"}}, - }, + }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Generic: structuralschema.Generic{Type: "object"}, + Properties: map[string]structuralschema.Structural{ + "specified": {Generic: structuralschema.Generic{Type: "object"}}, }, }, }, @@ -159,12 +159,12 @@ func TestPrune(t *testing.T) { "preservingAdditionalPropertiesKeyPruneValues": { Generic: structuralschema.Generic{ Type: "object", - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Generic: structuralschema.Generic{Type: "object"}, - Properties: map[string]structuralschema.Structural{ - "specified": {Generic: structuralschema.Generic{Type: "object"}}, - }, + }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Generic: structuralschema.Generic{Type: "object"}, + Properties: map[string]structuralschema.Structural{ + "specified": {Generic: structuralschema.Generic{Type: "object"}}, }, }, }, @@ -203,12 +203,10 @@ func TestPrune(t *testing.T) { Properties: map[string]structuralschema.Structural{ "a": {}, "c": { - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Structural: &structuralschema.Structural{ - Generic: structuralschema.Generic{ - Type: "integer", - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Structural: &structuralschema.Structural{ + Generic: structuralschema.Generic{ + Type: "integer", }, }, }, @@ -220,10 +218,8 @@ func TestPrune(t *testing.T) { Properties: map[string]structuralschema.Structural{ "a": {}, "c": { - Generic: structuralschema.Generic{ - AdditionalProperties: &structuralschema.StructuralOrBool{ - Bool: false, - }, + AdditionalProperties: &structuralschema.StructuralOrBool{ + Bool: false, }, }, }, diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go index 234998d904a..5688c2ac40b 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/structural.go @@ -25,11 +25,13 @@ import ( // Structural represents a structural schema. type Structural struct { - Items *Structural - Properties map[string]Structural + Items *Structural + Properties map[string]Structural + AdditionalProperties *StructuralOrBool Generic Extensions + ValidationExtensions ValueValidation *ValueValidation } @@ -51,11 +53,10 @@ type Generic struct { // It can be object, array, number, integer, boolean, string. // It is optional only if x-kubernetes-preserve-unknown-fields // or x-kubernetes-int-or-string is true. - Type string - Title string - Default JSON - AdditionalProperties *StructuralOrBool - Nullable bool + Type string + Title string + Default JSON + Nullable bool } // +k8s:deepcopy-gen=true @@ -128,7 +129,13 @@ type Extensions struct { // Atomic maps will be entirely replaced when updated. // +optional XMapType *string +} +// +k8s:deepcopy-gen=true + +// ValidationExtensions contains the Kubernetes OpenAPI v3 extensions that are +// used for validation rather than structure. +type ValidationExtensions struct { // x-kubernetes-validations describes a list of validation rules for expression validation. // Use the v1 struct since this gets serialized as an extension. XValidations apiextensionsv1.ValidationRules @@ -166,9 +173,11 @@ type ValueValidation struct { // under a logical junctor, and catch all structs for generic and vendor extensions schema fields. type NestedValueValidation struct { ValueValidation + ValidationExtensions - Items *NestedValueValidation - Properties map[string]NestedValueValidation + Items *NestedValueValidation + Properties map[string]NestedValueValidation + AdditionalProperties *NestedValueValidation // Anything set in the following will make the scheme // non-structural, with the exception of these two patterns if diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go index f7f29e70c4b..9fbacf434e4 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/validation.go @@ -91,6 +91,16 @@ func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path) for k, v := range s.Properties { allErrs = append(allErrs, validateStructuralInvariants(&v, fieldLevel, fldPath.Child("properties").Key(k))...) } + + if s.AdditionalProperties != nil { + if lvl == rootLevel { + allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must not be used at the root")) + } + if s.AdditionalProperties.Structural != nil { + allErrs = append(allErrs, validateStructuralInvariants(s.AdditionalProperties.Structural, fieldLevel, fldPath.Child("additionalProperties"))...) + } + } + allErrs = append(allErrs, validateGeneric(&s.Generic, lvl, fldPath)...) allErrs = append(allErrs, validateExtensions(&s.Extensions, fldPath)...) @@ -207,18 +217,7 @@ func validateGeneric(g *Generic, lvl level, fldPath *field.Path) field.ErrorList return nil } - allErrs := field.ErrorList{} - - if g.AdditionalProperties != nil { - if lvl == rootLevel { - allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must not be used at the root")) - } - if g.AdditionalProperties.Structural != nil { - allErrs = append(allErrs, validateStructuralInvariants(g.AdditionalProperties.Structural, fieldLevel, fldPath.Child("additionalProperties"))...) - } - } - - return allErrs + return nil } // validateExtensions checks Kubernetes vendor extensions of a structural schema. @@ -282,6 +281,7 @@ func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllO allErrs = append(allErrs, validateValueValidation(&v.ValueValidation, skipAnyOf, skipAllOfAnyOf, lvl, fldPath)...) allErrs = append(allErrs, validateNestedValueValidation(v.Items, false, false, lvl, fldPath.Child("items"))...) + allErrs = append(allErrs, validateNestedValueValidation(v.AdditionalProperties, false, false, lvl, fldPath.Child("additionalProperties"))...) for k, fld := range v.Properties { allErrs = append(allErrs, validateNestedValueValidation(&fld, false, false, fieldLevel, fldPath.Child("properties").Key(k))...) @@ -290,9 +290,6 @@ func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllO if len(v.ForbiddenGenerics.Type) > 0 { allErrs = append(allErrs, field.Forbidden(fldPath.Child("type"), "must be empty to be structural")) } - if v.ForbiddenGenerics.AdditionalProperties != nil { - allErrs = append(allErrs, field.Forbidden(fldPath.Child("additionalProperties"), "must be undefined to be structural")) - } if v.ForbiddenGenerics.Default.Object != nil { allErrs = append(allErrs, field.Forbidden(fldPath.Child("default"), "must be undefined to be structural")) } @@ -324,9 +321,6 @@ func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllO if v.ForbiddenExtensions.XMapType != nil { allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-map-type"), "must be undefined to be structural")) } - if len(v.ForbiddenExtensions.XValidations) > 0 { - allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-validations"), "must be empty to be structural")) - } // forbid reasoning about metadata because it can lead to metadata restriction we don't want if _, found := v.Properties["metadata"]; found { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go index 1f4267ddee5..37eb72ed012 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/visitor.go @@ -50,8 +50,8 @@ func (m *Visitor) visitStructural(s *Structural) bool { s.Properties[k] = v } } - if s.Generic.AdditionalProperties != nil && s.Generic.AdditionalProperties.Structural != nil { - m.visitStructural(s.Generic.AdditionalProperties.Structural) + if s.AdditionalProperties != nil && s.AdditionalProperties.Structural != nil { + m.visitStructural(s.AdditionalProperties.Structural) } if s.ValueValidation != nil { for i := range s.ValueValidation.AllOf { @@ -86,8 +86,8 @@ func (m *Visitor) visitNestedValueValidation(vv *NestedValueValidation) bool { vv.Properties[k] = v } } - if vv.ForbiddenGenerics.AdditionalProperties != nil && vv.ForbiddenGenerics.AdditionalProperties.Structural != nil { - m.visitStructural(vv.ForbiddenGenerics.AdditionalProperties.Structural) + if vv.AdditionalProperties != nil { + m.visitNestedValueValidation(vv.AdditionalProperties) } for i := range vv.ValueValidation.AllOf { m.visitNestedValueValidation(&vv.ValueValidation.AllOf[i])