From bde88c6ef75fe5705f992545e96c9ca8a307490e Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 18 Sep 2020 13:44:10 +0200 Subject: [PATCH] apiextensions: prune array type without items in published OpenAPI kubectl falls over arrays without item schema. Hence, we have to publish a less precise OpenAPI spec (similar to other pruning we already do for the same reason). --- .../pkg/controller/openapi/v2/BUILD | 1 + .../pkg/controller/openapi/v2/conversion.go | 8 +++++ .../controller/openapi/v2/conversion_test.go | 31 +++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/BUILD index a078e5a337d..4751762545b 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/BUILD @@ -24,6 +24,7 @@ go_test( "//vendor/github.com/googleapis/gnostic/openapiv2:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", + "//vendor/k8s.io/utils/pointer:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion.go index b1d7114bf3e..320529bf68d 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion.go @@ -73,6 +73,14 @@ func ToStructuralOpenAPIV2(in *structuralschema.Structural) *structuralschema.St changed = true } + if s.Items == nil && s.Type == "array" { + // kubectl cannot cope with array without item schema, e.g. due to XPreserveUnknownFields case above + // https://github.com/kubernetes/kube-openapi/blob/64514a1d5d596b96e6f957e2be275ae14d6b0804/pkg/util/proto/document.go#L185 + s.Type = "" + + changed = true + } + for f, fs := range s.Properties { if fs.Nullable { s.ValueValidation.Required, changed = filterOut(s.ValueValidation.Required, f) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion_test.go index 53ca83bb8dd..eacb92efb98 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/v2/conversion_test.go @@ -36,6 +36,7 @@ import ( apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" "k8s.io/kube-openapi/pkg/util/proto" + "k8s.io/utils/pointer" ) func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { @@ -643,6 +644,31 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2SchemaByType(t *testing.T) { WithExample(testStr), expectDiff: true, }, + { + name: "preserve-unknown-fields in arrays", + in: &apiextensions.JSONSchemaProps{ + XPreserveUnknownFields: pointer.BoolPtr(true), + Type: "array", + Items: &apiextensions.JSONSchemaPropsOrArray{Schema: &apiextensions.JSONSchemaProps{ + Type: "string", + }}, + }, + expected: withVendorExtensions(new(spec.Schema), "x-kubernetes-preserve-unknown-fields", true), + }, + { + name: "preserve-unknown-fields in objects", + in: &apiextensions.JSONSchemaProps{ + XPreserveUnknownFields: pointer.BoolPtr(true), + Type: "object", + Properties: map[string]apiextensions.JSONSchemaProps{ + "foo": { + Type: "string", + }, + }, + }, + expected: withVendorExtensions(new(spec.Schema), "x-kubernetes-preserve-unknown-fields", true). + Typed("object", ""), + }, } for _, test := range tests { @@ -666,6 +692,11 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2SchemaByType(t *testing.T) { } } +func withVendorExtensions(s *spec.Schema, key string, value interface{}) *spec.Schema { + s.VendorExtensible.AddExtension(key, value) + return s +} + func refEqual(x spec.Ref, y spec.Ref) bool { return x.String() == y.String() }