Change:
}) } } <ins> func TestBuildOpenAPIModelsForApply(t *testing.T) { // This is a list of validation that we expect to work. tests := []apiextensionsv1.CustomResourceValidation{ { OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ Type:       "object", Properties: map[string]apiextensionsv1.JSONSchemaProps{"num": {Type: "integer", Description: "v1beta1 num field"}}, }, }, { OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ Type:         "", XIntOrString: true, }, }, { OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ Type: "object", Properties: map[string]apiextensionsv1.JSONSchemaProps{ "oneOf": { OneOf: []apiextensionsv1.JSONSchemaProps{ {Type: "boolean"}, {Type: "string"}, }, }, }, }, }, { OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{ Type: "object", Properties: map[string]apiextensionsv1.JSONSchemaProps{ "nullable": { Type:     "integer", Nullable: true, }, }, }, }, } staticSpec, err := getOpenAPISpecFromFile() if err != nil { t.Fatalf("Failed to load openapi spec: %v", err) } crd := apiextensionsv1.CustomResourceDefinition{ ObjectMeta: metav1.ObjectMeta{Name: "example.stable.example.com", UID: types.UID("12345")}, Spec: apiextensionsv1.CustomResourceDefinitionSpec{ Group: "stable.example.com", Names: apiextensionsv1.CustomResourceDefinitionNames{ Plural: "examples", Singular: "example", Kind: "Example", ShortNames: []string{"ex"}, ListKind: "ExampleList", Categories: []string{"all"}, }, Conversion:            &apiextensionsv1.CustomResourceConversion{Strategy: apiextensionsv1.NoneConverter}, Scope:                 apiextensionsv1.ClusterScoped, PreserveUnknownFields: false, Versions: []apiextensionsv1.CustomResourceDefinitionVersion{ { Name: "v1beta1", Served: true, Storage: true, Subresources: &apiextensionsv1.CustomResourceSubresources{Status: &apiextensionsv1.CustomResourceSubresourceStatus{}}, }, }, }, } for _, test := range tests { crd.Spec.Versions[0].Schema = &test if _, err := buildOpenAPIModelsForApply(staticSpec, &crd); err != nil { t.Fatalf("failed to convert to apply model: %v", err) } } } func getOpenAPISpecFromFile() (*spec.Swagger, error) { path := filepath.Join( strings.Repeat(".."+string(filepath.Separator), 6), "api", "openapi-spec", "swagger.json") _, err := os.Stat(path) if err != nil { return nil, err } byteSpec, err := ioutil.ReadFile(path) if err != nil { return nil, err } staticSpec := &spec.Swagger{} err = yaml.Unmarshal(byteSpec, staticSpec) if err != nil { return nil, err } return staticSpec, nil } </ins>

Discussion:
With in a CRD we get: This is because gnostic's OpenAPI proto types have no nullable support. Luckily CRDs keep working because in the upper error case we don't stop serving, just set the SSA schema to nil.
Thanks for reporting!
I think it should be "This is because gnostic's OpenAPI v2 proto types have no nullable support.". The problem is that we're not supposed to pass-in v3.
Source:
OK, We'll write a test and fix this, thanks Stefan!
/reopen This still happens in slightly different shape: This comes from :
That's a fairly different bug but yes, thanks!
Is this related to the stating that use of "anyOf" wouldn't be allowed after 1.16? We're seeing issue on OpenFaaS after going to 1.17.5 and 1.18. Usually upgrading K8s versions hasn't caused breaking changes and we're trying to figure out where to start with this one.
No. That post was about structural schemas. v1 CRDs are limited to structural schemas. v1beta1 CRDs can continue to create non-structural schemas until v1beta1 is no longer served (in 1.22) Also, is not completely disallowed, specific restrictions inside an clause (like ) are what are not allowed ("logical constraints using oneOf, allOf, anyOf, not are allowed").
extracted the newly found issue to to track separately with isolated reproducer /close
Closing this issue. <detailsIn response to : extracted the newly found issue to to track separately Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details