Merge pull request #77207 from sttts/sttts-structural-schema
apiextensions: implement structural schema condition
This commit is contained in:
@@ -30,6 +30,9 @@ API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiexten
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSON,Raw
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaProps,Ref
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaProps,Schema
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaProps,XEmbeddedResource
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaProps,XIntOrString
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaProps,XPreserveUnknownFields
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrArray,JSONSchemas
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrArray,Schema
|
||||
API rule violation: names_match,k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1,JSONSchemaPropsOrBool,Allows
|
||||
|
12
api/openapi-spec/swagger.json
generated
12
api/openapi-spec/swagger.json
generated
@@ -16808,6 +16808,18 @@
|
||||
},
|
||||
"uniqueItems": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"x-kubernetes-embedded-resource": {
|
||||
"description": "x-kubernetes-embedded-resource defines that the value is an embedded Kubernetes runtime.Object, with TypeMeta and ObjectMeta. The type must be object. It is allowed to further restrict the embedded object. kind, apiVersion and metadata are validated automatically. x-kubernetes-preserve-unknown-fields is allowed to be true, but does not have to be if the object is fully specified (up to kind, apiVersion, metadata).",
|
||||
"type": "boolean"
|
||||
},
|
||||
"x-kubernetes-int-or-string": {
|
||||
"description": "x-kubernetes-int-or-string specifies that this value is either an integer or a string. If this is true, an empty type is allowed and type as child of anyOf is permitted if following one of the following patterns:\n\n1) anyOf:\n - type: integer\n - type: string\n2) allOf:\n - anyOf:\n - type: integer\n - type: string\n - ... zero or more",
|
||||
"type": "boolean"
|
||||
},
|
||||
"x-kubernetes-preserve-unknown-fields": {
|
||||
"description": "x-kubernetes-preserve-unknown-fields stops the API server decoding step from pruning fields which are not specified in the validation schema. This affects fields recursively, but switches back to normal pruning behaviour if nested properties or additionalProperties are specified in the schema.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"type": "object"
|
||||
|
@@ -47,6 +47,7 @@ filegroup(
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:all-srcs",
|
||||
|
@@ -264,6 +264,22 @@ const (
|
||||
// NamesAccepted means the names chosen for this CustomResourceDefinition do not conflict with others in
|
||||
// the group and are therefore accepted.
|
||||
NamesAccepted CustomResourceDefinitionConditionType = "NamesAccepted"
|
||||
// NonStructuralSchema means that one or more OpenAPI schema is not structural.
|
||||
//
|
||||
// A schema is structural if it specifies types for all values, with the only exceptions of those with
|
||||
// - x-kubernetes-int-or-string: true — for fields which can be integer or string
|
||||
// - x-kubernetes-preserve-unknown-fields: true — for raw, unspecified JSON values
|
||||
// and there is no type, additionalProperties, default, nullable or x-kubernetes-* vendor extenions
|
||||
// specified under allOf, anyOf, oneOf or not.
|
||||
//
|
||||
// Non-structural schemas will not be allowed anymore in v1 API groups. Moreover, new features will not be
|
||||
// available for non-structural CRDs:
|
||||
// - pruning
|
||||
// - defaulting
|
||||
// - read-only
|
||||
// - OpenAPI publishing
|
||||
// - webhook conversion
|
||||
NonStructuralSchema CustomResourceDefinitionConditionType = "NonStructuralSchema"
|
||||
// Terminating means that the CustomResourceDefinition has been deleted and is cleaning up.
|
||||
Terminating CustomResourceDefinitionConditionType = "Terminating"
|
||||
)
|
||||
|
@@ -55,6 +55,36 @@ type JSONSchemaProps struct {
|
||||
Definitions JSONSchemaDefinitions
|
||||
ExternalDocs *ExternalDocumentation
|
||||
Example *JSON
|
||||
|
||||
// x-kubernetes-preserve-unknown-fields stops the API server
|
||||
// decoding step from pruning fields which are not specified
|
||||
// in the validation schema. This affects fields recursively,
|
||||
// but switches back to normal pruning behaviour if nested
|
||||
// properties or additionalProperties are specified in the schema.
|
||||
XPreserveUnknownFields bool
|
||||
|
||||
// x-kubernetes-embedded-resource defines that the value is an
|
||||
// embedded Kubernetes runtime.Object, with TypeMeta and
|
||||
// ObjectMeta. The type must be object. It is allowed to further
|
||||
// restrict the embedded object. Both ObjectMeta and TypeMeta
|
||||
// are validated automatically. x-kubernetes-preserve-unknown-fields
|
||||
// must be true.
|
||||
XEmbeddedResource bool
|
||||
|
||||
// x-kubernetes-int-or-string specifies that this value is
|
||||
// either an integer or a string. If this is true, an empty
|
||||
// type is allowed and type as child of anyOf is permitted
|
||||
// if following one of the following patterns:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
XIntOrString bool
|
||||
}
|
||||
|
||||
// JSON represents any valid JSON value.
|
||||
|
@@ -1431,6 +1431,36 @@ func (m *JSONSchemaProps) MarshalTo(dAtA []byte) (int, error) {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
dAtA[i] = 0xb0
|
||||
i++
|
||||
dAtA[i] = 0x2
|
||||
i++
|
||||
if m.XPreserveUnknownFields {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
dAtA[i] = 0xb8
|
||||
i++
|
||||
dAtA[i] = 0x2
|
||||
i++
|
||||
if m.XEmbeddedResource {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
dAtA[i] = 0xc0
|
||||
i++
|
||||
dAtA[i] = 0x2
|
||||
i++
|
||||
if m.XIntOrString {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -2080,6 +2110,9 @@ func (m *JSONSchemaProps) Size() (n int) {
|
||||
n += 2 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
n += 3
|
||||
n += 3
|
||||
n += 3
|
||||
n += 3
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -2478,6 +2511,9 @@ func (this *JSONSchemaProps) String() string {
|
||||
`ExternalDocs:` + strings.Replace(fmt.Sprintf("%v", this.ExternalDocs), "ExternalDocumentation", "ExternalDocumentation", 1) + `,`,
|
||||
`Example:` + strings.Replace(fmt.Sprintf("%v", this.Example), "JSON", "JSON", 1) + `,`,
|
||||
`Nullable:` + fmt.Sprintf("%v", this.Nullable) + `,`,
|
||||
`XPreserveUnknownFields:` + fmt.Sprintf("%v", this.XPreserveUnknownFields) + `,`,
|
||||
`XEmbeddedResource:` + fmt.Sprintf("%v", this.XEmbeddedResource) + `,`,
|
||||
`XIntOrString:` + fmt.Sprintf("%v", this.XIntOrString) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -6603,6 +6639,66 @@ func (m *JSONSchemaProps) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
}
|
||||
m.Nullable = bool(v != 0)
|
||||
case 38:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field XPreserveUnknownFields", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.XPreserveUnknownFields = bool(v != 0)
|
||||
case 39:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field XEmbeddedResource", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.XEmbeddedResource = bool(v != 0)
|
||||
case 40:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field XIntOrString", wireType)
|
||||
}
|
||||
var v int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowGenerated
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
v |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
m.XIntOrString = bool(v != 0)
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -7365,180 +7461,186 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 2796 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0x4b, 0x6f, 0x23, 0xc7,
|
||||
0xf1, 0xdf, 0x21, 0x45, 0x89, 0x6a, 0x49, 0x2b, 0xa9, 0xd7, 0x5a, 0xcf, 0xea, 0x2f, 0x93, 0x12,
|
||||
0xfd, 0xb7, 0xa1, 0x38, 0xbb, 0x94, 0xbd, 0xb1, 0x63, 0xc7, 0x40, 0x0e, 0x22, 0x25, 0x1b, 0x72,
|
||||
0x56, 0x8f, 0x34, 0x77, 0x6d, 0x27, 0x7e, 0xb6, 0x86, 0x4d, 0x6a, 0x56, 0xf3, 0xda, 0xe9, 0x19,
|
||||
0x4a, 0x82, 0x93, 0x20, 0x0f, 0x18, 0x09, 0x82, 0x24, 0x0e, 0xe2, 0xbd, 0x04, 0x49, 0x0e, 0x4e,
|
||||
0x90, 0x4b, 0x0e, 0xc9, 0x21, 0xb9, 0x25, 0x1f, 0x60, 0x8f, 0x46, 0x4e, 0x46, 0x10, 0x10, 0x59,
|
||||
0xfa, 0x2b, 0x04, 0x08, 0xa0, 0x53, 0xd0, 0x8f, 0xe9, 0x19, 0x0e, 0xc9, 0x5d, 0xc1, 0x4b, 0x7a,
|
||||
0x73, 0xd3, 0x54, 0x55, 0xd7, 0xaf, 0xba, 0xba, 0xaa, 0xba, 0xba, 0x28, 0xd0, 0x38, 0x7c, 0x81,
|
||||
0x96, 0x4d, 0x77, 0xed, 0x30, 0xdc, 0x27, 0xbe, 0x43, 0x02, 0x42, 0xd7, 0x5a, 0xc4, 0xa9, 0xbb,
|
||||
0xfe, 0x9a, 0x64, 0x60, 0xcf, 0x24, 0xc7, 0x01, 0x71, 0xa8, 0xe9, 0x3a, 0xf4, 0x0a, 0xf6, 0x4c,
|
||||
0x4a, 0xfc, 0x16, 0xf1, 0xd7, 0xbc, 0xc3, 0x26, 0xe3, 0xd1, 0x6e, 0x81, 0xb5, 0xd6, 0x33, 0xfb,
|
||||
0x24, 0xc0, 0xcf, 0xac, 0x35, 0x89, 0x43, 0x7c, 0x1c, 0x90, 0x7a, 0xd9, 0xf3, 0xdd, 0xc0, 0x85,
|
||||
0x5f, 0x15, 0xea, 0xca, 0x5d, 0xd2, 0xef, 0x28, 0x75, 0x65, 0xef, 0xb0, 0xc9, 0x78, 0xb4, 0x5b,
|
||||
0xa0, 0x2c, 0xd5, 0x2d, 0x5e, 0x69, 0x9a, 0xc1, 0x41, 0xb8, 0x5f, 0x36, 0x5c, 0x7b, 0xad, 0xe9,
|
||||
0x36, 0xdd, 0x35, 0xae, 0x75, 0x3f, 0x6c, 0xf0, 0x2f, 0xfe, 0xc1, 0xff, 0x12, 0x68, 0x8b, 0xcf,
|
||||
0xc6, 0xc6, 0xdb, 0xd8, 0x38, 0x30, 0x1d, 0xe2, 0x9f, 0xc4, 0x16, 0xdb, 0x24, 0xc0, 0x6b, 0xad,
|
||||
0x1e, 0x1b, 0x17, 0xd7, 0x06, 0xad, 0xf2, 0x43, 0x27, 0x30, 0x6d, 0xd2, 0xb3, 0xe0, 0xcb, 0xf7,
|
||||
0x5b, 0x40, 0x8d, 0x03, 0x62, 0xe3, 0xf4, 0xba, 0xd2, 0xa9, 0x06, 0xe6, 0xab, 0xae, 0xd3, 0x22,
|
||||
0x3e, 0xdb, 0x25, 0x22, 0xb7, 0x42, 0x42, 0x03, 0x58, 0x01, 0xd9, 0xd0, 0xac, 0xeb, 0xda, 0xb2,
|
||||
0xb6, 0x3a, 0x59, 0x79, 0xfa, 0x4e, 0xbb, 0x78, 0xae, 0xd3, 0x2e, 0x66, 0x6f, 0x6c, 0x6d, 0x9c,
|
||||
0xb6, 0x8b, 0x2b, 0x83, 0x90, 0x82, 0x13, 0x8f, 0xd0, 0xf2, 0x8d, 0xad, 0x0d, 0xc4, 0x16, 0xc3,
|
||||
0x97, 0xc1, 0x7c, 0x9d, 0x50, 0xd3, 0x27, 0xf5, 0xf5, 0xbd, 0xad, 0x57, 0x85, 0x7e, 0x3d, 0xc3,
|
||||
0x35, 0x5e, 0x92, 0x1a, 0xe7, 0x37, 0xd2, 0x02, 0xa8, 0x77, 0x0d, 0x7c, 0x1d, 0x4c, 0xb8, 0xfb,
|
||||
0x37, 0x89, 0x11, 0x50, 0x3d, 0xbb, 0x9c, 0x5d, 0x9d, 0xba, 0x7a, 0xa5, 0x1c, 0x9f, 0xa0, 0x32,
|
||||
0x81, 0x1f, 0x9b, 0xdc, 0x6c, 0x19, 0xe1, 0xa3, 0xcd, 0xe8, 0xe4, 0x2a, 0xb3, 0x12, 0x6d, 0x62,
|
||||
0x57, 0x68, 0x41, 0x91, 0xba, 0xd2, 0xef, 0x32, 0x00, 0x26, 0x37, 0x4f, 0x3d, 0xd7, 0xa1, 0x64,
|
||||
0x28, 0xbb, 0xa7, 0x60, 0xce, 0xe0, 0x9a, 0x03, 0x52, 0x97, 0xb8, 0x7a, 0xe6, 0xb3, 0x58, 0xaf,
|
||||
0x4b, 0xfc, 0xb9, 0x6a, 0x4a, 0x1d, 0xea, 0x01, 0x80, 0xd7, 0xc1, 0xb8, 0x4f, 0x68, 0x68, 0x05,
|
||||
0x7a, 0x76, 0x59, 0x5b, 0x9d, 0xba, 0x7a, 0x79, 0x20, 0x14, 0x8f, 0x6f, 0x16, 0x7c, 0xe5, 0xd6,
|
||||
0x33, 0xe5, 0x5a, 0x80, 0x83, 0x90, 0x56, 0xce, 0x4b, 0xa4, 0x71, 0xc4, 0x75, 0x20, 0xa9, 0xab,
|
||||
0xf4, 0xa3, 0x0c, 0x98, 0x4b, 0x7a, 0xa9, 0x65, 0x92, 0x23, 0x78, 0x04, 0x26, 0x7c, 0x11, 0x2c,
|
||||
0xdc, 0x4f, 0x53, 0x57, 0xf7, 0xca, 0x0f, 0x94, 0x56, 0xe5, 0x9e, 0x20, 0xac, 0x4c, 0xb1, 0x33,
|
||||
0x93, 0x1f, 0x28, 0x42, 0x83, 0xef, 0x81, 0xbc, 0x2f, 0x0f, 0x8a, 0x47, 0xd3, 0xd4, 0xd5, 0xaf,
|
||||
0x0f, 0x11, 0x59, 0x28, 0xae, 0x4c, 0x77, 0xda, 0xc5, 0x7c, 0xf4, 0x85, 0x14, 0x60, 0xe9, 0xc3,
|
||||
0x0c, 0x28, 0x54, 0x43, 0x1a, 0xb8, 0x36, 0x22, 0xd4, 0x0d, 0x7d, 0x83, 0x54, 0x5d, 0x2b, 0xb4,
|
||||
0x9d, 0x0d, 0xd2, 0x30, 0x1d, 0x33, 0x60, 0xd1, 0xba, 0x0c, 0xc6, 0x1c, 0x6c, 0x13, 0x19, 0x3d,
|
||||
0xd3, 0xd2, 0xa7, 0x63, 0x3b, 0xd8, 0x26, 0x88, 0x73, 0x98, 0x04, 0x0b, 0x16, 0x99, 0x0b, 0x4a,
|
||||
0xe2, 0xfa, 0x89, 0x47, 0x10, 0xe7, 0xc0, 0x27, 0xc1, 0x78, 0xc3, 0xf5, 0x6d, 0x2c, 0xce, 0x71,
|
||||
0x32, 0x3e, 0x99, 0x97, 0x38, 0x15, 0x49, 0x2e, 0x7c, 0x0e, 0x4c, 0xd5, 0x09, 0x35, 0x7c, 0xd3,
|
||||
0x63, 0xd0, 0xfa, 0x18, 0x17, 0xbe, 0x20, 0x85, 0xa7, 0x36, 0x62, 0x16, 0x4a, 0xca, 0xc1, 0xcb,
|
||||
0x20, 0xef, 0xf9, 0xa6, 0xeb, 0x9b, 0xc1, 0x89, 0x9e, 0x5b, 0xd6, 0x56, 0x73, 0x95, 0x39, 0xb9,
|
||||
0x26, 0xbf, 0x27, 0xe9, 0x48, 0x49, 0xc0, 0x65, 0x90, 0x7f, 0xa5, 0xb6, 0xbb, 0xb3, 0x87, 0x83,
|
||||
0x03, 0x7d, 0x9c, 0x23, 0x8c, 0x31, 0x69, 0x94, 0xbf, 0x29, 0xa9, 0xa5, 0x7f, 0x66, 0x80, 0x9e,
|
||||
0xf6, 0x4a, 0xe4, 0x52, 0xf8, 0x12, 0xc8, 0xd3, 0x80, 0x55, 0x9c, 0xe6, 0x89, 0xf4, 0xc9, 0x53,
|
||||
0x11, 0x58, 0x4d, 0xd2, 0x4f, 0xdb, 0xc5, 0x8b, 0xf1, 0x8a, 0x88, 0xca, 0xfd, 0xa1, 0xd6, 0xc2,
|
||||
0xdf, 0x68, 0xe0, 0xc2, 0x11, 0xd9, 0x3f, 0x70, 0xdd, 0xc3, 0xaa, 0x65, 0x12, 0x27, 0xa8, 0xba,
|
||||
0x4e, 0xc3, 0x6c, 0xca, 0x18, 0x40, 0x0f, 0x18, 0x03, 0xaf, 0xf5, 0x6a, 0xae, 0x3c, 0xda, 0x69,
|
||||
0x17, 0x2f, 0xf4, 0x61, 0xa0, 0x7e, 0x76, 0xc0, 0xd7, 0x81, 0x6e, 0xa4, 0x92, 0x44, 0x16, 0x30,
|
||||
0x51, 0xb6, 0x26, 0x2b, 0x4b, 0x9d, 0x76, 0x51, 0xaf, 0x0e, 0x90, 0x41, 0x03, 0x57, 0x97, 0x7e,
|
||||
0x90, 0x4d, 0xbb, 0x37, 0x11, 0x6e, 0xef, 0x82, 0x3c, 0x4b, 0xe3, 0x3a, 0x0e, 0xb0, 0x4c, 0xc4,
|
||||
0xa7, 0xcf, 0x96, 0xf4, 0xa2, 0x66, 0x6c, 0x93, 0x00, 0x57, 0xa0, 0x3c, 0x10, 0x10, 0xd3, 0x90,
|
||||
0xd2, 0x0a, 0xbf, 0x0d, 0xc6, 0xa8, 0x47, 0x0c, 0xe9, 0xe8, 0x37, 0x1e, 0x34, 0xd9, 0x06, 0x6c,
|
||||
0xa4, 0xe6, 0x11, 0x23, 0xce, 0x05, 0xf6, 0x85, 0x38, 0x2c, 0x7c, 0x5f, 0x03, 0xe3, 0x94, 0x17,
|
||||
0x28, 0x59, 0xd4, 0xde, 0x1a, 0x95, 0x05, 0xa9, 0x2a, 0x28, 0xbe, 0x91, 0x04, 0x2f, 0xfd, 0x3b,
|
||||
0x03, 0x56, 0x06, 0x2d, 0xad, 0xba, 0x4e, 0x5d, 0x1c, 0xc7, 0x96, 0xcc, 0x6d, 0x11, 0xe9, 0xcf,
|
||||
0x25, 0x73, 0xfb, 0xb4, 0x5d, 0x7c, 0xe2, 0xbe, 0x0a, 0x12, 0x45, 0xe0, 0x2b, 0x6a, 0xdf, 0xa2,
|
||||
0x50, 0xac, 0x74, 0x1b, 0x76, 0xda, 0x2e, 0xce, 0xaa, 0x65, 0xdd, 0xb6, 0xc2, 0x16, 0x80, 0x16,
|
||||
0xa6, 0xc1, 0x75, 0x1f, 0x3b, 0x54, 0xa8, 0x35, 0x6d, 0x22, 0xdd, 0xf7, 0xd4, 0xd9, 0xc2, 0x83,
|
||||
0xad, 0xa8, 0x2c, 0x4a, 0x48, 0x78, 0xad, 0x47, 0x1b, 0xea, 0x83, 0xc0, 0xea, 0x96, 0x4f, 0x30,
|
||||
0x55, 0xa5, 0x28, 0x71, 0xa3, 0x30, 0x2a, 0x92, 0x5c, 0xf8, 0x05, 0x30, 0x61, 0x13, 0x4a, 0x71,
|
||||
0x93, 0xf0, 0xfa, 0x33, 0x19, 0x5f, 0xd1, 0xdb, 0x82, 0x8c, 0x22, 0x3e, 0xeb, 0x4f, 0x96, 0x06,
|
||||
0x79, 0xed, 0x9a, 0x49, 0x03, 0xf8, 0x66, 0x4f, 0x02, 0x94, 0xcf, 0xb6, 0x43, 0xb6, 0x9a, 0x87,
|
||||
0xbf, 0x2a, 0x7e, 0x11, 0x25, 0x11, 0xfc, 0xdf, 0x02, 0x39, 0x33, 0x20, 0x76, 0x74, 0x77, 0xbf,
|
||||
0x36, 0xa2, 0xd8, 0xab, 0xcc, 0x48, 0x1b, 0x72, 0x5b, 0x0c, 0x0d, 0x09, 0xd0, 0xd2, 0xef, 0x33,
|
||||
0xe0, 0xb1, 0x41, 0x4b, 0xd8, 0x85, 0x42, 0x99, 0xc7, 0x3d, 0x2b, 0xf4, 0xb1, 0x25, 0x23, 0x4e,
|
||||
0x79, 0x7c, 0x8f, 0x53, 0x91, 0xe4, 0xb2, 0x92, 0x4f, 0x4d, 0xa7, 0x19, 0x5a, 0xd8, 0x97, 0xe1,
|
||||
0xa4, 0x76, 0x5d, 0x93, 0x74, 0xa4, 0x24, 0x60, 0x19, 0x00, 0x7a, 0xe0, 0xfa, 0x01, 0xc7, 0x90,
|
||||
0xd5, 0xeb, 0x3c, 0x2b, 0x10, 0x35, 0x45, 0x45, 0x09, 0x09, 0x76, 0xa3, 0x1d, 0x9a, 0x4e, 0x5d,
|
||||
0x9e, 0xba, 0xca, 0xe2, 0xaf, 0x99, 0x4e, 0x1d, 0x71, 0x0e, 0xc3, 0xb7, 0x4c, 0x1a, 0x30, 0x8a,
|
||||
0x3c, 0xf2, 0x2e, 0xaf, 0x73, 0x49, 0x25, 0xc1, 0xf0, 0x0d, 0x56, 0xf5, 0x5d, 0xdf, 0x24, 0x54,
|
||||
0x1f, 0x8f, 0xf1, 0xab, 0x8a, 0x8a, 0x12, 0x12, 0xa5, 0x5f, 0xe5, 0x07, 0x07, 0x09, 0x2b, 0x25,
|
||||
0xf0, 0x71, 0x90, 0x6b, 0xfa, 0x6e, 0xe8, 0x49, 0x2f, 0x29, 0x6f, 0xbf, 0xcc, 0x88, 0x48, 0xf0,
|
||||
0x58, 0x54, 0xb6, 0xba, 0xda, 0x54, 0x15, 0x95, 0x51, 0x73, 0x1a, 0xf1, 0xe1, 0xf7, 0x34, 0x90,
|
||||
0x73, 0xa4, 0x73, 0x58, 0xc8, 0xbd, 0x39, 0xa2, 0xb8, 0xe0, 0xee, 0x8d, 0xcd, 0x15, 0x9e, 0x17,
|
||||
0xc8, 0xf0, 0x59, 0x90, 0xa3, 0x86, 0xeb, 0x11, 0xe9, 0xf5, 0x42, 0x24, 0x54, 0x63, 0xc4, 0xd3,
|
||||
0x76, 0x71, 0x26, 0x52, 0xc7, 0x09, 0x48, 0x08, 0xc3, 0x1f, 0x6a, 0x00, 0xb4, 0xb0, 0x65, 0xd6,
|
||||
0x31, 0x6f, 0x19, 0x72, 0xdc, 0xfc, 0xe1, 0x86, 0xf5, 0xab, 0x4a, 0xbd, 0x38, 0xb4, 0xf8, 0x1b,
|
||||
0x25, 0xa0, 0xe1, 0x07, 0x1a, 0x98, 0xa6, 0xe1, 0xbe, 0x2f, 0x57, 0x51, 0xde, 0x5c, 0x4c, 0x5d,
|
||||
0xfd, 0xc6, 0x50, 0x6d, 0xa9, 0x25, 0x00, 0x2a, 0x73, 0x9d, 0x76, 0x71, 0x3a, 0x49, 0x41, 0x5d,
|
||||
0x06, 0xc0, 0x9f, 0x68, 0x20, 0xdf, 0x8a, 0xee, 0xec, 0x09, 0x9e, 0xf0, 0x6f, 0x8f, 0xe8, 0x60,
|
||||
0x65, 0x44, 0xc5, 0x59, 0xa0, 0xfa, 0x00, 0x65, 0x01, 0xfc, 0xab, 0x06, 0x74, 0x5c, 0x17, 0x05,
|
||||
0x1e, 0x5b, 0x7b, 0xbe, 0xe9, 0x04, 0xc4, 0x17, 0xfd, 0x26, 0xd5, 0xf3, 0xdc, 0xbc, 0xe1, 0xde,
|
||||
0x85, 0xe9, 0x5e, 0xb6, 0xb2, 0x2c, 0xad, 0xd3, 0xd7, 0x07, 0x98, 0x81, 0x06, 0x1a, 0xc8, 0x03,
|
||||
0x2d, 0x6e, 0x69, 0xf4, 0xc9, 0x11, 0x04, 0x5a, 0xdc, 0x4b, 0xc9, 0xea, 0x10, 0x77, 0x50, 0x09,
|
||||
0xe8, 0xd2, 0x07, 0xd9, 0x74, 0xd3, 0x9e, 0xbe, 0xf4, 0xe1, 0x6d, 0x61, 0xac, 0xd8, 0x0a, 0xd5,
|
||||
0x35, 0xee, 0xdc, 0x77, 0x47, 0x74, 0xf6, 0xea, 0xd6, 0x8e, 0x1b, 0x2f, 0x45, 0xa2, 0x28, 0x61,
|
||||
0x07, 0xfc, 0xa5, 0x06, 0x66, 0xb0, 0x61, 0x10, 0x2f, 0x20, 0x75, 0x51, 0x8b, 0x33, 0x9f, 0x43,
|
||||
0xb9, 0x59, 0x90, 0x56, 0xcd, 0xac, 0x27, 0xa1, 0x51, 0xb7, 0x25, 0xf0, 0x45, 0x70, 0x9e, 0x06,
|
||||
0xae, 0x4f, 0xea, 0xa9, 0x2e, 0x17, 0x76, 0xda, 0xc5, 0xf3, 0xb5, 0x2e, 0x0e, 0x4a, 0x49, 0x96,
|
||||
0x3e, 0x1d, 0x03, 0xc5, 0xfb, 0x64, 0xc6, 0x19, 0xde, 0x51, 0x4f, 0x82, 0x71, 0xbe, 0xdd, 0x3a,
|
||||
0xf7, 0x4a, 0x3e, 0xd1, 0xb9, 0x71, 0x2a, 0x92, 0x5c, 0x56, 0xd7, 0x19, 0x3e, 0xeb, 0x36, 0xb2,
|
||||
0x5c, 0x50, 0xd5, 0xf5, 0x9a, 0x20, 0xa3, 0x88, 0x0f, 0xdf, 0x03, 0xe3, 0x62, 0x4e, 0xc2, 0x8b,
|
||||
0xea, 0x08, 0x0b, 0x23, 0xe0, 0x76, 0x72, 0x28, 0x24, 0x21, 0x7b, 0x0b, 0x62, 0xee, 0x61, 0x17,
|
||||
0xc4, 0x7b, 0x56, 0xa0, 0xf1, 0xff, 0xf1, 0x0a, 0x54, 0xfa, 0x8f, 0x96, 0xce, 0xfb, 0xc4, 0x56,
|
||||
0x6b, 0x06, 0xb6, 0x08, 0xdc, 0x00, 0x73, 0xec, 0x91, 0x81, 0x88, 0x67, 0x99, 0x06, 0xa6, 0xfc,
|
||||
0x8d, 0x2b, 0x02, 0x4e, 0x8d, 0x5d, 0x6a, 0x29, 0x3e, 0xea, 0x59, 0x01, 0x5f, 0x01, 0x50, 0x34,
|
||||
0xde, 0x5d, 0x7a, 0x44, 0x0f, 0xa1, 0x5a, 0xe8, 0x5a, 0x8f, 0x04, 0xea, 0xb3, 0x0a, 0x56, 0xc1,
|
||||
0xbc, 0x85, 0xf7, 0x89, 0x55, 0x23, 0x16, 0x31, 0x02, 0xd7, 0xe7, 0xaa, 0xc4, 0x14, 0x60, 0xa1,
|
||||
0xd3, 0x2e, 0xce, 0x5f, 0x4b, 0x33, 0x51, 0xaf, 0x7c, 0x69, 0x25, 0x9d, 0x5e, 0xc9, 0x8d, 0x8b,
|
||||
0xe7, 0xcc, 0x47, 0x19, 0xb0, 0x38, 0x38, 0x32, 0xe0, 0xf7, 0xe3, 0x57, 0x97, 0x68, 0xaa, 0xdf,
|
||||
0x1e, 0x55, 0x14, 0xca, 0x67, 0x17, 0xe8, 0x7d, 0x72, 0xc1, 0xef, 0xb0, 0x0e, 0x07, 0x5b, 0xd1,
|
||||
0x9c, 0xe7, 0xad, 0x91, 0x99, 0xc0, 0x40, 0x2a, 0x93, 0xa2, 0x79, 0xc2, 0x16, 0xef, 0x95, 0xb0,
|
||||
0x45, 0x4a, 0x7f, 0xd0, 0xd2, 0x0f, 0xef, 0x38, 0x83, 0xe1, 0x4f, 0x35, 0x30, 0xeb, 0x7a, 0xc4,
|
||||
0x59, 0xdf, 0xdb, 0x7a, 0xf5, 0x4b, 0x22, 0x93, 0xa5, 0xab, 0x76, 0x1e, 0xd0, 0xce, 0x57, 0x6a,
|
||||
0xbb, 0x3b, 0x42, 0xe1, 0x9e, 0xef, 0x7a, 0xb4, 0x72, 0xa1, 0xd3, 0x2e, 0xce, 0xee, 0x76, 0x43,
|
||||
0xa1, 0x34, 0x76, 0xc9, 0x06, 0x0b, 0x9b, 0xc7, 0x01, 0xf1, 0x1d, 0x6c, 0x6d, 0xb8, 0x46, 0x68,
|
||||
0x13, 0x27, 0x10, 0x86, 0xa6, 0x86, 0x44, 0xda, 0x19, 0x87, 0x44, 0x8f, 0x81, 0x6c, 0xe8, 0x5b,
|
||||
0x32, 0x8a, 0xa7, 0xd4, 0x10, 0x14, 0x5d, 0x43, 0x8c, 0x5e, 0x5a, 0x01, 0x63, 0xcc, 0x4e, 0x78,
|
||||
0x09, 0x64, 0x7d, 0x7c, 0xc4, 0xb5, 0x4e, 0x57, 0x26, 0x98, 0x08, 0xc2, 0x47, 0x88, 0xd1, 0x4a,
|
||||
0xff, 0x58, 0x02, 0xb3, 0xa9, 0xbd, 0xc0, 0x45, 0x90, 0x51, 0x93, 0x55, 0x20, 0x95, 0x66, 0xb6,
|
||||
0x36, 0x50, 0xc6, 0xac, 0xc3, 0xe7, 0x55, 0xf1, 0x15, 0xa0, 0x45, 0x55, 0xcf, 0x39, 0x95, 0xb5,
|
||||
0xb4, 0xb1, 0x3a, 0x66, 0x48, 0x54, 0x38, 0x99, 0x0d, 0xa4, 0x21, 0xb3, 0x44, 0xd8, 0x40, 0x1a,
|
||||
0x88, 0xd1, 0x3e, 0xeb, 0x84, 0x2c, 0x1a, 0xd1, 0xe5, 0xce, 0x30, 0xa2, 0x1b, 0xbf, 0xe7, 0x88,
|
||||
0xee, 0x71, 0x90, 0x0b, 0xcc, 0xc0, 0x22, 0xfa, 0x44, 0xf7, 0xcb, 0xe3, 0x3a, 0x23, 0x22, 0xc1,
|
||||
0x83, 0x37, 0xc1, 0x44, 0x9d, 0x34, 0x70, 0x68, 0x05, 0x7a, 0x9e, 0x87, 0x50, 0x75, 0x08, 0x21,
|
||||
0x24, 0xe6, 0xa7, 0x1b, 0x42, 0x2f, 0x8a, 0x00, 0xe0, 0x13, 0x60, 0xc2, 0xc6, 0xc7, 0xa6, 0x1d,
|
||||
0xda, 0xbc, 0x27, 0xd3, 0x84, 0xd8, 0xb6, 0x20, 0xa1, 0x88, 0xc7, 0x2a, 0x23, 0x39, 0x36, 0xac,
|
||||
0x90, 0x9a, 0x2d, 0x22, 0x99, 0x3a, 0xe0, 0xb7, 0xa7, 0xaa, 0x8c, 0x9b, 0x29, 0x3e, 0xea, 0x59,
|
||||
0xc1, 0xc1, 0x4c, 0x87, 0x2f, 0x9e, 0x4a, 0x80, 0x09, 0x12, 0x8a, 0x78, 0xdd, 0x60, 0x52, 0x7e,
|
||||
0x7a, 0x10, 0x98, 0x5c, 0xdc, 0xb3, 0x02, 0x7e, 0x11, 0x4c, 0xda, 0xf8, 0xf8, 0x1a, 0x71, 0x9a,
|
||||
0xc1, 0x81, 0x3e, 0xb3, 0xac, 0xad, 0x66, 0x2b, 0x33, 0x9d, 0x76, 0x71, 0x72, 0x3b, 0x22, 0xa2,
|
||||
0x98, 0xcf, 0x85, 0x4d, 0x47, 0x0a, 0x9f, 0x4f, 0x08, 0x47, 0x44, 0x14, 0xf3, 0x59, 0x07, 0xe1,
|
||||
0xe1, 0x80, 0x25, 0x97, 0x3e, 0xdb, 0xfd, 0x32, 0xdc, 0x13, 0x64, 0x14, 0xf1, 0xe1, 0x2a, 0xc8,
|
||||
0xdb, 0xf8, 0x98, 0xbf, 0xe2, 0xf5, 0x39, 0xae, 0x96, 0xcf, 0x92, 0xb7, 0x25, 0x0d, 0x29, 0x2e,
|
||||
0x97, 0x34, 0x1d, 0x21, 0x39, 0x9f, 0x90, 0x94, 0x34, 0xa4, 0xb8, 0x2c, 0x88, 0x43, 0xc7, 0xbc,
|
||||
0x15, 0x12, 0x21, 0x0c, 0xb9, 0x67, 0x54, 0x10, 0xdf, 0x88, 0x59, 0x28, 0x29, 0xc7, 0x5e, 0xd1,
|
||||
0x76, 0x68, 0x05, 0xa6, 0x67, 0x91, 0xdd, 0x86, 0x7e, 0x81, 0xfb, 0x9f, 0xf7, 0xc9, 0xdb, 0x8a,
|
||||
0x8a, 0x12, 0x12, 0x90, 0x80, 0x31, 0xe2, 0x84, 0xb6, 0xfe, 0x08, 0xbf, 0xd8, 0x87, 0x12, 0x82,
|
||||
0x2a, 0x73, 0x36, 0x9d, 0xd0, 0x46, 0x5c, 0x3d, 0x7c, 0x1e, 0xcc, 0xd8, 0xf8, 0x98, 0x95, 0x03,
|
||||
0xe2, 0x07, 0xec, 0x7d, 0xbf, 0xc0, 0x37, 0x3f, 0xcf, 0x3a, 0xce, 0xed, 0x24, 0x03, 0x75, 0xcb,
|
||||
0xf1, 0x85, 0xa6, 0x93, 0x58, 0x78, 0x31, 0xb1, 0x30, 0xc9, 0x40, 0xdd, 0x72, 0xcc, 0xd3, 0x3e,
|
||||
0xb9, 0x15, 0x9a, 0x3e, 0xa9, 0xeb, 0x8f, 0xf2, 0x26, 0x55, 0xce, 0xf7, 0x05, 0x0d, 0x29, 0x2e,
|
||||
0x6c, 0x45, 0xe3, 0x1e, 0x9d, 0xa7, 0xe1, 0x8d, 0xe1, 0x56, 0xf2, 0x5d, 0x7f, 0xdd, 0xf7, 0xf1,
|
||||
0x89, 0xb8, 0x69, 0x92, 0x83, 0x1e, 0x48, 0x41, 0x0e, 0x5b, 0xd6, 0x6e, 0x43, 0xbf, 0xc4, 0x7d,
|
||||
0x3f, 0xec, 0x1b, 0x44, 0x55, 0x9d, 0x75, 0x06, 0x82, 0x04, 0x16, 0x03, 0x75, 0x1d, 0x16, 0x1a,
|
||||
0x8b, 0xa3, 0x05, 0xdd, 0x65, 0x20, 0x48, 0x60, 0xf1, 0x9d, 0x3a, 0x27, 0xbb, 0x0d, 0xfd, 0xff,
|
||||
0x46, 0xbc, 0x53, 0x06, 0x82, 0x04, 0x16, 0x34, 0x41, 0xd6, 0x71, 0x03, 0x7d, 0x69, 0x24, 0xd7,
|
||||
0x33, 0xbf, 0x70, 0x76, 0xdc, 0x00, 0x31, 0x0c, 0xf8, 0x0b, 0x0d, 0x00, 0x2f, 0x0e, 0xd1, 0xc7,
|
||||
0x86, 0x32, 0x45, 0x48, 0x41, 0x96, 0xe3, 0xd8, 0xde, 0x74, 0x02, 0xff, 0x24, 0x7e, 0x47, 0x26,
|
||||
0x72, 0x20, 0x61, 0x05, 0xfc, 0xad, 0x06, 0x1e, 0x49, 0xb6, 0xc9, 0xca, 0xbc, 0x02, 0xf7, 0xc8,
|
||||
0xf5, 0x61, 0x87, 0x79, 0xc5, 0x75, 0xad, 0x8a, 0xde, 0x69, 0x17, 0x1f, 0x59, 0xef, 0x83, 0x8a,
|
||||
0xfa, 0xda, 0x02, 0xff, 0xa8, 0x81, 0x79, 0x59, 0x45, 0x13, 0x16, 0x16, 0xb9, 0x03, 0xc9, 0xb0,
|
||||
0x1d, 0x98, 0xc6, 0x11, 0x7e, 0x54, 0xbf, 0x4b, 0xf7, 0xf0, 0x51, 0xaf, 0x69, 0xf0, 0x2f, 0x1a,
|
||||
0x98, 0xae, 0x13, 0x8f, 0x38, 0x75, 0xe2, 0x18, 0xcc, 0xd6, 0xe5, 0xa1, 0x8c, 0x0d, 0xd2, 0xb6,
|
||||
0x6e, 0x24, 0x20, 0x84, 0x99, 0x65, 0x69, 0xe6, 0x74, 0x92, 0x75, 0xda, 0x2e, 0x5e, 0x8c, 0x97,
|
||||
0x26, 0x39, 0xa8, 0xcb, 0x4a, 0xf8, 0xa1, 0x06, 0x66, 0xe3, 0x03, 0x10, 0x57, 0xca, 0xca, 0x08,
|
||||
0xe3, 0x80, 0xb7, 0xaf, 0xeb, 0xdd, 0x80, 0x28, 0x6d, 0x01, 0xfc, 0x93, 0xc6, 0x3a, 0xb5, 0xe8,
|
||||
0xdd, 0x47, 0xf5, 0x12, 0xf7, 0xe5, 0x3b, 0x43, 0xf7, 0xa5, 0x42, 0x10, 0xae, 0xbc, 0x1c, 0xb7,
|
||||
0x82, 0x8a, 0x73, 0xda, 0x2e, 0x2e, 0x24, 0x3d, 0xa9, 0x18, 0x28, 0x69, 0x21, 0xfc, 0xb1, 0x06,
|
||||
0xa6, 0x49, 0xdc, 0x71, 0x53, 0xfd, 0xf1, 0xa1, 0x38, 0xb1, 0x6f, 0x13, 0x2f, 0x5e, 0xea, 0x09,
|
||||
0x16, 0x45, 0x5d, 0xd8, 0xac, 0x83, 0x24, 0xc7, 0xd8, 0xf6, 0x2c, 0xa2, 0xff, 0xff, 0x90, 0x3b,
|
||||
0xc8, 0x4d, 0xa1, 0x17, 0x45, 0x00, 0xf0, 0x32, 0xc8, 0x3b, 0xa1, 0x65, 0xe1, 0x7d, 0x8b, 0xe8,
|
||||
0x4f, 0xf0, 0x5e, 0x44, 0x4d, 0x31, 0x77, 0x24, 0x1d, 0x29, 0x89, 0x45, 0xf6, 0x4e, 0x4a, 0xe5,
|
||||
0x19, 0x9c, 0x03, 0xd9, 0x43, 0x22, 0x7f, 0x0e, 0x46, 0xec, 0x4f, 0x58, 0x07, 0xb9, 0x16, 0xb6,
|
||||
0xc2, 0xe8, 0xa9, 0x37, 0xe4, 0x1a, 0x8d, 0x84, 0xf2, 0x17, 0x33, 0x2f, 0x68, 0x8b, 0xb7, 0x35,
|
||||
0x70, 0xb1, 0x7f, 0xfa, 0x3f, 0x54, 0xb3, 0x7e, 0xad, 0x81, 0xf9, 0x9e, 0x4c, 0xef, 0x63, 0xd1,
|
||||
0xad, 0x6e, 0x8b, 0xde, 0x18, 0x76, 0xca, 0xd6, 0x02, 0xdf, 0x74, 0x9a, 0xbc, 0x4f, 0x49, 0x9a,
|
||||
0xf7, 0x33, 0x0d, 0xcc, 0xa5, 0x93, 0xe7, 0x61, 0xfa, 0xab, 0x74, 0x3b, 0x03, 0x2e, 0xf6, 0x6f,
|
||||
0xaf, 0xa0, 0xaf, 0xde, 0x91, 0xa3, 0x79, 0x8f, 0xf7, 0x9b, 0xdd, 0xbd, 0xaf, 0x81, 0xa9, 0x9b,
|
||||
0x4a, 0x2e, 0xfa, 0xb9, 0x70, 0xe8, 0x93, 0x80, 0xa8, 0x5a, 0xc5, 0x0c, 0x8a, 0x92, 0xb8, 0xa5,
|
||||
0x3f, 0x6b, 0x60, 0xa1, 0x6f, 0x19, 0x66, 0x0f, 0x56, 0x6c, 0x59, 0xee, 0x91, 0x18, 0xe8, 0x24,
|
||||
0xa6, 0xa5, 0xeb, 0x9c, 0x8a, 0x24, 0x37, 0xe1, 0xbd, 0xcc, 0xe7, 0xe5, 0xbd, 0xd2, 0xdf, 0x34,
|
||||
0xb0, 0x74, 0xaf, 0x48, 0x7c, 0x28, 0x47, 0xba, 0x0a, 0xf2, 0xb2, 0x85, 0x3a, 0xe1, 0xc7, 0x29,
|
||||
0x5f, 0x0d, 0xb2, 0x68, 0xf0, 0xff, 0x90, 0x11, 0x7f, 0x95, 0x3e, 0xd2, 0xc0, 0x5c, 0x8d, 0xf8,
|
||||
0x2d, 0xd3, 0x20, 0x88, 0x34, 0x88, 0x4f, 0x1c, 0x83, 0xc0, 0x35, 0x30, 0xc9, 0x7f, 0xa7, 0xf3,
|
||||
0xb0, 0x11, 0x0d, 0xb1, 0xe7, 0xa5, 0xcb, 0x27, 0x77, 0x22, 0x06, 0x8a, 0x65, 0xd4, 0xc0, 0x3b,
|
||||
0x33, 0x70, 0xe0, 0xbd, 0x04, 0xc6, 0xbc, 0x78, 0x1c, 0x98, 0x67, 0x5c, 0x3e, 0x01, 0xe4, 0x54,
|
||||
0xce, 0x75, 0xfd, 0x80, 0xcf, 0x38, 0x72, 0x92, 0xeb, 0xfa, 0x01, 0xe2, 0xd4, 0xd2, 0xdf, 0x35,
|
||||
0xd0, 0xef, 0x7f, 0x59, 0x60, 0x0b, 0x4c, 0x50, 0x61, 0xba, 0x74, 0xed, 0xee, 0x03, 0xba, 0x36,
|
||||
0xed, 0x08, 0x71, 0x89, 0x44, 0xd4, 0x08, 0x8c, 0x79, 0xd7, 0xc0, 0x95, 0xd0, 0xa9, 0xcb, 0xf1,
|
||||
0xde, 0xb4, 0xf0, 0x6e, 0x75, 0x5d, 0xd0, 0x90, 0xe2, 0xc2, 0x4b, 0x62, 0x10, 0x95, 0x98, 0xee,
|
||||
0x44, 0x43, 0xa8, 0xca, 0x95, 0x3b, 0x77, 0x0b, 0xe7, 0x3e, 0xbe, 0x5b, 0x38, 0xf7, 0xc9, 0xdd,
|
||||
0xc2, 0xb9, 0xef, 0x76, 0x0a, 0xda, 0x9d, 0x4e, 0x41, 0xfb, 0xb8, 0x53, 0xd0, 0x3e, 0xe9, 0x14,
|
||||
0xb4, 0x7f, 0x75, 0x0a, 0xda, 0xcf, 0x3f, 0x2d, 0x9c, 0xfb, 0xe6, 0x84, 0x34, 0xed, 0xbf, 0x01,
|
||||
0x00, 0x00, 0xff, 0xff, 0xd1, 0xda, 0xdd, 0xd0, 0x61, 0x2a, 0x00, 0x00,
|
||||
// 2884 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0x1c, 0x47,
|
||||
0x15, 0xf7, 0xac, 0xb4, 0xd2, 0xaa, 0x25, 0x59, 0x52, 0xdb, 0x56, 0xc6, 0x8a, 0xb3, 0x2b, 0x6f,
|
||||
0x48, 0x10, 0xc1, 0x5e, 0x25, 0x26, 0x21, 0x21, 0x55, 0x1c, 0xb4, 0x92, 0x92, 0x52, 0x62, 0x7d,
|
||||
0xd0, 0x6b, 0x27, 0x86, 0x7c, 0xb6, 0x76, 0x5a, 0xab, 0xb1, 0xe6, 0xcb, 0xdd, 0x33, 0x2b, 0xa9,
|
||||
0x02, 0x14, 0x1f, 0x95, 0x82, 0xa2, 0x80, 0x50, 0x24, 0x17, 0x0a, 0x38, 0x04, 0x8a, 0x0b, 0x07,
|
||||
0x38, 0xc0, 0x0d, 0xfe, 0x80, 0x1c, 0x53, 0x9c, 0x72, 0xa0, 0xb6, 0xf0, 0xe6, 0x2f, 0xa0, 0x8a,
|
||||
0x2a, 0xaa, 0x74, 0xa2, 0xfa, 0x63, 0x7a, 0x66, 0x67, 0x77, 0x6d, 0x57, 0xbc, 0x1b, 0x73, 0xd3,
|
||||
0xbc, 0xaf, 0xdf, 0xeb, 0xd7, 0xaf, 0x5f, 0xbf, 0x7e, 0x2b, 0xb0, 0x77, 0xf0, 0x1c, 0xab, 0xd8,
|
||||
0xfe, 0xf2, 0x41, 0xb4, 0x4b, 0xa8, 0x47, 0x42, 0xc2, 0x96, 0x9b, 0xc4, 0xb3, 0x7c, 0xba, 0xac,
|
||||
0x18, 0x38, 0xb0, 0xc9, 0x51, 0x48, 0x3c, 0x66, 0xfb, 0x1e, 0xbb, 0x8c, 0x03, 0x9b, 0x11, 0xda,
|
||||
0x24, 0x74, 0x39, 0x38, 0x68, 0x70, 0x1e, 0xeb, 0x14, 0x58, 0x6e, 0x3e, 0xb5, 0x4b, 0x42, 0xfc,
|
||||
0xd4, 0x72, 0x83, 0x78, 0x84, 0xe2, 0x90, 0x58, 0x95, 0x80, 0xfa, 0xa1, 0x0f, 0xbf, 0x2e, 0xcd,
|
||||
0x55, 0x3a, 0xa4, 0xdf, 0xd2, 0xe6, 0x2a, 0xc1, 0x41, 0x83, 0xf3, 0x58, 0xa7, 0x40, 0x45, 0x99,
|
||||
0x5b, 0xb8, 0xdc, 0xb0, 0xc3, 0xfd, 0x68, 0xb7, 0x52, 0xf7, 0xdd, 0xe5, 0x86, 0xdf, 0xf0, 0x97,
|
||||
0x85, 0xd5, 0xdd, 0x68, 0x4f, 0x7c, 0x89, 0x0f, 0xf1, 0x97, 0x44, 0x5b, 0x78, 0x3a, 0x71, 0xde,
|
||||
0xc5, 0xf5, 0x7d, 0xdb, 0x23, 0xf4, 0x38, 0xf1, 0xd8, 0x25, 0x21, 0x5e, 0x6e, 0x76, 0xf9, 0xb8,
|
||||
0xb0, 0xdc, 0x4f, 0x8b, 0x46, 0x5e, 0x68, 0xbb, 0xa4, 0x4b, 0xe1, 0xab, 0x77, 0x53, 0x60, 0xf5,
|
||||
0x7d, 0xe2, 0xe2, 0xac, 0x5e, 0xf9, 0xc4, 0x00, 0x73, 0xab, 0xbe, 0xd7, 0x24, 0x94, 0xaf, 0x12,
|
||||
0x91, 0x5b, 0x11, 0x61, 0x21, 0xac, 0x82, 0x91, 0xc8, 0xb6, 0x4c, 0x63, 0xd1, 0x58, 0x9a, 0xa8,
|
||||
0x3e, 0xf9, 0x51, 0xab, 0x74, 0xaa, 0xdd, 0x2a, 0x8d, 0x5c, 0xdf, 0x58, 0x3b, 0x69, 0x95, 0x2e,
|
||||
0xf6, 0x43, 0x0a, 0x8f, 0x03, 0xc2, 0x2a, 0xd7, 0x37, 0xd6, 0x10, 0x57, 0x86, 0x2f, 0x82, 0x39,
|
||||
0x8b, 0x30, 0x9b, 0x12, 0x6b, 0x65, 0x67, 0xe3, 0x15, 0x69, 0xdf, 0xcc, 0x09, 0x8b, 0xe7, 0x95,
|
||||
0xc5, 0xb9, 0xb5, 0xac, 0x00, 0xea, 0xd6, 0x81, 0x37, 0xc0, 0xb8, 0xbf, 0x7b, 0x93, 0xd4, 0x43,
|
||||
0x66, 0x8e, 0x2c, 0x8e, 0x2c, 0x4d, 0x5e, 0xb9, 0x5c, 0x49, 0x76, 0x50, 0xbb, 0x20, 0xb6, 0x4d,
|
||||
0x2d, 0xb6, 0x82, 0xf0, 0xe1, 0x7a, 0xbc, 0x73, 0xd5, 0x19, 0x85, 0x36, 0xbe, 0x2d, 0xad, 0xa0,
|
||||
0xd8, 0x5c, 0xf9, 0xf7, 0x39, 0x00, 0xd3, 0x8b, 0x67, 0x81, 0xef, 0x31, 0x32, 0x90, 0xd5, 0x33,
|
||||
0x30, 0x5b, 0x17, 0x96, 0x43, 0x62, 0x29, 0x5c, 0x33, 0xf7, 0x59, 0xbc, 0x37, 0x15, 0xfe, 0xec,
|
||||
0x6a, 0xc6, 0x1c, 0xea, 0x02, 0x80, 0xd7, 0xc0, 0x18, 0x25, 0x2c, 0x72, 0x42, 0x73, 0x64, 0xd1,
|
||||
0x58, 0x9a, 0xbc, 0x72, 0xa9, 0x2f, 0x94, 0xc8, 0x6f, 0x9e, 0x7c, 0x95, 0xe6, 0x53, 0x95, 0x5a,
|
||||
0x88, 0xc3, 0x88, 0x55, 0x4f, 0x2b, 0xa4, 0x31, 0x24, 0x6c, 0x20, 0x65, 0xab, 0xfc, 0xe3, 0x1c,
|
||||
0x98, 0x4d, 0x47, 0xa9, 0x69, 0x93, 0x43, 0x78, 0x08, 0xc6, 0xa9, 0x4c, 0x16, 0x11, 0xa7, 0xc9,
|
||||
0x2b, 0x3b, 0x95, 0xfb, 0x3a, 0x56, 0x95, 0xae, 0x24, 0xac, 0x4e, 0xf2, 0x3d, 0x53, 0x1f, 0x28,
|
||||
0x46, 0x83, 0xef, 0x80, 0x02, 0x55, 0x1b, 0x25, 0xb2, 0x69, 0xf2, 0xca, 0x37, 0x06, 0x88, 0x2c,
|
||||
0x0d, 0x57, 0xa7, 0xda, 0xad, 0x52, 0x21, 0xfe, 0x42, 0x1a, 0xb0, 0xfc, 0x7e, 0x0e, 0x14, 0x57,
|
||||
0x23, 0x16, 0xfa, 0x2e, 0x22, 0xcc, 0x8f, 0x68, 0x9d, 0xac, 0xfa, 0x4e, 0xe4, 0x7a, 0x6b, 0x64,
|
||||
0xcf, 0xf6, 0xec, 0x90, 0x67, 0xeb, 0x22, 0x18, 0xf5, 0xb0, 0x4b, 0x54, 0xf6, 0x4c, 0xa9, 0x98,
|
||||
0x8e, 0x6e, 0x61, 0x97, 0x20, 0xc1, 0xe1, 0x12, 0x3c, 0x59, 0xd4, 0x59, 0xd0, 0x12, 0xd7, 0x8e,
|
||||
0x03, 0x82, 0x04, 0x07, 0x3e, 0x0e, 0xc6, 0xf6, 0x7c, 0xea, 0x62, 0xb9, 0x8f, 0x13, 0xc9, 0xce,
|
||||
0xbc, 0x20, 0xa8, 0x48, 0x71, 0xe1, 0x33, 0x60, 0xd2, 0x22, 0xac, 0x4e, 0xed, 0x80, 0x43, 0x9b,
|
||||
0xa3, 0x42, 0xf8, 0x8c, 0x12, 0x9e, 0x5c, 0x4b, 0x58, 0x28, 0x2d, 0x07, 0x2f, 0x81, 0x42, 0x40,
|
||||
0x6d, 0x9f, 0xda, 0xe1, 0xb1, 0x99, 0x5f, 0x34, 0x96, 0xf2, 0xd5, 0x59, 0xa5, 0x53, 0xd8, 0x51,
|
||||
0x74, 0xa4, 0x25, 0xe0, 0x22, 0x28, 0xbc, 0x54, 0xdb, 0xde, 0xda, 0xc1, 0xe1, 0xbe, 0x39, 0x26,
|
||||
0x10, 0x46, 0xb9, 0x34, 0x2a, 0xdc, 0x54, 0xd4, 0xf2, 0x3f, 0x73, 0xc0, 0xcc, 0x46, 0x25, 0x0e,
|
||||
0x29, 0x7c, 0x01, 0x14, 0x58, 0xc8, 0x2b, 0x4e, 0xe3, 0x58, 0xc5, 0xe4, 0x89, 0x18, 0xac, 0xa6,
|
||||
0xe8, 0x27, 0xad, 0xd2, 0x7c, 0xa2, 0x11, 0x53, 0x45, 0x3c, 0xb4, 0x2e, 0xfc, 0xad, 0x01, 0xce,
|
||||
0x1c, 0x92, 0xdd, 0x7d, 0xdf, 0x3f, 0x58, 0x75, 0x6c, 0xe2, 0x85, 0xab, 0xbe, 0xb7, 0x67, 0x37,
|
||||
0x54, 0x0e, 0xa0, 0xfb, 0xcc, 0x81, 0x57, 0xbb, 0x2d, 0x57, 0x1f, 0x6a, 0xb7, 0x4a, 0x67, 0x7a,
|
||||
0x30, 0x50, 0x2f, 0x3f, 0xe0, 0x0d, 0x60, 0xd6, 0x33, 0x87, 0x44, 0x15, 0x30, 0x59, 0xb6, 0x26,
|
||||
0xaa, 0x17, 0xda, 0xad, 0x92, 0xb9, 0xda, 0x47, 0x06, 0xf5, 0xd5, 0x2e, 0xff, 0x70, 0x24, 0x1b,
|
||||
0xde, 0x54, 0xba, 0xbd, 0x0d, 0x0a, 0xfc, 0x18, 0x5b, 0x38, 0xc4, 0xea, 0x20, 0x3e, 0x79, 0x6f,
|
||||
0x87, 0x5e, 0xd6, 0x8c, 0x4d, 0x12, 0xe2, 0x2a, 0x54, 0x1b, 0x02, 0x12, 0x1a, 0xd2, 0x56, 0xe1,
|
||||
0x77, 0xc0, 0x28, 0x0b, 0x48, 0x5d, 0x05, 0xfa, 0xb5, 0xfb, 0x3d, 0x6c, 0x7d, 0x16, 0x52, 0x0b,
|
||||
0x48, 0x3d, 0x39, 0x0b, 0xfc, 0x0b, 0x09, 0x58, 0xf8, 0xae, 0x01, 0xc6, 0x98, 0x28, 0x50, 0xaa,
|
||||
0xa8, 0xbd, 0x31, 0x2c, 0x0f, 0x32, 0x55, 0x50, 0x7e, 0x23, 0x05, 0x5e, 0xfe, 0x4f, 0x0e, 0x5c,
|
||||
0xec, 0xa7, 0xba, 0xea, 0x7b, 0x96, 0xdc, 0x8e, 0x0d, 0x75, 0xb6, 0x65, 0xa6, 0x3f, 0x93, 0x3e,
|
||||
0xdb, 0x27, 0xad, 0xd2, 0x63, 0x77, 0x35, 0x90, 0x2a, 0x02, 0x5f, 0xd3, 0xeb, 0x96, 0x85, 0xe2,
|
||||
0x62, 0xa7, 0x63, 0x27, 0xad, 0xd2, 0x8c, 0x56, 0xeb, 0xf4, 0x15, 0x36, 0x01, 0x74, 0x30, 0x0b,
|
||||
0xaf, 0x51, 0xec, 0x31, 0x69, 0xd6, 0x76, 0x89, 0x0a, 0xdf, 0x13, 0xf7, 0x96, 0x1e, 0x5c, 0xa3,
|
||||
0xba, 0xa0, 0x20, 0xe1, 0xd5, 0x2e, 0x6b, 0xa8, 0x07, 0x02, 0xaf, 0x5b, 0x94, 0x60, 0xa6, 0x4b,
|
||||
0x51, 0xea, 0x46, 0xe1, 0x54, 0xa4, 0xb8, 0xf0, 0x4b, 0x60, 0xdc, 0x25, 0x8c, 0xe1, 0x06, 0x11,
|
||||
0xf5, 0x67, 0x22, 0xb9, 0xa2, 0x37, 0x25, 0x19, 0xc5, 0x7c, 0xde, 0x9f, 0x5c, 0xe8, 0x17, 0xb5,
|
||||
0xab, 0x36, 0x0b, 0xe1, 0xeb, 0x5d, 0x07, 0xa0, 0x72, 0x6f, 0x2b, 0xe4, 0xda, 0x22, 0xfd, 0x75,
|
||||
0xf1, 0x8b, 0x29, 0xa9, 0xe4, 0xff, 0x36, 0xc8, 0xdb, 0x21, 0x71, 0xe3, 0xbb, 0xfb, 0xd5, 0x21,
|
||||
0xe5, 0x5e, 0x75, 0x5a, 0xf9, 0x90, 0xdf, 0xe0, 0x68, 0x48, 0x82, 0x96, 0xff, 0x90, 0x03, 0x8f,
|
||||
0xf4, 0x53, 0xe1, 0x17, 0x0a, 0xe3, 0x11, 0x0f, 0x9c, 0x88, 0x62, 0x47, 0x65, 0x9c, 0x8e, 0xf8,
|
||||
0x8e, 0xa0, 0x22, 0xc5, 0xe5, 0x25, 0x9f, 0xd9, 0x5e, 0x23, 0x72, 0x30, 0x55, 0xe9, 0xa4, 0x57,
|
||||
0x5d, 0x53, 0x74, 0xa4, 0x25, 0x60, 0x05, 0x00, 0xb6, 0xef, 0xd3, 0x50, 0x60, 0xa8, 0xea, 0x75,
|
||||
0x9a, 0x17, 0x88, 0x9a, 0xa6, 0xa2, 0x94, 0x04, 0xbf, 0xd1, 0x0e, 0x6c, 0xcf, 0x52, 0xbb, 0xae,
|
||||
0x4f, 0xf1, 0xcb, 0xb6, 0x67, 0x21, 0xc1, 0xe1, 0xf8, 0x8e, 0xcd, 0x42, 0x4e, 0x51, 0x5b, 0xde,
|
||||
0x11, 0x75, 0x21, 0xa9, 0x25, 0x38, 0x7e, 0x9d, 0x57, 0x7d, 0x9f, 0xda, 0x84, 0x99, 0x63, 0x09,
|
||||
0xfe, 0xaa, 0xa6, 0xa2, 0x94, 0x44, 0xf9, 0xd7, 0x85, 0xfe, 0x49, 0xc2, 0x4b, 0x09, 0x7c, 0x14,
|
||||
0xe4, 0x1b, 0xd4, 0x8f, 0x02, 0x15, 0x25, 0x1d, 0xed, 0x17, 0x39, 0x11, 0x49, 0x1e, 0xcf, 0xca,
|
||||
0x66, 0x47, 0x9b, 0xaa, 0xb3, 0x32, 0x6e, 0x4e, 0x63, 0x3e, 0xfc, 0xbe, 0x01, 0xf2, 0x9e, 0x0a,
|
||||
0x0e, 0x4f, 0xb9, 0xd7, 0x87, 0x94, 0x17, 0x22, 0xbc, 0x89, 0xbb, 0x32, 0xf2, 0x12, 0x19, 0x3e,
|
||||
0x0d, 0xf2, 0xac, 0xee, 0x07, 0x44, 0x45, 0xbd, 0x18, 0x0b, 0xd5, 0x38, 0xf1, 0xa4, 0x55, 0x9a,
|
||||
0x8e, 0xcd, 0x09, 0x02, 0x92, 0xc2, 0xf0, 0x47, 0x06, 0x00, 0x4d, 0xec, 0xd8, 0x16, 0x16, 0x2d,
|
||||
0x43, 0x5e, 0xb8, 0x3f, 0xd8, 0xb4, 0x7e, 0x45, 0x9b, 0x97, 0x9b, 0x96, 0x7c, 0xa3, 0x14, 0x34,
|
||||
0x7c, 0xcf, 0x00, 0x53, 0x2c, 0xda, 0xa5, 0x4a, 0x8b, 0x89, 0xe6, 0x62, 0xf2, 0xca, 0x37, 0x07,
|
||||
0xea, 0x4b, 0x2d, 0x05, 0x50, 0x9d, 0x6d, 0xb7, 0x4a, 0x53, 0x69, 0x0a, 0xea, 0x70, 0x00, 0xfe,
|
||||
0xd4, 0x00, 0x85, 0x66, 0x7c, 0x67, 0x8f, 0x8b, 0x03, 0xff, 0xe6, 0x90, 0x36, 0x56, 0x65, 0x54,
|
||||
0x72, 0x0a, 0x74, 0x1f, 0xa0, 0x3d, 0x80, 0x7f, 0x33, 0x80, 0x89, 0x2d, 0x59, 0xe0, 0xb1, 0xb3,
|
||||
0x43, 0x6d, 0x2f, 0x24, 0x54, 0xf6, 0x9b, 0xcc, 0x2c, 0x08, 0xf7, 0x06, 0x7b, 0x17, 0x66, 0x7b,
|
||||
0xd9, 0xea, 0xa2, 0xf2, 0xce, 0x5c, 0xe9, 0xe3, 0x06, 0xea, 0xeb, 0xa0, 0x48, 0xb4, 0xa4, 0xa5,
|
||||
0x31, 0x27, 0x86, 0x90, 0x68, 0x49, 0x2f, 0xa5, 0xaa, 0x43, 0xd2, 0x41, 0xa5, 0xa0, 0xcb, 0xef,
|
||||
0x8d, 0x64, 0x9b, 0xf6, 0xec, 0xa5, 0x0f, 0x3f, 0x90, 0xce, 0xca, 0xa5, 0x30, 0xd3, 0x10, 0xc1,
|
||||
0x7d, 0x7b, 0x48, 0x7b, 0xaf, 0x6f, 0xed, 0xa4, 0xf1, 0xd2, 0x24, 0x86, 0x52, 0x7e, 0xc0, 0x5f,
|
||||
0x19, 0x60, 0x1a, 0xd7, 0xeb, 0x24, 0x08, 0x89, 0x25, 0x6b, 0x71, 0xee, 0x73, 0x28, 0x37, 0xe7,
|
||||
0x94, 0x57, 0xd3, 0x2b, 0x69, 0x68, 0xd4, 0xe9, 0x09, 0x7c, 0x1e, 0x9c, 0x66, 0xa1, 0x4f, 0x89,
|
||||
0x95, 0xe9, 0x72, 0x61, 0xbb, 0x55, 0x3a, 0x5d, 0xeb, 0xe0, 0xa0, 0x8c, 0x64, 0xf9, 0xd3, 0x51,
|
||||
0x50, 0xba, 0xcb, 0xc9, 0xb8, 0x87, 0x77, 0xd4, 0xe3, 0x60, 0x4c, 0x2c, 0xd7, 0x12, 0x51, 0x29,
|
||||
0xa4, 0x3a, 0x37, 0x41, 0x45, 0x8a, 0xcb, 0xeb, 0x3a, 0xc7, 0xe7, 0xdd, 0xc6, 0x88, 0x10, 0xd4,
|
||||
0x75, 0xbd, 0x26, 0xc9, 0x28, 0xe6, 0xc3, 0x77, 0xc0, 0x98, 0x9c, 0x93, 0x88, 0xa2, 0x3a, 0xc4,
|
||||
0xc2, 0x08, 0x84, 0x9f, 0x02, 0x0a, 0x29, 0xc8, 0xee, 0x82, 0x98, 0x7f, 0xd0, 0x05, 0xf1, 0x8e,
|
||||
0x15, 0x68, 0xec, 0xff, 0xbc, 0x02, 0x95, 0xff, 0x6b, 0x64, 0xcf, 0x7d, 0x6a, 0xa9, 0xb5, 0x3a,
|
||||
0x76, 0x08, 0x5c, 0x03, 0xb3, 0xfc, 0x91, 0x81, 0x48, 0xe0, 0xd8, 0x75, 0xcc, 0xc4, 0x1b, 0x57,
|
||||
0x26, 0x9c, 0x1e, 0xbb, 0xd4, 0x32, 0x7c, 0xd4, 0xa5, 0x01, 0x5f, 0x02, 0x50, 0x36, 0xde, 0x1d,
|
||||
0x76, 0x64, 0x0f, 0xa1, 0x5b, 0xe8, 0x5a, 0x97, 0x04, 0xea, 0xa1, 0x05, 0x57, 0xc1, 0x9c, 0x83,
|
||||
0x77, 0x89, 0x53, 0x23, 0x0e, 0xa9, 0x87, 0x3e, 0x15, 0xa6, 0xe4, 0x14, 0xe0, 0x5c, 0xbb, 0x55,
|
||||
0x9a, 0xbb, 0x9a, 0x65, 0xa2, 0x6e, 0xf9, 0xf2, 0xc5, 0xec, 0xf1, 0x4a, 0x2f, 0x5c, 0x3e, 0x67,
|
||||
0x3e, 0xcc, 0x81, 0x85, 0xfe, 0x99, 0x01, 0x7f, 0x90, 0xbc, 0xba, 0x64, 0x53, 0xfd, 0xe6, 0xb0,
|
||||
0xb2, 0x50, 0x3d, 0xbb, 0x40, 0xf7, 0x93, 0x0b, 0x7e, 0x97, 0x77, 0x38, 0xd8, 0x89, 0xe7, 0x3c,
|
||||
0x6f, 0x0c, 0xcd, 0x05, 0x0e, 0x52, 0x9d, 0x90, 0xcd, 0x13, 0x76, 0x44, 0xaf, 0x84, 0x1d, 0x52,
|
||||
0xfe, 0xa3, 0x91, 0x7d, 0x78, 0x27, 0x27, 0x18, 0xfe, 0xcc, 0x00, 0x33, 0x7e, 0x40, 0xbc, 0x95,
|
||||
0x9d, 0x8d, 0x57, 0xbe, 0x22, 0x4f, 0xb2, 0x0a, 0xd5, 0xd6, 0x7d, 0xfa, 0xf9, 0x52, 0x6d, 0x7b,
|
||||
0x4b, 0x1a, 0xdc, 0xa1, 0x7e, 0xc0, 0xaa, 0x67, 0xda, 0xad, 0xd2, 0xcc, 0x76, 0x27, 0x14, 0xca,
|
||||
0x62, 0x97, 0x5d, 0x70, 0x6e, 0xfd, 0x28, 0x24, 0xd4, 0xc3, 0xce, 0x9a, 0x5f, 0x8f, 0x5c, 0xe2,
|
||||
0x85, 0xd2, 0xd1, 0xcc, 0x90, 0xc8, 0xb8, 0xc7, 0x21, 0xd1, 0x23, 0x60, 0x24, 0xa2, 0x8e, 0xca,
|
||||
0xe2, 0x49, 0x3d, 0x04, 0x45, 0x57, 0x11, 0xa7, 0x97, 0x2f, 0x82, 0x51, 0xee, 0x27, 0x3c, 0x0f,
|
||||
0x46, 0x28, 0x3e, 0x14, 0x56, 0xa7, 0xaa, 0xe3, 0x5c, 0x04, 0xe1, 0x43, 0xc4, 0x69, 0xe5, 0x7f,
|
||||
0x17, 0xc1, 0x4c, 0x66, 0x2d, 0x70, 0x01, 0xe4, 0xf4, 0x64, 0x15, 0x28, 0xa3, 0xb9, 0x8d, 0x35,
|
||||
0x94, 0xb3, 0x2d, 0xf8, 0xac, 0x2e, 0xbe, 0x12, 0xb4, 0xa4, 0xeb, 0xb9, 0xa0, 0xf2, 0x96, 0x36,
|
||||
0x31, 0xc7, 0x1d, 0x89, 0x0b, 0x27, 0xf7, 0x81, 0xec, 0xa9, 0x53, 0x22, 0x7d, 0x20, 0x7b, 0x88,
|
||||
0xd3, 0x3e, 0xeb, 0x84, 0x2c, 0x1e, 0xd1, 0xe5, 0xef, 0x61, 0x44, 0x37, 0x76, 0xc7, 0x11, 0xdd,
|
||||
0xa3, 0x20, 0x1f, 0xda, 0xa1, 0x43, 0xcc, 0xf1, 0xce, 0x97, 0xc7, 0x35, 0x4e, 0x44, 0x92, 0x07,
|
||||
0x6f, 0x82, 0x71, 0x8b, 0xec, 0xe1, 0xc8, 0x09, 0xcd, 0x82, 0x48, 0xa1, 0xd5, 0x01, 0xa4, 0x90,
|
||||
0x9c, 0x9f, 0xae, 0x49, 0xbb, 0x28, 0x06, 0x80, 0x8f, 0x81, 0x71, 0x17, 0x1f, 0xd9, 0x6e, 0xe4,
|
||||
0x8a, 0x9e, 0xcc, 0x90, 0x62, 0x9b, 0x92, 0x84, 0x62, 0x1e, 0xaf, 0x8c, 0xe4, 0xa8, 0xee, 0x44,
|
||||
0xcc, 0x6e, 0x12, 0xc5, 0x34, 0x81, 0xb8, 0x3d, 0x75, 0x65, 0x5c, 0xcf, 0xf0, 0x51, 0x97, 0x86,
|
||||
0x00, 0xb3, 0x3d, 0xa1, 0x3c, 0x99, 0x02, 0x93, 0x24, 0x14, 0xf3, 0x3a, 0xc1, 0x94, 0xfc, 0x54,
|
||||
0x3f, 0x30, 0xa5, 0xdc, 0xa5, 0x01, 0xbf, 0x0c, 0x26, 0x5c, 0x7c, 0x74, 0x95, 0x78, 0x8d, 0x70,
|
||||
0xdf, 0x9c, 0x5e, 0x34, 0x96, 0x46, 0xaa, 0xd3, 0xed, 0x56, 0x69, 0x62, 0x33, 0x26, 0xa2, 0x84,
|
||||
0x2f, 0x84, 0x6d, 0x4f, 0x09, 0x9f, 0x4e, 0x09, 0xc7, 0x44, 0x94, 0xf0, 0x79, 0x07, 0x11, 0xe0,
|
||||
0x90, 0x1f, 0x2e, 0x73, 0xa6, 0xf3, 0x65, 0xb8, 0x23, 0xc9, 0x28, 0xe6, 0xc3, 0x25, 0x50, 0x70,
|
||||
0xf1, 0x91, 0x78, 0xc5, 0x9b, 0xb3, 0xc2, 0xac, 0x98, 0x25, 0x6f, 0x2a, 0x1a, 0xd2, 0x5c, 0x21,
|
||||
0x69, 0x7b, 0x52, 0x72, 0x2e, 0x25, 0xa9, 0x68, 0x48, 0x73, 0x79, 0x12, 0x47, 0x9e, 0x7d, 0x2b,
|
||||
0x22, 0x52, 0x18, 0x8a, 0xc8, 0xe8, 0x24, 0xbe, 0x9e, 0xb0, 0x50, 0x5a, 0x8e, 0xbf, 0xa2, 0xdd,
|
||||
0xc8, 0x09, 0xed, 0xc0, 0x21, 0xdb, 0x7b, 0xe6, 0x19, 0x11, 0x7f, 0xd1, 0x27, 0x6f, 0x6a, 0x2a,
|
||||
0x4a, 0x49, 0x40, 0x02, 0x46, 0x89, 0x17, 0xb9, 0xe6, 0x59, 0x71, 0xb1, 0x0f, 0x24, 0x05, 0xf5,
|
||||
0xc9, 0x59, 0xf7, 0x22, 0x17, 0x09, 0xf3, 0xf0, 0x59, 0x30, 0xed, 0xe2, 0x23, 0x5e, 0x0e, 0x08,
|
||||
0x0d, 0xf9, 0xfb, 0xfe, 0x9c, 0x58, 0xfc, 0x1c, 0xef, 0x38, 0x37, 0xd3, 0x0c, 0xd4, 0x29, 0x27,
|
||||
0x14, 0x6d, 0x2f, 0xa5, 0x38, 0x9f, 0x52, 0x4c, 0x33, 0x50, 0xa7, 0x1c, 0x8f, 0x34, 0x25, 0xb7,
|
||||
0x22, 0x9b, 0x12, 0xcb, 0x7c, 0x48, 0x34, 0xa9, 0x6a, 0xbe, 0x2f, 0x69, 0x48, 0x73, 0x61, 0x33,
|
||||
0x1e, 0xf7, 0x98, 0xe2, 0x18, 0x5e, 0x1f, 0x6c, 0x25, 0xdf, 0xa6, 0x2b, 0x94, 0xe2, 0x63, 0x79,
|
||||
0xd3, 0xa4, 0x07, 0x3d, 0x90, 0x81, 0x3c, 0x76, 0x9c, 0xed, 0x3d, 0xf3, 0xbc, 0x88, 0xfd, 0xa0,
|
||||
0x6f, 0x10, 0x5d, 0x75, 0x56, 0x38, 0x08, 0x92, 0x58, 0x1c, 0xd4, 0xf7, 0x78, 0x6a, 0x2c, 0x0c,
|
||||
0x17, 0x74, 0x9b, 0x83, 0x20, 0x89, 0x25, 0x56, 0xea, 0x1d, 0x6f, 0xef, 0x99, 0x0f, 0x0f, 0x79,
|
||||
0xa5, 0x1c, 0x04, 0x49, 0x2c, 0x68, 0x83, 0x11, 0xcf, 0x0f, 0xcd, 0x0b, 0x43, 0xb9, 0x9e, 0xc5,
|
||||
0x85, 0xb3, 0xe5, 0x87, 0x88, 0x63, 0xc0, 0x5f, 0x1a, 0x00, 0x04, 0x49, 0x8a, 0x3e, 0x32, 0x90,
|
||||
0x29, 0x42, 0x06, 0xb2, 0x92, 0xe4, 0xf6, 0xba, 0x17, 0xd2, 0xe3, 0xe4, 0x1d, 0x99, 0x3a, 0x03,
|
||||
0x29, 0x2f, 0xe0, 0xef, 0x0c, 0x70, 0x36, 0xdd, 0x26, 0x6b, 0xf7, 0x8a, 0x22, 0x22, 0xd7, 0x06,
|
||||
0x9d, 0xe6, 0x55, 0xdf, 0x77, 0xaa, 0x66, 0xbb, 0x55, 0x3a, 0xbb, 0xd2, 0x03, 0x15, 0xf5, 0xf4,
|
||||
0x05, 0xfe, 0xc9, 0x00, 0x73, 0xaa, 0x8a, 0xa6, 0x3c, 0x2c, 0x89, 0x00, 0x92, 0x41, 0x07, 0x30,
|
||||
0x8b, 0x23, 0xe3, 0xa8, 0x7f, 0x97, 0xee, 0xe2, 0xa3, 0x6e, 0xd7, 0xe0, 0x5f, 0x0d, 0x30, 0x65,
|
||||
0x91, 0x80, 0x78, 0x16, 0xf1, 0xea, 0xdc, 0xd7, 0xc5, 0x81, 0x8c, 0x0d, 0xb2, 0xbe, 0xae, 0xa5,
|
||||
0x20, 0xa4, 0x9b, 0x15, 0xe5, 0xe6, 0x54, 0x9a, 0x75, 0xd2, 0x2a, 0xcd, 0x27, 0xaa, 0x69, 0x0e,
|
||||
0xea, 0xf0, 0x12, 0xbe, 0x6f, 0x80, 0x99, 0x64, 0x03, 0xe4, 0x95, 0x72, 0x71, 0x88, 0x79, 0x20,
|
||||
0xda, 0xd7, 0x95, 0x4e, 0x40, 0x94, 0xf5, 0x00, 0xfe, 0xd9, 0xe0, 0x9d, 0x5a, 0xfc, 0xee, 0x63,
|
||||
0x66, 0x59, 0xc4, 0xf2, 0xad, 0x81, 0xc7, 0x52, 0x23, 0xc8, 0x50, 0x5e, 0x4a, 0x5a, 0x41, 0xcd,
|
||||
0x39, 0x69, 0x95, 0xce, 0xa5, 0x23, 0xa9, 0x19, 0x28, 0xed, 0x21, 0xfc, 0x89, 0x01, 0xa6, 0x48,
|
||||
0xd2, 0x71, 0x33, 0xf3, 0xd1, 0x81, 0x04, 0xb1, 0x67, 0x13, 0x2f, 0x5f, 0xea, 0x29, 0x16, 0x43,
|
||||
0x1d, 0xd8, 0xbc, 0x83, 0x24, 0x47, 0xd8, 0x0d, 0x1c, 0x62, 0x7e, 0x61, 0xc0, 0x1d, 0xe4, 0xba,
|
||||
0xb4, 0x8b, 0x62, 0x00, 0x78, 0x09, 0x14, 0xbc, 0xc8, 0x71, 0xf0, 0xae, 0x43, 0xcc, 0xc7, 0x44,
|
||||
0x2f, 0xa2, 0xa7, 0x98, 0x5b, 0x8a, 0x8e, 0xb4, 0x04, 0xbc, 0x09, 0x16, 0x8f, 0x5e, 0xd6, 0xff,
|
||||
0xd1, 0xb3, 0x43, 0x89, 0xc0, 0xbf, 0xee, 0x1d, 0x78, 0xfe, 0xa1, 0xf7, 0x82, 0x4d, 0x1c, 0x8b,
|
||||
0x99, 0x8f, 0x0b, 0x2b, 0xf1, 0x04, 0x7b, 0xfe, 0x46, 0x4f, 0x29, 0x74, 0x57, 0x3b, 0xf0, 0x35,
|
||||
0xf0, 0x70, 0x4a, 0x66, 0xdd, 0xdd, 0x25, 0x96, 0x45, 0xac, 0xf8, 0xf1, 0x66, 0x7e, 0x51, 0xc0,
|
||||
0xe8, 0x43, 0x7e, 0x23, 0x2b, 0x80, 0xee, 0xa4, 0x0d, 0xaf, 0x82, 0xf9, 0x14, 0x7b, 0xc3, 0x0b,
|
||||
0xb7, 0x69, 0x2d, 0xa4, 0xb6, 0xd7, 0x30, 0x97, 0x84, 0xdd, 0xb3, 0xf1, 0xa9, 0xbc, 0x91, 0xe2,
|
||||
0xa1, 0x3e, 0x3a, 0x0b, 0xfc, 0xf9, 0x98, 0x29, 0x3f, 0x70, 0x16, 0x8c, 0x1c, 0x10, 0xf5, 0x2b,
|
||||
0x39, 0xe2, 0x7f, 0x42, 0x0b, 0xe4, 0x9b, 0xd8, 0x89, 0xe2, 0x17, 0xf0, 0x80, 0xaf, 0x2e, 0x24,
|
||||
0x8d, 0x3f, 0x9f, 0x7b, 0xce, 0x58, 0xf8, 0xc0, 0x00, 0xf3, 0xbd, 0xab, 0xe2, 0x03, 0x75, 0xeb,
|
||||
0x37, 0x06, 0x98, 0xeb, 0x2a, 0x80, 0x3d, 0x3c, 0xba, 0xd5, 0xe9, 0xd1, 0x6b, 0x83, 0xae, 0x64,
|
||||
0x72, 0xd7, 0x44, 0xfb, 0x96, 0x76, 0xef, 0xe7, 0x06, 0x98, 0xcd, 0xd6, 0x94, 0x07, 0x19, 0xaf,
|
||||
0xf2, 0x07, 0x39, 0x30, 0xdf, 0xbb, 0xeb, 0x84, 0x54, 0x3f, 0xaf, 0x87, 0x33, 0xa6, 0xe8, 0x35,
|
||||
0xd2, 0x7c, 0xd7, 0x00, 0x93, 0x37, 0xb5, 0x5c, 0xfc, 0x2b, 0xea, 0xc0, 0x07, 0x24, 0x71, 0x11,
|
||||
0x4f, 0x18, 0x0c, 0xa5, 0x71, 0xcb, 0x7f, 0x31, 0xc0, 0xb9, 0x9e, 0xb7, 0x13, 0x7f, 0xc7, 0x63,
|
||||
0xc7, 0xf1, 0x0f, 0xe5, 0x9c, 0x2b, 0x35, 0x44, 0x5e, 0x11, 0x54, 0xa4, 0xb8, 0xa9, 0xe8, 0xe5,
|
||||
0x3e, 0xaf, 0xe8, 0x95, 0xff, 0x6e, 0x80, 0x0b, 0x77, 0xca, 0xc4, 0x07, 0xb2, 0xa5, 0x4b, 0xa0,
|
||||
0xa0, 0x3a, 0xcb, 0x63, 0xb1, 0x9d, 0xea, 0x31, 0xa5, 0x8a, 0x86, 0xf8, 0xc7, 0x21, 0xf9, 0x57,
|
||||
0xf9, 0x43, 0x03, 0xcc, 0xd6, 0x08, 0x6d, 0xda, 0x75, 0x82, 0xc8, 0x1e, 0xa1, 0xc4, 0xab, 0x13,
|
||||
0xb8, 0x0c, 0x26, 0xc4, 0xcf, 0x97, 0x01, 0xae, 0xc7, 0xb3, 0xfd, 0x39, 0x15, 0xf2, 0x89, 0xad,
|
||||
0x98, 0x81, 0x12, 0x19, 0xfd, 0x3b, 0x40, 0xae, 0xef, 0xef, 0x00, 0x17, 0xc0, 0x68, 0x90, 0x4c,
|
||||
0x49, 0x0b, 0x9c, 0x2b, 0x06, 0xa3, 0x82, 0x2a, 0xb8, 0x3e, 0x0d, 0xc5, 0xe8, 0x27, 0xaf, 0xb8,
|
||||
0x3e, 0x0d, 0x91, 0xa0, 0x96, 0xff, 0x61, 0x80, 0x5e, 0xff, 0xe2, 0x03, 0x9b, 0x60, 0x9c, 0x49,
|
||||
0xd7, 0x55, 0x68, 0xb7, 0xef, 0x33, 0xb4, 0xd9, 0x40, 0xc8, 0xbb, 0x35, 0xa6, 0xc6, 0x60, 0x3c,
|
||||
0xba, 0x75, 0x5c, 0x8d, 0x3c, 0x4b, 0x4d, 0x3d, 0xa7, 0x64, 0x74, 0x57, 0x57, 0x24, 0x0d, 0x69,
|
||||
0x2e, 0x3c, 0x2f, 0xe7, 0x73, 0xa9, 0xa1, 0x57, 0x3c, 0x9b, 0xab, 0x5e, 0xfe, 0xe8, 0x76, 0xf1,
|
||||
0xd4, 0xc7, 0xb7, 0x8b, 0xa7, 0x3e, 0xb9, 0x5d, 0x3c, 0xf5, 0xbd, 0x76, 0xd1, 0xf8, 0xa8, 0x5d,
|
||||
0x34, 0x3e, 0x6e, 0x17, 0x8d, 0x4f, 0xda, 0x45, 0xe3, 0x5f, 0xed, 0xa2, 0xf1, 0x8b, 0x4f, 0x8b,
|
||||
0xa7, 0xbe, 0x35, 0xae, 0x5c, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x44, 0x57, 0xd1, 0xa5,
|
||||
0x78, 0x2b, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -443,6 +443,37 @@ message JSONSchemaProps {
|
||||
optional JSON example = 36;
|
||||
|
||||
optional bool nullable = 37;
|
||||
|
||||
// x-kubernetes-preserve-unknown-fields stops the API server
|
||||
// decoding step from pruning fields which are not specified
|
||||
// in the validation schema. This affects fields recursively,
|
||||
// but switches back to normal pruning behaviour if nested
|
||||
// properties or additionalProperties are specified in the schema.
|
||||
optional bool xKubernetesPreserveUnknownFields = 38;
|
||||
|
||||
// x-kubernetes-embedded-resource defines that the value is an
|
||||
// embedded Kubernetes runtime.Object, with TypeMeta and
|
||||
// ObjectMeta. The type must be object. It is allowed to further
|
||||
// restrict the embedded object. kind, apiVersion and metadata
|
||||
// are validated automatically. x-kubernetes-preserve-unknown-fields
|
||||
// is allowed to be true, but does not have to be if the object
|
||||
// is fully specified (up to kind, apiVersion, metadata).
|
||||
optional bool xKubernetesEmbeddedResource = 39;
|
||||
|
||||
// x-kubernetes-int-or-string specifies that this value is
|
||||
// either an integer or a string. If this is true, an empty
|
||||
// type is allowed and type as child of anyOf is permitted
|
||||
// if following one of the following patterns:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
optional bool xKubernetesIntOrString = 40;
|
||||
}
|
||||
|
||||
// JSONSchemaPropsOrArray represents a value that can either be a JSONSchemaProps
|
||||
|
@@ -279,6 +279,22 @@ const (
|
||||
// NamesAccepted means the names chosen for this CustomResourceDefinition do not conflict with others in
|
||||
// the group and are therefore accepted.
|
||||
NamesAccepted CustomResourceDefinitionConditionType = "NamesAccepted"
|
||||
// NonStructuralSchema means that one or more OpenAPI schema is not structural.
|
||||
//
|
||||
// A schema is structural if it specifies types for all values, with the only exceptions of those with
|
||||
// - x-kubernetes-int-or-string: true — for fields which can be integer or string
|
||||
// - x-kubernetes-preserve-unknown-fields: true — for raw, unspecified JSON values
|
||||
// and there is no type, additionalProperties, default, nullable or x-kubernetes-* vendor extenions
|
||||
// specified under allOf, anyOf, oneOf or not.
|
||||
//
|
||||
// Non-structural schemas will not be allowed anymore in v1 API groups. Moreover, new features will not be
|
||||
// available for non-structural CRDs:
|
||||
// - pruning
|
||||
// - defaulting
|
||||
// - read-only
|
||||
// - OpenAPI publishing
|
||||
// - webhook conversion
|
||||
NonStructuralSchema CustomResourceDefinitionConditionType = "NonStructuralSchema"
|
||||
// Terminating means that the CustomResourceDefinition has been deleted and is cleaning up.
|
||||
Terminating CustomResourceDefinitionConditionType = "Terminating"
|
||||
)
|
||||
|
@@ -55,6 +55,37 @@ type JSONSchemaProps struct {
|
||||
ExternalDocs *ExternalDocumentation `json:"externalDocs,omitempty" protobuf:"bytes,35,opt,name=externalDocs"`
|
||||
Example *JSON `json:"example,omitempty" protobuf:"bytes,36,opt,name=example"`
|
||||
Nullable bool `json:"nullable,omitempty" protobuf:"bytes,37,opt,name=nullable"`
|
||||
|
||||
// x-kubernetes-preserve-unknown-fields stops the API server
|
||||
// decoding step from pruning fields which are not specified
|
||||
// in the validation schema. This affects fields recursively,
|
||||
// but switches back to normal pruning behaviour if nested
|
||||
// properties or additionalProperties are specified in the schema.
|
||||
XPreserveUnknownFields bool `json:"x-kubernetes-preserve-unknown-fields,omitempty" protobuf:"bytes,38,opt,name=xKubernetesPreserveUnknownFields"`
|
||||
|
||||
// x-kubernetes-embedded-resource defines that the value is an
|
||||
// embedded Kubernetes runtime.Object, with TypeMeta and
|
||||
// ObjectMeta. The type must be object. It is allowed to further
|
||||
// restrict the embedded object. kind, apiVersion and metadata
|
||||
// are validated automatically. x-kubernetes-preserve-unknown-fields
|
||||
// is allowed to be true, but does not have to be if the object
|
||||
// is fully specified (up to kind, apiVersion, metadata).
|
||||
XEmbeddedResource bool `json:"x-kubernetes-embedded-resource,omitempty" protobuf:"bytes,39,opt,name=xKubernetesEmbeddedResource"`
|
||||
|
||||
// x-kubernetes-int-or-string specifies that this value is
|
||||
// either an integer or a string. If this is true, an empty
|
||||
// type is allowed and type as child of anyOf is permitted
|
||||
// if following one of the following patterns:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
XIntOrString bool `json:"x-kubernetes-int-or-string,omitempty" protobuf:"bytes,40,opt,name=xKubernetesIntOrString"`
|
||||
}
|
||||
|
||||
// JSON represents any valid JSON value.
|
||||
|
@@ -938,6 +938,9 @@ func autoConvert_v1beta1_JSONSchemaProps_To_apiextensions_JSONSchemaProps(in *JS
|
||||
out.Example = nil
|
||||
}
|
||||
out.Nullable = in.Nullable
|
||||
out.XPreserveUnknownFields = in.XPreserveUnknownFields
|
||||
out.XEmbeddedResource = in.XEmbeddedResource
|
||||
out.XIntOrString = in.XIntOrString
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1120,6 +1123,9 @@ func autoConvert_apiextensions_JSONSchemaProps_To_v1beta1_JSONSchemaProps(in *ap
|
||||
} else {
|
||||
out.Example = nil
|
||||
}
|
||||
out.XPreserveUnknownFields = in.XPreserveUnknownFields
|
||||
out.XEmbeddedResource = in.XEmbeddedResource
|
||||
out.XIntOrString = in.XIntOrString
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -686,6 +686,10 @@ func (v *specStandardValidatorV3) validate(schema *apiextensions.JSONSchemaProps
|
||||
return allErrs
|
||||
}
|
||||
|
||||
//
|
||||
// WARNING: if anything new is allowed below, NewStructural must be adapted to support it.
|
||||
//
|
||||
|
||||
if schema.Default != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("default"), "default is not supported"))
|
||||
}
|
||||
@@ -786,7 +790,7 @@ func validateSimpleJSONPath(s string, fldPath *field.Path) field.ErrorList {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
var allowedFieldsAtRootSchema = []string{"Description", "Type", "Format", "Title", "Maximum", "ExclusiveMaximum", "Minimum", "ExclusiveMinimum", "MaxLength", "MinLength", "Pattern", "MaxItems", "MinItems", "UniqueItems", "MultipleOf", "Required", "Items", "Properties", "ExternalDocs", "Example"}
|
||||
var allowedFieldsAtRootSchema = []string{"Description", "Type", "Format", "Title", "Maximum", "ExclusiveMaximum", "Minimum", "ExclusiveMinimum", "MaxLength", "MinLength", "Pattern", "MaxItems", "MinItems", "UniqueItems", "MultipleOf", "Required", "Items", "Properties", "ExternalDocs", "Example", "XPreserveUnknownFields"}
|
||||
|
||||
func allowedAtRootSchema(field string) bool {
|
||||
for _, v := range allowedFieldsAtRootSchema {
|
||||
|
@@ -32,6 +32,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/establish:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:go_default_library",
|
||||
@@ -119,6 +120,7 @@ filegroup(
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:all-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
|
@@ -30,6 +30,7 @@ import (
|
||||
internalinformers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion"
|
||||
"k8s.io/apiextensions-apiserver/pkg/controller/establish"
|
||||
"k8s.io/apiextensions-apiserver/pkg/controller/finalizer"
|
||||
"k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema"
|
||||
openapicontroller "k8s.io/apiextensions-apiserver/pkg/controller/openapi"
|
||||
"k8s.io/apiextensions-apiserver/pkg/controller/status"
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
@@ -195,6 +196,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
||||
|
||||
crdController := NewDiscoveryController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), versionDiscoveryHandler, groupDiscoveryHandler)
|
||||
namingController := status.NewNamingConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions())
|
||||
nonStructuralSchemaController := nonstructuralschema.NewConditionController(s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(), crdClient.Apiextensions())
|
||||
finalizingController := finalizer.NewCRDFinalizer(
|
||||
s.Informers.Apiextensions().InternalVersion().CustomResourceDefinitions(),
|
||||
crdClient.Apiextensions(),
|
||||
@@ -217,6 +219,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget)
|
||||
go crdController.Run(context.StopCh)
|
||||
go namingController.Run(context.StopCh)
|
||||
go establishingController.Run(context.StopCh)
|
||||
go nonStructuralSchemaController.Run(5, context.StopCh)
|
||||
go finalizingController.Run(5, context.StopCh)
|
||||
return nil
|
||||
})
|
||||
|
@@ -0,0 +1,44 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"complete.go",
|
||||
"convert.go",
|
||||
"structural.go",
|
||||
"validation.go",
|
||||
"zz_generated.deepcopy.go",
|
||||
],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema",
|
||||
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/schema",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["validation_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/rand:go_default_library",
|
||||
"//vendor/github.com/google/gofuzz:go_default_library",
|
||||
],
|
||||
)
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2019 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 schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// validateStructuralCompleteness checks that every specified field or array in s is also specified
|
||||
// outside of value validation.
|
||||
func validateStructuralCompleteness(s *Structural, fldPath *field.Path) field.ErrorList {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return validateValueValidationCompleteness(s.ValueValidation, s, fldPath, fldPath)
|
||||
}
|
||||
|
||||
func validateValueValidationCompleteness(v *ValueValidation, s *Structural, sPath, vPath *field.Path) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if s == nil {
|
||||
return field.ErrorList{field.Required(sPath, fmt.Sprintf("because it is defined in %s", vPath.String()))}
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Not, s, sPath, vPath.Child("not"))...)
|
||||
for i := range v.AllOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AllOf[i], s, sPath, vPath.Child("allOf").Index(i))...)
|
||||
}
|
||||
for i := range v.AnyOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.AnyOf[i], s, sPath, vPath.Child("anyOf").Index(i))...)
|
||||
}
|
||||
for i := range v.OneOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&v.OneOf[i], s, sPath, vPath.Child("oneOf").Index(i))...)
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateNestedValueValidationCompleteness(v *NestedValueValidation, s *Structural, sPath, vPath *field.Path) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
if s == nil {
|
||||
return field.ErrorList{field.Required(sPath, fmt.Sprintf("because it is defined in %s", vPath.String()))}
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateValueValidationCompleteness(&v.ValueValidation, s, sPath, vPath)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(v.Items, s.Items, sPath.Child("items"), vPath.Child("items"))...)
|
||||
for k, vFld := range v.Properties {
|
||||
if sFld, ok := s.Properties[k]; !ok {
|
||||
allErrs = append(allErrs, field.Required(sPath.Child("properties").Key(k), fmt.Sprintf("because it is defined in %s", vPath.Child("properties").Key(k))))
|
||||
} else {
|
||||
allErrs = append(allErrs, validateNestedValueValidationCompleteness(&vFld, &sFld, sPath.Child("properties").Key(k), vPath.Child("properties").Key(k))...)
|
||||
}
|
||||
}
|
||||
|
||||
// don't check additionalProperties as this is not allowed (and checked during validation)
|
||||
|
||||
return allErrs
|
||||
}
|
@@ -0,0 +1,276 @@
|
||||
/*
|
||||
Copyright 2019 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 schema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
)
|
||||
|
||||
// NewStructural converts an OpenAPI v3 schema into a structural schema. A pre-validated JSONSchemaProps will
|
||||
// not fail on NewStructural. This means that we require that:
|
||||
//
|
||||
// - items is not an array of schemas
|
||||
// - the following fields are not set:
|
||||
// - id
|
||||
// - schema
|
||||
// - $ref
|
||||
// - patternProperties
|
||||
// - dependencies
|
||||
// - additionalItems
|
||||
// - definitions.
|
||||
//
|
||||
// The follow fields are not preserved:
|
||||
// - externalDocs
|
||||
// - example.
|
||||
func NewStructural(s *apiextensions.JSONSchemaProps) (*Structural, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := validateUnsupportedFields(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vv, err := newValueValidation(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err := newGenerics(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x, err := newExtensions(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ss := &Structural{
|
||||
Generic: *g,
|
||||
Extensions: *x,
|
||||
ValueValidation: vv,
|
||||
}
|
||||
|
||||
if s.Items != nil {
|
||||
if len(s.Items.JSONSchemas) > 0 {
|
||||
// we validate that it is not an array
|
||||
return nil, fmt.Errorf("OpenAPIV3Schema 'items' must be a schema, but is an array")
|
||||
}
|
||||
item, err := NewStructural(s.Items.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ss.Items = item
|
||||
}
|
||||
|
||||
if len(s.Properties) > 0 {
|
||||
ss.Properties = make(map[string]Structural, len(s.Properties))
|
||||
for k, x := range s.Properties {
|
||||
fld, err := NewStructural(&x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ss.Properties[k] = *fld
|
||||
}
|
||||
}
|
||||
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func newGenerics(s *apiextensions.JSONSchemaProps) (*Generic, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
g := &Generic{
|
||||
Type: s.Type,
|
||||
Description: s.Description,
|
||||
Title: s.Title,
|
||||
Nullable: s.Nullable,
|
||||
}
|
||||
if s.Default != nil {
|
||||
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}
|
||||
} else {
|
||||
g.AdditionalProperties = &StructuralOrBool{Bool: s.AdditionalProperties.Allows}
|
||||
}
|
||||
}
|
||||
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func newValueValidation(s *apiextensions.JSONSchemaProps) (*ValueValidation, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
not, err := newNestedValueValidation(s.Not)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v := &ValueValidation{
|
||||
Format: s.Format,
|
||||
Maximum: s.Maximum,
|
||||
ExclusiveMaximum: s.ExclusiveMaximum,
|
||||
Minimum: s.Minimum,
|
||||
ExclusiveMinimum: s.ExclusiveMinimum,
|
||||
MaxLength: s.MaxLength,
|
||||
MinLength: s.MinLength,
|
||||
Pattern: s.Pattern,
|
||||
MaxItems: s.MaxItems,
|
||||
MinItems: s.MinItems,
|
||||
UniqueItems: s.UniqueItems,
|
||||
MultipleOf: s.MultipleOf,
|
||||
MaxProperties: s.MaxProperties,
|
||||
MinProperties: s.MinProperties,
|
||||
Required: s.Required,
|
||||
Not: not,
|
||||
}
|
||||
|
||||
for _, e := range s.Enum {
|
||||
v.Enum = append(v.Enum, JSON{e})
|
||||
}
|
||||
|
||||
for _, x := range s.AllOf {
|
||||
clause, err := newNestedValueValidation(&x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.AllOf = append(v.AllOf, *clause)
|
||||
}
|
||||
|
||||
for _, x := range s.AnyOf {
|
||||
clause, err := newNestedValueValidation(&x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.AnyOf = append(v.AnyOf, *clause)
|
||||
}
|
||||
|
||||
for _, x := range s.OneOf {
|
||||
clause, err := newNestedValueValidation(&x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.OneOf = append(v.OneOf, *clause)
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func newNestedValueValidation(s *apiextensions.JSONSchemaProps) (*NestedValueValidation, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if err := validateUnsupportedFields(s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
vv, err := newValueValidation(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err := newGenerics(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
x, err := newExtensions(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &NestedValueValidation{
|
||||
ValueValidation: *vv,
|
||||
ForbiddenGenerics: *g,
|
||||
ForbiddenExtensions: *x,
|
||||
}
|
||||
|
||||
if s.Items != nil {
|
||||
if len(s.Items.JSONSchemas) > 0 {
|
||||
// we validate that it is not an array
|
||||
return nil, fmt.Errorf("OpenAPIV3Schema 'items' must be a schema, but is an array")
|
||||
}
|
||||
nvv, err := newNestedValueValidation(s.Items.Schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Items = nvv
|
||||
}
|
||||
if s.Properties != nil {
|
||||
v.Properties = make(map[string]NestedValueValidation, len(s.Properties))
|
||||
for k, x := range s.Properties {
|
||||
nvv, err := newNestedValueValidation(&x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.Properties[k] = *nvv
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func newExtensions(s *apiextensions.JSONSchemaProps) (*Extensions, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &Extensions{
|
||||
XPreserveUnknownFields: s.XPreserveUnknownFields,
|
||||
XEmbeddedResource: s.XEmbeddedResource,
|
||||
XIntOrString: s.XIntOrString,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// validateUnsupportedFields checks that those fields rejected by validation are actually unset.
|
||||
func validateUnsupportedFields(s *apiextensions.JSONSchemaProps) error {
|
||||
if len(s.ID) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'id' is not supported")
|
||||
}
|
||||
if len(s.Schema) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'schema' is not supported")
|
||||
}
|
||||
if s.Ref != nil && len(*s.Ref) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema '$ref' is not supported")
|
||||
}
|
||||
if len(s.PatternProperties) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'patternProperties' is not supported")
|
||||
}
|
||||
if len(s.Dependencies) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'dependencies' is not supported")
|
||||
}
|
||||
if s.AdditionalItems != nil {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'additionalItems' is not supported")
|
||||
}
|
||||
if len(s.Definitions) > 0 {
|
||||
return fmt.Errorf("OpenAPIV3Schema 'definitions' is not supported")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
Copyright 2019 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 schema
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// Structural represents a structural schema.
|
||||
type Structural struct {
|
||||
Items *Structural
|
||||
Properties map[string]Structural
|
||||
|
||||
Generic
|
||||
Extensions
|
||||
|
||||
*ValueValidation
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// StructuralOrBool is either a structural schema or a boolean.
|
||||
type StructuralOrBool struct {
|
||||
Structural *Structural
|
||||
Bool bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// Generic contains the generic schema fields not allowed in value validation.
|
||||
type Generic struct {
|
||||
Description string
|
||||
// type specifies the type of a value.
|
||||
// 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
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// Extensions contains the Kubernetes OpenAPI v3 vendor extensions.
|
||||
type Extensions struct {
|
||||
// x-kubernetes-preserve-unknown-fields stops the API server
|
||||
// decoding step from pruning fields which are not specified
|
||||
// in the validation schema. This affects fields recursively,
|
||||
// but switches back to normal pruning behaviour if nested
|
||||
// properties or additionalProperties are specified in the schema.
|
||||
XPreserveUnknownFields bool
|
||||
|
||||
// x-kubernetes-embedded-resource defines that the value is an
|
||||
// embedded Kubernetes runtime.Object, with TypeMeta and
|
||||
// ObjectMeta. The type must be object. It is allowed to further
|
||||
// restrict the embedded object. Both ObjectMeta and TypeMeta
|
||||
// are validated automatically. x-kubernetes-preserve-unknown-fields
|
||||
// must be true.
|
||||
XEmbeddedResource bool
|
||||
|
||||
// x-kubernetes-int-or-string specifies that this value is
|
||||
// either an integer or a string. If this is true, an empty
|
||||
// type is allowed and type as child of anyOf is permitted
|
||||
// if following one of the following patterns:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
XIntOrString bool
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ValueValidation contains all schema fields not contributing to the structure of the schema.
|
||||
type ValueValidation struct {
|
||||
Format string
|
||||
Maximum *float64
|
||||
ExclusiveMaximum bool
|
||||
Minimum *float64
|
||||
ExclusiveMinimum bool
|
||||
MaxLength *int64
|
||||
MinLength *int64
|
||||
Pattern string
|
||||
MaxItems *int64
|
||||
MinItems *int64
|
||||
UniqueItems bool
|
||||
MultipleOf *float64
|
||||
Enum []JSON
|
||||
MaxProperties *int64
|
||||
MinProperties *int64
|
||||
Required []string
|
||||
AllOf []NestedValueValidation
|
||||
OneOf []NestedValueValidation
|
||||
AnyOf []NestedValueValidation
|
||||
Not *NestedValueValidation
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// NestedValueValidation contains value validations, items and properties usable when nested
|
||||
// under a logical junctor, and catch all structs for generic and vendor extensions schema fields.
|
||||
type NestedValueValidation struct {
|
||||
ValueValidation
|
||||
|
||||
Items *NestedValueValidation
|
||||
Properties map[string]NestedValueValidation
|
||||
|
||||
// Anything set in the following will make the scheme
|
||||
// non-structural, with the exception of these two patterns if
|
||||
// x-kubernetes-int-or-string is true:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
ForbiddenGenerics Generic
|
||||
ForbiddenExtensions Extensions
|
||||
}
|
||||
|
||||
// JSON wraps an arbitrary JSON value to be able to implement deepcopy.
|
||||
type JSON struct {
|
||||
Object interface{}
|
||||
}
|
||||
|
||||
// DeepCopy creates a deep copy of the wrapped JSON value.
|
||||
func (j JSON) DeepCopy() JSON {
|
||||
return JSON{runtime.DeepCopyJSONValue(j.Object)}
|
||||
}
|
||||
|
||||
// DeepCopyInto creates a deep copy of the wrapped JSON value and stores it in into.
|
||||
func (j JSON) DeepCopyInto(into *JSON) {
|
||||
into.Object = runtime.DeepCopyJSONValue(j.Object)
|
||||
}
|
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
Copyright 2019 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 schema
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
var intOrStringAnyOf = []NestedValueValidation{
|
||||
{ForbiddenGenerics: Generic{
|
||||
Type: "integer",
|
||||
}},
|
||||
{ForbiddenGenerics: Generic{
|
||||
Type: "string",
|
||||
}},
|
||||
}
|
||||
|
||||
type level int
|
||||
|
||||
const (
|
||||
rootLevel level = iota
|
||||
itemLevel
|
||||
fieldLevel
|
||||
)
|
||||
|
||||
// ValidateStructural checks that s is a structural schema with the invariants:
|
||||
//
|
||||
// * structurality: both `ForbiddenGenerics` and `ForbiddenExtensions` only have zero values, with the two exceptions for IntOrString.
|
||||
// * RawExtension: for every schema with `x-kubernetes-embedded-resource: true`, `x-kubernetes-preserve-unknown-fields: true` and `type: object` are set
|
||||
// * IntOrString: for `x-kubernetes-int-or-string: true` either `type` is empty under `anyOf` and `allOf` or the schema structure is one of these:
|
||||
//
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
//
|
||||
// * every specified field or array in s is also specified outside of value validation.
|
||||
// * additionalProperties at the root is not allowed.
|
||||
func ValidateStructural(s *Structural, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s, rootLevel, fldPath)...)
|
||||
allErrs = append(allErrs, validateStructuralCompleteness(s, fldPath)...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateStructuralInvariants checks the invariants of a structural schema.
|
||||
func validateStructuralInvariants(s *Structural, lvl level, fldPath *field.Path) field.ErrorList {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateStructuralInvariants(s.Items, itemLevel, fldPath.Child("items"))...)
|
||||
for k, v := range s.Properties {
|
||||
allErrs = append(allErrs, validateStructuralInvariants(&v, fieldLevel, fldPath.Child("properties").Key(k))...)
|
||||
}
|
||||
allErrs = append(allErrs, validateGeneric(&s.Generic, lvl, fldPath)...)
|
||||
allErrs = append(allErrs, validateExtensions(&s.Extensions, fldPath)...)
|
||||
|
||||
// detect the two IntOrString exceptions:
|
||||
// 1) anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// 2) allOf:
|
||||
// - anyOf:
|
||||
// - type: integer
|
||||
// - type: string
|
||||
// - ... zero or more
|
||||
skipAnyOf := false
|
||||
skipFirstAllOfAnyOf := false
|
||||
if s.XIntOrString && s.ValueValidation != nil {
|
||||
if len(s.ValueValidation.AnyOf) == 2 && reflect.DeepEqual(s.ValueValidation.AnyOf, intOrStringAnyOf) {
|
||||
skipAnyOf = true
|
||||
} else if len(s.ValueValidation.AllOf) >= 1 && len(s.ValueValidation.AllOf[0].AnyOf) == 2 && reflect.DeepEqual(s.ValueValidation.AllOf[0].AnyOf, intOrStringAnyOf) {
|
||||
skipFirstAllOfAnyOf = true
|
||||
}
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateValueValidation(s.ValueValidation, skipAnyOf, skipFirstAllOfAnyOf, fldPath)...)
|
||||
|
||||
if s.XEmbeddedResource && s.Type != "object" {
|
||||
if len(s.Type) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must be object if x-kubernetes-embedded-resource is true"))
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), s.Type, "must be object if x-kubernetes-embedded-resource is true"))
|
||||
}
|
||||
} else if len(s.Type) == 0 && !s.Extensions.XIntOrString && !s.Extensions.XPreserveUnknownFields {
|
||||
switch lvl {
|
||||
case rootLevel:
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty at the root"))
|
||||
case itemLevel:
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty for specified array items"))
|
||||
case fieldLevel:
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("type"), "must not be empty for specified object fields"))
|
||||
}
|
||||
}
|
||||
|
||||
if lvl == rootLevel && len(s.Type) > 0 && s.Type != "object" {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("type"), s.Type, "must be object at the root"))
|
||||
}
|
||||
|
||||
if s.XEmbeddedResource && !s.XPreserveUnknownFields && s.Properties == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("properties"), "must not be empty if x-kubernetes-embedded-resource is true without x-kubernetes-preserve-unknown-fields"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateGeneric checks the generic fields of a structural schema.
|
||||
func validateGeneric(g *Generic, lvl level, fldPath *field.Path) field.ErrorList {
|
||||
if g == nil {
|
||||
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
|
||||
}
|
||||
|
||||
// validateExtensions checks Kubernetes vendor extensions of a structural schema.
|
||||
func validateExtensions(x *Extensions, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if x.XIntOrString && x.XPreserveUnknownFields {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("x-kubernetes-preserve-unknown-fields"), x.XPreserveUnknownFields, "must be false if x-kubernetes-int-or-string is true"))
|
||||
}
|
||||
if x.XIntOrString && x.XEmbeddedResource {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("x-kubernetes-embedded-resource"), x.XEmbeddedResource, "must be false if x-kubernetes-int-or-string is true"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateValueValidation checks the value validation in a structural schema.
|
||||
func validateValueValidation(v *ValueValidation, skipAnyOf, skipFirstAllOfAnyOf bool, fldPath *field.Path) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if !skipAnyOf {
|
||||
for i := range v.AnyOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AnyOf[i], false, false, fldPath.Child("anyOf").Index(i))...)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range v.AllOf {
|
||||
skipAnyOf := false
|
||||
if skipFirstAllOfAnyOf && i == 0 {
|
||||
skipAnyOf = true
|
||||
}
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.AllOf[i], skipAnyOf, false, fldPath.Child("allOf").Index(i))...)
|
||||
}
|
||||
|
||||
for i := range v.OneOf {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&v.OneOf[i], false, false, fldPath.Child("oneOf").Index(i))...)
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Not, false, false, fldPath.Child("not"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateNestedValueValidation checks the nested value validation under a logic junctor in a structural schema.
|
||||
func validateNestedValueValidation(v *NestedValueValidation, skipAnyOf, skipAllOfAnyOf bool, fldPath *field.Path) field.ErrorList {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
allErrs = append(allErrs, validateValueValidation(&v.ValueValidation, skipAnyOf, skipAllOfAnyOf, fldPath)...)
|
||||
allErrs = append(allErrs, validateNestedValueValidation(v.Items, false, false, fldPath.Child("items"))...)
|
||||
|
||||
for k, fld := range v.Properties {
|
||||
allErrs = append(allErrs, validateNestedValueValidation(&fld, false, false, fldPath.Child("properties").Key(k))...)
|
||||
}
|
||||
|
||||
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"))
|
||||
}
|
||||
if len(v.ForbiddenGenerics.Title) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("title"), "must be empty to be structural"))
|
||||
}
|
||||
if len(v.ForbiddenGenerics.Description) > 0 {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("description"), "must be empty to be structural"))
|
||||
}
|
||||
if v.ForbiddenGenerics.Nullable {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("nullable"), "must be false to be structural"))
|
||||
}
|
||||
|
||||
if v.ForbiddenExtensions.XPreserveUnknownFields {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-preserve-unknown-fields"), "must be false to be structural"))
|
||||
}
|
||||
if v.ForbiddenExtensions.XEmbeddedResource {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-embedded-resource"), "must be false to be structural"))
|
||||
}
|
||||
if v.ForbiddenExtensions.XIntOrString {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("x-kubernetes-int-or-string"), "must be false to be structural"))
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
Copyright 2019 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 schema
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
fuzz "github.com/google/gofuzz"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/rand"
|
||||
)
|
||||
|
||||
func TestValidateNestedValueValidationComplete(t *testing.T) {
|
||||
fuzzer := fuzz.New()
|
||||
fuzzer.Funcs(
|
||||
func(s *JSON, c fuzz.Continue) {
|
||||
if c.RandBool() {
|
||||
s.Object = float64(42.0)
|
||||
}
|
||||
},
|
||||
func(s **StructuralOrBool, c fuzz.Continue) {
|
||||
if c.RandBool() {
|
||||
*s = &StructuralOrBool{}
|
||||
}
|
||||
},
|
||||
)
|
||||
fuzzer.NilChance(0)
|
||||
|
||||
// check that we didn't forget to check any forbidden generic field
|
||||
tt := reflect.TypeOf(Generic{})
|
||||
for i := 0; i < tt.NumField(); i++ {
|
||||
vv := &NestedValueValidation{}
|
||||
x := reflect.ValueOf(&vv.ForbiddenGenerics).Elem()
|
||||
i := rand.Intn(x.NumField())
|
||||
fuzzer.Fuzz(x.Field(i).Addr().Interface())
|
||||
|
||||
errs := validateNestedValueValidation(vv, false, false, nil)
|
||||
if len(errs) == 0 && !reflect.DeepEqual(vv.ForbiddenGenerics, Generic{}) {
|
||||
t.Errorf("expected ForbiddenGenerics validation errors for: %#v", vv)
|
||||
}
|
||||
}
|
||||
|
||||
// check that we didn't forget to check any forbidden extension field
|
||||
tt = reflect.TypeOf(Extensions{})
|
||||
for i := 0; i < tt.NumField(); i++ {
|
||||
vv := &NestedValueValidation{}
|
||||
x := reflect.ValueOf(&vv.ForbiddenExtensions).Elem()
|
||||
i := rand.Intn(x.NumField())
|
||||
fuzzer.Fuzz(x.Field(i).Addr().Interface())
|
||||
|
||||
errs := validateNestedValueValidation(vv, false, false, nil)
|
||||
if len(errs) == 0 && !reflect.DeepEqual(vv.ForbiddenExtensions, Extensions{}) {
|
||||
t.Errorf("expected ForbiddenExtensions validation errors for: %#v", vv)
|
||||
}
|
||||
}
|
||||
}
|
245
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/zz_generated.deepcopy.go
generated
Normal file
245
staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/zz_generated.deepcopy.go
generated
Normal file
@@ -0,0 +1,245 @@
|
||||
// +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 deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package schema
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Extensions) DeepCopyInto(out *Extensions) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Extensions.
|
||||
func (in *Extensions) DeepCopy() *Extensions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Extensions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Generic) DeepCopyInto(out *Generic) {
|
||||
*out = *in
|
||||
out.Default = in.Default.DeepCopy()
|
||||
if in.AdditionalProperties != nil {
|
||||
in, out := &in.AdditionalProperties, &out.AdditionalProperties
|
||||
*out = new(StructuralOrBool)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Generic.
|
||||
func (in *Generic) DeepCopy() *Generic {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Generic)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NestedValueValidation) DeepCopyInto(out *NestedValueValidation) {
|
||||
*out = *in
|
||||
in.ValueValidation.DeepCopyInto(&out.ValueValidation)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = new(NestedValueValidation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Properties != nil {
|
||||
in, out := &in.Properties, &out.Properties
|
||||
*out = make(map[string]NestedValueValidation, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
in.ForbiddenGenerics.DeepCopyInto(&out.ForbiddenGenerics)
|
||||
out.ForbiddenExtensions = in.ForbiddenExtensions
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NestedValueValidation.
|
||||
func (in *NestedValueValidation) DeepCopy() *NestedValueValidation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NestedValueValidation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Structural) DeepCopyInto(out *Structural) {
|
||||
*out = *in
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = new(Structural)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Properties != nil {
|
||||
in, out := &in.Properties, &out.Properties
|
||||
*out = make(map[string]Structural, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = *val.DeepCopy()
|
||||
}
|
||||
}
|
||||
in.Generic.DeepCopyInto(&out.Generic)
|
||||
out.Extensions = in.Extensions
|
||||
if in.ValueValidation != nil {
|
||||
in, out := &in.ValueValidation, &out.ValueValidation
|
||||
*out = new(ValueValidation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Structural.
|
||||
func (in *Structural) DeepCopy() *Structural {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Structural)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StructuralOrBool) DeepCopyInto(out *StructuralOrBool) {
|
||||
*out = *in
|
||||
if in.Structural != nil {
|
||||
in, out := &in.Structural, &out.Structural
|
||||
*out = new(Structural)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StructuralOrBool.
|
||||
func (in *StructuralOrBool) DeepCopy() *StructuralOrBool {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StructuralOrBool)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ValueValidation) DeepCopyInto(out *ValueValidation) {
|
||||
*out = *in
|
||||
if in.Maximum != nil {
|
||||
in, out := &in.Maximum, &out.Maximum
|
||||
*out = new(float64)
|
||||
**out = **in
|
||||
}
|
||||
if in.Minimum != nil {
|
||||
in, out := &in.Minimum, &out.Minimum
|
||||
*out = new(float64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MaxLength != nil {
|
||||
in, out := &in.MaxLength, &out.MaxLength
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MinLength != nil {
|
||||
in, out := &in.MinLength, &out.MinLength
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MaxItems != nil {
|
||||
in, out := &in.MaxItems, &out.MaxItems
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MinItems != nil {
|
||||
in, out := &in.MinItems, &out.MinItems
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MultipleOf != nil {
|
||||
in, out := &in.MultipleOf, &out.MultipleOf
|
||||
*out = new(float64)
|
||||
**out = **in
|
||||
}
|
||||
if in.Enum != nil {
|
||||
in, out := &in.Enum, &out.Enum
|
||||
*out = make([]JSON, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.MaxProperties != nil {
|
||||
in, out := &in.MaxProperties, &out.MaxProperties
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.MinProperties != nil {
|
||||
in, out := &in.MinProperties, &out.MinProperties
|
||||
*out = new(int64)
|
||||
**out = **in
|
||||
}
|
||||
if in.Required != nil {
|
||||
in, out := &in.Required, &out.Required
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllOf != nil {
|
||||
in, out := &in.AllOf, &out.AllOf
|
||||
*out = make([]NestedValueValidation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.OneOf != nil {
|
||||
in, out := &in.OneOf, &out.OneOf
|
||||
*out = make([]NestedValueValidation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.AnyOf != nil {
|
||||
in, out := &in.AnyOf, &out.AnyOf
|
||||
*out = make([]NestedValueValidation, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
if in.Not != nil {
|
||||
in, out := &in.Not, &out.Not
|
||||
*out = new(NestedValueValidation)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ValueValidation.
|
||||
func (in *ValueValidation) DeepCopy() *ValueValidation {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ValueValidation)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
@@ -194,6 +194,16 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou
|
||||
}
|
||||
}
|
||||
|
||||
if in.XPreserveUnknownFields {
|
||||
out.VendorExtensible.AddExtension("x-kubernetes-preserve-unknown-fields", true)
|
||||
}
|
||||
if in.XEmbeddedResource {
|
||||
out.VendorExtensible.AddExtension("x-kubernetes-embedded-resource", true)
|
||||
}
|
||||
if in.XIntOrString {
|
||||
out.VendorExtensible.AddExtension("x-kubernetes-int-or-string", true)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,38 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["nonstructuralschema_controller.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema",
|
||||
importpath = "k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/tools/cache:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
@@ -0,0 +1,236 @@
|
||||
/*
|
||||
Copyright 2019 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 nonstructuralschema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
client "k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion"
|
||||
informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion"
|
||||
listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion"
|
||||
)
|
||||
|
||||
// ConditionController is maintaining the NonStructuralSchema condition.
|
||||
type ConditionController struct {
|
||||
crdClient client.CustomResourceDefinitionsGetter
|
||||
|
||||
crdLister listers.CustomResourceDefinitionLister
|
||||
crdSynced cache.InformerSynced
|
||||
|
||||
// To allow injection for testing.
|
||||
syncFn func(key string) error
|
||||
|
||||
queue workqueue.RateLimitingInterface
|
||||
}
|
||||
|
||||
// NewConditionController constructs a non-structural schema condition controller.
|
||||
func NewConditionController(
|
||||
crdInformer informers.CustomResourceDefinitionInformer,
|
||||
crdClient client.CustomResourceDefinitionsGetter,
|
||||
) *ConditionController {
|
||||
c := &ConditionController{
|
||||
crdClient: crdClient,
|
||||
crdLister: crdInformer.Lister(),
|
||||
crdSynced: crdInformer.Informer().HasSynced,
|
||||
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "non_structural_schema_condition_controller"),
|
||||
}
|
||||
|
||||
crdInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
|
||||
AddFunc: c.addCustomResourceDefinition,
|
||||
UpdateFunc: c.updateCustomResourceDefinition,
|
||||
DeleteFunc: nil,
|
||||
})
|
||||
|
||||
c.syncFn = c.sync
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func calculateCondition(in *apiextensions.CustomResourceDefinition) *apiextensions.CustomResourceDefinitionCondition {
|
||||
cond := &apiextensions.CustomResourceDefinitionCondition{
|
||||
Type: apiextensions.NonStructuralSchema,
|
||||
Status: apiextensions.ConditionUnknown,
|
||||
}
|
||||
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if in.Spec.Validation != nil && in.Spec.Validation.OpenAPIV3Schema != nil {
|
||||
s, err := schema.NewStructural(in.Spec.Validation.OpenAPIV3Schema)
|
||||
if err != nil {
|
||||
cond.Reason = "StructuralError"
|
||||
cond.Message = fmt.Sprintf("failed to check global validation schema: %v", err)
|
||||
return cond
|
||||
}
|
||||
|
||||
pth := field.NewPath("spec", "validation", "openAPIV3Schema")
|
||||
|
||||
allErrs = append(allErrs, schema.ValidateStructural(s, pth)...)
|
||||
}
|
||||
|
||||
for _, v := range in.Spec.Versions {
|
||||
if v.Schema == nil || v.Schema.OpenAPIV3Schema == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s, err := schema.NewStructural(v.Schema.OpenAPIV3Schema)
|
||||
if err != nil {
|
||||
cond.Reason = "StructuralError"
|
||||
cond.Message = fmt.Sprintf("failed to check validation schema for version %s: %v", v.Name, err)
|
||||
return cond
|
||||
}
|
||||
|
||||
pth := field.NewPath("spec", "version").Key(v.Name).Child("schema", "openAPIV3Schema")
|
||||
|
||||
allErrs = append(allErrs, schema.ValidateStructural(s, pth)...)
|
||||
}
|
||||
|
||||
if len(allErrs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sort error messages. Otherwise, the condition message will change every sync due to
|
||||
// randomized map iteration.
|
||||
sort.Slice(allErrs, func(i, j int) bool {
|
||||
return allErrs[i].Error() < allErrs[j].Error()
|
||||
})
|
||||
|
||||
cond.Status = apiextensions.ConditionTrue
|
||||
cond.Reason = "Violations"
|
||||
cond.Message = allErrs.ToAggregate().Error()
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
func (c *ConditionController) sync(key string) error {
|
||||
inCustomResourceDefinition, err := c.crdLister.Get(key)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check old condition
|
||||
cond := calculateCondition(inCustomResourceDefinition)
|
||||
old := apiextensions.FindCRDCondition(inCustomResourceDefinition, apiextensions.NonStructuralSchema)
|
||||
|
||||
if cond == nil && old == nil {
|
||||
return nil
|
||||
}
|
||||
if cond != nil && old != nil && old.Status == cond.Status && old.Reason == cond.Reason && old.Message == cond.Message {
|
||||
return nil
|
||||
}
|
||||
|
||||
// update condition
|
||||
crd := inCustomResourceDefinition.DeepCopy()
|
||||
if cond == nil {
|
||||
apiextensions.RemoveCRDCondition(crd, apiextensions.NonStructuralSchema)
|
||||
} else {
|
||||
cond.LastTransitionTime = metav1.NewTime(time.Now())
|
||||
apiextensions.SetCRDCondition(crd, *cond)
|
||||
}
|
||||
|
||||
_, err = c.crdClient.CustomResourceDefinitions().UpdateStatus(crd)
|
||||
if apierrors.IsNotFound(err) || apierrors.IsConflict(err) {
|
||||
// deleted or changed in the meantime, we'll get called again
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Run starts the controller.
|
||||
func (c *ConditionController) Run(threadiness int, stopCh <-chan struct{}) {
|
||||
defer utilruntime.HandleCrash()
|
||||
defer c.queue.ShutDown()
|
||||
|
||||
klog.Infof("Starting NonStructuralSchemaConditionController")
|
||||
defer klog.Infof("Shutting down NonStructuralSchemaConditionController")
|
||||
|
||||
if !cache.WaitForCacheSync(stopCh, c.crdSynced) {
|
||||
return
|
||||
}
|
||||
|
||||
for i := 0; i < threadiness; i++ {
|
||||
go wait.Until(c.runWorker, time.Second, stopCh)
|
||||
}
|
||||
|
||||
<-stopCh
|
||||
}
|
||||
|
||||
func (c *ConditionController) runWorker() {
|
||||
for c.processNextWorkItem() {
|
||||
}
|
||||
}
|
||||
|
||||
// processNextWorkItem deals with one key off the queue. It returns false when it's time to quit.
|
||||
func (c *ConditionController) processNextWorkItem() bool {
|
||||
key, quit := c.queue.Get()
|
||||
if quit {
|
||||
return false
|
||||
}
|
||||
defer c.queue.Done(key)
|
||||
|
||||
err := c.syncFn(key.(string))
|
||||
if err == nil {
|
||||
c.queue.Forget(key)
|
||||
return true
|
||||
}
|
||||
|
||||
utilruntime.HandleError(fmt.Errorf("%v failed with: %v", key, err))
|
||||
c.queue.AddRateLimited(key)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *ConditionController) enqueue(obj *apiextensions.CustomResourceDefinition) {
|
||||
key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("Couldn't get key for object %#v: %v", obj, err))
|
||||
return
|
||||
}
|
||||
|
||||
c.queue.Add(key)
|
||||
}
|
||||
|
||||
func (c *ConditionController) addCustomResourceDefinition(obj interface{}) {
|
||||
castObj := obj.(*apiextensions.CustomResourceDefinition)
|
||||
klog.V(4).Infof("Adding %s", castObj.Name)
|
||||
c.enqueue(castObj)
|
||||
}
|
||||
|
||||
func (c *ConditionController) updateCustomResourceDefinition(obj, _ interface{}) {
|
||||
castObj := obj.(*apiextensions.CustomResourceDefinition)
|
||||
klog.V(4).Infof("Updating %s", castObj.Name)
|
||||
c.enqueue(castObj)
|
||||
}
|
@@ -26,6 +26,7 @@ go_test(
|
||||
"//staging/src/k8s.io/api/autoscaling/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/options:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/test/integration/fixtures:go_default_library",
|
||||
@@ -40,6 +41,7 @@ go_test(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/yaml:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/watch:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
|
@@ -22,10 +22,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
clientschema "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
|
||||
@@ -539,6 +541,802 @@ func TestForbiddenFieldsInSchema(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonStructuralSchemaConditionUpdate(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
|
||||
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
manifest := `
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: foos.tests.apiextensions.k8s.io
|
||||
spec:
|
||||
group: tests.apiextensions.k8s.io
|
||||
version: v1beta1
|
||||
names:
|
||||
plural: foos
|
||||
singular: foo
|
||||
kind: Foo
|
||||
listKind: Foolist
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
properties:
|
||||
a: {}
|
||||
versions:
|
||||
- name: v1beta1
|
||||
served: true
|
||||
storage: true
|
||||
`
|
||||
|
||||
// decode CRD manifest
|
||||
obj, _, err := clientschema.Codecs.UniversalDeserializer().Decode([]byte(manifest), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed decoding of: %v\n\n%s", err, manifest)
|
||||
}
|
||||
crd := obj.(*apiextensionsv1beta1.CustomResourceDefinition)
|
||||
name := crd.Name
|
||||
|
||||
// save schema for later
|
||||
origSchema := crd.Spec.Validation.OpenAPIV3Schema
|
||||
|
||||
// create CRDs
|
||||
t.Logf("Creating CRD %s", crd.Name)
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected create error: %v", err)
|
||||
}
|
||||
|
||||
// wait for condition with violations
|
||||
t.Log("Waiting for NonStructuralSchema condition")
|
||||
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition
|
||||
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
|
||||
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
|
||||
return cond != nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error waiting for NonStructuralSchema condition: %v", cond)
|
||||
}
|
||||
if v := "spec.validation.openAPIV3Schema.properties[a].type: Required value: must not be empty for specified object fields"; !strings.Contains(cond.Message, v) {
|
||||
t.Fatalf("expected violation %q, but got: %v", v, cond.Message)
|
||||
}
|
||||
|
||||
// remove schema
|
||||
t.Log("Remove schema")
|
||||
for retry := 0; retry < 5; retry++ {
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected get error: %v", err)
|
||||
}
|
||||
crd.Spec.Validation = nil
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd)
|
||||
if apierrors.IsConflict(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected update error: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected update error: %v", err)
|
||||
}
|
||||
|
||||
// wait for condition to go away
|
||||
t.Log("Wait for condition to disappear")
|
||||
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
|
||||
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
|
||||
return cond == nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error waiting for NonStructuralSchema condition: %v", cond)
|
||||
}
|
||||
|
||||
// readd schema
|
||||
t.Log("Readd schema")
|
||||
for retry := 0; retry < 5; retry++ {
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected get error: %v", err)
|
||||
}
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{OpenAPIV3Schema: origSchema}
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd)
|
||||
if apierrors.IsConflict(err) {
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected update error: %v", err)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected update error: %v", err)
|
||||
}
|
||||
|
||||
// wait for condition with violations
|
||||
t.Log("Wait for condition to reappear")
|
||||
err = wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
|
||||
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
|
||||
return cond != nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error waiting for NonStructuralSchema condition: %v", cond)
|
||||
}
|
||||
if v := "spec.validation.openAPIV3Schema.properties[a].type: Required value: must not be empty for specified object fields"; !strings.Contains(cond.Message, v) {
|
||||
t.Fatalf("expected violation %q, but got: %v", v, cond.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNonStructuralSchemaCondition(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, apiextensionsfeatures.CustomResourceWebhookConversion, true)()
|
||||
|
||||
tearDown, apiExtensionClient, _, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
tmpl := `
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
spec:
|
||||
version: v1beta1
|
||||
names:
|
||||
plural: foos
|
||||
singular: foo
|
||||
kind: Foo
|
||||
listKind: Foolist
|
||||
scope: Namespaced
|
||||
validation: GLOBAL_SCHEMA
|
||||
versions:
|
||||
- name: v1beta1
|
||||
served: true
|
||||
storage: true
|
||||
schema: V1BETA1_SCHEMA
|
||||
- name: v1
|
||||
served: true
|
||||
schema: V1_SCHEMA
|
||||
`
|
||||
|
||||
type Test struct {
|
||||
desc string
|
||||
globalSchema, v1Schema, v1beta1Schema string
|
||||
expectedCreateError bool
|
||||
expectedViolations []string
|
||||
unexpectedViolations []string
|
||||
}
|
||||
tests := []Test{
|
||||
{"empty", "", "", "", false, nil, nil},
|
||||
{
|
||||
desc: "int-or-string and preserve-unknown-fields true",
|
||||
globalSchema: `
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
x-kubernetes-int-or-string: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.x-kubernetes-preserve-unknown-fields: Invalid value: true: must be false if x-kubernetes-int-or-string is true",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "int-or-string and embedded-resource true",
|
||||
globalSchema: `
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-int-or-string: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.x-kubernetes-embedded-resource: Invalid value: true: must be false if x-kubernetes-int-or-string is true",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "embedded-resource without preserve-unknown-fields",
|
||||
globalSchema: `
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.properties: Required value: must not be empty if x-kubernetes-embedded-resource is true without x-kubernetes-preserve-unknown-fields",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "embedded-resource without preserve-unknown-fields, but properties",
|
||||
globalSchema: `
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
`,
|
||||
expectedViolations: []string{},
|
||||
},
|
||||
{
|
||||
desc: "embedded-resource with preserve-unknown-fields",
|
||||
globalSchema: `
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`,
|
||||
expectedViolations: []string{},
|
||||
},
|
||||
{
|
||||
desc: "embedded-resource with wrong type",
|
||||
globalSchema: `
|
||||
type: array
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.type: Invalid value: \"array\": must be object if x-kubernetes-embedded-resource is true",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "embedded-resource with empty type",
|
||||
globalSchema: `
|
||||
type: ""
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.type: Required value: must be object if x-kubernetes-embedded-resource is true",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "no top-level type",
|
||||
globalSchema: `
|
||||
type: ""
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.type: Required value: must not be empty at the root",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "non-object top-level type",
|
||||
globalSchema: `
|
||||
type: "integer"
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.type: Invalid value: \"integer\": must be object at the root",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forbidden in nested value validation",
|
||||
globalSchema: `
|
||||
type: object
|
||||
properties:
|
||||
foo:
|
||||
type: string
|
||||
not:
|
||||
type: string
|
||||
additionalProperties: true
|
||||
title: hello
|
||||
description: world
|
||||
nullable: true
|
||||
allOf:
|
||||
- properties:
|
||||
foo:
|
||||
type: string
|
||||
additionalProperties: true
|
||||
title: hello
|
||||
description: world
|
||||
nullable: true
|
||||
anyOf:
|
||||
- items:
|
||||
type: string
|
||||
additionalProperties: true
|
||||
title: hello
|
||||
description: world
|
||||
nullable: true
|
||||
oneOf:
|
||||
- properties:
|
||||
foo:
|
||||
type: string
|
||||
additionalProperties: true
|
||||
title: hello
|
||||
description: world
|
||||
nullable: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].items.type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].items.additionalProperties: Forbidden: must be undefined to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].items.title: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].items.description: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].items.nullable: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[foo].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[foo].additionalProperties: Forbidden: must be undefined to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[foo].title: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[foo].description: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[foo].nullable: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[foo].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[foo].additionalProperties: Forbidden: must be undefined to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[foo].title: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[foo].description: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[foo].nullable: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.additionalProperties: Forbidden: must be undefined to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.title: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.description: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.nullable: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.items: Required value: because it is defined in spec.validation.openAPIV3Schema.anyOf[0].items",
|
||||
},
|
||||
unexpectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.not.default",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forbidden vendor extensions in nested value validation",
|
||||
globalSchema: `
|
||||
type: object
|
||||
properties:
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
embedded-resource:
|
||||
type: object
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
not:
|
||||
properties:
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
embedded-resource:
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
allOf:
|
||||
- properties:
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
embedded-resource:
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
anyOf:
|
||||
- properties:
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
embedded-resource:
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
oneOf:
|
||||
- properties:
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
embedded-resource:
|
||||
x-kubernetes-embedded-resource: true
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[embedded-resource].x-kubernetes-preserve-unknown-fields: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[embedded-resource].x-kubernetes-embedded-resource: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.allOf[0].properties[int-or-string].x-kubernetes-int-or-string: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].properties[embedded-resource].x-kubernetes-preserve-unknown-fields: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].properties[embedded-resource].x-kubernetes-embedded-resource: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.anyOf[0].properties[int-or-string].x-kubernetes-int-or-string: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[embedded-resource].x-kubernetes-preserve-unknown-fields: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[embedded-resource].x-kubernetes-embedded-resource: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.oneOf[0].properties[int-or-string].x-kubernetes-int-or-string: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.properties[embedded-resource].x-kubernetes-preserve-unknown-fields: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.properties[embedded-resource].x-kubernetes-embedded-resource: Forbidden: must be false to be structural",
|
||||
"spec.validation.openAPIV3Schema.not.properties[int-or-string].x-kubernetes-int-or-string: Forbidden: must be false to be structural",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "missing types",
|
||||
globalSchema: `
|
||||
properties:
|
||||
foo:
|
||||
properties:
|
||||
a: {}
|
||||
bar:
|
||||
items:
|
||||
additionalProperties:
|
||||
properties:
|
||||
a: {}
|
||||
items: {}
|
||||
abc:
|
||||
additionalProperties:
|
||||
properties:
|
||||
a:
|
||||
items:
|
||||
additionalProperties:
|
||||
items:
|
||||
json:
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
properties:
|
||||
a: {}
|
||||
int-or-string:
|
||||
x-kubernetes-int-or-string: true
|
||||
properties:
|
||||
a: {}
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.properties[foo].properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[foo].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[int-or-string].properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[json].properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[abc].additionalProperties.properties[a].items.additionalProperties.type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[abc].additionalProperties.properties[a].items.type: Required value: must not be empty for specified array items",
|
||||
"spec.validation.openAPIV3Schema.properties[abc].additionalProperties.properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[abc].additionalProperties.type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[abc].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[bar].items.additionalProperties.items.type: Required value: must not be empty for specified array items",
|
||||
"spec.validation.openAPIV3Schema.properties[bar].items.additionalProperties.properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[bar].items.additionalProperties.type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.properties[bar].items.type: Required value: must not be empty for specified array items",
|
||||
"spec.validation.openAPIV3Schema.properties[bar].type: Required value: must not be empty for specified object fields",
|
||||
"spec.validation.openAPIV3Schema.type: Required value: must not be empty at the root",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "int-or-string variants",
|
||||
globalSchema: `
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
x-kubernetes-int-or-string: true
|
||||
b:
|
||||
x-kubernetes-int-or-string: true
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
allOf:
|
||||
- pattern: abc
|
||||
c:
|
||||
x-kubernetes-int-or-string: true
|
||||
allOf:
|
||||
- anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- pattern: abc
|
||||
- pattern: abc
|
||||
d:
|
||||
x-kubernetes-int-or-string: true
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: abc
|
||||
e:
|
||||
x-kubernetes-int-or-string: true
|
||||
allOf:
|
||||
- anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
pattern: abc
|
||||
- pattern: abc
|
||||
f:
|
||||
x-kubernetes-int-or-string: true
|
||||
anyOf:
|
||||
- type: integer
|
||||
- type: string
|
||||
- pattern: abc
|
||||
g:
|
||||
x-kubernetes-int-or-string: true
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: integer
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.properties[d].anyOf[0].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[d].anyOf[1].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[e].allOf[0].anyOf[0].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[e].allOf[0].anyOf[1].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[f].anyOf[0].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[f].anyOf[1].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[g].anyOf[0].type: Forbidden: must be empty to be structural",
|
||||
"spec.validation.openAPIV3Schema.properties[g].anyOf[1].type: Forbidden: must be empty to be structural",
|
||||
},
|
||||
unexpectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.properties[a]",
|
||||
"spec.validation.openAPIV3Schema.properties[b]",
|
||||
"spec.validation.openAPIV3Schema.properties[c]",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "forbidden additionalProperties at the root",
|
||||
globalSchema: `
|
||||
type: object
|
||||
additionalProperties: false
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.additionalProperties: Forbidden: must not be used at the root",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "structural incomplete",
|
||||
globalSchema: `
|
||||
type: object
|
||||
properties:
|
||||
b:
|
||||
type: object
|
||||
properties:
|
||||
b:
|
||||
type: array
|
||||
c:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
d:
|
||||
type: array
|
||||
not:
|
||||
properties:
|
||||
a: {}
|
||||
b:
|
||||
not:
|
||||
properties:
|
||||
a: {}
|
||||
b:
|
||||
items: {}
|
||||
c:
|
||||
items:
|
||||
not:
|
||||
items:
|
||||
properties:
|
||||
a: {}
|
||||
d:
|
||||
items: {}
|
||||
allOf:
|
||||
- properties:
|
||||
e: {}
|
||||
anyOf:
|
||||
- properties:
|
||||
f: {}
|
||||
oneOf:
|
||||
- properties:
|
||||
g: {}
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.validation.openAPIV3Schema.properties[d].items: Required value: because it is defined in spec.validation.openAPIV3Schema.not.properties[d].items",
|
||||
"spec.validation.openAPIV3Schema.properties[a]: Required value: because it is defined in spec.validation.openAPIV3Schema.not.properties[a]",
|
||||
"spec.validation.openAPIV3Schema.properties[b].properties[a]: Required value: because it is defined in spec.validation.openAPIV3Schema.not.properties[b].not.properties[a]",
|
||||
"spec.validation.openAPIV3Schema.properties[b].properties[b].items: Required value: because it is defined in spec.validation.openAPIV3Schema.not.properties[b].not.properties[b].items",
|
||||
"spec.validation.openAPIV3Schema.properties[c].items.items: Required value: because it is defined in spec.validation.openAPIV3Schema.not.properties[c].items.not.items",
|
||||
"spec.validation.openAPIV3Schema.properties[e]: Required value: because it is defined in spec.validation.openAPIV3Schema.allOf[0].properties[e]",
|
||||
"spec.validation.openAPIV3Schema.properties[f]: Required value: because it is defined in spec.validation.openAPIV3Schema.anyOf[0].properties[f]",
|
||||
"spec.validation.openAPIV3Schema.properties[g]: Required value: because it is defined in spec.validation.openAPIV3Schema.oneOf[0].properties[g]",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "structural complete",
|
||||
globalSchema: `
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
b:
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
b:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
c:
|
||||
type: array
|
||||
items:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
d:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
e:
|
||||
type: string
|
||||
f:
|
||||
type: string
|
||||
g:
|
||||
type: string
|
||||
not:
|
||||
properties:
|
||||
a: {}
|
||||
b:
|
||||
not:
|
||||
properties:
|
||||
a: {}
|
||||
b:
|
||||
items: {}
|
||||
c:
|
||||
items:
|
||||
not:
|
||||
items:
|
||||
properties:
|
||||
a: {}
|
||||
d:
|
||||
items: {}
|
||||
allOf:
|
||||
- properties:
|
||||
e: {}
|
||||
anyOf:
|
||||
- properties:
|
||||
f: {}
|
||||
oneOf:
|
||||
- properties:
|
||||
g: {}
|
||||
`,
|
||||
expectedViolations: nil,
|
||||
},
|
||||
{
|
||||
desc: "invalid v1beta1 schema",
|
||||
v1beta1Schema: `
|
||||
type: object
|
||||
properties:
|
||||
a: {}
|
||||
not:
|
||||
properties:
|
||||
b: {}
|
||||
`,
|
||||
v1Schema: `
|
||||
type: object
|
||||
properties:
|
||||
a:
|
||||
type: string
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.version[v1beta1].schema.openAPIV3Schema.properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.version[v1beta1].schema.openAPIV3Schema.properties[b]: Required value: because it is defined in spec.version[v1beta1].schema.openAPIV3Schema.not.properties[b]",
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid v1beta1 and v1 schemas",
|
||||
v1beta1Schema: `
|
||||
type: object
|
||||
properties:
|
||||
a: {}
|
||||
not:
|
||||
properties:
|
||||
b: {}
|
||||
`,
|
||||
v1Schema: `
|
||||
type: object
|
||||
properties:
|
||||
c: {}
|
||||
not:
|
||||
properties:
|
||||
d: {}
|
||||
`,
|
||||
expectedViolations: []string{
|
||||
"spec.version[v1beta1].schema.openAPIV3Schema.properties[a].type: Required value: must not be empty for specified object fields",
|
||||
"spec.version[v1beta1].schema.openAPIV3Schema.properties[b]: Required value: because it is defined in spec.version[v1beta1].schema.openAPIV3Schema.not.properties[b]",
|
||||
"spec.version[v1].schema.openAPIV3Schema.properties[c].type: Required value: must not be empty for specified object fields",
|
||||
"spec.version[v1].schema.openAPIV3Schema.properties[d]: Required value: because it is defined in spec.version[v1].schema.openAPIV3Schema.not.properties[d]",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i := range tests {
|
||||
tst := tests[i]
|
||||
t.Run(tst.desc, func(t *testing.T) {
|
||||
// plug in schemas
|
||||
manifest := strings.NewReplacer(
|
||||
"GLOBAL_SCHEMA", toValidationJSON(tst.globalSchema),
|
||||
"V1BETA1_SCHEMA", toValidationJSON(tst.v1beta1Schema),
|
||||
"V1_SCHEMA", toValidationJSON(tst.v1Schema),
|
||||
).Replace(tmpl)
|
||||
|
||||
// decode CRD manifest
|
||||
obj, _, err := clientschema.Codecs.UniversalDeserializer().Decode([]byte(manifest), nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed decoding of: %v\n\n%s", err, manifest)
|
||||
}
|
||||
crd := obj.(*apiextensionsv1beta1.CustomResourceDefinition)
|
||||
crd.Spec.Group = fmt.Sprintf("tests-%d.apiextension.k8s.io", i)
|
||||
crd.Name = fmt.Sprintf("foos.%s", crd.Spec.Group)
|
||||
|
||||
// create CRDs
|
||||
crd, err = apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Create(crd)
|
||||
if tst.expectedCreateError && err == nil {
|
||||
t.Fatalf("expected error, got none")
|
||||
} else if !tst.expectedCreateError && err != nil {
|
||||
t.Fatalf("unexpected create error: %v", err)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(tst.expectedViolations) == 0 {
|
||||
// wait for condition to not appear
|
||||
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition
|
||||
err := wait.PollImmediate(100*time.Millisecond, 5*time.Second, func() (bool, error) {
|
||||
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
|
||||
if cond == nil {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != wait.ErrWaitTimeout {
|
||||
t.Fatalf("expected no NonStructuralSchema condition, but got one: %v", cond)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// wait for condition to appear with the given violations
|
||||
var cond *apiextensionsv1beta1.CustomResourceDefinitionCondition
|
||||
err = wait.PollImmediate(100*time.Millisecond, wait.ForeverTestTimeout, func() (bool, error) {
|
||||
obj, err := apiExtensionClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
cond = findCRDCondition(obj, apiextensionsv1beta1.NonStructuralSchema)
|
||||
if cond != nil {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error waiting for violations in NonStructuralSchema condition: %v", err)
|
||||
}
|
||||
|
||||
// check that the condition looks good
|
||||
if cond.Reason != "Violations" {
|
||||
t.Errorf("expected reason Violations, got: %v", cond.Reason)
|
||||
}
|
||||
if cond.Status != apiextensionsv1beta1.ConditionTrue {
|
||||
t.Errorf("expected reason True, got: %v", cond.Status)
|
||||
}
|
||||
|
||||
// check that we got all violations
|
||||
t.Logf("Got violations: %q", cond.Message)
|
||||
for _, v := range tst.expectedViolations {
|
||||
if strings.Index(cond.Message, v) == -1 {
|
||||
t.Errorf("expected violation %q, but didn't get it", v)
|
||||
}
|
||||
}
|
||||
for _, v := range tst.unexpectedViolations {
|
||||
if strings.Index(cond.Message, v) != -1 {
|
||||
t.Errorf("unexpected violation %q", v)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// findCRDCondition returns the condition you're looking for or nil.
|
||||
func findCRDCondition(crd *apiextensionsv1beta1.CustomResourceDefinition, conditionType apiextensionsv1beta1.CustomResourceDefinitionConditionType) *apiextensionsv1beta1.CustomResourceDefinitionCondition {
|
||||
for i := range crd.Status.Conditions {
|
||||
if crd.Status.Conditions[i].Type == conditionType {
|
||||
return &crd.Status.Conditions[i]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func toValidationJSON(yml string) string {
|
||||
if len(yml) == 0 {
|
||||
return "null"
|
||||
}
|
||||
bs, err := yaml.ToJSON([]byte(yml))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return fmt.Sprintf("{\"openAPIV3Schema\": %s}", string(bs))
|
||||
}
|
||||
|
||||
func float64Ptr(f float64) *float64 {
|
||||
return &f
|
||||
}
|
||||
|
2
vendor/modules.txt
vendored
2
vendor/modules.txt
vendored
@@ -1066,6 +1066,7 @@ k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1
|
||||
k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation
|
||||
k8s.io/apiextensions-apiserver/pkg/apiserver
|
||||
k8s.io/apiextensions-apiserver/pkg/apiserver/conversion
|
||||
k8s.io/apiextensions-apiserver/pkg/apiserver/schema
|
||||
k8s.io/apiextensions-apiserver/pkg/apiserver/validation
|
||||
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset
|
||||
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme
|
||||
@@ -1087,6 +1088,7 @@ k8s.io/apiextensions-apiserver/pkg/cmd/server/options
|
||||
k8s.io/apiextensions-apiserver/pkg/cmd/server/testing
|
||||
k8s.io/apiextensions-apiserver/pkg/controller/establish
|
||||
k8s.io/apiextensions-apiserver/pkg/controller/finalizer
|
||||
k8s.io/apiextensions-apiserver/pkg/controller/nonstructuralschema
|
||||
k8s.io/apiextensions-apiserver/pkg/controller/openapi
|
||||
k8s.io/apiextensions-apiserver/pkg/controller/status
|
||||
k8s.io/apiextensions-apiserver/pkg/crdserverscheme
|
||||
|
Reference in New Issue
Block a user