Merge pull request #77333 from sttts/sttts-structural-crd-pruning
apiextensions: implement structural schema CRD pruning
This commit is contained in:
4
api/openapi-spec/swagger.json
generated
4
api/openapi-spec/swagger.json
generated
@@ -16500,6 +16500,10 @@
|
||||
"$ref": "#/definitions/io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.CustomResourceDefinitionNames",
|
||||
"description": "Names are the names used to describe this custom resource"
|
||||
},
|
||||
"preserveUnknownFields": {
|
||||
"description": "preserveUnknownFields disables pruning of object fields which are not specified in the OpenAPI schema. apiVersion, kind, metadata and known fields inside metadata are always preserved. Defaults to true in v1beta and will default to false in v1.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"scope": {
|
||||
"description": "Scope indicates whether this resource is cluster or namespace scoped. Default is namespaced",
|
||||
"type": "string"
|
||||
|
@@ -15,6 +15,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/github.com/google/gofuzz:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@@ -25,6 +25,7 @@ import (
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
var swaggerMetadataDescriptions = metav1.ObjectMeta{}.SwaggerDoc()
|
||||
@@ -69,6 +70,9 @@ func Funcs(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
if obj.Conversion.Strategy == apiextensions.WebhookConverter && len(obj.Conversion.ConversionReviewVersions) == 0 {
|
||||
obj.Conversion.ConversionReviewVersions = []string{"v1beta1"}
|
||||
}
|
||||
if obj.PreserveUnknownFields == nil {
|
||||
obj.PreserveUnknownFields = pointer.BoolPtr(true)
|
||||
}
|
||||
},
|
||||
func(obj *apiextensions.CustomResourceDefinition, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj)
|
||||
|
@@ -73,6 +73,12 @@ type CustomResourceDefinitionSpec struct {
|
||||
|
||||
// `conversion` defines conversion settings for the CRD.
|
||||
Conversion *CustomResourceConversion
|
||||
|
||||
// preserveUnknownFields disables pruning of object fields which are not
|
||||
// specified in the OpenAPI schema. apiVersion, kind, metadata and known
|
||||
// fields inside metadata are always preserved.
|
||||
// Defaults to true in v1beta and will default to false in v1.
|
||||
PreserveUnknownFields *bool
|
||||
}
|
||||
|
||||
// CustomResourceConversion describes how to convert different versions of a CR.
|
||||
|
@@ -72,6 +72,9 @@ func SetDefaults_CustomResourceDefinitionSpec(obj *CustomResourceDefinitionSpec)
|
||||
if obj.Conversion.Strategy == WebhookConverter && len(obj.Conversion.ConversionReviewVersions) == 0 {
|
||||
obj.Conversion.ConversionReviewVersions = []string{SchemeGroupVersion.Version}
|
||||
}
|
||||
if obj.PreserveUnknownFields == nil {
|
||||
obj.PreserveUnknownFields = utilpointer.BoolPtr(true)
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefaults_ServiceReference sets defaults for Webhook's ServiceReference
|
||||
|
@@ -709,6 +709,16 @@ func (m *CustomResourceDefinitionSpec) MarshalTo(dAtA []byte) (int, error) {
|
||||
}
|
||||
i += n13
|
||||
}
|
||||
if m.PreserveUnknownFields != nil {
|
||||
dAtA[i] = 0x50
|
||||
i++
|
||||
if *m.PreserveUnknownFields {
|
||||
dAtA[i] = 1
|
||||
} else {
|
||||
dAtA[i] = 0
|
||||
}
|
||||
i++
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
@@ -1855,6 +1865,9 @@ func (m *CustomResourceDefinitionSpec) Size() (n int) {
|
||||
l = m.Conversion.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
if m.PreserveUnknownFields != nil {
|
||||
n += 2
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -2339,6 +2352,7 @@ func (this *CustomResourceDefinitionSpec) String() string {
|
||||
`Versions:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.Versions), "CustomResourceDefinitionVersion", "CustomResourceDefinitionVersion", 1), `&`, ``, 1) + `,`,
|
||||
`AdditionalPrinterColumns:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.AdditionalPrinterColumns), "CustomResourceColumnDefinition", "CustomResourceColumnDefinition", 1), `&`, ``, 1) + `,`,
|
||||
`Conversion:` + strings.Replace(fmt.Sprintf("%v", this.Conversion), "CustomResourceConversion", "CustomResourceConversion", 1) + `,`,
|
||||
`PreserveUnknownFields:` + valueToStringGenerated(this.PreserveUnknownFields) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -4316,6 +4330,27 @@ func (m *CustomResourceDefinitionSpec) Unmarshal(dAtA []byte) error {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 10:
|
||||
if wireType != 0 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field PreserveUnknownFields", 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
|
||||
}
|
||||
}
|
||||
b := bool(v != 0)
|
||||
m.PreserveUnknownFields = &b
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
@@ -7466,186 +7501,187 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptorGenerated = []byte{
|
||||
// 2884 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0x1c, 0x47,
|
||||
0x15, 0xf7, 0xec, 0x6a, 0xa5, 0x55, 0x4b, 0xb2, 0xa4, 0xb6, 0xad, 0x8c, 0x15, 0x67, 0x57, 0xde,
|
||||
0x90, 0x20, 0x82, 0xbd, 0x4a, 0x4c, 0x42, 0x42, 0xaa, 0x38, 0x68, 0x25, 0x25, 0xa5, 0xc4, 0xfa,
|
||||
0xa0, 0xd7, 0x4e, 0x0c, 0xf9, 0x6c, 0xcd, 0xf4, 0xae, 0xc6, 0x9a, 0x2f, 0x4f, 0xcf, 0xac, 0xa4,
|
||||
0x0a, 0x50, 0x7c, 0x54, 0x0a, 0x8a, 0x02, 0x42, 0x91, 0x5c, 0x28, 0xe0, 0x10, 0x28, 0x2e, 0x1c,
|
||||
0xe0, 0x00, 0x37, 0xf8, 0x03, 0x72, 0x4c, 0x71, 0xca, 0x81, 0xda, 0xc2, 0x9b, 0x33, 0x37, 0xaa,
|
||||
0xa8, 0xd2, 0x89, 0xea, 0x8f, 0xe9, 0x99, 0x9d, 0xdd, 0xb5, 0x5d, 0xf1, 0x6e, 0xcc, 0x4d, 0xf3,
|
||||
0xbe, 0x7e, 0xaf, 0x5f, 0xbf, 0x7e, 0xfd, 0xfa, 0xad, 0x40, 0xe3, 0xe0, 0x39, 0x5a, 0xb5, 0xbc,
|
||||
0x95, 0x83, 0x68, 0x8f, 0x04, 0x2e, 0x09, 0x09, 0x5d, 0x69, 0x11, 0xd7, 0xf4, 0x82, 0x15, 0xc9,
|
||||
0xc0, 0xbe, 0x45, 0x8e, 0x42, 0xe2, 0x52, 0xcb, 0x73, 0xe9, 0x65, 0xec, 0x5b, 0x94, 0x04, 0x2d,
|
||||
0x12, 0xac, 0xf8, 0x07, 0x4d, 0xc6, 0xa3, 0xdd, 0x02, 0x2b, 0xad, 0xa7, 0xf6, 0x48, 0x88, 0x9f,
|
||||
0x5a, 0x69, 0x12, 0x97, 0x04, 0x38, 0x24, 0x66, 0xd5, 0x0f, 0xbc, 0xd0, 0x83, 0x5f, 0x17, 0xe6,
|
||||
0xaa, 0x5d, 0xd2, 0x6f, 0x29, 0x73, 0x55, 0xff, 0xa0, 0xc9, 0x78, 0xb4, 0x5b, 0xa0, 0x2a, 0xcd,
|
||||
0x2d, 0x5e, 0x6e, 0x5a, 0xe1, 0x7e, 0xb4, 0x57, 0x35, 0x3c, 0x67, 0xa5, 0xe9, 0x35, 0xbd, 0x15,
|
||||
0x6e, 0x75, 0x2f, 0x6a, 0xf0, 0x2f, 0xfe, 0xc1, 0xff, 0x12, 0x68, 0x8b, 0x4f, 0x27, 0xce, 0x3b,
|
||||
0xd8, 0xd8, 0xb7, 0x5c, 0x12, 0x1c, 0x27, 0x1e, 0x3b, 0x24, 0xc4, 0x2b, 0xad, 0x1e, 0x1f, 0x17,
|
||||
0x57, 0x06, 0x69, 0x05, 0x91, 0x1b, 0x5a, 0x0e, 0xe9, 0x51, 0xf8, 0xea, 0xdd, 0x14, 0xa8, 0xb1,
|
||||
0x4f, 0x1c, 0x9c, 0xd5, 0xab, 0x9c, 0x68, 0x60, 0x7e, 0xcd, 0x73, 0x5b, 0x24, 0x60, 0xab, 0x44,
|
||||
0xe4, 0x56, 0x44, 0x68, 0x08, 0x6b, 0x20, 0x1f, 0x59, 0xa6, 0xae, 0x2d, 0x69, 0xcb, 0x93, 0xb5,
|
||||
0x27, 0x3f, 0x6a, 0x97, 0x4f, 0x75, 0xda, 0xe5, 0xfc, 0xf5, 0xcd, 0xf5, 0x93, 0x76, 0xf9, 0xe2,
|
||||
0x20, 0xa4, 0xf0, 0xd8, 0x27, 0xb4, 0x7a, 0x7d, 0x73, 0x1d, 0x31, 0x65, 0xf8, 0x22, 0x98, 0x37,
|
||||
0x09, 0xb5, 0x02, 0x62, 0xae, 0xee, 0x6e, 0xbe, 0x22, 0xec, 0xeb, 0x39, 0x6e, 0xf1, 0xbc, 0xb4,
|
||||
0x38, 0xbf, 0x9e, 0x15, 0x40, 0xbd, 0x3a, 0xf0, 0x06, 0x98, 0xf0, 0xf6, 0x6e, 0x12, 0x23, 0xa4,
|
||||
0x7a, 0x7e, 0x29, 0xbf, 0x3c, 0x75, 0xe5, 0x72, 0x35, 0xd9, 0x41, 0xe5, 0x02, 0xdf, 0x36, 0xb9,
|
||||
0xd8, 0x2a, 0xc2, 0x87, 0x1b, 0xf1, 0xce, 0xd5, 0x66, 0x25, 0xda, 0xc4, 0x8e, 0xb0, 0x82, 0x62,
|
||||
0x73, 0x95, 0xdf, 0xe7, 0x00, 0x4c, 0x2f, 0x9e, 0xfa, 0x9e, 0x4b, 0xc9, 0x50, 0x56, 0x4f, 0xc1,
|
||||
0x9c, 0xc1, 0x2d, 0x87, 0xc4, 0x94, 0xb8, 0x7a, 0xee, 0xb3, 0x78, 0xaf, 0x4b, 0xfc, 0xb9, 0xb5,
|
||||
0x8c, 0x39, 0xd4, 0x03, 0x00, 0xaf, 0x81, 0xf1, 0x80, 0xd0, 0xc8, 0x0e, 0xf5, 0xfc, 0x92, 0xb6,
|
||||
0x3c, 0x75, 0xe5, 0xd2, 0x40, 0x28, 0x9e, 0xdf, 0x2c, 0xf9, 0xaa, 0xad, 0xa7, 0xaa, 0xf5, 0x10,
|
||||
0x87, 0x11, 0xad, 0x9d, 0x96, 0x48, 0xe3, 0x88, 0xdb, 0x40, 0xd2, 0x56, 0xe5, 0xc7, 0x39, 0x30,
|
||||
0x97, 0x8e, 0x52, 0xcb, 0x22, 0x87, 0xf0, 0x10, 0x4c, 0x04, 0x22, 0x59, 0x78, 0x9c, 0xa6, 0xae,
|
||||
0xec, 0x56, 0xef, 0xeb, 0x58, 0x55, 0x7b, 0x92, 0xb0, 0x36, 0xc5, 0xf6, 0x4c, 0x7e, 0xa0, 0x18,
|
||||
0x0d, 0xbe, 0x03, 0x8a, 0x81, 0xdc, 0x28, 0x9e, 0x4d, 0x53, 0x57, 0xbe, 0x31, 0x44, 0x64, 0x61,
|
||||
0xb8, 0x36, 0xdd, 0x69, 0x97, 0x8b, 0xf1, 0x17, 0x52, 0x80, 0x95, 0xf7, 0x73, 0xa0, 0xb4, 0x16,
|
||||
0xd1, 0xd0, 0x73, 0x10, 0xa1, 0x5e, 0x14, 0x18, 0x64, 0xcd, 0xb3, 0x23, 0xc7, 0x5d, 0x27, 0x0d,
|
||||
0xcb, 0xb5, 0x42, 0x96, 0xad, 0x4b, 0x60, 0xcc, 0xc5, 0x0e, 0x91, 0xd9, 0x33, 0x2d, 0x63, 0x3a,
|
||||
0xb6, 0x8d, 0x1d, 0x82, 0x38, 0x87, 0x49, 0xb0, 0x64, 0x91, 0x67, 0x41, 0x49, 0x5c, 0x3b, 0xf6,
|
||||
0x09, 0xe2, 0x1c, 0xf8, 0x38, 0x18, 0x6f, 0x78, 0x81, 0x83, 0xc5, 0x3e, 0x4e, 0x26, 0x3b, 0xf3,
|
||||
0x02, 0xa7, 0x22, 0xc9, 0x85, 0xcf, 0x80, 0x29, 0x93, 0x50, 0x23, 0xb0, 0x7c, 0x06, 0xad, 0x8f,
|
||||
0x71, 0xe1, 0x33, 0x52, 0x78, 0x6a, 0x3d, 0x61, 0xa1, 0xb4, 0x1c, 0xbc, 0x04, 0x8a, 0x7e, 0x60,
|
||||
0x79, 0x81, 0x15, 0x1e, 0xeb, 0x85, 0x25, 0x6d, 0xb9, 0x50, 0x9b, 0x93, 0x3a, 0xc5, 0x5d, 0x49,
|
||||
0x47, 0x4a, 0x02, 0x2e, 0x81, 0xe2, 0x4b, 0xf5, 0x9d, 0xed, 0x5d, 0x1c, 0xee, 0xeb, 0xe3, 0x1c,
|
||||
0x61, 0x8c, 0x49, 0xa3, 0xe2, 0x4d, 0x49, 0xad, 0xfc, 0x33, 0x07, 0xf4, 0x6c, 0x54, 0xe2, 0x90,
|
||||
0xc2, 0x17, 0x40, 0x91, 0x86, 0xac, 0xe2, 0x34, 0x8f, 0x65, 0x4c, 0x9e, 0x88, 0xc1, 0xea, 0x92,
|
||||
0x7e, 0xd2, 0x2e, 0x2f, 0x24, 0x1a, 0x31, 0x95, 0xc7, 0x43, 0xe9, 0xc2, 0xdf, 0x6a, 0xe0, 0xcc,
|
||||
0x21, 0xd9, 0xdb, 0xf7, 0xbc, 0x83, 0x35, 0xdb, 0x22, 0x6e, 0xb8, 0xe6, 0xb9, 0x0d, 0xab, 0x29,
|
||||
0x73, 0x00, 0xdd, 0x67, 0x0e, 0xbc, 0xda, 0x6b, 0xb9, 0xf6, 0x50, 0xa7, 0x5d, 0x3e, 0xd3, 0x87,
|
||||
0x81, 0xfa, 0xf9, 0x01, 0x6f, 0x00, 0xdd, 0xc8, 0x1c, 0x12, 0x59, 0xc0, 0x44, 0xd9, 0x9a, 0xac,
|
||||
0x5d, 0xe8, 0xb4, 0xcb, 0xfa, 0xda, 0x00, 0x19, 0x34, 0x50, 0xbb, 0xf2, 0xc3, 0x7c, 0x36, 0xbc,
|
||||
0xa9, 0x74, 0x7b, 0x1b, 0x14, 0xd9, 0x31, 0x36, 0x71, 0x88, 0xe5, 0x41, 0x7c, 0xf2, 0xde, 0x0e,
|
||||
0xbd, 0xa8, 0x19, 0x5b, 0x24, 0xc4, 0x35, 0x28, 0x37, 0x04, 0x24, 0x34, 0xa4, 0xac, 0xc2, 0xef,
|
||||
0x80, 0x31, 0xea, 0x13, 0x43, 0x06, 0xfa, 0xb5, 0xfb, 0x3d, 0x6c, 0x03, 0x16, 0x52, 0xf7, 0x89,
|
||||
0x91, 0x9c, 0x05, 0xf6, 0x85, 0x38, 0x2c, 0x7c, 0x57, 0x03, 0xe3, 0x94, 0x17, 0x28, 0x59, 0xd4,
|
||||
0xde, 0x18, 0x95, 0x07, 0x99, 0x2a, 0x28, 0xbe, 0x91, 0x04, 0xaf, 0xfc, 0x27, 0x07, 0x2e, 0x0e,
|
||||
0x52, 0x5d, 0xf3, 0x5c, 0x53, 0x6c, 0xc7, 0xa6, 0x3c, 0xdb, 0x22, 0xd3, 0x9f, 0x49, 0x9f, 0xed,
|
||||
0x93, 0x76, 0xf9, 0xb1, 0xbb, 0x1a, 0x48, 0x15, 0x81, 0xaf, 0xa9, 0x75, 0x8b, 0x42, 0x71, 0xb1,
|
||||
0xdb, 0xb1, 0x93, 0x76, 0x79, 0x56, 0xa9, 0x75, 0xfb, 0x0a, 0x5b, 0x00, 0xda, 0x98, 0x86, 0xd7,
|
||||
0x02, 0xec, 0x52, 0x61, 0xd6, 0x72, 0x88, 0x0c, 0xdf, 0x13, 0xf7, 0x96, 0x1e, 0x4c, 0xa3, 0xb6,
|
||||
0x28, 0x21, 0xe1, 0xd5, 0x1e, 0x6b, 0xa8, 0x0f, 0x02, 0xab, 0x5b, 0x01, 0xc1, 0x54, 0x95, 0xa2,
|
||||
0xd4, 0x8d, 0xc2, 0xa8, 0x48, 0x72, 0xe1, 0x97, 0xc0, 0x84, 0x43, 0x28, 0xc5, 0x4d, 0xc2, 0xeb,
|
||||
0xcf, 0x64, 0x72, 0x45, 0x6f, 0x09, 0x32, 0x8a, 0xf9, 0xac, 0x3f, 0xb9, 0x30, 0x28, 0x6a, 0x57,
|
||||
0x2d, 0x1a, 0xc2, 0xd7, 0x7b, 0x0e, 0x40, 0xf5, 0xde, 0x56, 0xc8, 0xb4, 0x79, 0xfa, 0xab, 0xe2,
|
||||
0x17, 0x53, 0x52, 0xc9, 0xff, 0x6d, 0x50, 0xb0, 0x42, 0xe2, 0xc4, 0x77, 0xf7, 0xab, 0x23, 0xca,
|
||||
0xbd, 0xda, 0x8c, 0xf4, 0xa1, 0xb0, 0xc9, 0xd0, 0x90, 0x00, 0xad, 0xfc, 0x21, 0x07, 0x1e, 0x19,
|
||||
0xa4, 0xc2, 0x2e, 0x14, 0xca, 0x22, 0xee, 0xdb, 0x51, 0x80, 0x6d, 0x99, 0x71, 0x2a, 0xe2, 0xbb,
|
||||
0x9c, 0x8a, 0x24, 0x97, 0x95, 0x7c, 0x6a, 0xb9, 0xcd, 0xc8, 0xc6, 0x81, 0x4c, 0x27, 0xb5, 0xea,
|
||||
0xba, 0xa4, 0x23, 0x25, 0x01, 0xab, 0x00, 0xd0, 0x7d, 0x2f, 0x08, 0x39, 0x86, 0xac, 0x5e, 0xa7,
|
||||
0x59, 0x81, 0xa8, 0x2b, 0x2a, 0x4a, 0x49, 0xb0, 0x1b, 0xed, 0xc0, 0x72, 0x4d, 0xb9, 0xeb, 0xea,
|
||||
0x14, 0xbf, 0x6c, 0xb9, 0x26, 0xe2, 0x1c, 0x86, 0x6f, 0x5b, 0x34, 0x64, 0x14, 0xb9, 0xe5, 0x5d,
|
||||
0x51, 0xe7, 0x92, 0x4a, 0x82, 0xe1, 0x1b, 0xac, 0xea, 0x7b, 0x81, 0x45, 0xa8, 0x3e, 0x9e, 0xe0,
|
||||
0xaf, 0x29, 0x2a, 0x4a, 0x49, 0x54, 0x7e, 0x5d, 0x1c, 0x9c, 0x24, 0xac, 0x94, 0xc0, 0x47, 0x41,
|
||||
0xa1, 0x19, 0x78, 0x91, 0x2f, 0xa3, 0xa4, 0xa2, 0xfd, 0x22, 0x23, 0x22, 0xc1, 0x63, 0x59, 0xd9,
|
||||
0xea, 0x6a, 0x53, 0x55, 0x56, 0xc6, 0xcd, 0x69, 0xcc, 0x87, 0xdf, 0xd7, 0x40, 0xc1, 0x95, 0xc1,
|
||||
0x61, 0x29, 0xf7, 0xfa, 0x88, 0xf2, 0x82, 0x87, 0x37, 0x71, 0x57, 0x44, 0x5e, 0x20, 0xc3, 0xa7,
|
||||
0x41, 0x81, 0x1a, 0x9e, 0x4f, 0x64, 0xd4, 0x4b, 0xb1, 0x50, 0x9d, 0x11, 0x4f, 0xda, 0xe5, 0x99,
|
||||
0xd8, 0x1c, 0x27, 0x20, 0x21, 0x0c, 0x7f, 0xa4, 0x01, 0xd0, 0xc2, 0xb6, 0x65, 0x62, 0xde, 0x32,
|
||||
0x14, 0xb8, 0xfb, 0xc3, 0x4d, 0xeb, 0x57, 0x94, 0x79, 0xb1, 0x69, 0xc9, 0x37, 0x4a, 0x41, 0xc3,
|
||||
0xf7, 0x34, 0x30, 0x4d, 0xa3, 0xbd, 0x40, 0x6a, 0x51, 0xde, 0x5c, 0x4c, 0x5d, 0xf9, 0xe6, 0x50,
|
||||
0x7d, 0xa9, 0xa7, 0x00, 0x6a, 0x73, 0x9d, 0x76, 0x79, 0x3a, 0x4d, 0x41, 0x5d, 0x0e, 0xc0, 0x9f,
|
||||
0x6a, 0xa0, 0xd8, 0x8a, 0xef, 0xec, 0x09, 0x7e, 0xe0, 0xdf, 0x1c, 0xd1, 0xc6, 0xca, 0x8c, 0x4a,
|
||||
0x4e, 0x81, 0xea, 0x03, 0x94, 0x07, 0xf0, 0x6f, 0x1a, 0xd0, 0xb1, 0x29, 0x0a, 0x3c, 0xb6, 0x77,
|
||||
0x03, 0xcb, 0x0d, 0x49, 0x20, 0xfa, 0x4d, 0xaa, 0x17, 0xb9, 0x7b, 0xc3, 0xbd, 0x0b, 0xb3, 0xbd,
|
||||
0x6c, 0x6d, 0x49, 0x7a, 0xa7, 0xaf, 0x0e, 0x70, 0x03, 0x0d, 0x74, 0x90, 0x27, 0x5a, 0xd2, 0xd2,
|
||||
0xe8, 0x93, 0x23, 0x48, 0xb4, 0xa4, 0x97, 0x92, 0xd5, 0x21, 0xe9, 0xa0, 0x52, 0xd0, 0x95, 0xf7,
|
||||
0xf2, 0xd9, 0xa6, 0x3d, 0x7b, 0xe9, 0xc3, 0x0f, 0x84, 0xb3, 0x62, 0x29, 0x54, 0xd7, 0x78, 0x70,
|
||||
0xdf, 0x1e, 0xd1, 0xde, 0xab, 0x5b, 0x3b, 0x69, 0xbc, 0x14, 0x89, 0xa2, 0x94, 0x1f, 0xf0, 0x57,
|
||||
0x1a, 0x98, 0xc1, 0x86, 0x41, 0xfc, 0x90, 0x98, 0xa2, 0x16, 0xe7, 0x3e, 0x87, 0x72, 0x73, 0x4e,
|
||||
0x7a, 0x35, 0xb3, 0x9a, 0x86, 0x46, 0xdd, 0x9e, 0xc0, 0xe7, 0xc1, 0x69, 0x1a, 0x7a, 0x01, 0x31,
|
||||
0x33, 0x5d, 0x2e, 0xec, 0xb4, 0xcb, 0xa7, 0xeb, 0x5d, 0x1c, 0x94, 0x91, 0xac, 0x7c, 0x3a, 0x06,
|
||||
0xca, 0x77, 0x39, 0x19, 0xf7, 0xf0, 0x8e, 0x7a, 0x1c, 0x8c, 0xf3, 0xe5, 0x9a, 0x3c, 0x2a, 0xc5,
|
||||
0x54, 0xe7, 0xc6, 0xa9, 0x48, 0x72, 0x59, 0x5d, 0x67, 0xf8, 0xac, 0xdb, 0xc8, 0x73, 0x41, 0x55,
|
||||
0xd7, 0xeb, 0x82, 0x8c, 0x62, 0x3e, 0x7c, 0x07, 0x8c, 0x8b, 0x39, 0x09, 0x2f, 0xaa, 0x23, 0x2c,
|
||||
0x8c, 0x80, 0xfb, 0xc9, 0xa1, 0x90, 0x84, 0xec, 0x2d, 0x88, 0x85, 0x07, 0x5d, 0x10, 0xef, 0x58,
|
||||
0x81, 0xc6, 0xff, 0xcf, 0x2b, 0x50, 0xe5, 0xbf, 0x5a, 0xf6, 0xdc, 0xa7, 0x96, 0x5a, 0x37, 0xb0,
|
||||
0x4d, 0xe0, 0x3a, 0x98, 0x63, 0x8f, 0x0c, 0x44, 0x7c, 0xdb, 0x32, 0x30, 0xe5, 0x6f, 0x5c, 0x91,
|
||||
0x70, 0x6a, 0xec, 0x52, 0xcf, 0xf0, 0x51, 0x8f, 0x06, 0x7c, 0x09, 0x40, 0xd1, 0x78, 0x77, 0xd9,
|
||||
0x11, 0x3d, 0x84, 0x6a, 0xa1, 0xeb, 0x3d, 0x12, 0xa8, 0x8f, 0x16, 0x5c, 0x03, 0xf3, 0x36, 0xde,
|
||||
0x23, 0x76, 0x9d, 0xd8, 0xc4, 0x08, 0xbd, 0x80, 0x9b, 0x12, 0x53, 0x80, 0x73, 0x9d, 0x76, 0x79,
|
||||
0xfe, 0x6a, 0x96, 0x89, 0x7a, 0xe5, 0x2b, 0x17, 0xb3, 0xc7, 0x2b, 0xbd, 0x70, 0xf1, 0x9c, 0xf9,
|
||||
0x30, 0x07, 0x16, 0x07, 0x67, 0x06, 0xfc, 0x41, 0xf2, 0xea, 0x12, 0x4d, 0xf5, 0x9b, 0xa3, 0xca,
|
||||
0x42, 0xf9, 0xec, 0x02, 0xbd, 0x4f, 0x2e, 0xf8, 0x5d, 0xd6, 0xe1, 0x60, 0x3b, 0x9e, 0xf3, 0xbc,
|
||||
0x31, 0x32, 0x17, 0x18, 0x48, 0x6d, 0x52, 0x34, 0x4f, 0xd8, 0xe6, 0xbd, 0x12, 0xb6, 0x49, 0xe5,
|
||||
0x8f, 0x5a, 0xf6, 0xe1, 0x9d, 0x9c, 0x60, 0xf8, 0x33, 0x0d, 0xcc, 0x7a, 0x3e, 0x71, 0x57, 0x77,
|
||||
0x37, 0x5f, 0xf9, 0x8a, 0x38, 0xc9, 0x32, 0x54, 0xdb, 0xf7, 0xe9, 0xe7, 0x4b, 0xf5, 0x9d, 0x6d,
|
||||
0x61, 0x70, 0x37, 0xf0, 0x7c, 0x5a, 0x3b, 0xd3, 0x69, 0x97, 0x67, 0x77, 0xba, 0xa1, 0x50, 0x16,
|
||||
0xbb, 0xe2, 0x80, 0x73, 0x1b, 0x47, 0x21, 0x09, 0x5c, 0x6c, 0xaf, 0x7b, 0x46, 0xe4, 0x10, 0x37,
|
||||
0x14, 0x8e, 0x66, 0x86, 0x44, 0xda, 0x3d, 0x0e, 0x89, 0x1e, 0x01, 0xf9, 0x28, 0xb0, 0x65, 0x16,
|
||||
0x4f, 0xa9, 0x21, 0x28, 0xba, 0x8a, 0x18, 0xbd, 0x72, 0x11, 0x8c, 0x31, 0x3f, 0xe1, 0x79, 0x90,
|
||||
0x0f, 0xf0, 0x21, 0xb7, 0x3a, 0x5d, 0x9b, 0x60, 0x22, 0x08, 0x1f, 0x22, 0x46, 0xab, 0xfc, 0xbb,
|
||||
0x04, 0x66, 0x33, 0x6b, 0x81, 0x8b, 0x20, 0xa7, 0x26, 0xab, 0x40, 0x1a, 0xcd, 0x6d, 0xae, 0xa3,
|
||||
0x9c, 0x65, 0xc2, 0x67, 0x55, 0xf1, 0x15, 0xa0, 0x65, 0x55, 0xcf, 0x39, 0x95, 0xb5, 0xb4, 0x89,
|
||||
0x39, 0xe6, 0x48, 0x5c, 0x38, 0x99, 0x0f, 0xa4, 0x21, 0x4f, 0x89, 0xf0, 0x81, 0x34, 0x10, 0xa3,
|
||||
0x7d, 0xd6, 0x09, 0x59, 0x3c, 0xa2, 0x2b, 0xdc, 0xc3, 0x88, 0x6e, 0xfc, 0x8e, 0x23, 0xba, 0x47,
|
||||
0x41, 0x21, 0xb4, 0x42, 0x9b, 0xe8, 0x13, 0xdd, 0x2f, 0x8f, 0x6b, 0x8c, 0x88, 0x04, 0x0f, 0xde,
|
||||
0x04, 0x13, 0x26, 0x69, 0xe0, 0xc8, 0x0e, 0xf5, 0x22, 0x4f, 0xa1, 0xb5, 0x21, 0xa4, 0x90, 0x98,
|
||||
0x9f, 0xae, 0x0b, 0xbb, 0x28, 0x06, 0x80, 0x8f, 0x81, 0x09, 0x07, 0x1f, 0x59, 0x4e, 0xe4, 0xf0,
|
||||
0x9e, 0x4c, 0x13, 0x62, 0x5b, 0x82, 0x84, 0x62, 0x1e, 0xab, 0x8c, 0xe4, 0xc8, 0xb0, 0x23, 0x6a,
|
||||
0xb5, 0x88, 0x64, 0xea, 0x80, 0xdf, 0x9e, 0xaa, 0x32, 0x6e, 0x64, 0xf8, 0xa8, 0x47, 0x83, 0x83,
|
||||
0x59, 0x2e, 0x57, 0x9e, 0x4a, 0x81, 0x09, 0x12, 0x8a, 0x79, 0xdd, 0x60, 0x52, 0x7e, 0x7a, 0x10,
|
||||
0x98, 0x54, 0xee, 0xd1, 0x80, 0x5f, 0x06, 0x93, 0x0e, 0x3e, 0xba, 0x4a, 0xdc, 0x66, 0xb8, 0xaf,
|
||||
0xcf, 0x2c, 0x69, 0xcb, 0xf9, 0xda, 0x4c, 0xa7, 0x5d, 0x9e, 0xdc, 0x8a, 0x89, 0x28, 0xe1, 0x73,
|
||||
0x61, 0xcb, 0x95, 0xc2, 0xa7, 0x53, 0xc2, 0x31, 0x11, 0x25, 0x7c, 0xd6, 0x41, 0xf8, 0x38, 0x64,
|
||||
0x87, 0x4b, 0x9f, 0xed, 0x7e, 0x19, 0xee, 0x0a, 0x32, 0x8a, 0xf9, 0x70, 0x19, 0x14, 0x1d, 0x7c,
|
||||
0xc4, 0x5f, 0xf1, 0xfa, 0x1c, 0x37, 0xcb, 0x67, 0xc9, 0x5b, 0x92, 0x86, 0x14, 0x97, 0x4b, 0x5a,
|
||||
0xae, 0x90, 0x9c, 0x4f, 0x49, 0x4a, 0x1a, 0x52, 0x5c, 0x96, 0xc4, 0x91, 0x6b, 0xdd, 0x8a, 0x88,
|
||||
0x10, 0x86, 0x3c, 0x32, 0x2a, 0x89, 0xaf, 0x27, 0x2c, 0x94, 0x96, 0x63, 0xaf, 0x68, 0x27, 0xb2,
|
||||
0x43, 0xcb, 0xb7, 0xc9, 0x4e, 0x43, 0x3f, 0xc3, 0xe3, 0xcf, 0xfb, 0xe4, 0x2d, 0x45, 0x45, 0x29,
|
||||
0x09, 0x48, 0xc0, 0x18, 0x71, 0x23, 0x47, 0x3f, 0xcb, 0x2f, 0xf6, 0xa1, 0xa4, 0xa0, 0x3a, 0x39,
|
||||
0x1b, 0x6e, 0xe4, 0x20, 0x6e, 0x1e, 0x3e, 0x0b, 0x66, 0x1c, 0x7c, 0xc4, 0xca, 0x01, 0x09, 0x42,
|
||||
0xf6, 0xbe, 0x3f, 0xc7, 0x17, 0x3f, 0xcf, 0x3a, 0xce, 0xad, 0x34, 0x03, 0x75, 0xcb, 0x71, 0x45,
|
||||
0xcb, 0x4d, 0x29, 0x2e, 0xa4, 0x14, 0xd3, 0x0c, 0xd4, 0x2d, 0xc7, 0x22, 0x1d, 0x90, 0x5b, 0x91,
|
||||
0x15, 0x10, 0x53, 0x7f, 0x88, 0x37, 0xa9, 0x72, 0xbe, 0x2f, 0x68, 0x48, 0x71, 0x61, 0x2b, 0x1e,
|
||||
0xf7, 0xe8, 0xfc, 0x18, 0x5e, 0x1f, 0x6e, 0x25, 0xdf, 0x09, 0x56, 0x83, 0x00, 0x1f, 0x8b, 0x9b,
|
||||
0x26, 0x3d, 0xe8, 0x81, 0x14, 0x14, 0xb0, 0x6d, 0xef, 0x34, 0xf4, 0xf3, 0x3c, 0xf6, 0xc3, 0xbe,
|
||||
0x41, 0x54, 0xd5, 0x59, 0x65, 0x20, 0x48, 0x60, 0x31, 0x50, 0xcf, 0x65, 0xa9, 0xb1, 0x38, 0x5a,
|
||||
0xd0, 0x1d, 0x06, 0x82, 0x04, 0x16, 0x5f, 0xa9, 0x7b, 0xbc, 0xd3, 0xd0, 0x1f, 0x1e, 0xf1, 0x4a,
|
||||
0x19, 0x08, 0x12, 0x58, 0xd0, 0x02, 0x79, 0xd7, 0x0b, 0xf5, 0x0b, 0x23, 0xb9, 0x9e, 0xf9, 0x85,
|
||||
0xb3, 0xed, 0x85, 0x88, 0x61, 0xc0, 0x5f, 0x6a, 0x00, 0xf8, 0x49, 0x8a, 0x3e, 0x32, 0x94, 0x29,
|
||||
0x42, 0x06, 0xb2, 0x9a, 0xe4, 0xf6, 0x86, 0x1b, 0x06, 0xc7, 0xc9, 0x3b, 0x32, 0x75, 0x06, 0x52,
|
||||
0x5e, 0xc0, 0xdf, 0x69, 0xe0, 0x6c, 0xba, 0x4d, 0x56, 0xee, 0x95, 0x78, 0x44, 0xae, 0x0d, 0x3b,
|
||||
0xcd, 0x6b, 0x9e, 0x67, 0xd7, 0xf4, 0x4e, 0xbb, 0x7c, 0x76, 0xb5, 0x0f, 0x2a, 0xea, 0xeb, 0x0b,
|
||||
0xfc, 0x93, 0x06, 0xe6, 0x65, 0x15, 0x4d, 0x79, 0x58, 0xe6, 0x01, 0x24, 0xc3, 0x0e, 0x60, 0x16,
|
||||
0x47, 0xc4, 0x51, 0xfd, 0x2e, 0xdd, 0xc3, 0x47, 0xbd, 0xae, 0xc1, 0xbf, 0x6a, 0x60, 0xda, 0x24,
|
||||
0x3e, 0x71, 0x4d, 0xe2, 0x1a, 0xcc, 0xd7, 0xa5, 0xa1, 0x8c, 0x0d, 0xb2, 0xbe, 0xae, 0xa7, 0x20,
|
||||
0x84, 0x9b, 0x55, 0xe9, 0xe6, 0x74, 0x9a, 0x75, 0xd2, 0x2e, 0x2f, 0x24, 0xaa, 0x69, 0x0e, 0xea,
|
||||
0xf2, 0x12, 0xbe, 0xaf, 0x81, 0xd9, 0x64, 0x03, 0xc4, 0x95, 0x72, 0x71, 0x84, 0x79, 0xc0, 0xdb,
|
||||
0xd7, 0xd5, 0x6e, 0x40, 0x94, 0xf5, 0x00, 0xfe, 0x59, 0x63, 0x9d, 0x5a, 0xfc, 0xee, 0xa3, 0x7a,
|
||||
0x85, 0xc7, 0xf2, 0xad, 0xa1, 0xc7, 0x52, 0x21, 0x88, 0x50, 0x5e, 0x4a, 0x5a, 0x41, 0xc5, 0x39,
|
||||
0x69, 0x97, 0xcf, 0xa5, 0x23, 0xa9, 0x18, 0x28, 0xed, 0x21, 0xfc, 0x89, 0x06, 0xa6, 0x49, 0xd2,
|
||||
0x71, 0x53, 0xfd, 0xd1, 0xa1, 0x04, 0xb1, 0x6f, 0x13, 0x2f, 0x5e, 0xea, 0x29, 0x16, 0x45, 0x5d,
|
||||
0xd8, 0xac, 0x83, 0x24, 0x47, 0xd8, 0xf1, 0x6d, 0xa2, 0x7f, 0x61, 0xc8, 0x1d, 0xe4, 0x86, 0xb0,
|
||||
0x8b, 0x62, 0x00, 0x78, 0x09, 0x14, 0xdd, 0xc8, 0xb6, 0xf1, 0x9e, 0x4d, 0xf4, 0xc7, 0x78, 0x2f,
|
||||
0xa2, 0xa6, 0x98, 0xdb, 0x92, 0x8e, 0x94, 0x04, 0x6c, 0x80, 0xa5, 0xa3, 0x97, 0xd5, 0x7f, 0xf4,
|
||||
0xec, 0x06, 0x84, 0xe3, 0x5f, 0x77, 0x0f, 0x5c, 0xef, 0xd0, 0x7d, 0xc1, 0x22, 0xb6, 0x49, 0xf5,
|
||||
0xc7, 0xb9, 0x95, 0xc5, 0x4e, 0xbb, 0xbc, 0x70, 0xa3, 0xaf, 0x04, 0xba, 0xab, 0x0d, 0xf8, 0x1a,
|
||||
0x78, 0x38, 0x25, 0xb3, 0xe1, 0xec, 0x11, 0xd3, 0x24, 0x66, 0xfc, 0x70, 0xd3, 0xbf, 0xc8, 0x21,
|
||||
0xd4, 0x01, 0xbf, 0x91, 0x15, 0x40, 0x77, 0xd2, 0x86, 0x57, 0xc1, 0x42, 0x8a, 0xbd, 0xe9, 0x86,
|
||||
0x3b, 0x41, 0x3d, 0x0c, 0x2c, 0xb7, 0xa9, 0x2f, 0x73, 0xbb, 0x67, 0xe3, 0x13, 0x79, 0x23, 0xc5,
|
||||
0x43, 0x03, 0x74, 0x16, 0xd9, 0xd3, 0x31, 0x53, 0x7a, 0xe0, 0x1c, 0xc8, 0x1f, 0x10, 0xf9, 0x0b,
|
||||
0x39, 0x62, 0x7f, 0x42, 0x13, 0x14, 0x5a, 0xd8, 0x8e, 0xe2, 0xd7, 0xef, 0x90, 0xaf, 0x2d, 0x24,
|
||||
0x8c, 0x3f, 0x9f, 0x7b, 0x4e, 0x5b, 0xfc, 0x40, 0x03, 0x0b, 0xfd, 0x2b, 0xe2, 0x03, 0x75, 0xeb,
|
||||
0x37, 0x1a, 0x98, 0xef, 0x29, 0x7e, 0x7d, 0x3c, 0xba, 0xd5, 0xed, 0xd1, 0x6b, 0xc3, 0xae, 0x62,
|
||||
0x62, 0xd7, 0x78, 0xeb, 0x96, 0x76, 0xef, 0xe7, 0x1a, 0x98, 0xcb, 0xd6, 0x93, 0x07, 0x19, 0xaf,
|
||||
0xca, 0x07, 0x39, 0xb0, 0xd0, 0xbf, 0xe3, 0x84, 0x81, 0x7a, 0x5a, 0x8f, 0x66, 0x44, 0xd1, 0x6f,
|
||||
0x9c, 0xf9, 0xae, 0x06, 0xa6, 0x6e, 0x2a, 0xb9, 0xf8, 0x17, 0xd4, 0xa1, 0x0f, 0x47, 0xe2, 0x02,
|
||||
0x9e, 0x30, 0x28, 0x4a, 0xe3, 0x56, 0xfe, 0xa2, 0x81, 0x73, 0x7d, 0x6f, 0x26, 0xf6, 0x86, 0xc7,
|
||||
0xb6, 0xed, 0x1d, 0x8a, 0x19, 0x57, 0x6a, 0x80, 0xbc, 0xca, 0xa9, 0x48, 0x72, 0x53, 0xd1, 0xcb,
|
||||
0x7d, 0x5e, 0xd1, 0xab, 0xfc, 0x5d, 0x03, 0x17, 0xee, 0x94, 0x89, 0x0f, 0x64, 0x4b, 0x97, 0x41,
|
||||
0x51, 0x76, 0x95, 0xc7, 0x7c, 0x3b, 0xe5, 0x43, 0x4a, 0x16, 0x0d, 0xfe, 0x4f, 0x43, 0xe2, 0xaf,
|
||||
0xca, 0x87, 0x1a, 0x98, 0xab, 0x93, 0xa0, 0x65, 0x19, 0x04, 0x91, 0x06, 0x09, 0x88, 0x6b, 0x10,
|
||||
0xb8, 0x02, 0x26, 0xf9, 0x4f, 0x97, 0x3e, 0x36, 0xe2, 0xb9, 0xfe, 0xbc, 0x0c, 0xf9, 0xe4, 0x76,
|
||||
0xcc, 0x40, 0x89, 0x8c, 0xfa, 0x0d, 0x20, 0x37, 0xf0, 0x37, 0x80, 0x0b, 0x60, 0xcc, 0x4f, 0x26,
|
||||
0xa4, 0x45, 0xc6, 0xe5, 0x43, 0x51, 0x4e, 0xe5, 0x5c, 0x2f, 0x08, 0xf9, 0xd8, 0xa7, 0x20, 0xb9,
|
||||
0x5e, 0x10, 0x22, 0x4e, 0xad, 0xfc, 0x43, 0x03, 0xfd, 0xfe, 0xbd, 0x07, 0xb6, 0xc0, 0x04, 0x15,
|
||||
0xae, 0xcb, 0xd0, 0xee, 0xdc, 0x67, 0x68, 0xb3, 0x81, 0x10, 0xf7, 0x6a, 0x4c, 0x8d, 0xc1, 0x58,
|
||||
0x74, 0x0d, 0x5c, 0x8b, 0x5c, 0x53, 0x4e, 0x3c, 0xa7, 0x45, 0x74, 0xd7, 0x56, 0x05, 0x0d, 0x29,
|
||||
0x2e, 0x3c, 0x2f, 0x66, 0x73, 0xa9, 0x81, 0x57, 0x3c, 0x97, 0xab, 0x5d, 0xfe, 0xe8, 0x76, 0xe9,
|
||||
0xd4, 0xc7, 0xb7, 0x4b, 0xa7, 0x3e, 0xb9, 0x5d, 0x3a, 0xf5, 0xbd, 0x4e, 0x49, 0xfb, 0xa8, 0x53,
|
||||
0xd2, 0x3e, 0xee, 0x94, 0xb4, 0x4f, 0x3a, 0x25, 0xed, 0x5f, 0x9d, 0x92, 0xf6, 0x8b, 0x4f, 0x4b,
|
||||
0xa7, 0xbe, 0x35, 0x21, 0x5d, 0xfb, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xe1, 0x05, 0x6b, 0x7f,
|
||||
0x74, 0x2b, 0x00, 0x00,
|
||||
// 2908 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x5a, 0xcd, 0x73, 0x23, 0x47,
|
||||
0x15, 0xdf, 0x91, 0x2c, 0x5b, 0x6e, 0xdb, 0x6b, 0xbb, 0x77, 0xed, 0xcc, 0x3a, 0x1b, 0xc9, 0xab,
|
||||
0x90, 0x60, 0xc2, 0xae, 0x9c, 0x2c, 0x09, 0x09, 0xa9, 0xe2, 0x60, 0xd9, 0x4e, 0xca, 0xc9, 0xda,
|
||||
0x32, 0xad, 0xdd, 0x64, 0x21, 0x9f, 0x6d, 0x4d, 0x4b, 0x9e, 0xf5, 0x7c, 0xed, 0xf4, 0x8c, 0x6c,
|
||||
0x57, 0x80, 0xe2, 0xa3, 0x52, 0x50, 0x14, 0x10, 0x8a, 0xe4, 0x42, 0x15, 0x1c, 0x02, 0xc5, 0x85,
|
||||
0x03, 0x1c, 0xe0, 0x06, 0x7f, 0x40, 0x8e, 0x29, 0x4e, 0x39, 0x50, 0x2a, 0x56, 0xb9, 0xc2, 0x8d,
|
||||
0x2a, 0xaa, 0x7c, 0xa2, 0xfa, 0x63, 0x7a, 0x46, 0x23, 0x69, 0xd7, 0x95, 0x95, 0xb2, 0xdc, 0xac,
|
||||
0xf7, 0xf5, 0x7b, 0xfd, 0xfa, 0xf5, 0xeb, 0xd7, 0x6f, 0x0c, 0x1a, 0x07, 0xcf, 0xd1, 0xb2, 0xe9,
|
||||
0xae, 0x1e, 0x84, 0x7b, 0xc4, 0x77, 0x48, 0x40, 0xe8, 0x6a, 0x8b, 0x38, 0x86, 0xeb, 0xaf, 0x4a,
|
||||
0x06, 0xf6, 0x4c, 0x72, 0x14, 0x10, 0x87, 0x9a, 0xae, 0x43, 0xaf, 0x60, 0xcf, 0xa4, 0xc4, 0x6f,
|
||||
0x11, 0x7f, 0xd5, 0x3b, 0x68, 0x32, 0x1e, 0xed, 0x16, 0x58, 0x6d, 0x3d, 0xb5, 0x47, 0x02, 0xfc,
|
||||
0xd4, 0x6a, 0x93, 0x38, 0xc4, 0xc7, 0x01, 0x31, 0xca, 0x9e, 0xef, 0x06, 0x2e, 0xfc, 0xba, 0x30,
|
||||
0x57, 0xee, 0x92, 0x7e, 0x4b, 0x99, 0x2b, 0x7b, 0x07, 0x4d, 0xc6, 0xa3, 0xdd, 0x02, 0x65, 0x69,
|
||||
0x6e, 0xe9, 0x4a, 0xd3, 0x0c, 0xf6, 0xc3, 0xbd, 0x72, 0xdd, 0xb5, 0x57, 0x9b, 0x6e, 0xd3, 0x5d,
|
||||
0xe5, 0x56, 0xf7, 0xc2, 0x06, 0xff, 0xc5, 0x7f, 0xf0, 0xbf, 0x04, 0xda, 0xd2, 0xd3, 0xb1, 0xf3,
|
||||
0x36, 0xae, 0xef, 0x9b, 0x0e, 0xf1, 0x8f, 0x63, 0x8f, 0x6d, 0x12, 0xe0, 0xd5, 0x56, 0x8f, 0x8f,
|
||||
0x4b, 0xab, 0x83, 0xb4, 0xfc, 0xd0, 0x09, 0x4c, 0x9b, 0xf4, 0x28, 0x7c, 0xf5, 0x5e, 0x0a, 0xb4,
|
||||
0xbe, 0x4f, 0x6c, 0x9c, 0xd6, 0x2b, 0x9d, 0x68, 0x60, 0x7e, 0xdd, 0x75, 0x5a, 0xc4, 0x67, 0xab,
|
||||
0x44, 0xe4, 0x76, 0x48, 0x68, 0x00, 0x2b, 0x20, 0x1b, 0x9a, 0x86, 0xae, 0x2d, 0x6b, 0x2b, 0x93,
|
||||
0x95, 0x27, 0x3f, 0x6a, 0x17, 0xcf, 0x74, 0xda, 0xc5, 0xec, 0x8d, 0xad, 0x8d, 0x93, 0x76, 0xf1,
|
||||
0xd2, 0x20, 0xa4, 0xe0, 0xd8, 0x23, 0xb4, 0x7c, 0x63, 0x6b, 0x03, 0x31, 0x65, 0xf8, 0x22, 0x98,
|
||||
0x37, 0x08, 0x35, 0x7d, 0x62, 0xac, 0xed, 0x6e, 0xbd, 0x22, 0xec, 0xeb, 0x19, 0x6e, 0xf1, 0x82,
|
||||
0xb4, 0x38, 0xbf, 0x91, 0x16, 0x40, 0xbd, 0x3a, 0xf0, 0x26, 0x98, 0x70, 0xf7, 0x6e, 0x91, 0x7a,
|
||||
0x40, 0xf5, 0xec, 0x72, 0x76, 0x65, 0xea, 0xea, 0x95, 0x72, 0xbc, 0x83, 0xca, 0x05, 0xbe, 0x6d,
|
||||
0x72, 0xb1, 0x65, 0x84, 0x0f, 0x37, 0xa3, 0x9d, 0xab, 0xcc, 0x4a, 0xb4, 0x89, 0xaa, 0xb0, 0x82,
|
||||
0x22, 0x73, 0xa5, 0xdf, 0x65, 0x00, 0x4c, 0x2e, 0x9e, 0x7a, 0xae, 0x43, 0xc9, 0x50, 0x56, 0x4f,
|
||||
0xc1, 0x5c, 0x9d, 0x5b, 0x0e, 0x88, 0x21, 0x71, 0xf5, 0xcc, 0x67, 0xf1, 0x5e, 0x97, 0xf8, 0x73,
|
||||
0xeb, 0x29, 0x73, 0xa8, 0x07, 0x00, 0x5e, 0x07, 0xe3, 0x3e, 0xa1, 0xa1, 0x15, 0xe8, 0xd9, 0x65,
|
||||
0x6d, 0x65, 0xea, 0xea, 0xe5, 0x81, 0x50, 0x3c, 0xbf, 0x59, 0xf2, 0x95, 0x5b, 0x4f, 0x95, 0x6b,
|
||||
0x01, 0x0e, 0x42, 0x5a, 0x39, 0x2b, 0x91, 0xc6, 0x11, 0xb7, 0x81, 0xa4, 0xad, 0xd2, 0x8f, 0x33,
|
||||
0x60, 0x2e, 0x19, 0xa5, 0x96, 0x49, 0x0e, 0xe1, 0x21, 0x98, 0xf0, 0x45, 0xb2, 0xf0, 0x38, 0x4d,
|
||||
0x5d, 0xdd, 0x2d, 0xdf, 0xd7, 0xb1, 0x2a, 0xf7, 0x24, 0x61, 0x65, 0x8a, 0xed, 0x99, 0xfc, 0x81,
|
||||
0x22, 0x34, 0xf8, 0x0e, 0xc8, 0xfb, 0x72, 0xa3, 0x78, 0x36, 0x4d, 0x5d, 0xfd, 0xc6, 0x10, 0x91,
|
||||
0x85, 0xe1, 0xca, 0x74, 0xa7, 0x5d, 0xcc, 0x47, 0xbf, 0x90, 0x02, 0x2c, 0xbd, 0x9f, 0x01, 0x85,
|
||||
0xf5, 0x90, 0x06, 0xae, 0x8d, 0x08, 0x75, 0x43, 0xbf, 0x4e, 0xd6, 0x5d, 0x2b, 0xb4, 0x9d, 0x0d,
|
||||
0xd2, 0x30, 0x1d, 0x33, 0x60, 0xd9, 0xba, 0x0c, 0xc6, 0x1c, 0x6c, 0x13, 0x99, 0x3d, 0xd3, 0x32,
|
||||
0xa6, 0x63, 0x3b, 0xd8, 0x26, 0x88, 0x73, 0x98, 0x04, 0x4b, 0x16, 0x79, 0x16, 0x94, 0xc4, 0xf5,
|
||||
0x63, 0x8f, 0x20, 0xce, 0x81, 0x8f, 0x83, 0xf1, 0x86, 0xeb, 0xdb, 0x58, 0xec, 0xe3, 0x64, 0xbc,
|
||||
0x33, 0x2f, 0x70, 0x2a, 0x92, 0x5c, 0xf8, 0x0c, 0x98, 0x32, 0x08, 0xad, 0xfb, 0xa6, 0xc7, 0xa0,
|
||||
0xf5, 0x31, 0x2e, 0x7c, 0x4e, 0x0a, 0x4f, 0x6d, 0xc4, 0x2c, 0x94, 0x94, 0x83, 0x97, 0x41, 0xde,
|
||||
0xf3, 0x4d, 0xd7, 0x37, 0x83, 0x63, 0x3d, 0xb7, 0xac, 0xad, 0xe4, 0x2a, 0x73, 0x52, 0x27, 0xbf,
|
||||
0x2b, 0xe9, 0x48, 0x49, 0xc0, 0x65, 0x90, 0x7f, 0xa9, 0x56, 0xdd, 0xd9, 0xc5, 0xc1, 0xbe, 0x3e,
|
||||
0xce, 0x11, 0xc6, 0x98, 0x34, 0xca, 0xdf, 0x92, 0xd4, 0xd2, 0x3f, 0x32, 0x40, 0x4f, 0x47, 0x25,
|
||||
0x0a, 0x29, 0x7c, 0x01, 0xe4, 0x69, 0xc0, 0x2a, 0x4e, 0xf3, 0x58, 0xc6, 0xe4, 0x89, 0x08, 0xac,
|
||||
0x26, 0xe9, 0x27, 0xed, 0xe2, 0x62, 0xac, 0x11, 0x51, 0x79, 0x3c, 0x94, 0x2e, 0xfc, 0x8d, 0x06,
|
||||
0xce, 0x1d, 0x92, 0xbd, 0x7d, 0xd7, 0x3d, 0x58, 0xb7, 0x4c, 0xe2, 0x04, 0xeb, 0xae, 0xd3, 0x30,
|
||||
0x9b, 0x32, 0x07, 0xd0, 0x7d, 0xe6, 0xc0, 0xab, 0xbd, 0x96, 0x2b, 0x0f, 0x75, 0xda, 0xc5, 0x73,
|
||||
0x7d, 0x18, 0xa8, 0x9f, 0x1f, 0xf0, 0x26, 0xd0, 0xeb, 0xa9, 0x43, 0x22, 0x0b, 0x98, 0x28, 0x5b,
|
||||
0x93, 0x95, 0x8b, 0x9d, 0x76, 0x51, 0x5f, 0x1f, 0x20, 0x83, 0x06, 0x6a, 0x97, 0x7e, 0x98, 0x4d,
|
||||
0x87, 0x37, 0x91, 0x6e, 0x6f, 0x83, 0x3c, 0x3b, 0xc6, 0x06, 0x0e, 0xb0, 0x3c, 0x88, 0x4f, 0x9e,
|
||||
0xee, 0xd0, 0x8b, 0x9a, 0xb1, 0x4d, 0x02, 0x5c, 0x81, 0x72, 0x43, 0x40, 0x4c, 0x43, 0xca, 0x2a,
|
||||
0xfc, 0x0e, 0x18, 0xa3, 0x1e, 0xa9, 0xcb, 0x40, 0xbf, 0x76, 0xbf, 0x87, 0x6d, 0xc0, 0x42, 0x6a,
|
||||
0x1e, 0xa9, 0xc7, 0x67, 0x81, 0xfd, 0x42, 0x1c, 0x16, 0xbe, 0xab, 0x81, 0x71, 0xca, 0x0b, 0x94,
|
||||
0x2c, 0x6a, 0x6f, 0x8c, 0xca, 0x83, 0x54, 0x15, 0x14, 0xbf, 0x91, 0x04, 0x2f, 0xfd, 0x27, 0x03,
|
||||
0x2e, 0x0d, 0x52, 0x5d, 0x77, 0x1d, 0x43, 0x6c, 0xc7, 0x96, 0x3c, 0xdb, 0x22, 0xd3, 0x9f, 0x49,
|
||||
0x9e, 0xed, 0x93, 0x76, 0xf1, 0xb1, 0x7b, 0x1a, 0x48, 0x14, 0x81, 0xaf, 0xa9, 0x75, 0x8b, 0x42,
|
||||
0x71, 0xa9, 0xdb, 0xb1, 0x93, 0x76, 0x71, 0x56, 0xa9, 0x75, 0xfb, 0x0a, 0x5b, 0x00, 0x5a, 0x98,
|
||||
0x06, 0xd7, 0x7d, 0xec, 0x50, 0x61, 0xd6, 0xb4, 0x89, 0x0c, 0xdf, 0x13, 0xa7, 0x4b, 0x0f, 0xa6,
|
||||
0x51, 0x59, 0x92, 0x90, 0xf0, 0x5a, 0x8f, 0x35, 0xd4, 0x07, 0x81, 0xd5, 0x2d, 0x9f, 0x60, 0xaa,
|
||||
0x4a, 0x51, 0xe2, 0x46, 0x61, 0x54, 0x24, 0xb9, 0xf0, 0x4b, 0x60, 0xc2, 0x26, 0x94, 0xe2, 0x26,
|
||||
0xe1, 0xf5, 0x67, 0x32, 0xbe, 0xa2, 0xb7, 0x05, 0x19, 0x45, 0x7c, 0xd6, 0x9f, 0x5c, 0x1c, 0x14,
|
||||
0xb5, 0x6b, 0x26, 0x0d, 0xe0, 0xeb, 0x3d, 0x07, 0xa0, 0x7c, 0xba, 0x15, 0x32, 0x6d, 0x9e, 0xfe,
|
||||
0xaa, 0xf8, 0x45, 0x94, 0x44, 0xf2, 0x7f, 0x1b, 0xe4, 0xcc, 0x80, 0xd8, 0xd1, 0xdd, 0xfd, 0xea,
|
||||
0x88, 0x72, 0xaf, 0x32, 0x23, 0x7d, 0xc8, 0x6d, 0x31, 0x34, 0x24, 0x40, 0x4b, 0xbf, 0xcf, 0x80,
|
||||
0x47, 0x06, 0xa9, 0xb0, 0x0b, 0x85, 0xb2, 0x88, 0x7b, 0x56, 0xe8, 0x63, 0x4b, 0x66, 0x9c, 0x8a,
|
||||
0xf8, 0x2e, 0xa7, 0x22, 0xc9, 0x65, 0x25, 0x9f, 0x9a, 0x4e, 0x33, 0xb4, 0xb0, 0x2f, 0xd3, 0x49,
|
||||
0xad, 0xba, 0x26, 0xe9, 0x48, 0x49, 0xc0, 0x32, 0x00, 0x74, 0xdf, 0xf5, 0x03, 0x8e, 0x21, 0xab,
|
||||
0xd7, 0x59, 0x56, 0x20, 0x6a, 0x8a, 0x8a, 0x12, 0x12, 0xec, 0x46, 0x3b, 0x30, 0x1d, 0x43, 0xee,
|
||||
0xba, 0x3a, 0xc5, 0x2f, 0x9b, 0x8e, 0x81, 0x38, 0x87, 0xe1, 0x5b, 0x26, 0x0d, 0x18, 0x45, 0x6e,
|
||||
0x79, 0x57, 0xd4, 0xb9, 0xa4, 0x92, 0x60, 0xf8, 0x75, 0x56, 0xf5, 0x5d, 0xdf, 0x24, 0x54, 0x1f,
|
||||
0x8f, 0xf1, 0xd7, 0x15, 0x15, 0x25, 0x24, 0x4a, 0xff, 0xca, 0x0f, 0x4e, 0x12, 0x56, 0x4a, 0xe0,
|
||||
0xa3, 0x20, 0xd7, 0xf4, 0xdd, 0xd0, 0x93, 0x51, 0x52, 0xd1, 0x7e, 0x91, 0x11, 0x91, 0xe0, 0xb1,
|
||||
0xac, 0x6c, 0x75, 0xb5, 0xa9, 0x2a, 0x2b, 0xa3, 0xe6, 0x34, 0xe2, 0xc3, 0xef, 0x6b, 0x20, 0xe7,
|
||||
0xc8, 0xe0, 0xb0, 0x94, 0x7b, 0x7d, 0x44, 0x79, 0xc1, 0xc3, 0x1b, 0xbb, 0x2b, 0x22, 0x2f, 0x90,
|
||||
0xe1, 0xd3, 0x20, 0x47, 0xeb, 0xae, 0x47, 0x64, 0xd4, 0x0b, 0x91, 0x50, 0x8d, 0x11, 0x4f, 0xda,
|
||||
0xc5, 0x99, 0xc8, 0x1c, 0x27, 0x20, 0x21, 0x0c, 0x7f, 0xa4, 0x01, 0xd0, 0xc2, 0x96, 0x69, 0x60,
|
||||
0xde, 0x32, 0xe4, 0xb8, 0xfb, 0xc3, 0x4d, 0xeb, 0x57, 0x94, 0x79, 0xb1, 0x69, 0xf1, 0x6f, 0x94,
|
||||
0x80, 0x86, 0xef, 0x69, 0x60, 0x9a, 0x86, 0x7b, 0xbe, 0xd4, 0xa2, 0xbc, 0xb9, 0x98, 0xba, 0xfa,
|
||||
0xcd, 0xa1, 0xfa, 0x52, 0x4b, 0x00, 0x54, 0xe6, 0x3a, 0xed, 0xe2, 0x74, 0x92, 0x82, 0xba, 0x1c,
|
||||
0x80, 0x3f, 0xd5, 0x40, 0xbe, 0x15, 0xdd, 0xd9, 0x13, 0xfc, 0xc0, 0xbf, 0x39, 0xa2, 0x8d, 0x95,
|
||||
0x19, 0x15, 0x9f, 0x02, 0xd5, 0x07, 0x28, 0x0f, 0xe0, 0x5f, 0x35, 0xa0, 0x63, 0x43, 0x14, 0x78,
|
||||
0x6c, 0xed, 0xfa, 0xa6, 0x13, 0x10, 0x5f, 0xf4, 0x9b, 0x54, 0xcf, 0x73, 0xf7, 0x86, 0x7b, 0x17,
|
||||
0xa6, 0x7b, 0xd9, 0xca, 0xb2, 0xf4, 0x4e, 0x5f, 0x1b, 0xe0, 0x06, 0x1a, 0xe8, 0x20, 0x4f, 0xb4,
|
||||
0xb8, 0xa5, 0xd1, 0x27, 0x47, 0x90, 0x68, 0x71, 0x2f, 0x25, 0xab, 0x43, 0xdc, 0x41, 0x25, 0xa0,
|
||||
0x61, 0x15, 0x2c, 0x78, 0x3e, 0xe1, 0x00, 0x37, 0x9c, 0x03, 0xc7, 0x3d, 0x74, 0x5e, 0x30, 0x89,
|
||||
0x65, 0x50, 0x1d, 0x2c, 0x6b, 0x2b, 0xf9, 0xca, 0x85, 0x4e, 0xbb, 0xb8, 0xb0, 0xdb, 0x4f, 0x00,
|
||||
0xf5, 0xd7, 0x2b, 0xbd, 0x97, 0x4d, 0xbf, 0x02, 0xd2, 0x5d, 0x04, 0xfc, 0x40, 0xac, 0x5e, 0xc4,
|
||||
0x86, 0xea, 0x1a, 0xdf, 0xad, 0xb7, 0x47, 0x94, 0x4c, 0xaa, 0x0d, 0x88, 0x3b, 0x39, 0x45, 0xa2,
|
||||
0x28, 0xe1, 0x07, 0xfc, 0x95, 0x06, 0x66, 0x70, 0xbd, 0x4e, 0xbc, 0x80, 0x18, 0xa2, 0xb8, 0x67,
|
||||
0x3e, 0x87, 0xfa, 0xb5, 0x20, 0xbd, 0x9a, 0x59, 0x4b, 0x42, 0xa3, 0x6e, 0x4f, 0xe0, 0xf3, 0xe0,
|
||||
0x2c, 0x0d, 0x5c, 0x9f, 0x18, 0xa9, 0xb6, 0x19, 0x76, 0xda, 0xc5, 0xb3, 0xb5, 0x2e, 0x0e, 0x4a,
|
||||
0x49, 0x96, 0x3e, 0x1d, 0x03, 0xc5, 0x7b, 0x1c, 0xb5, 0x53, 0x3c, 0xcc, 0x1e, 0x07, 0xe3, 0x7c,
|
||||
0xb9, 0x06, 0x8f, 0x4a, 0x3e, 0xd1, 0x0a, 0x72, 0x2a, 0x92, 0x5c, 0x76, 0x51, 0x30, 0x7c, 0xd6,
|
||||
0xbe, 0x64, 0xb9, 0xa0, 0xba, 0x28, 0x6a, 0x82, 0x8c, 0x22, 0x3e, 0x7c, 0x07, 0x8c, 0x8b, 0xc1,
|
||||
0x0b, 0xaf, 0xd2, 0x23, 0xac, 0xb4, 0x80, 0xfb, 0xc9, 0xa1, 0x90, 0x84, 0xec, 0xad, 0xb0, 0xb9,
|
||||
0x07, 0x5d, 0x61, 0xef, 0x5a, 0xd2, 0xc6, 0xff, 0xcf, 0x4b, 0x5a, 0xe9, 0xbf, 0x5a, 0xfa, 0xdc,
|
||||
0x27, 0x96, 0x5a, 0xab, 0x63, 0x8b, 0xc0, 0x0d, 0x30, 0xc7, 0x5e, 0x2d, 0x88, 0x78, 0x96, 0x59,
|
||||
0xc7, 0x94, 0x3f, 0x9a, 0x45, 0xc2, 0xa9, 0x39, 0x4e, 0x2d, 0xc5, 0x47, 0x3d, 0x1a, 0xf0, 0x25,
|
||||
0x00, 0x45, 0x27, 0xdf, 0x65, 0x47, 0x34, 0x25, 0xaa, 0x27, 0xaf, 0xf5, 0x48, 0xa0, 0x3e, 0x5a,
|
||||
0x70, 0x1d, 0xcc, 0x5b, 0x78, 0x8f, 0x58, 0x35, 0x62, 0x91, 0x7a, 0xe0, 0xfa, 0xdc, 0x94, 0x18,
|
||||
0x2b, 0x2c, 0x74, 0xda, 0xc5, 0xf9, 0x6b, 0x69, 0x26, 0xea, 0x95, 0x2f, 0x5d, 0x4a, 0x1f, 0xaf,
|
||||
0xe4, 0xc2, 0xc5, 0xfb, 0xe8, 0xc3, 0x0c, 0x58, 0x1a, 0x9c, 0x19, 0xf0, 0x07, 0xf1, 0x33, 0x4e,
|
||||
0x74, 0xe9, 0x6f, 0x8e, 0x2a, 0x0b, 0xe5, 0x3b, 0x0e, 0xf4, 0xbe, 0xe1, 0xe0, 0x77, 0x59, 0xcb,
|
||||
0x84, 0xad, 0x68, 0x70, 0xf4, 0xc6, 0xc8, 0x5c, 0x60, 0x20, 0x95, 0x49, 0xd1, 0x8d, 0x61, 0x8b,
|
||||
0x37, 0x5f, 0xd8, 0x22, 0xa5, 0x3f, 0x68, 0xe9, 0x97, 0x7c, 0x7c, 0x82, 0xe1, 0xcf, 0x34, 0x30,
|
||||
0xeb, 0x7a, 0xc4, 0x59, 0xdb, 0xdd, 0x7a, 0xe5, 0x2b, 0xe2, 0x24, 0xcb, 0x50, 0xed, 0xdc, 0xa7,
|
||||
0x9f, 0x2f, 0xd5, 0xaa, 0x3b, 0xc2, 0xe0, 0xae, 0xef, 0x7a, 0xb4, 0x72, 0xae, 0xd3, 0x2e, 0xce,
|
||||
0x56, 0xbb, 0xa1, 0x50, 0x1a, 0xbb, 0x64, 0x83, 0x85, 0xcd, 0xa3, 0x80, 0xf8, 0x0e, 0xb6, 0x36,
|
||||
0xdc, 0x7a, 0x68, 0x13, 0x27, 0x10, 0x8e, 0xa6, 0xa6, 0x4e, 0xda, 0x29, 0xa7, 0x4e, 0x8f, 0x80,
|
||||
0x6c, 0xe8, 0x5b, 0x32, 0x8b, 0xa7, 0xd4, 0x54, 0x15, 0x5d, 0x43, 0x8c, 0x5e, 0xba, 0x04, 0xc6,
|
||||
0x98, 0x9f, 0xf0, 0x02, 0xc8, 0xfa, 0xf8, 0x90, 0x5b, 0x9d, 0xae, 0x4c, 0x30, 0x11, 0x84, 0x0f,
|
||||
0x11, 0xa3, 0x95, 0xfe, 0x5d, 0x00, 0xb3, 0xa9, 0xb5, 0xc0, 0x25, 0x90, 0x51, 0xa3, 0x5a, 0x20,
|
||||
0x8d, 0x66, 0xb6, 0x36, 0x50, 0xc6, 0x34, 0xe0, 0xb3, 0xaa, 0xf8, 0x0a, 0xd0, 0xa2, 0xaa, 0xe7,
|
||||
0x9c, 0xca, 0x7a, 0xe4, 0xd8, 0x1c, 0x73, 0x24, 0x2a, 0x9c, 0xcc, 0x07, 0xd2, 0x90, 0xa7, 0x44,
|
||||
0xf8, 0x40, 0x1a, 0x88, 0xd1, 0x3e, 0xeb, 0xc8, 0x2d, 0x9a, 0xf9, 0xe5, 0x4e, 0x31, 0xf3, 0x1b,
|
||||
0xbf, 0xeb, 0xcc, 0xef, 0x51, 0x90, 0x0b, 0xcc, 0xc0, 0x22, 0xfa, 0x44, 0xf7, 0x53, 0xe6, 0x3a,
|
||||
0x23, 0x22, 0xc1, 0x83, 0xb7, 0xc0, 0x84, 0x41, 0x1a, 0x38, 0xb4, 0x02, 0x3d, 0xcf, 0x53, 0x68,
|
||||
0x7d, 0x08, 0x29, 0x24, 0x06, 0xb2, 0x1b, 0xc2, 0x2e, 0x8a, 0x00, 0xe0, 0x63, 0x60, 0xc2, 0xc6,
|
||||
0x47, 0xa6, 0x1d, 0xda, 0xbc, 0xc9, 0xd3, 0x84, 0xd8, 0xb6, 0x20, 0xa1, 0x88, 0xc7, 0x2a, 0x23,
|
||||
0x39, 0xaa, 0x5b, 0x21, 0x35, 0x5b, 0x44, 0x32, 0x65, 0x03, 0xa6, 0x2a, 0xe3, 0x66, 0x8a, 0x8f,
|
||||
0x7a, 0x34, 0x38, 0x98, 0xe9, 0x70, 0xe5, 0xa9, 0x04, 0x98, 0x20, 0xa1, 0x88, 0xd7, 0x0d, 0x26,
|
||||
0xe5, 0xa7, 0x07, 0x81, 0x49, 0xe5, 0x1e, 0x0d, 0xf8, 0x65, 0x30, 0x69, 0xe3, 0xa3, 0x6b, 0xc4,
|
||||
0x69, 0x06, 0xfb, 0xfa, 0xcc, 0xb2, 0xb6, 0x92, 0xad, 0xcc, 0x74, 0xda, 0xc5, 0xc9, 0xed, 0x88,
|
||||
0x88, 0x62, 0x3e, 0x17, 0x36, 0x1d, 0x29, 0x7c, 0x36, 0x21, 0x1c, 0x11, 0x51, 0xcc, 0x67, 0x1d,
|
||||
0x84, 0x87, 0x03, 0x76, 0xb8, 0xf4, 0xd9, 0xee, 0xa7, 0xe6, 0xae, 0x20, 0xa3, 0x88, 0x0f, 0x57,
|
||||
0x40, 0xde, 0xc6, 0x47, 0x7c, 0x2c, 0xa0, 0xcf, 0x71, 0xb3, 0x7c, 0x38, 0xbd, 0x2d, 0x69, 0x48,
|
||||
0x71, 0xb9, 0xa4, 0xe9, 0x08, 0xc9, 0xf9, 0x84, 0xa4, 0xa4, 0x21, 0xc5, 0x65, 0x49, 0x1c, 0x3a,
|
||||
0xe6, 0xed, 0x90, 0x08, 0x61, 0xc8, 0x23, 0xa3, 0x92, 0xf8, 0x46, 0xcc, 0x42, 0x49, 0x39, 0xf6,
|
||||
0x2c, 0xb7, 0x43, 0x2b, 0x30, 0x3d, 0x8b, 0x54, 0x1b, 0xfa, 0x39, 0x1e, 0x7f, 0xde, 0x78, 0x6f,
|
||||
0x2b, 0x2a, 0x4a, 0x48, 0x40, 0x02, 0xc6, 0x88, 0x13, 0xda, 0xfa, 0x79, 0x7e, 0xb1, 0x0f, 0x25,
|
||||
0x05, 0xd5, 0xc9, 0xd9, 0x74, 0x42, 0x1b, 0x71, 0xf3, 0xf0, 0x59, 0x30, 0x63, 0xe3, 0x23, 0x56,
|
||||
0x0e, 0x88, 0x1f, 0x98, 0x84, 0xea, 0x0b, 0x7c, 0xf1, 0xf3, 0xac, 0xe3, 0xdc, 0x4e, 0x32, 0x50,
|
||||
0xb7, 0x1c, 0x57, 0x34, 0x9d, 0x84, 0xe2, 0x62, 0x42, 0x31, 0xc9, 0x40, 0xdd, 0x72, 0x2c, 0xd2,
|
||||
0x3e, 0xb9, 0x1d, 0x9a, 0x3e, 0x31, 0xf4, 0x87, 0x78, 0x93, 0x2a, 0x3f, 0x18, 0x08, 0x1a, 0x52,
|
||||
0x5c, 0xd8, 0x8a, 0xe6, 0x47, 0x3a, 0x3f, 0x86, 0x37, 0x86, 0x5b, 0xc9, 0xab, 0xfe, 0x9a, 0xef,
|
||||
0xe3, 0x63, 0x71, 0xd3, 0x24, 0x27, 0x47, 0x90, 0x82, 0x1c, 0xb6, 0xac, 0x6a, 0x43, 0xbf, 0xc0,
|
||||
0x63, 0x3f, 0xec, 0x1b, 0x44, 0x55, 0x9d, 0x35, 0x06, 0x82, 0x04, 0x16, 0x03, 0x75, 0x1d, 0x96,
|
||||
0x1a, 0x4b, 0xa3, 0x05, 0xad, 0x32, 0x10, 0x24, 0xb0, 0xf8, 0x4a, 0x9d, 0xe3, 0x6a, 0x43, 0x7f,
|
||||
0x78, 0xc4, 0x2b, 0x65, 0x20, 0x48, 0x60, 0x41, 0x13, 0x64, 0x1d, 0x37, 0xd0, 0x2f, 0x8e, 0xe4,
|
||||
0x7a, 0xe6, 0x17, 0xce, 0x8e, 0x1b, 0x20, 0x86, 0x01, 0x7f, 0xa9, 0x01, 0xe0, 0xc5, 0x29, 0xfa,
|
||||
0xc8, 0x50, 0xc6, 0x12, 0x29, 0xc8, 0x72, 0x9c, 0xdb, 0x9b, 0x4e, 0xe0, 0x1f, 0xc7, 0xef, 0xc8,
|
||||
0xc4, 0x19, 0x48, 0x78, 0x01, 0x7f, 0xab, 0x81, 0xf3, 0xc9, 0x36, 0x59, 0xb9, 0x57, 0xe0, 0x11,
|
||||
0xb9, 0x3e, 0xec, 0x34, 0xaf, 0xb8, 0xae, 0x55, 0xd1, 0x3b, 0xed, 0xe2, 0xf9, 0xb5, 0x3e, 0xa8,
|
||||
0xa8, 0xaf, 0x2f, 0xf0, 0x8f, 0x1a, 0x98, 0x97, 0x55, 0x34, 0xe1, 0x61, 0x91, 0x07, 0x90, 0x0c,
|
||||
0x3b, 0x80, 0x69, 0x1c, 0x11, 0x47, 0xf5, 0xa1, 0xbb, 0x87, 0x8f, 0x7a, 0x5d, 0x83, 0x7f, 0xd1,
|
||||
0xc0, 0xb4, 0x41, 0x3c, 0xe2, 0x18, 0xc4, 0xa9, 0x33, 0x5f, 0x97, 0x87, 0x32, 0x36, 0x48, 0xfb,
|
||||
0xba, 0x91, 0x80, 0x10, 0x6e, 0x96, 0xa5, 0x9b, 0xd3, 0x49, 0xd6, 0x49, 0xbb, 0xb8, 0x18, 0xab,
|
||||
0x26, 0x39, 0xa8, 0xcb, 0x4b, 0xf8, 0xbe, 0x06, 0x66, 0xe3, 0x0d, 0x10, 0x57, 0xca, 0xa5, 0x11,
|
||||
0xe6, 0x01, 0x6f, 0x5f, 0xd7, 0xba, 0x01, 0x51, 0xda, 0x03, 0xf8, 0x27, 0x8d, 0x75, 0x6a, 0xd1,
|
||||
0xbb, 0x8f, 0xea, 0x25, 0x1e, 0xcb, 0xb7, 0x86, 0x1e, 0x4b, 0x85, 0x20, 0x42, 0x79, 0x39, 0x6e,
|
||||
0x05, 0x15, 0xe7, 0xa4, 0x5d, 0x5c, 0x48, 0x46, 0x52, 0x31, 0x50, 0xd2, 0x43, 0xf8, 0x13, 0x0d,
|
||||
0x4c, 0x93, 0xb8, 0xe3, 0xa6, 0xfa, 0xa3, 0x43, 0x09, 0x62, 0xdf, 0x26, 0x5e, 0xbc, 0xd4, 0x13,
|
||||
0x2c, 0x8a, 0xba, 0xb0, 0x59, 0x07, 0x49, 0x8e, 0xb0, 0xed, 0x59, 0x44, 0xff, 0xc2, 0x90, 0x3b,
|
||||
0xc8, 0x4d, 0x61, 0x17, 0x45, 0x00, 0xf0, 0x32, 0xc8, 0x3b, 0xa1, 0x65, 0xe1, 0x3d, 0x8b, 0xe8,
|
||||
0x8f, 0xf1, 0x5e, 0x44, 0x8d, 0x45, 0x77, 0x24, 0x1d, 0x29, 0x09, 0xd8, 0x00, 0xcb, 0x47, 0x2f,
|
||||
0xab, 0x7f, 0x11, 0xea, 0x3b, 0xb8, 0xd3, 0x1f, 0xe7, 0x56, 0x96, 0x3a, 0xed, 0xe2, 0xe2, 0xcd,
|
||||
0xfe, 0xa3, 0xbd, 0x7b, 0xda, 0x80, 0xaf, 0x81, 0x87, 0x13, 0x32, 0x9b, 0xf6, 0x1e, 0x31, 0x0c,
|
||||
0x62, 0x44, 0x0f, 0x37, 0xfd, 0x8b, 0x62, 0x78, 0x18, 0x1d, 0xf0, 0x9b, 0x69, 0x01, 0x74, 0x37,
|
||||
0x6d, 0x78, 0x0d, 0x2c, 0x26, 0xd8, 0x5b, 0x4e, 0x50, 0xf5, 0x6b, 0x81, 0x6f, 0x3a, 0x4d, 0x7d,
|
||||
0x85, 0xdb, 0x3d, 0x1f, 0x9d, 0xc8, 0x9b, 0x09, 0x1e, 0x1a, 0xa0, 0xb3, 0xc4, 0x9e, 0x8e, 0xa9,
|
||||
0xd2, 0x03, 0xe7, 0x40, 0xf6, 0x80, 0xc8, 0x4f, 0xee, 0x88, 0xfd, 0x09, 0x0d, 0x90, 0x6b, 0x61,
|
||||
0x2b, 0x8c, 0x5e, 0xbf, 0x43, 0xbe, 0xb6, 0x90, 0x30, 0xfe, 0x7c, 0xe6, 0x39, 0x6d, 0xe9, 0x03,
|
||||
0x0d, 0x2c, 0xf6, 0xaf, 0x88, 0x0f, 0xd4, 0xad, 0x5f, 0x6b, 0x60, 0xbe, 0xa7, 0xf8, 0xf5, 0xf1,
|
||||
0xe8, 0x76, 0xb7, 0x47, 0xaf, 0x0d, 0xbb, 0x8a, 0x89, 0x5d, 0xe3, 0xad, 0x5b, 0xd2, 0xbd, 0x9f,
|
||||
0x6b, 0x60, 0x2e, 0x5d, 0x4f, 0x1e, 0x64, 0xbc, 0x4a, 0x1f, 0x64, 0xc0, 0x62, 0xff, 0x8e, 0x13,
|
||||
0xfa, 0xea, 0x69, 0x3d, 0x9a, 0x11, 0x45, 0xbf, 0x71, 0xe6, 0xbb, 0x1a, 0x98, 0xba, 0xa5, 0xe4,
|
||||
0xa2, 0x4f, 0xb2, 0x43, 0x1f, 0x8e, 0x44, 0x05, 0x3c, 0x66, 0x50, 0x94, 0xc4, 0x2d, 0xfd, 0x59,
|
||||
0x03, 0x0b, 0x7d, 0x6f, 0x26, 0xf6, 0x86, 0xc7, 0x96, 0xe5, 0x1e, 0x8a, 0x19, 0x57, 0x62, 0x80,
|
||||
0xbc, 0xc6, 0xa9, 0x48, 0x72, 0x13, 0xd1, 0xcb, 0x7c, 0x5e, 0xd1, 0x2b, 0xfd, 0x4d, 0x03, 0x17,
|
||||
0xef, 0x96, 0x89, 0x0f, 0x64, 0x4b, 0x57, 0x40, 0x5e, 0x76, 0x95, 0xc7, 0x7c, 0x3b, 0xe5, 0x43,
|
||||
0x4a, 0x16, 0x0d, 0xfe, 0x5f, 0x48, 0xe2, 0xaf, 0xd2, 0x87, 0x1a, 0x98, 0xab, 0x11, 0xbf, 0x65,
|
||||
0xd6, 0x09, 0x22, 0x0d, 0xe2, 0x13, 0xa7, 0x4e, 0xe0, 0x2a, 0x98, 0xe4, 0xdf, 0x42, 0x3d, 0x5c,
|
||||
0x8f, 0xe6, 0xfa, 0xf3, 0x32, 0xe4, 0x93, 0x3b, 0x11, 0x03, 0xc5, 0x32, 0xea, 0x1b, 0x40, 0x66,
|
||||
0xe0, 0x37, 0x80, 0x8b, 0x60, 0xcc, 0x8b, 0x27, 0xa4, 0x79, 0xc6, 0xe5, 0x43, 0x51, 0x4e, 0xe5,
|
||||
0x5c, 0xd7, 0x0f, 0xf8, 0xd8, 0x27, 0x27, 0xb9, 0xae, 0x1f, 0x20, 0x4e, 0x2d, 0xfd, 0x5d, 0x03,
|
||||
0xfd, 0xfe, 0x5f, 0x08, 0xb6, 0xc0, 0x04, 0x15, 0xae, 0xcb, 0xd0, 0x56, 0xef, 0x33, 0xb4, 0xe9,
|
||||
0x40, 0x88, 0x7b, 0x35, 0xa2, 0x46, 0x60, 0x2c, 0xba, 0x75, 0x5c, 0x09, 0x1d, 0x43, 0x4e, 0x3c,
|
||||
0xa7, 0x45, 0x74, 0xd7, 0xd7, 0x04, 0x0d, 0x29, 0x2e, 0xbc, 0x20, 0x66, 0x73, 0x89, 0x81, 0x57,
|
||||
0x34, 0x97, 0xab, 0x5c, 0xf9, 0xe8, 0x4e, 0xe1, 0xcc, 0xc7, 0x77, 0x0a, 0x67, 0x3e, 0xb9, 0x53,
|
||||
0x38, 0xf3, 0xbd, 0x4e, 0x41, 0xfb, 0xa8, 0x53, 0xd0, 0x3e, 0xee, 0x14, 0xb4, 0x4f, 0x3a, 0x05,
|
||||
0xed, 0x9f, 0x9d, 0x82, 0xf6, 0x8b, 0x4f, 0x0b, 0x67, 0xbe, 0x35, 0x21, 0x5d, 0xfb, 0x5f, 0x00,
|
||||
0x00, 0x00, 0xff, 0xff, 0x10, 0x75, 0x48, 0x06, 0xc5, 0x2b, 0x00, 0x00,
|
||||
}
|
||||
|
@@ -247,6 +247,12 @@ message CustomResourceDefinitionSpec {
|
||||
// `conversion` defines conversion settings for the CRD.
|
||||
// +optional
|
||||
optional CustomResourceConversion conversion = 9;
|
||||
|
||||
// preserveUnknownFields disables pruning of object fields which are not
|
||||
// specified in the OpenAPI schema. apiVersion, kind, metadata and known
|
||||
// fields inside metadata are always preserved.
|
||||
// Defaults to true in v1beta and will default to false in v1.
|
||||
optional bool preserveUnknownFields = 10;
|
||||
}
|
||||
|
||||
// CustomResourceDefinitionStatus indicates the state of the CustomResourceDefinition
|
||||
|
@@ -78,6 +78,12 @@ type CustomResourceDefinitionSpec struct {
|
||||
// `conversion` defines conversion settings for the CRD.
|
||||
// +optional
|
||||
Conversion *CustomResourceConversion `json:"conversion,omitempty" protobuf:"bytes,9,opt,name=conversion"`
|
||||
|
||||
// preserveUnknownFields disables pruning of object fields which are not
|
||||
// specified in the OpenAPI schema. apiVersion, kind, metadata and known
|
||||
// fields inside metadata are always preserved.
|
||||
// Defaults to true in v1beta and will default to false in v1.
|
||||
PreserveUnknownFields *bool `json:"preserveUnknownFields,omitempty" protobuf:"varint,10,opt,name=preserveUnknownFields"`
|
||||
}
|
||||
|
||||
// CustomResourceConversion describes how to convert different versions of a CR.
|
||||
|
@@ -504,6 +504,7 @@ func autoConvert_v1beta1_CustomResourceDefinitionSpec_To_apiextensions_CustomRes
|
||||
} else {
|
||||
out.Conversion = nil
|
||||
}
|
||||
out.PreserveUnknownFields = (*bool)(unsafe.Pointer(in.PreserveUnknownFields))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -550,6 +551,7 @@ func autoConvert_apiextensions_CustomResourceDefinitionSpec_To_v1beta1_CustomRes
|
||||
} else {
|
||||
out.Conversion = nil
|
||||
}
|
||||
out.PreserveUnknownFields = (*bool)(unsafe.Pointer(in.PreserveUnknownFields))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -283,6 +283,11 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti
|
||||
*out = new(CustomResourceConversion)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PreserveUnknownFields != nil {
|
||||
in, out := &in.PreserveUnknownFields, &out.PreserveUnknownFields
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -14,6 +14,7 @@ go_library(
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
|
@@ -21,6 +21,7 @@ import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
@@ -102,9 +103,9 @@ func ValidateUpdateCustomResourceDefinitionStatus(obj, oldObj *apiextensions.Cus
|
||||
}
|
||||
|
||||
// ValidateCustomResourceDefinitionVersion statically validates.
|
||||
func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path, statusEnabled bool) field.ErrorList {
|
||||
func ValidateCustomResourceDefinitionVersion(version *apiextensions.CustomResourceDefinitionVersion, fldPath *field.Path, mustBeStructural, statusEnabled bool) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(version.Schema, statusEnabled, fldPath.Child("schema"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(version.Schema, mustBeStructural, statusEnabled, fldPath.Child("schema"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(version.Subresources, fldPath.Child("subresources"))...)
|
||||
for i := range version.AdditionalPrinterColumns {
|
||||
allErrs = append(allErrs, ValidateCustomResourceColumnDefinition(&version.AdditionalPrinterColumns[i], fldPath.Child("additionalPrinterColumns").Index(i))...)
|
||||
@@ -130,6 +131,20 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||
|
||||
allErrs = append(allErrs, validateEnumStrings(fldPath.Child("scope"), string(spec.Scope), []string{string(apiextensions.ClusterScoped), string(apiextensions.NamespaceScoped)}, true)...)
|
||||
|
||||
mustBeStructural := false
|
||||
if spec.PreserveUnknownFields == nil || *spec.PreserveUnknownFields == false {
|
||||
mustBeStructural = true
|
||||
// check that either a global schema or versioned schemas are set
|
||||
if spec.Validation == nil || spec.Validation.OpenAPIV3Schema == nil {
|
||||
for i, v := range spec.Versions {
|
||||
schemaPath := fldPath.Child("versions").Index(i).Child("schema", "openAPIV3Schema")
|
||||
if v.Served && (v.Schema == nil || v.Schema.OpenAPIV3Schema == nil) {
|
||||
allErrs = append(allErrs, field.Required(schemaPath, "because otherwise all fields are pruned"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storageFlagCount := 0
|
||||
versionsMap := map[string]bool{}
|
||||
uniqueNames := true
|
||||
@@ -146,7 +161,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("versions").Index(i).Child("name"), spec.Versions[i].Name, strings.Join(errs, ",")))
|
||||
}
|
||||
subresources := getSubresourcesForVersion(spec, version.Name)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionVersion(&version, fldPath.Child("versions").Index(i), hasStatusEnabled(subresources))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionVersion(&version, fldPath.Child("versions").Index(i), mustBeStructural, hasStatusEnabled(subresources))...)
|
||||
}
|
||||
|
||||
// The top-level and per-version fields are mutual exclusive
|
||||
@@ -201,7 +216,7 @@ func validateCustomResourceDefinitionSpec(spec *apiextensions.CustomResourceDefi
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionNames(&spec.Names, fldPath.Child("names"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, hasAnyStatusEnabled(spec), fldPath.Child("validation"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionValidation(spec.Validation, mustBeStructural, hasAnyStatusEnabled(spec), fldPath.Child("validation"))...)
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionSubresources(spec.Subresources, fldPath.Child("subresources"))...)
|
||||
|
||||
for i := range spec.AdditionalPrinterColumns {
|
||||
@@ -531,7 +546,7 @@ type specStandardValidator interface {
|
||||
}
|
||||
|
||||
// ValidateCustomResourceDefinitionValidation statically validates
|
||||
func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiextensions.CustomResourceValidation, statusSubresourceEnabled bool, fldPath *field.Path) field.ErrorList {
|
||||
func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiextensions.CustomResourceValidation, mustBeStructural, statusSubresourceEnabled bool, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if customResourceValidation == nil {
|
||||
@@ -573,6 +588,17 @@ func ValidateCustomResourceDefinitionValidation(customResourceValidation *apiext
|
||||
|
||||
openAPIV3Schema := &specStandardValidatorV3{}
|
||||
allErrs = append(allErrs, ValidateCustomResourceDefinitionOpenAPISchema(schema, fldPath.Child("openAPIV3Schema"), openAPIV3Schema)...)
|
||||
|
||||
if mustBeStructural {
|
||||
if ss, err := structuralschema.NewStructural(schema); err != nil {
|
||||
// if the generic schema validation did its job, we should never get an error here. Hence, we hide it if there are validation errors already.
|
||||
if len(allErrs) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("openAPIV3Schema"), "", err.Error()))
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, structuralschema.ValidateStructural(ss, fldPath.Child("openAPIV3Schema"))...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if validation passed otherwise, make sure we can actually construct a schema validator from this custom resource validation.
|
||||
|
@@ -103,6 +103,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -147,6 +148,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -191,6 +193,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -231,6 +234,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
URL: strPtr(""),
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -272,6 +276,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
URL: strPtr("https://example.com/webhook"),
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -310,6 +315,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||
ConversionReviewVersions: []string{"v1beta1"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -351,6 +357,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -392,6 +399,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"0v"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -434,6 +442,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version", "v1beta1"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -473,6 +482,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"v1beta1", "v1beta1"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -510,6 +520,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("Webhook"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -547,6 +558,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("non_existing_conversion"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -584,6 +596,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -621,6 +634,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -659,6 +673,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -691,6 +706,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Conversion: &apiextensions.CustomResourceConversion{
|
||||
Strategy: apiextensions.ConversionStrategyType("None"),
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{},
|
||||
@@ -709,6 +725,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
@@ -752,6 +769,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Kind: "value()*a",
|
||||
ListKind: "value()*a",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -798,6 +816,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Kind: "matching",
|
||||
ListKind: "matching",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -843,6 +862,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{Allows: false},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -880,6 +900,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -923,6 +944,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -962,6 +984,7 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -971,36 +994,206 @@ func TestValidateCustomResourceDefinition(t *testing.T) {
|
||||
invalid("spec", "validation", "openAPIV3Schema", "x-kubernetes-preserve-unknown-fields"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preserveUnknownFields with unstructural global schema",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validUnstructuralValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
required("spec", "validation", "openAPIV3Schema", "properties[spec]", "type"),
|
||||
required("spec", "validation", "openAPIV3Schema", "properties[status]", "type"),
|
||||
required("spec", "validation", "openAPIV3Schema", "items", "type"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preserveUnknownFields with unstructural schema in one version",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
Schema: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validValidationSchema,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Schema: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validUnstructuralValidationSchema,
|
||||
},
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
required("spec", "versions[1]", "schema", "openAPIV3Schema", "properties[spec]", "type"),
|
||||
required("spec", "versions[1]", "schema", "openAPIV3Schema", "properties[status]", "type"),
|
||||
required("spec", "versions[1]", "schema", "openAPIV3Schema", "items", "type"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preserveUnknownFields with no schema in one version",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
Schema: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validValidationSchema,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Schema: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
required("spec", "versions[1]", "schema", "openAPIV3Schema"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "preserveUnknownFields with no schema at all",
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "plural.group.com"},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
Schema: nil,
|
||||
},
|
||||
{
|
||||
Name: "version2",
|
||||
Served: true,
|
||||
Storage: false,
|
||||
Schema: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: nil,
|
||||
},
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
required("spec", "versions[0]", "schema", "openAPIV3Schema"),
|
||||
required("spec", "versions[1]", "schema", "openAPIV3Schema"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
// duplicate defaulting behaviour
|
||||
if tc.resource.Spec.Conversion != nil && tc.resource.Spec.Conversion.Strategy == apiextensions.WebhookConverter && len(tc.resource.Spec.Conversion.ConversionReviewVersions) == 0 {
|
||||
tc.resource.Spec.Conversion.ConversionReviewVersions = []string{"v1beta1"}
|
||||
}
|
||||
errs := ValidateCustomResourceDefinition(tc.resource)
|
||||
seenErrs := make([]bool, len(errs))
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// duplicate defaulting behaviour
|
||||
if tc.resource.Spec.Conversion != nil && tc.resource.Spec.Conversion.Strategy == apiextensions.WebhookConverter && len(tc.resource.Spec.Conversion.ConversionReviewVersions) == 0 {
|
||||
tc.resource.Spec.Conversion.ConversionReviewVersions = []string{"v1beta1"}
|
||||
}
|
||||
errs := ValidateCustomResourceDefinition(tc.resource)
|
||||
seenErrs := make([]bool, len(errs))
|
||||
|
||||
for _, expectedError := range tc.errors {
|
||||
found := false
|
||||
for i, err := range errs {
|
||||
if expectedError.matches(err) && !seenErrs[i] {
|
||||
found = true
|
||||
seenErrs[i] = true
|
||||
break
|
||||
for _, expectedError := range tc.errors {
|
||||
found := false
|
||||
for i, err := range errs {
|
||||
if expectedError.matches(err) && !seenErrs[i] {
|
||||
found = true
|
||||
seenErrs[i] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("expected %v at %v, got %v", expectedError.errorType, expectedError.path.String(), errs)
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("%s: expected %v at %v, got %v", tc.name, expectedError.errorType, expectedError.path.String(), errs)
|
||||
for i, seen := range seenErrs {
|
||||
if !seen {
|
||||
t.Errorf("unexpected error: %v", errs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for i, seen := range seenErrs {
|
||||
if !seen {
|
||||
t.Errorf("%s: unexpected error: %v", tc.name, errs[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1043,6 +1236,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1078,6 +1272,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version_0, invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1117,6 +1312,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1152,6 +1348,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"v1beta1", "invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1193,6 +1390,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
},
|
||||
ConversionReviewVersions: []string{"invalid-version"},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1227,6 +1425,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
URL: strPtr("https://example.com/webhook"),
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1260,6 +1459,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1292,6 +1492,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1329,6 +1530,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1364,6 +1566,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1406,6 +1609,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1442,6 +1646,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1481,6 +1686,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1516,6 +1722,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind2",
|
||||
ListKind: "listkind2",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1556,6 +1763,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind",
|
||||
ListKind: "listkind",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1591,6 +1799,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "kind2",
|
||||
ListKind: "listkind2",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
AcceptedNames: apiextensions.CustomResourceDefinitionNames{
|
||||
@@ -1639,6 +1848,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1680,6 +1890,7 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
@@ -1690,6 +1901,208 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
forbidden("spec", "subresources"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "switch off preserveUnknownFields with structural schema before and after",
|
||||
old: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validUnstructuralValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{
|
||||
required("spec", "validation", "openAPIV3Schema", "properties[spec]", "type"),
|
||||
required("spec", "validation", "openAPIV3Schema", "properties[status]", "type"),
|
||||
required("spec", "validation", "openAPIV3Schema", "items", "type"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "switch off preserveUnknownFields without structural schema before, but with after",
|
||||
old: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validUnstructuralValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{},
|
||||
},
|
||||
{
|
||||
name: "switch on preserveUnknownFields without structural schema",
|
||||
old: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
resource: &apiextensions.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "plural.group.com",
|
||||
ResourceVersion: "42",
|
||||
},
|
||||
Spec: apiextensions.CustomResourceDefinitionSpec{
|
||||
Group: "group.com",
|
||||
Version: "version",
|
||||
Validation: &apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: validUnstructuralValidationSchema,
|
||||
},
|
||||
Versions: []apiextensions.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "version",
|
||||
Served: true,
|
||||
Storage: true,
|
||||
},
|
||||
},
|
||||
Scope: apiextensions.NamespaceScoped,
|
||||
Names: apiextensions.CustomResourceDefinitionNames{
|
||||
Plural: "plural",
|
||||
Singular: "singular",
|
||||
Kind: "Plural",
|
||||
ListKind: "PluralList",
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(true),
|
||||
},
|
||||
Status: apiextensions.CustomResourceDefinitionStatus{
|
||||
StoredVersions: []string{"version"},
|
||||
},
|
||||
},
|
||||
errors: []validationMatch{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@@ -1721,10 +2134,11 @@ func TestValidateCustomResourceDefinitionUpdate(t *testing.T) {
|
||||
|
||||
func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input apiextensions.CustomResourceValidation
|
||||
statusEnabled bool
|
||||
wantError bool
|
||||
name string
|
||||
input apiextensions.CustomResourceValidation
|
||||
mustBeStructural bool
|
||||
statusEnabled bool
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
@@ -1844,10 +2258,28 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
|
||||
},
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "must be structural, but isn't",
|
||||
input: apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{},
|
||||
},
|
||||
mustBeStructural: true,
|
||||
wantError: true,
|
||||
},
|
||||
{
|
||||
name: "must be structural",
|
||||
input: apiextensions.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensions.JSONSchemaProps{
|
||||
Type: "object",
|
||||
},
|
||||
},
|
||||
mustBeStructural: true,
|
||||
wantError: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ValidateCustomResourceDefinitionValidation(&tt.input, tt.statusEnabled, field.NewPath("spec", "validation"))
|
||||
got := ValidateCustomResourceDefinitionValidation(&tt.input, tt.mustBeStructural, tt.statusEnabled, field.NewPath("spec", "validation"))
|
||||
if !tt.wantError && len(got) > 0 {
|
||||
t.Errorf("Expected no error, but got: %v", got)
|
||||
} else if tt.wantError && len(got) == 0 {
|
||||
@@ -1860,6 +2292,42 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
|
||||
var example = apiextensions.JSON(`"This is an example"`)
|
||||
|
||||
var validValidationSchema = &apiextensions.JSONSchemaProps{
|
||||
Description: "This is a description",
|
||||
Type: "object",
|
||||
Format: "date-time",
|
||||
Title: "This is a title",
|
||||
Maximum: float64Ptr(10),
|
||||
ExclusiveMaximum: true,
|
||||
Minimum: float64Ptr(5),
|
||||
ExclusiveMinimum: true,
|
||||
MaxLength: int64Ptr(10),
|
||||
MinLength: int64Ptr(5),
|
||||
Pattern: "^[a-z]$",
|
||||
MaxItems: int64Ptr(10),
|
||||
MinItems: int64Ptr(5),
|
||||
MultipleOf: float64Ptr(3),
|
||||
Required: []string{"spec", "status"},
|
||||
Properties: map[string]apiextensions.JSONSchemaProps{
|
||||
"spec": {
|
||||
Type: "object",
|
||||
Items: &apiextensions.JSONSchemaPropsOrArray{
|
||||
Schema: &apiextensions.JSONSchemaProps{
|
||||
Description: "This is a schema nested under Items",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
Type: "object",
|
||||
},
|
||||
},
|
||||
ExternalDocs: &apiextensions.ExternalDocumentation{
|
||||
Description: "This is an external documentation description",
|
||||
},
|
||||
Example: &example,
|
||||
}
|
||||
|
||||
var validUnstructuralValidationSchema = &apiextensions.JSONSchemaProps{
|
||||
Description: "This is a description",
|
||||
Type: "object",
|
||||
Format: "date-time",
|
||||
|
@@ -201,6 +201,11 @@ func (in *CustomResourceDefinitionSpec) DeepCopyInto(out *CustomResourceDefiniti
|
||||
*out = new(CustomResourceConversion)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.PreserveUnknownFields != nil {
|
||||
in, out := &in.PreserveUnknownFields, &out.PreserveUnknownFields
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,8 @@ go_library(
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/conversion:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning:go_default_library",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation: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/internalclientset:go_default_library",
|
||||
|
@@ -30,6 +30,8 @@ import (
|
||||
"github.com/go-openapi/validate"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
structuralpruning "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning"
|
||||
apiservervalidation "k8s.io/apiextensions-apiserver/pkg/apiserver/validation"
|
||||
informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion"
|
||||
listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion"
|
||||
@@ -39,6 +41,7 @@ import (
|
||||
apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features"
|
||||
"k8s.io/apiextensions-apiserver/pkg/registry/customresource"
|
||||
"k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor"
|
||||
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
@@ -520,6 +523,20 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check for nil because we dereference this throughout the handler code.
|
||||
// Note: we always default this to non-nil. But we should guard these dereferences any way.
|
||||
if crd.Spec.PreserveUnknownFields == nil {
|
||||
return nil, fmt.Errorf("unexpected nil spec.preserveUnknownFields in the CustomResourceDefinition")
|
||||
}
|
||||
|
||||
var structuralSchema *structuralschema.Structural
|
||||
if validationSchema != nil {
|
||||
structuralSchema, err = structuralschema.NewStructural(validationSchema.OpenAPIV3Schema)
|
||||
if *crd.Spec.PreserveUnknownFields == false && err != nil {
|
||||
return nil, err // validation should avoid this
|
||||
}
|
||||
}
|
||||
|
||||
var statusSpec *apiextensions.CustomResourceSubresourceStatus
|
||||
var statusValidator *validate.SchemaValidator
|
||||
subresources, err := apiextensions.GetSubresourcesForVersion(crd, v.Name)
|
||||
@@ -570,10 +587,13 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
scaleSpec,
|
||||
),
|
||||
crdConversionRESTOptionsGetter{
|
||||
RESTOptionsGetter: r.restOptionsGetter,
|
||||
converter: safeConverter,
|
||||
decoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name},
|
||||
encoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: storageVersion},
|
||||
RESTOptionsGetter: r.restOptionsGetter,
|
||||
converter: safeConverter,
|
||||
decoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: v.Name},
|
||||
encoderVersion: schema.GroupVersion{Group: crd.Spec.Group, Version: storageVersion},
|
||||
structuralSchema: structuralSchema,
|
||||
structuralSchemaGK: kind.GroupKind(),
|
||||
preserveUnknownFields: *crd.Spec.PreserveUnknownFields,
|
||||
},
|
||||
crd.Status.AcceptedNames.Categories,
|
||||
table,
|
||||
@@ -595,7 +615,14 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
ClusterScoped: clusterScoped,
|
||||
SelfLinkPathPrefix: selfLinkPrefix,
|
||||
},
|
||||
Serializer: unstructuredNegotiatedSerializer{typer: typer, creator: creator, converter: safeConverter},
|
||||
Serializer: unstructuredNegotiatedSerializer{
|
||||
typer: typer,
|
||||
creator: creator,
|
||||
converter: safeConverter,
|
||||
structuralSchema: structuralSchema,
|
||||
structuralSchemaGK: kind.GroupKind(),
|
||||
preserveUnknownFields: *crd.Spec.PreserveUnknownFields,
|
||||
},
|
||||
ParameterCodec: parameterCodec,
|
||||
|
||||
Creater: creator,
|
||||
@@ -646,6 +673,13 @@ func (r *crdHandler) getOrCreateServingInfoFor(crd *apiextensions.CustomResource
|
||||
// shallow copy
|
||||
statusScope := *requestScopes[v.Name]
|
||||
statusScope.Subresource = "status"
|
||||
statusScope.Serializer = unstructuredNegotiatedSerializer{
|
||||
typer: typer, creator: creator,
|
||||
converter: safeConverter,
|
||||
structuralSchema: structuralSchema,
|
||||
structuralSchemaGK: kind.GroupKind(),
|
||||
preserveUnknownFields: *crd.Spec.PreserveUnknownFields,
|
||||
}
|
||||
statusScope.Namer = handlers.ContextBasedNaming{
|
||||
SelfLinker: meta.NewAccessor(),
|
||||
ClusterScoped: clusterScoped,
|
||||
@@ -680,6 +714,10 @@ type unstructuredNegotiatedSerializer struct {
|
||||
typer runtime.ObjectTyper
|
||||
creator runtime.ObjectCreater
|
||||
converter runtime.ObjectConvertor
|
||||
|
||||
structuralSchema *structuralschema.Structural
|
||||
structuralSchemaGK schema.GroupKind
|
||||
preserveUnknownFields bool
|
||||
}
|
||||
|
||||
func (s unstructuredNegotiatedSerializer) SupportedMediaTypes() []runtime.SerializerInfo {
|
||||
@@ -712,7 +750,7 @@ func (s unstructuredNegotiatedSerializer) EncoderForVersion(encoder runtime.Enco
|
||||
}
|
||||
|
||||
func (s unstructuredNegotiatedSerializer) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
|
||||
d := schemaCoercingDecoder{delegate: decoder, validator: unstructuredSchemaCoercer{}}
|
||||
d := schemaCoercingDecoder{delegate: decoder, validator: unstructuredSchemaCoercer{structuralSchema: s.structuralSchema, structuralSchemaGK: s.structuralSchemaGK, preserveUnknownFields: s.preserveUnknownFields}}
|
||||
return versioning.NewDefaultingCodecForScheme(Scheme, nil, d, nil, gv)
|
||||
}
|
||||
|
||||
@@ -801,19 +839,29 @@ func (in crdStorageMap) clone() crdStorageMap {
|
||||
// provided custom converter and custom encoder and decoder version.
|
||||
type crdConversionRESTOptionsGetter struct {
|
||||
generic.RESTOptionsGetter
|
||||
converter runtime.ObjectConvertor
|
||||
encoderVersion schema.GroupVersion
|
||||
decoderVersion schema.GroupVersion
|
||||
converter runtime.ObjectConvertor
|
||||
encoderVersion schema.GroupVersion
|
||||
decoderVersion schema.GroupVersion
|
||||
structuralSchema *structuralschema.Structural
|
||||
structuralSchemaGK schema.GroupKind
|
||||
preserveUnknownFields bool
|
||||
}
|
||||
|
||||
func (t crdConversionRESTOptionsGetter) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
ret, err := t.RESTOptionsGetter.GetRESTOptions(resource)
|
||||
if err == nil {
|
||||
d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{
|
||||
// drop invalid fields while decoding old CRs (before we had any ObjectMeta validation)
|
||||
dropInvalidMetadata: true,
|
||||
// drop invalid fields while decoding old CRs (before we haven't had any ObjectMeta validation)
|
||||
dropInvalidMetadata: true,
|
||||
structuralSchema: t.structuralSchema,
|
||||
structuralSchemaGK: t.structuralSchemaGK,
|
||||
preserveUnknownFields: t.preserveUnknownFields,
|
||||
}}
|
||||
c := schemaCoercingConverter{delegate: t.converter, validator: unstructuredSchemaCoercer{
|
||||
structuralSchema: t.structuralSchema,
|
||||
structuralSchemaGK: t.structuralSchemaGK,
|
||||
preserveUnknownFields: t.preserveUnknownFields,
|
||||
}}
|
||||
c := schemaCoercingConverter{delegate: t.converter, validator: unstructuredSchemaCoercer{}}
|
||||
ret.StorageConfig.Codec = versioning.NewCodec(
|
||||
ret.StorageConfig.Codec,
|
||||
d,
|
||||
@@ -894,13 +942,17 @@ func (v schemaCoercingConverter) ConvertFieldLabel(gvk schema.GroupVersionKind,
|
||||
return v.delegate.ConvertFieldLabel(gvk, label, value)
|
||||
}
|
||||
|
||||
// unstructuredSchemaCoercer does the validation for Unstructured that json.Unmarshal
|
||||
// does for native types. This includes:
|
||||
// - validating and pruning ObjectMeta (here with optional error instead of pruning)
|
||||
// - TODO: application of an OpenAPI validator (against the whole object or a top-level field of it).
|
||||
// - TODO: optionally application of post-validation algorithms like defaulting and/or OpenAPI based pruning.
|
||||
// unstructuredSchemaCoercer adds to unstructured unmarshalling what json.Unmarshal does
|
||||
// in addition for native types when decoding into Golang structs:
|
||||
//
|
||||
// - validating and pruning ObjectMeta
|
||||
// - generic pruning of unknown fields following a structural schema.
|
||||
type unstructuredSchemaCoercer struct {
|
||||
dropInvalidMetadata bool
|
||||
|
||||
structuralSchema *structuralschema.Structural
|
||||
structuralSchemaGK schema.GroupKind
|
||||
preserveUnknownFields bool
|
||||
}
|
||||
|
||||
func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error {
|
||||
@@ -918,6 +970,15 @@ func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// compare group and kind because also other object like DeleteCollection options pass through here
|
||||
gv, err := schema.ParseGroupVersion(apiVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !v.preserveUnknownFields && gv.Group == v.structuralSchemaGK.Group && kind == v.structuralSchemaGK.Kind {
|
||||
structuralpruning.Prune(u.Object, v.structuralSchema)
|
||||
}
|
||||
|
||||
// restore meta fields, starting clean
|
||||
if foundKind {
|
||||
u.SetKind(kind)
|
||||
|
@@ -31,7 +31,10 @@ filegroup(
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
@@ -0,0 +1,35 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["algorithm.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning",
|
||||
importpath = "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/pruning",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["algorithm_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/json: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,91 @@
|
||||
/*
|
||||
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 pruning
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
)
|
||||
|
||||
// Prune removes object fields in obj which are not specified in s.
|
||||
func Prune(obj map[string]interface{}, s *structuralschema.Structural) {
|
||||
prune(obj, s)
|
||||
}
|
||||
|
||||
func prune(x interface{}, s *structuralschema.Structural) {
|
||||
if s != nil && s.XPreserveUnknownFields {
|
||||
skipPrune(x, s)
|
||||
return
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case map[string]interface{}:
|
||||
if s == nil {
|
||||
for k := range x {
|
||||
delete(x, k)
|
||||
}
|
||||
return
|
||||
}
|
||||
for k, v := range x {
|
||||
prop, ok := s.Properties[k]
|
||||
if ok {
|
||||
prune(v, &prop)
|
||||
} else if s.AdditionalProperties != nil {
|
||||
prune(v, s.AdditionalProperties.Structural)
|
||||
} else {
|
||||
delete(x, k)
|
||||
}
|
||||
fmt.Printf("deleting %q => %#v\n", k, x)
|
||||
}
|
||||
case []interface{}:
|
||||
if s == nil {
|
||||
for _, v := range x {
|
||||
prune(v, nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
for _, v := range x {
|
||||
prune(v, s.Items)
|
||||
}
|
||||
default:
|
||||
// scalars, do nothing
|
||||
}
|
||||
}
|
||||
|
||||
func skipPrune(x interface{}, s *structuralschema.Structural) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := x.(type) {
|
||||
case map[string]interface{}:
|
||||
for k, v := range x {
|
||||
if prop, ok := s.Properties[k]; ok {
|
||||
prune(v, &prop)
|
||||
} else {
|
||||
skipPrune(v, nil)
|
||||
}
|
||||
}
|
||||
case []interface{}:
|
||||
for _, v := range x {
|
||||
skipPrune(v, s.Items)
|
||||
}
|
||||
default:
|
||||
// scalars, do nothing
|
||||
}
|
||||
}
|
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
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 pruning
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
)
|
||||
|
||||
func TestPrune(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
json string
|
||||
schema *structuralschema.Structural
|
||||
expected string
|
||||
}{
|
||||
{"empty", "null", nil, "null"},
|
||||
{"scalar", "4", &structuralschema.Structural{}, "4"},
|
||||
{"scalar array", "[1,2]", &structuralschema.Structural{
|
||||
Items: &structuralschema.Structural{},
|
||||
}, "[1,2]"},
|
||||
{"object array", `[{"a":1},{"b":1},{"a":1,"b":2,"c":3}]`, &structuralschema.Structural{
|
||||
Items: &structuralschema.Structural{
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"a": {},
|
||||
"c": {},
|
||||
},
|
||||
},
|
||||
}, `[{"a":1},{},{"a":1,"c":3}]`},
|
||||
{"object array with nil schema", `[{"a":1},{"b":1},{"a":1,"b":2,"c":3}]`, nil, `[{},{},{}]`},
|
||||
{"object array object", `{"array":[{"a":1},{"b":1},{"a":1,"b":2,"c":3}],"unspecified":{"a":1},"specified":{"a":1,"b":2,"c":3}}`, &structuralschema.Structural{
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"array": {
|
||||
Items: &structuralschema.Structural{
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"a": {},
|
||||
"c": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
"specified": {
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"a": {},
|
||||
"c": {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, `{"array":[{"a":1},{},{"a":1,"c":3}],"specified":{"a":1,"c":3}}`},
|
||||
{"nested x-kubernetes-preserve-unknown-fields", `
|
||||
{
|
||||
"unspecified":"bar",
|
||||
"alpha": "abc",
|
||||
"beta": 42.0,
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {"unspecified": "bar"},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
},
|
||||
"preserving": {
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {"unspecified": "bar"},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
}
|
||||
}
|
||||
`, &structuralschema.Structural{
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"alpha": {Generic: structuralschema.Generic{Type: "string"}},
|
||||
"beta": {Generic: structuralschema.Generic{Type: "number"}},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, `
|
||||
{
|
||||
"unspecified":"bar",
|
||||
"alpha": "abc",
|
||||
"beta": 42.0,
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {
|
||||
"pruning": {},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
},
|
||||
"preserving": {
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
}
|
||||
}
|
||||
`},
|
||||
{"additionalProperties with schema", `{"a":1,"b":1,"c":{"a":1,"b":2,"c":{"a":1}}}`, &structuralschema.Structural{
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"a": {},
|
||||
"c": {
|
||||
Generic: structuralschema.Generic{
|
||||
AdditionalProperties: &structuralschema.StructuralOrBool{
|
||||
Structural: &structuralschema.Structural{
|
||||
Generic: structuralschema.Generic{
|
||||
Type: "integer",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, `{"a":1,"c":{"a":1,"b":2,"c":{}}}`},
|
||||
{"additionalProperties with bool", `{"a":1,"b":1,"c":{"a":1,"b":2,"c":{"a":1}}}`, &structuralschema.Structural{
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"a": {},
|
||||
"c": {
|
||||
Generic: structuralschema.Generic{
|
||||
AdditionalProperties: &structuralschema.StructuralOrBool{
|
||||
Bool: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, `{"a":1,"c":{"a":1,"b":2,"c":{}}}`},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var in interface{}
|
||||
if err := json.Unmarshal([]byte(tt.json), &in); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var expected interface{}
|
||||
if err := json.Unmarshal([]byte(tt.expected), &expected); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
prune(in, tt.schema)
|
||||
if !reflect.DeepEqual(in, expected) {
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
enc.SetIndent("", " ")
|
||||
err := enc.Encode(in)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected result mashalling error: %v", err)
|
||||
}
|
||||
t.Errorf("expected: %s\ngot: %s", tt.expected, buf.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const smallInstance = `
|
||||
{
|
||||
"unspecified":"bar",
|
||||
"alpha": "abc",
|
||||
"beta": 42.0,
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {
|
||||
"pruning": {},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
},
|
||||
"preserving": {
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": {"unspecified": "bar"},
|
||||
"pruning": {},
|
||||
"preserving": {"unspecified": "bar"}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func BenchmarkPrune(b *testing.B) {
|
||||
b.StopTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
schema := &structuralschema.Structural{
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"alpha": {Generic: structuralschema.Generic{Type: "string"}},
|
||||
"beta": {Generic: structuralschema.Generic{Type: "number"}},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
},
|
||||
},
|
||||
},
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
Properties: map[string]structuralschema.Structural{
|
||||
"preserving": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
Extensions: structuralschema.Extensions{XPreserveUnknownFields: true},
|
||||
},
|
||||
"pruning": {
|
||||
Generic: structuralschema.Generic{Type: "object"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(smallInstance), &obj)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
instances := make([]map[string]interface{}, 0, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
instances = append(instances, runtime.DeepCopyJSON(obj))
|
||||
}
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Prune(instances[i], schema)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDeepCopy(b *testing.B) {
|
||||
b.StopTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
var obj map[string]interface{}
|
||||
err := json.Unmarshal([]byte(smallInstance), &obj)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
instances := make([]map[string]interface{}, 0, b.N)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
instances = append(instances, runtime.DeepCopyJSON(obj))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshal(b *testing.B) {
|
||||
b.StopTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
instances := make([]map[string]interface{}, b.N)
|
||||
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := json.Unmarshal([]byte(smallInstance), &instances[i])
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
@@ -14,6 +14,7 @@ go_test(
|
||||
"change_test.go",
|
||||
"finalization_test.go",
|
||||
"objectmeta_test.go",
|
||||
"pruning_test.go",
|
||||
"registration_test.go",
|
||||
"subresources_test.go",
|
||||
"table_test.go",
|
||||
@@ -54,6 +55,7 @@ go_test(
|
||||
"//vendor/github.com/coreos/etcd/pkg/transport:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/require:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
Copyright 2018 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 integration
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/etcd/clientv3"
|
||||
"github.com/coreos/etcd/pkg/transport"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/utils/pointer"
|
||||
|
||||
apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
"k8s.io/apiextensions-apiserver/test/integration/fixtures"
|
||||
)
|
||||
|
||||
var pruningFixture = &apiextensionsv1beta1.CustomResourceDefinition{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "foos.tests.apiextensions.k8s.io"},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "tests.apiextensions.k8s.io",
|
||||
Version: "v1beta1",
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "foos",
|
||||
Singular: "foo",
|
||||
Kind: "Foo",
|
||||
ListKind: "FooList",
|
||||
},
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
Subresources: &apiextensionsv1beta1.CustomResourceSubresources{
|
||||
Status: &apiextensionsv1beta1.CustomResourceSubresourceStatus{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
const (
|
||||
fooSchema = `
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
`
|
||||
|
||||
fooStatusSchema = `
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
`
|
||||
|
||||
fooSchemaPreservingUnknownFields = `
|
||||
type: object
|
||||
properties:
|
||||
alpha:
|
||||
type: string
|
||||
beta:
|
||||
type: number
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
properties:
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
pruning:
|
||||
type: object
|
||||
pruning:
|
||||
type: object
|
||||
properties:
|
||||
preserving:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
pruning:
|
||||
type: object
|
||||
x-kubernetes-preserve-unknown-fields: true
|
||||
`
|
||||
|
||||
fooInstance = `
|
||||
kind: Foo
|
||||
apiVersion: tests.apiextensions.k8s.io/v1beta1
|
||||
metadata:
|
||||
name: foo
|
||||
`
|
||||
)
|
||||
|
||||
func TestPruningCreate(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(foo.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "labels", "foo")
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningStatus(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooStatusSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' fields to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
unstructured.SetNestedField(foo.Object, "bar", "status", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "status", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "status", "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
|
||||
foo, err = fooClient.UpdateStatus(foo, metav1.UpdateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to update status: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'status.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "status", "alpha"); !found {
|
||||
t.Errorf("Expected specified 'status.alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "status", "beta"); !found {
|
||||
t.Errorf("Expected specified 'status.beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningFromStorage(t *testing.T) {
|
||||
tearDown, config, options, err := fixtures.StartDefaultServer(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDown()
|
||||
|
||||
apiExtensionClient, err := clientset.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dynamicClient, err := dynamic.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
serverConfig, err := options.Config()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
restOptions, err := serverConfig.GenericConfig.RESTOptionsGetter.GetRESTOptions(schema.GroupResource{Group: crd.Spec.Group, Resource: crd.Spec.Names.Plural})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlsInfo := transport.TLSInfo{
|
||||
CertFile: restOptions.StorageConfig.Transport.CertFile,
|
||||
KeyFile: restOptions.StorageConfig.Transport.KeyFile,
|
||||
CAFile: restOptions.StorageConfig.Transport.CAFile,
|
||||
}
|
||||
tlsConfig, err := tlsInfo.ClientConfig()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
etcdConfig := clientv3.Config{
|
||||
Endpoints: restOptions.StorageConfig.Transport.ServerList,
|
||||
TLS: tlsConfig,
|
||||
}
|
||||
etcdclient, err := clientv3.New(etcdConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating object with unknown field manually in etcd")
|
||||
|
||||
original := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &original.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(original.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(original.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(original.Object, float64(42), "beta")
|
||||
unstructured.SetNestedField(original.Object, "bar", "metadata", "labels", "foo")
|
||||
|
||||
// Note: we don't add metadata.unspecified as in the other tests. ObjectMeta pruning is independent of the generic pruning
|
||||
// and we do not guarantee that we prune ObjectMeta on read from etcd.
|
||||
|
||||
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), metav1.NamespaceDefault)
|
||||
key := path.Join("/", restOptions.StorageConfig.Prefix, crd.Spec.Group, "foos/foo")
|
||||
val, _ := json.Marshal(original.UnstructuredContent())
|
||||
if _, err := etcdclient.Put(ctx, key, string(val)); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
t.Logf("Checking that CustomResource is pruned from unknown fields")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo, err := fooClient.Get("foo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
|
||||
// Note: we don't check metadata.foo as in the other tests. ObjectMeta pruning is independent of the generic pruning
|
||||
// and we do not guarantee that we prune ObjectMeta on read from etcd.
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningPatch(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchema), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
// a patch with a change
|
||||
patch := []byte(`{"alpha": "abc", "beta": 42.0, "unspecified": "bar", "metadata": {"unspecified": "bar", "labels":{"foo":"bar"}}}`)
|
||||
if foo, err = fooClient.Patch("foo", types.MergePatchType, patch, metav1.PatchOptions{}); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "unspecified"); found {
|
||||
t.Errorf("Expected 'unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "alpha"); !found {
|
||||
t.Errorf("Expected specified 'alpha' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "beta"); !found {
|
||||
t.Errorf("Expected specified 'beta' field to stay, but it was pruned")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "unspecified"); found {
|
||||
t.Errorf("Expected 'metadata.unspecified' field to be pruned, but it was not")
|
||||
}
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, "metadata", "labels", "foo"); !found {
|
||||
t.Errorf("Expected specified 'metadata.labels[foo]' field to stay, but it was pruned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPruningCreatePreservingUnknownFields(t *testing.T) {
|
||||
tearDownFn, apiExtensionClient, dynamicClient, err := fixtures.StartDefaultServerWithClients(t)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer tearDownFn()
|
||||
|
||||
crd := pruningFixture.DeepCopy()
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{}
|
||||
if err := yaml.Unmarshal([]byte(fooSchemaPreservingUnknownFields), &crd.Spec.Validation.OpenAPIV3Schema); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
crd, err = fixtures.CreateNewCustomResourceDefinition(crd, apiExtensionClient, dynamicClient)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Creating CR and expect 'unspecified' field to be pruned")
|
||||
fooClient := dynamicClient.Resource(schema.GroupVersionResource{crd.Spec.Group, crd.Spec.Version, crd.Spec.Names.Plural})
|
||||
foo := &unstructured.Unstructured{}
|
||||
if err := yaml.Unmarshal([]byte(fooInstance), &foo.Object); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
unstructured.SetNestedField(foo.Object, "bar", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "abc", "alpha")
|
||||
unstructured.SetNestedField(foo.Object, float64(42.0), "beta")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "unspecified")
|
||||
unstructured.SetNestedField(foo.Object, "bar", "metadata", "labels", "foo")
|
||||
unstructured.SetNestedField(foo.Object, map[string]interface{}{
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": map[string]interface{}{"unspecified": "bar"},
|
||||
"pruning": map[string]interface{}{"unspecified": "bar"},
|
||||
"preserving": map[string]interface{}{"unspecified": "bar"},
|
||||
}, "pruning")
|
||||
unstructured.SetNestedField(foo.Object, map[string]interface{}{
|
||||
"unspecified": "bar",
|
||||
"unspecifiedObject": map[string]interface{}{"unspecified": "bar"},
|
||||
"pruning": map[string]interface{}{"unspecified": "bar"},
|
||||
"preserving": map[string]interface{}{"unspecified": "bar"},
|
||||
}, "preserving")
|
||||
|
||||
foo, err = fooClient.Create(foo, metav1.CreateOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create CR: %v", err)
|
||||
}
|
||||
t.Logf("CR created: %#v", foo.UnstructuredContent())
|
||||
|
||||
for _, pth := range [][]string{
|
||||
{"unspecified"},
|
||||
{"alpha"},
|
||||
{"beta"},
|
||||
{"metadata", "labels", "foo"},
|
||||
|
||||
{"pruning", "pruning"},
|
||||
{"pruning", "preserving"},
|
||||
{"pruning", "preserving", "unspecified"},
|
||||
|
||||
{"preserving", "unspecified"},
|
||||
{"preserving", "unspecifiedObject"},
|
||||
{"preserving", "unspecifiedObject", "unspecified"},
|
||||
{"preserving", "pruning"},
|
||||
{"preserving", "preserving"},
|
||||
{"preserving", "preserving", "unspecified"},
|
||||
} {
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, pth...); !found {
|
||||
t.Errorf("Expected '%s' field to stay, but it was pruned", strings.Join(pth, "."))
|
||||
}
|
||||
}
|
||||
for _, pth := range [][]string{
|
||||
{"metadata", "unspecified"},
|
||||
|
||||
{"pruning", "unspecified"},
|
||||
{"pruning", "unspecifiedObject"},
|
||||
{"pruning", "unspecifiedObject", "unspecified"},
|
||||
{"pruning", "pruning", "unspecified"},
|
||||
|
||||
{"preserving", "pruning", "unspecified"},
|
||||
} {
|
||||
if _, found, _ := unstructured.NestedFieldNoCopy(foo.Object, pth...); found {
|
||||
t.Errorf("Expected '%s' field to be pruned, but it was not", strings.Join(pth, "."))
|
||||
}
|
||||
}
|
||||
}
|
@@ -181,7 +181,7 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
||||
defer testcrd.CleanUp()
|
||||
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
||||
defer webhookCleanup()
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"])
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], false)
|
||||
})
|
||||
|
||||
ginkgo.It("Should deny crd creation", func() {
|
||||
@@ -202,6 +202,35 @@ var _ = SIGDescribe("AdmissionWebhook", func() {
|
||||
testMultiVersionCustomResourceWebhook(f, testcrd)
|
||||
})
|
||||
|
||||
ginkgo.It("Should mutate custom resource with pruning", func() {
|
||||
const prune = true
|
||||
testcrd, err := createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.PreserveUnknownFields = pointer.BoolPtr(false)
|
||||
crd.Spec.Validation = &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"data": {
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"mutation-start": {Type: "string"},
|
||||
"mutation-stage-1": {Type: "string"},
|
||||
// mutation-stage-2 is intentionally missing such that it is pruned
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer testcrd.CleanUp()
|
||||
webhookCleanup := registerMutatingWebhookForCustomResource(f, context, testcrd)
|
||||
defer webhookCleanup()
|
||||
testMutatingCustomResourceWebhook(f, testcrd.Crd, testcrd.DynamicClients["v1"], prune)
|
||||
})
|
||||
|
||||
ginkgo.It("Should deny crd creation", func() {
|
||||
crdWebhookCleanup := registerValidatingWebhookForCRD(f, context)
|
||||
defer crdWebhookCleanup()
|
||||
@@ -1329,7 +1358,7 @@ func testCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1
|
||||
}
|
||||
}
|
||||
|
||||
func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface) {
|
||||
func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextensionsv1beta1.CustomResourceDefinition, customResourceClient dynamic.ResourceInterface, prune bool) {
|
||||
ginkgo.By("Creating a custom resource that should be mutated by the webhook")
|
||||
crName := "cr-instance-1"
|
||||
cr := &unstructured.Unstructured{
|
||||
@@ -1350,7 +1379,9 @@ func testMutatingCustomResourceWebhook(f *framework.Framework, crd *apiextension
|
||||
expectedCRData := map[string]interface{}{
|
||||
"mutation-start": "yes",
|
||||
"mutation-stage-1": "yes",
|
||||
"mutation-stage-2": "yes",
|
||||
}
|
||||
if !prune {
|
||||
expectedCRData["mutation-stage-2"] = "yes"
|
||||
}
|
||||
if !reflect.DeepEqual(expectedCRData, mutatedCR.Object["data"]) {
|
||||
framework.Failf("\nexpected %#v\n, got %#v\n", expectedCRData, mutatedCR.Object["data"])
|
||||
@@ -1571,9 +1602,9 @@ func testSlowWebhookTimeoutNoError(f *framework.Framework) {
|
||||
|
||||
// createAdmissionWebhookMultiVersionTestCRDWithV1Storage creates a new CRD specifically
|
||||
// for the admissin webhook calling test.
|
||||
func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framework) (*crd.TestCrd, error) {
|
||||
func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framework, opts ...crd.Option) (*crd.TestCrd, error) {
|
||||
group := fmt.Sprintf("%s-multiversion-crd-test.k8s.io", f.BaseName)
|
||||
return crd.CreateMultiVersionTestCRD(f, group, func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
return crd.CreateMultiVersionTestCRD(f, group, append([]crd.Option{func(crd *apiextensionsv1beta1.CustomResourceDefinition) {
|
||||
crd.Spec.Versions = []apiextensionsv1beta1.CustomResourceDefinitionVersion{
|
||||
{
|
||||
Name: "v1",
|
||||
@@ -1586,7 +1617,7 @@ func createAdmissionWebhookMultiVersionTestCRDWithV1Storage(f *framework.Framewo
|
||||
Storage: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
}}, opts...)...)
|
||||
}
|
||||
|
||||
// servedAPIVersions returns the API versions served by the CRD.
|
||||
|
@@ -113,6 +113,9 @@ var (
|
||||
gvr("", "v1", "nodes/proxy"): {"*": testSubresourceProxy},
|
||||
gvr("", "v1", "pods/proxy"): {"*": testSubresourceProxy},
|
||||
gvr("", "v1", "services/proxy"): {"*": testSubresourceProxy},
|
||||
|
||||
gvr("random.numbers.com", "v1", "integers"): {"create": testPruningRandomNumbers},
|
||||
gvr("custom.fancy.com", "v2", "pants"): {"create": testNoPruningCustomFancy},
|
||||
}
|
||||
|
||||
// admissionExemptResources lists objects which are exempt from admission validation/mutation,
|
||||
@@ -921,6 +924,46 @@ func testSubresourceProxy(c *testContext) {
|
||||
}
|
||||
}
|
||||
|
||||
func testPruningRandomNumbers(c *testContext) {
|
||||
testResourceCreate(c)
|
||||
|
||||
cr2pant, err := c.client.Resource(c.gvr).Get("fortytwo", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
c.t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
foo, found, err := unstructured.NestedString(cr2pant.Object, "foo")
|
||||
if err != nil {
|
||||
c.t.Error(err)
|
||||
return
|
||||
}
|
||||
if found {
|
||||
c.t.Errorf("expected .foo to be pruned, but got: %s", foo)
|
||||
}
|
||||
}
|
||||
|
||||
func testNoPruningCustomFancy(c *testContext) {
|
||||
testResourceCreate(c)
|
||||
|
||||
cr2pant, err := c.client.Resource(c.gvr).Get("cr2pant", metav1.GetOptions{})
|
||||
if err != nil {
|
||||
c.t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
foo, _, err := unstructured.NestedString(cr2pant.Object, "foo")
|
||||
if err != nil {
|
||||
c.t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
// check that no pruning took place
|
||||
if expected, got := "test", foo; expected != got {
|
||||
c.t.Errorf("expected /foo to be %q, got: %q", expected, got)
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// utility methods
|
||||
//
|
||||
|
@@ -76,5 +76,6 @@ go_library(
|
||||
"//test/integration/framework:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3:go_default_library",
|
||||
"//vendor/github.com/coreos/etcd/clientv3/concurrency:go_default_library",
|
||||
"//vendor/k8s.io/utils/pointer:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@@ -22,6 +22,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
"k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
// GetEtcdStorageData returns etcd data for all persisted objects.
|
||||
@@ -485,6 +486,10 @@ func GetEtcdStorageDataForNamespace(namespace string) map[schema.GroupVersionRes
|
||||
ExpectedEtcdPath: "/registry/awesome.bears.com/pandas/cr4panda",
|
||||
ExpectedGVK: gvkP("awesome.bears.com", "v1", "Panda"),
|
||||
},
|
||||
gvr("random.numbers.com", "v1", "integers"): {
|
||||
Stub: `{"kind": "Integer", "apiVersion": "random.numbers.com/v1", "metadata": {"name": "fortytwo"}, "value": 42, "garbage": "oiujnasdf"}`, // requires TypeMeta due to CRD scheme's UnstructuredObjectTyper
|
||||
ExpectedEtcdPath: "/registry/random.numbers.com/integers/fortytwo",
|
||||
},
|
||||
// --
|
||||
|
||||
// k8s.io/kubernetes/pkg/apis/auditregistration/v1alpha1
|
||||
@@ -580,6 +585,32 @@ func GetCustomResourceDefinitionData() []*apiextensionsv1beta1.CustomResourceDef
|
||||
},
|
||||
},
|
||||
},
|
||||
// cluster scoped with legacy version field and pruning.
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "integers.random.numbers.com",
|
||||
},
|
||||
Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{
|
||||
Group: "random.numbers.com",
|
||||
Version: "v1",
|
||||
Scope: apiextensionsv1beta1.ClusterScoped,
|
||||
Names: apiextensionsv1beta1.CustomResourceDefinitionNames{
|
||||
Plural: "integers",
|
||||
Kind: "Integer",
|
||||
},
|
||||
Validation: &apiextensionsv1beta1.CustomResourceValidation{
|
||||
OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{
|
||||
"value": {
|
||||
Type: "number",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PreserveUnknownFields: pointer.BoolPtr(false),
|
||||
},
|
||||
},
|
||||
// cluster scoped with versions field
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@@ -1073,6 +1073,7 @@ 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/schema/pruning
|
||||
k8s.io/apiextensions-apiserver/pkg/apiserver/validation
|
||||
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset
|
||||
k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme
|
||||
|
Reference in New Issue
Block a user