From ad2b916d7c1537294cd5f7a6b71c570f89a99803 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 16 Nov 2018 16:36:04 -0500 Subject: [PATCH 1/2] Revert "Merge pull request #71137 from sttts/sttts-crd-openapi-spec-recursive-v2-prune" This reverts commit 3ea3cfc3bea08c425593a25533bf14c703f90420, reversing changes made to fab7009997d9da9f374f9aa48092fc94c55829b5. --- .../customresource_discovery_controller.go | 13 +- .../pkg/apiserver/validation/validation.go | 83 +++---- .../apiextensions-apiserver/pkg/openapi/BUILD | 5 +- .../pkg/openapi/conversion.go | 28 ++- .../pkg/openapi/conversion_test.go | 222 ++++++------------ .../pkg/openapi/swagger_util.go | 3 +- 6 files changed, 134 insertions(+), 220 deletions(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go index 2e03f0ae3b3..da54fca6af5 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go @@ -36,6 +36,7 @@ import ( "k8s.io/client-go/util/workqueue" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion" listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion" apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" @@ -130,10 +131,18 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if err != nil { return err } - + // Convert internal CustomResourceValidation to versioned CustomResourceValidation + versionedSchema := new(v1beta1.CustomResourceValidation) + if validationSchema == nil { + versionedSchema = nil + } else { + if err := v1beta1.Convert_apiextensions_CustomResourceValidation_To_v1beta1_CustomResourceValidation(validationSchema, versionedSchema, nil); err != nil { + return err + } + } // We aggregate the schema even if it's nil as it maybe a removal of the schema for this CRD, // and the aggreated OpenAPI spec should reflect this change. - crdspec, etag, err := apiextensionsopenapi.CustomResourceDefinitionOpenAPISpec(&crd.Spec, version.Version, validationSchema) + crdspec, etag, err := apiextensionsopenapi.CustomResourceDefinitionOpenAPISpec(&crd.Spec, version.Version, versionedSchema) if err != nil { return err } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go index 99ee9211650..558ddb1fcdc 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation/validation.go @@ -50,17 +50,8 @@ func ValidateCustomResource(customResource interface{}, validator *validate.Sche return nil } -// ConvertJSONSchemaProps converts the schema from apiextensions.JSONSchemaPropos to go-openapi/spec.Schema. +// ConvertJSONSchemaProps converts the schema from apiextensions.JSONSchemaPropos to go-openapi/spec.Schema func ConvertJSONSchemaProps(in *apiextensions.JSONSchemaProps, out *spec.Schema) error { - return ConvertJSONSchemaPropsWithPostProcess(in, out, nil) -} - -// PostProcessFunc post-processes one node of a spec.Schema. -type PostProcessFunc func(*spec.Schema) error - -// ConvertJSONSchemaPropsWithPostProcess converts the schema from apiextensions.JSONSchemaPropos to go-openapi/spec.Schema -// and run a post process step on each JSONSchemaProps node. -func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, out *spec.Schema, postProcess PostProcessFunc) error { if in == nil { return nil } @@ -95,43 +86,41 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou out.Example = *(in.Example) } - if in.Enum != nil { - out.Enum = make([]interface{}, len(in.Enum)) - for k, v := range in.Enum { - out.Enum[k] = v - } + out.Enum = make([]interface{}, len(in.Enum)) + for k, v := range in.Enum { + out.Enum[k] = v } - if err := convertSliceOfJSONSchemaProps(&in.AllOf, &out.AllOf, postProcess); err != nil { + if err := convertSliceOfJSONSchemaProps(&in.AllOf, &out.AllOf); err != nil { return err } - if err := convertSliceOfJSONSchemaProps(&in.OneOf, &out.OneOf, postProcess); err != nil { + if err := convertSliceOfJSONSchemaProps(&in.OneOf, &out.OneOf); err != nil { return err } - if err := convertSliceOfJSONSchemaProps(&in.AnyOf, &out.AnyOf, postProcess); err != nil { + if err := convertSliceOfJSONSchemaProps(&in.AnyOf, &out.AnyOf); err != nil { return err } if in.Not != nil { in, out := &in.Not, &out.Not *out = new(spec.Schema) - if err := ConvertJSONSchemaPropsWithPostProcess(*in, *out, postProcess); err != nil { + if err := ConvertJSONSchemaProps(*in, *out); err != nil { return err } } var err error - out.Properties, err = convertMapOfJSONSchemaProps(in.Properties, postProcess) + out.Properties, err = convertMapOfJSONSchemaProps(in.Properties) if err != nil { return err } - out.PatternProperties, err = convertMapOfJSONSchemaProps(in.PatternProperties, postProcess) + out.PatternProperties, err = convertMapOfJSONSchemaProps(in.PatternProperties) if err != nil { return err } - out.Definitions, err = convertMapOfJSONSchemaProps(in.Definitions, postProcess) + out.Definitions, err = convertMapOfJSONSchemaProps(in.Definitions) if err != nil { return err } @@ -146,7 +135,7 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou if in.AdditionalProperties != nil { in, out := &in.AdditionalProperties, &out.AdditionalProperties *out = new(spec.SchemaOrBool) - if err := convertJSONSchemaPropsorBool(*in, *out, postProcess); err != nil { + if err := convertJSONSchemaPropsorBool(*in, *out); err != nil { return err } } @@ -154,7 +143,7 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou if in.AdditionalItems != nil { in, out := &in.AdditionalItems, &out.AdditionalItems *out = new(spec.SchemaOrBool) - if err := convertJSONSchemaPropsorBool(*in, *out, postProcess); err != nil { + if err := convertJSONSchemaPropsorBool(*in, *out); err != nil { return err } } @@ -162,7 +151,7 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou if in.Items != nil { in, out := &in.Items, &out.Items *out = new(spec.SchemaOrArray) - if err := convertJSONSchemaPropsOrArray(*in, *out, postProcess); err != nil { + if err := convertJSONSchemaPropsOrArray(*in, *out); err != nil { return err } } @@ -172,7 +161,7 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou *out = make(spec.Dependencies, len(*in)) for key, val := range *in { newVal := new(spec.SchemaOrStringArray) - if err := convertJSONSchemaPropsOrStringArray(&val, newVal, postProcess); err != nil { + if err := convertJSONSchemaPropsOrStringArray(&val, newVal); err != nil { return err } (*out)[key] = *newVal @@ -185,20 +174,14 @@ func ConvertJSONSchemaPropsWithPostProcess(in *apiextensions.JSONSchemaProps, ou out.ExternalDocs.URL = in.ExternalDocs.URL } - if postProcess != nil { - if err := postProcess(out); err != nil { - return err - } - } - return nil } -func convertSliceOfJSONSchemaProps(in *[]apiextensions.JSONSchemaProps, out *[]spec.Schema, postProcess PostProcessFunc) error { +func convertSliceOfJSONSchemaProps(in *[]apiextensions.JSONSchemaProps, out *[]spec.Schema) error { if in != nil { for _, jsonSchemaProps := range *in { schema := spec.Schema{} - if err := ConvertJSONSchemaPropsWithPostProcess(&jsonSchemaProps, &schema, postProcess); err != nil { + if err := ConvertJSONSchemaProps(&jsonSchemaProps, &schema); err != nil { return err } *out = append(*out, schema) @@ -207,27 +190,25 @@ func convertSliceOfJSONSchemaProps(in *[]apiextensions.JSONSchemaProps, out *[]s return nil } -func convertMapOfJSONSchemaProps(in map[string]apiextensions.JSONSchemaProps, postProcess PostProcessFunc) (map[string]spec.Schema, error) { - if in == nil { - return nil, nil - } - +func convertMapOfJSONSchemaProps(in map[string]apiextensions.JSONSchemaProps) (map[string]spec.Schema, error) { out := make(map[string]spec.Schema) - for k, jsonSchemaProps := range in { - schema := spec.Schema{} - if err := ConvertJSONSchemaPropsWithPostProcess(&jsonSchemaProps, &schema, postProcess); err != nil { - return nil, err + if len(in) != 0 { + for k, jsonSchemaProps := range in { + schema := spec.Schema{} + if err := ConvertJSONSchemaProps(&jsonSchemaProps, &schema); err != nil { + return nil, err + } + out[k] = schema } - out[k] = schema } return out, nil } -func convertJSONSchemaPropsOrArray(in *apiextensions.JSONSchemaPropsOrArray, out *spec.SchemaOrArray, postProcess PostProcessFunc) error { +func convertJSONSchemaPropsOrArray(in *apiextensions.JSONSchemaPropsOrArray, out *spec.SchemaOrArray) error { if in.Schema != nil { in, out := &in.Schema, &out.Schema *out = new(spec.Schema) - if err := ConvertJSONSchemaPropsWithPostProcess(*in, *out, postProcess); err != nil { + if err := ConvertJSONSchemaProps(*in, *out); err != nil { return err } } @@ -235,7 +216,7 @@ func convertJSONSchemaPropsOrArray(in *apiextensions.JSONSchemaPropsOrArray, out in, out := &in.JSONSchemas, &out.Schemas *out = make([]spec.Schema, len(*in)) for i := range *in { - if err := ConvertJSONSchemaPropsWithPostProcess(&(*in)[i], &(*out)[i], postProcess); err != nil { + if err := ConvertJSONSchemaProps(&(*in)[i], &(*out)[i]); err != nil { return err } } @@ -243,24 +224,24 @@ func convertJSONSchemaPropsOrArray(in *apiextensions.JSONSchemaPropsOrArray, out return nil } -func convertJSONSchemaPropsorBool(in *apiextensions.JSONSchemaPropsOrBool, out *spec.SchemaOrBool, postProcess PostProcessFunc) error { +func convertJSONSchemaPropsorBool(in *apiextensions.JSONSchemaPropsOrBool, out *spec.SchemaOrBool) error { out.Allows = in.Allows if in.Schema != nil { in, out := &in.Schema, &out.Schema *out = new(spec.Schema) - if err := ConvertJSONSchemaPropsWithPostProcess(*in, *out, postProcess); err != nil { + if err := ConvertJSONSchemaProps(*in, *out); err != nil { return err } } return nil } -func convertJSONSchemaPropsOrStringArray(in *apiextensions.JSONSchemaPropsOrStringArray, out *spec.SchemaOrStringArray, postProcess PostProcessFunc) error { +func convertJSONSchemaPropsOrStringArray(in *apiextensions.JSONSchemaPropsOrStringArray, out *spec.SchemaOrStringArray) error { out.Property = in.Property if in.Schema != nil { in, out := &in.Schema, &out.Schema *out = new(spec.Schema) - if err := ConvertJSONSchemaPropsWithPostProcess(*in, *out, postProcess); err != nil { + if err := ConvertJSONSchemaProps(*in, *out); err != nil { return err } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD index 659ef33b7c4..d1d11eabec9 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD @@ -14,7 +14,7 @@ go_library( deps = [ "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/validation:go_default_library", + "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library", @@ -28,8 +28,7 @@ go_test( srcs = ["conversion_test.go"], embed = [":go_default_library"], deps = [ - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library", + "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library", ], ) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go index db425892fe6..61853acb2ff 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go @@ -17,29 +17,33 @@ limitations under the License. package openapi import ( + "encoding/json" + "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apiextensions-apiserver/pkg/apiserver/validation" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" ) // ConvertJSONSchemaPropsToOpenAPIv2Schema converts our internal OpenAPI v3 schema // (*apiextensions.JSONSchemaProps) to an OpenAPI v2 schema (*spec.Schema). // NOTE: we use versioned type (v1beta1) here so that we can properly marshal the object // using the JSON tags -func ConvertJSONSchemaPropsToOpenAPIv2Schema(in *apiextensions.JSONSchemaProps) (*spec.Schema, error) { +func ConvertJSONSchemaPropsToOpenAPIv2Schema(in *v1beta1.JSONSchemaProps) (*spec.Schema, error) { if in == nil { return nil, nil } + // Marshal JSONSchemaProps into JSON and unmarshal the data into spec.Schema + data, err := json.Marshal(*in) + if err != nil { + return nil, err + } out := new(spec.Schema) - validation.ConvertJSONSchemaPropsWithPostProcess(in, out, func(p *spec.Schema) error { - // Remove unsupported fields in OpenAPI v2 - p.OneOf = nil - p.AnyOf = nil - p.Not = nil - - return nil - }) - + if err := out.UnmarshalJSON(data); err != nil { + return nil, err + } + // Remove unsupported fields in OpenAPI v2 + out.OneOf = nil + out.AnyOf = nil + out.Not = nil return out, nil } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go index 6628ad81368..d8c330280b6 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go @@ -17,12 +17,12 @@ limitations under the License. package openapi import ( + "encoding/json" "reflect" "testing" "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apimachinery/pkg/util/diff" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" ) func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { @@ -30,16 +30,18 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { testStr2 := "test2" testFloat64 := float64(6.4) testInt64 := int64(64) - testApiextensionsJSON := apiextensions.JSON(testStr) + raw, _ := json.Marshal(testStr) + raw2, _ := json.Marshal(testStr2) + testApiextensionsJSON := v1beta1.JSON{Raw: raw} tests := []struct { name string - in *apiextensions.JSONSchemaProps + in *v1beta1.JSONSchemaProps expected *spec.Schema }{ { name: "id", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ ID: testStr, }, expected: new(spec.Schema). @@ -47,7 +49,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "$schema", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Schema: "test", }, expected: &spec.Schema{ @@ -58,14 +60,14 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "$ref", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Ref: &testStr, }, expected: spec.RefSchema(testStr), }, { name: "description", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Description: testStr, }, expected: new(spec.Schema). @@ -73,7 +75,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "type and format", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Type: testStr, Format: testStr2, }, @@ -82,7 +84,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "title", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Title: testStr, }, expected: new(spec.Schema). @@ -90,7 +92,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "default", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Default: &testApiextensionsJSON, }, expected: new(spec.Schema). @@ -98,7 +100,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "maximum and exclusiveMaximum", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Maximum: &testFloat64, ExclusiveMaximum: true, }, @@ -107,7 +109,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "minimum and exclusiveMinimum", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Minimum: &testFloat64, ExclusiveMinimum: true, }, @@ -116,7 +118,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "maxLength", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MaxLength: &testInt64, }, expected: new(spec.Schema). @@ -124,7 +126,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "minLength", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MinLength: &testInt64, }, expected: new(spec.Schema). @@ -132,7 +134,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "pattern", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Pattern: testStr, }, expected: new(spec.Schema). @@ -140,7 +142,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "maxItems", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MaxItems: &testInt64, }, expected: new(spec.Schema). @@ -148,7 +150,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "minItems", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MinItems: &testInt64, }, expected: new(spec.Schema). @@ -156,7 +158,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "uniqueItems", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ UniqueItems: true, }, expected: new(spec.Schema). @@ -164,7 +166,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "multipleOf", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MultipleOf: &testFloat64, }, expected: new(spec.Schema). @@ -172,15 +174,15 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "enum", - in: &apiextensions.JSONSchemaProps{ - Enum: []apiextensions.JSON{apiextensions.JSON(testStr), apiextensions.JSON(testStr2)}, + in: &v1beta1.JSONSchemaProps{ + Enum: []v1beta1.JSON{{Raw: raw}, {Raw: raw2}}, }, expected: new(spec.Schema). WithEnum(testStr, testStr2), }, { name: "maxProperties", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MaxProperties: &testInt64, }, expected: new(spec.Schema). @@ -188,7 +190,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "minProperties", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ MinProperties: &testInt64, }, expected: new(spec.Schema). @@ -196,7 +198,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "required", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Required: []string{testStr, testStr2}, }, expected: new(spec.Schema). @@ -204,9 +206,9 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "items single props", - in: &apiextensions.JSONSchemaProps{ - Items: &apiextensions.JSONSchemaPropsOrArray{ - Schema: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + Items: &v1beta1.JSONSchemaPropsOrArray{ + Schema: &v1beta1.JSONSchemaProps{ Type: "boolean", }, }, @@ -221,9 +223,9 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "items array props", - in: &apiextensions.JSONSchemaProps{ - Items: &apiextensions.JSONSchemaPropsOrArray{ - JSONSchemas: []apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + Items: &v1beta1.JSONSchemaPropsOrArray{ + JSONSchemas: []v1beta1.JSONSchemaProps{ {Type: "boolean"}, {Type: "string"}, }, @@ -242,8 +244,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "allOf", - in: &apiextensions.JSONSchemaProps{ - AllOf: []apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + AllOf: []v1beta1.JSONSchemaProps{ {Type: "boolean"}, {Type: "string"}, }, @@ -253,8 +255,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "oneOf", - in: &apiextensions.JSONSchemaProps{ - OneOf: []apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + OneOf: []v1beta1.JSONSchemaProps{ {Type: "boolean"}, {Type: "string"}, }, @@ -271,8 +273,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "anyOf", - in: &apiextensions.JSONSchemaProps{ - AnyOf: []apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + AnyOf: []v1beta1.JSONSchemaProps{ {Type: "boolean"}, {Type: "string"}, }, @@ -289,8 +291,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "not", - in: &apiextensions.JSONSchemaProps{ - Not: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + Not: &v1beta1.JSONSchemaProps{ Type: "boolean", }, }, @@ -301,90 +303,10 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { // }, // }, }, - { - name: "nested logic", - in: &apiextensions.JSONSchemaProps{ - AllOf: []apiextensions.JSONSchemaProps{ - { - Not: &apiextensions.JSONSchemaProps{ - Type: "boolean", - }, - }, - { - AnyOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - { - OneOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - {Type: "string"}, - }, - AnyOf: []apiextensions.JSONSchemaProps{ - { - Not: &apiextensions.JSONSchemaProps{ - Type: "boolean", - }, - }, - { - AnyOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - { - OneOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - {Type: "string"}, - }, - OneOf: []apiextensions.JSONSchemaProps{ - { - Not: &apiextensions.JSONSchemaProps{ - Type: "boolean", - }, - }, - { - AnyOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - { - OneOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - {Type: "string"}, - }, - Not: &apiextensions.JSONSchemaProps{ - Not: &apiextensions.JSONSchemaProps{ - Type: "boolean", - }, - AnyOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - OneOf: []apiextensions.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - }, - expected: new(spec.Schema). - WithAllOf(spec.Schema{}, spec.Schema{}, spec.Schema{}, *spec.StringProperty()), - }, { name: "properties", - in: &apiextensions.JSONSchemaProps{ - Properties: map[string]apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + Properties: map[string]v1beta1.JSONSchemaProps{ testStr: {Type: "boolean"}, }, }, @@ -393,10 +315,10 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "additionalProperties", - in: &apiextensions.JSONSchemaProps{ - AdditionalProperties: &apiextensions.JSONSchemaPropsOrBool{ + in: &v1beta1.JSONSchemaProps{ + AdditionalProperties: &v1beta1.JSONSchemaPropsOrBool{ Allows: true, - Schema: &apiextensions.JSONSchemaProps{Type: "boolean"}, + Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, }, }, expected: &spec.Schema{ @@ -410,8 +332,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "patternProperties", - in: &apiextensions.JSONSchemaProps{ - PatternProperties: map[string]apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ + PatternProperties: map[string]v1beta1.JSONSchemaProps{ testStr: {Type: "boolean"}, }, }, @@ -425,10 +347,10 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "dependencies schema", - in: &apiextensions.JSONSchemaProps{ - Dependencies: apiextensions.JSONSchemaDependencies{ - testStr: apiextensions.JSONSchemaPropsOrStringArray{ - Schema: &apiextensions.JSONSchemaProps{Type: "boolean"}, + in: &v1beta1.JSONSchemaProps{ + Dependencies: v1beta1.JSONSchemaDependencies{ + testStr: v1beta1.JSONSchemaPropsOrStringArray{ + Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, }, }, }, @@ -444,9 +366,9 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "dependencies string array", - in: &apiextensions.JSONSchemaProps{ - Dependencies: apiextensions.JSONSchemaDependencies{ - testStr: apiextensions.JSONSchemaPropsOrStringArray{ + in: &v1beta1.JSONSchemaProps{ + Dependencies: v1beta1.JSONSchemaDependencies{ + testStr: v1beta1.JSONSchemaPropsOrStringArray{ Property: []string{testStr2}, }, }, @@ -463,10 +385,10 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "additionalItems", - in: &apiextensions.JSONSchemaProps{ - AdditionalItems: &apiextensions.JSONSchemaPropsOrBool{ + in: &v1beta1.JSONSchemaProps{ + AdditionalItems: &v1beta1.JSONSchemaPropsOrBool{ Allows: true, - Schema: &apiextensions.JSONSchemaProps{Type: "boolean"}, + Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, }, }, expected: &spec.Schema{ @@ -480,9 +402,9 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "definitions", - in: &apiextensions.JSONSchemaProps{ - Definitions: apiextensions.JSONSchemaDefinitions{ - testStr: apiextensions.JSONSchemaProps{Type: "boolean"}, + in: &v1beta1.JSONSchemaProps{ + Definitions: v1beta1.JSONSchemaDefinitions{ + testStr: v1beta1.JSONSchemaProps{Type: "boolean"}, }, }, expected: &spec.Schema{ @@ -495,8 +417,8 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "externalDocs", - in: &apiextensions.JSONSchemaProps{ - ExternalDocs: &apiextensions.ExternalDocumentation{ + in: &v1beta1.JSONSchemaProps{ + ExternalDocs: &v1beta1.ExternalDocumentation{ Description: testStr, URL: testStr2, }, @@ -506,7 +428,7 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { }, { name: "example", - in: &apiextensions.JSONSchemaProps{ + in: &v1beta1.JSONSchemaProps{ Example: &testApiextensionsJSON, }, expected: new(spec.Schema). @@ -515,14 +437,12 @@ func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { } for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - out, err := ConvertJSONSchemaPropsToOpenAPIv2Schema(test.in) - if err != nil { - t.Fatalf("unexpected error in converting openapi schema: %v", err) - } - if !reflect.DeepEqual(*out, *test.expected) { - t.Errorf("unexpected result:\n want=%v\n got=%v\n\n%s", *test.expected, *out, diff.ObjectDiff(*test.expected, *out)) - } - }) + out, err := ConvertJSONSchemaPropsToOpenAPIv2Schema(test.in) + if err != nil { + t.Errorf("unexpected error in converting openapi schema: %v", test.name) + } + if !reflect.DeepEqual(out, test.expected) { + t.Errorf("result of conversion test '%v' didn't match, want: %v; got: %v", test.name, *test.expected, *out) + } } } diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go index 13e479d7ca2..797fbeddc3a 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go @@ -24,6 +24,7 @@ import ( "github.com/go-openapi/spec" autoscalingv1 "k8s.io/api/autoscaling/v1" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -205,7 +206,7 @@ func scaleStatusSchema() *spec.Schema { // NOTE: in apiserver we general operates on internal types. We are using versioned (v1beta1) // validation schema here because we need the json tags to properly marshal the object to // JSON. -func CustomResourceDefinitionOpenAPISpec(crdSpec *apiextensions.CustomResourceDefinitionSpec, version string, validationSchema *apiextensions.CustomResourceValidation) (*spec.Swagger, string, error) { +func CustomResourceDefinitionOpenAPISpec(crdSpec *apiextensions.CustomResourceDefinitionSpec, version string, validationSchema *v1beta1.CustomResourceValidation) (*spec.Swagger, string, error) { schema := &spec.Schema{} if validationSchema != nil && validationSchema.OpenAPIV3Schema != nil { var err error From 8799eb4e2e12e5d144871a35c6288b8433a74c9b Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 16 Nov 2018 16:36:24 -0500 Subject: [PATCH 2/2] Revert "Merge pull request #67205 from roycaihw/crd-openapi-spec" This reverts commit 54ee58b2d613c636f972312ca80357563292b57d, reversing changes made to 9e2820e4c9541a7718e9d5b7a0ee62429552023a. --- hack/import-restrictions.yaml | 1 - .../src/k8s.io/apiextensions-apiserver/BUILD | 1 - .../Godeps/Godeps.json | 4 - .../pkg/apiserver/BUILD | 1 - .../pkg/apiserver/apiserver.go | 9 +- .../customresource_discovery_controller.go | 54 +-- .../apiextensions-apiserver/pkg/openapi/BUILD | 48 -- .../pkg/openapi/aggregator.go | 232 --------- .../pkg/openapi/construction.go | 371 --------------- .../pkg/openapi/conversion.go | 49 -- .../pkg/openapi/conversion_test.go | 448 ------------------ .../pkg/openapi/swagger_util.go | 228 --------- staging/src/k8s.io/apiserver/pkg/server/BUILD | 1 - .../apiserver/pkg/server/genericapiserver.go | 11 +- .../k8s.io/apiserver/pkg/server/routes/BUILD | 2 - .../apiserver/pkg/server/routes/openapi.go | 20 +- .../pkg/controllers/openapi/aggregator.go | 21 - .../pkg/controllers/openapi/controller.go | 15 - .../custom_resource_definition.go | 59 --- test/integration/master/BUILD | 1 - test/integration/master/crd_test.go | 88 ---- 21 files changed, 8 insertions(+), 1656 deletions(-) delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go delete mode 100644 staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go diff --git a/hack/import-restrictions.yaml b/hack/import-restrictions.yaml index f0e098a4573..3ae27381c33 100644 --- a/hack/import-restrictions.yaml +++ b/hack/import-restrictions.yaml @@ -123,7 +123,6 @@ - k8s.io/apiserver - k8s.io/client-go - k8s.io/klog - - k8s.io/kube-openapi - baseImportPath: "./vendor/k8s.io/kube-openapi/" allowedImports: diff --git a/staging/src/k8s.io/apiextensions-apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/BUILD index 476d9658fae..e555fb761c0 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/BUILD @@ -50,7 +50,6 @@ filegroup( "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:all-srcs", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/openapi:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition:all-srcs", "//staging/src/k8s.io/apiextensions-apiserver/test/integration:all-srcs", diff --git a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json index 06f0b31eb0d..df164ef7363 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json +++ b/staging/src/k8s.io/apiextensions-apiserver/Godeps/Godeps.json @@ -2262,10 +2262,6 @@ "ImportPath": "k8s.io/klog", "Rev": "8139d8cb77af419532b33dfa7dd09fbc5f1d344f" }, - { - "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d" - }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", "Rev": "c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d" diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD index d2ac06cd03e..5da6c14cda3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/BUILD @@ -36,7 +36,6 @@ go_library( "//staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/crdserverscheme:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/features:go_default_library", - "//staging/src/k8s.io/apiextensions-apiserver/pkg/openapi:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/tableconvertor:go_default_library", "//staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go index 30ec82074ee..b6830d762c8 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go @@ -41,7 +41,6 @@ import ( "k8s.io/apiextensions-apiserver/pkg/controller/establish" "k8s.io/apiextensions-apiserver/pkg/controller/finalizer" "k8s.io/apiextensions-apiserver/pkg/controller/status" - openapiaggregator "k8s.io/apiextensions-apiserver/pkg/openapi" "k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition" _ "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" @@ -205,13 +204,7 @@ func (c completedConfig) New(delegationTarget genericapiserver.DelegationTarget) return nil }) s.GenericAPIServer.AddPostStartHook("start-apiextensions-controllers", func(context genericapiserver.PostStartHookContext) error { - // create OpenAPI aggregation manager in the last step because only now genericapiserver's the OpenAPI services and spec is available (after PrepareRun). - crdOpenAPIAggregationManager, err := openapiaggregator.NewAggregationManager(s.GenericAPIServer.OpenAPIService, s.GenericAPIServer.OpenAPIVersionedService, s.GenericAPIServer.StaticOpenAPISpec) - if err != nil { - return err - } - - go crdController.Run(context.StopCh, crdOpenAPIAggregationManager) + go crdController.Run(context.StopCh) go namingController.Run(context.StopCh) go establishingController.Run(context.StopCh) go finalizingController.Run(5, context.StopCh) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go index da54fca6af5..3b13e8357db 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go @@ -31,16 +31,12 @@ import ( "k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/version" "k8s.io/apiserver/pkg/endpoints/discovery" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" informers "k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion" listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion" - apiextensionsfeatures "k8s.io/apiextensions-apiserver/pkg/features" - apiextensionsopenapi "k8s.io/apiextensions-apiserver/pkg/openapi" ) type DiscoveryController struct { @@ -54,8 +50,6 @@ type DiscoveryController struct { syncFn func(version schema.GroupVersion) error queue workqueue.RateLimitingInterface - - openAPIAggregationManager apiextensionsopenapi.AggregationManager } func NewDiscoveryController(crdInformer informers.CustomResourceDefinitionInformer, versionHandler *versionDiscoveryHandler, groupHandler *groupDiscoveryHandler) *DiscoveryController { @@ -89,7 +83,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if err != nil { return err } - apiServiceName := version.Group + "." + version.Version foundVersion := false foundGroup := false for _, crd := range crds { @@ -126,33 +119,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { continue } foundVersion = true - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - validationSchema, err := getSchemaForVersion(crd, version.Version) - if err != nil { - return err - } - // Convert internal CustomResourceValidation to versioned CustomResourceValidation - versionedSchema := new(v1beta1.CustomResourceValidation) - if validationSchema == nil { - versionedSchema = nil - } else { - if err := v1beta1.Convert_apiextensions_CustomResourceValidation_To_v1beta1_CustomResourceValidation(validationSchema, versionedSchema, nil); err != nil { - return err - } - } - // We aggregate the schema even if it's nil as it maybe a removal of the schema for this CRD, - // and the aggreated OpenAPI spec should reflect this change. - crdspec, etag, err := apiextensionsopenapi.CustomResourceDefinitionOpenAPISpec(&crd.Spec, version.Version, versionedSchema) - if err != nil { - return err - } - - // Add/update the local API service's spec for the CRD in apiExtensionsServer's - // openAPIAggregationManager - if err := c.openAPIAggregationManager.AddUpdateLocalAPIServiceSpec(apiServiceName, crdspec, etag); err != nil { - return err - } - } verbs := metav1.Verbs([]string{"delete", "deletecollection", "get", "list", "patch", "create", "update", "watch"}) // if we're terminating we don't allow some verbs @@ -198,14 +164,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if !foundGroup { c.groupHandler.unsetDiscovery(version.Group) c.versionHandler.unsetDiscovery(version) - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - // Remove the local API service for the CRD in apiExtensionsServer's - // openAPIAggregationManager. - // Note that we don't check if apiServiceName exists in openAPIAggregationManager - // because RemoveAPIServiceSpec properly handles non-existing API service by - // returning no error. - return c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName) - } return nil } @@ -222,14 +180,6 @@ func (c *DiscoveryController) sync(version schema.GroupVersion) error { if !foundVersion { c.versionHandler.unsetDiscovery(version) - if c.openAPIAggregationManager != nil && utilfeature.DefaultFeatureGate.Enabled(apiextensionsfeatures.CustomResourceValidation) { - // Remove the local API service for the CRD in apiExtensionsServer's - // openAPIAggregationManager. - // Note that we don't check if apiServiceName exists in openAPIAggregationManager - // because RemoveAPIServiceSpec properly handles non-existing API service by - // returning no error. - return c.openAPIAggregationManager.RemoveAPIServiceSpec(apiServiceName) - } return nil } c.versionHandler.setDiscovery(version, discovery.NewAPIVersionHandler(Codecs, version, discovery.APIResourceListerFunc(func() []metav1.APIResource { @@ -245,15 +195,13 @@ func sortGroupDiscoveryByKubeAwareVersion(gd []metav1.GroupVersionForDiscovery) }) } -func (c *DiscoveryController) Run(stopCh <-chan struct{}, crdOpenAPIAggregationManager apiextensionsopenapi.AggregationManager) { +func (c *DiscoveryController) Run(stopCh <-chan struct{}) { defer utilruntime.HandleCrash() defer c.queue.ShutDown() defer klog.Infof("Shutting down DiscoveryController") klog.Infof("Starting DiscoveryController") - c.openAPIAggregationManager = crdOpenAPIAggregationManager - if !cache.WaitForCacheSync(stopCh, c.crdsSynced) { utilruntime.HandleError(fmt.Errorf("timed out waiting for caches to sync")) return diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD deleted file mode 100644 index d1d11eabec9..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/BUILD +++ /dev/null @@ -1,48 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -go_library( - name = "go_default_library", - srcs = [ - "aggregator.go", - "construction.go", - "conversion.go", - "swagger_util.go", - ], - importmap = "k8s.io/kubernetes/vendor/k8s.io/apiextensions-apiserver/pkg/openapi", - importpath = "k8s.io/apiextensions-apiserver/pkg/openapi", - visibility = ["//visibility:public"], - deps = [ - "//staging/src/k8s.io/api/autoscaling/v1:go_default_library", - "//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/apimachinery/pkg/apis/meta/v1:go_default_library", - "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/aggregator:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["conversion_test.go"], - embed = [":go_default_library"], - deps = [ - "//staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library", - "//vendor/github.com/go-openapi/spec: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"], -) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go deleted file mode 100644 index 90bd042f097..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/aggregator.go +++ /dev/null @@ -1,232 +0,0 @@ -/* -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 openapi - -import ( - "fmt" - "sync" - - "github.com/go-openapi/spec" - - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/kube-openapi/pkg/aggregator" - "k8s.io/kube-openapi/pkg/handler" -) - -// AggregationManager is the interface between OpenAPI Aggregator service and a controller -// that manages CRD openapi spec aggregation -type AggregationManager interface { - // AddUpdateLocalAPIService allows adding/updating local API service with nil handler and - // nil Spec.Service. This function can be used for local dynamic OpenAPI spec aggregation - // management (e.g. CRD) - AddUpdateLocalAPIServiceSpec(name string, spec *spec.Swagger, etag string) error - RemoveAPIServiceSpec(apiServiceName string) error -} - -type specAggregator struct { - // mutex protects all members of this struct. - rwMutex sync.RWMutex - - // Map of API Services' OpenAPI specs by their name - openAPISpecs map[string]*openAPISpecInfo - - // provided for dynamic OpenAPI spec - openAPIService *handler.OpenAPIService - openAPIVersionedService *handler.OpenAPIService -} - -var _ AggregationManager = &specAggregator{} - -// NewAggregationManager constructs a specAggregator from input openAPIService, openAPIVersionedService and -// recorded static OpenAPI spec. The function returns an AggregationManager interface. -func NewAggregationManager(openAPIService, openAPIVersionedService *handler.OpenAPIService, staticSpec *spec.Swagger) (AggregationManager, error) { - // openAPIVersionedService and deprecated openAPIService should be initialized together - if (openAPIService == nil) != (openAPIVersionedService == nil) { - return nil, fmt.Errorf("unexpected openapi service initialization error") - } - return &specAggregator{ - openAPISpecs: map[string]*openAPISpecInfo{ - "initial_static_spec": { - spec: staticSpec, - }, - }, - openAPIService: openAPIService, - openAPIVersionedService: openAPIVersionedService, - }, nil -} - -// openAPISpecInfo is used to store OpenAPI spec with its priority. -// It can be used to sort specs with their priorities. -type openAPISpecInfo struct { - // Name of a registered ApiService - name string - - // Specification of this API Service. If null then the spec is not loaded yet. - spec *spec.Swagger - etag string -} - -// buildOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe. The caller is responsible to hold proper locks. -func (s *specAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err error) { - specs := []openAPISpecInfo{} - for _, specInfo := range s.openAPISpecs { - if specInfo.spec == nil { - continue - } - specs = append(specs, *specInfo) - } - if len(specs) == 0 { - return &spec.Swagger{}, nil - } - for _, specInfo := range specs { - if specToReturn == nil { - specToReturn, err = aggregator.CloneSpec(specInfo.spec) - if err != nil { - return nil, err - } - continue - } - mergeSpecs(specToReturn, specInfo.spec) - } - // Add minimum required keys if missing, to properly serve the OpenAPI spec - // through apiextensions-apiserver HTTP handler. These keys will not be - // aggregated to top-level OpenAPI spec (only paths and definitions will). - // However these keys make the OpenAPI->proto serialization happy. - if specToReturn.Info == nil { - specToReturn.Info = &spec.Info{ - InfoProps: spec.InfoProps{ - Title: "Kubernetes", - }, - } - } - if len(specToReturn.Swagger) == 0 { - specToReturn.Swagger = "2.0" - } - return specToReturn, nil -} - -// updateOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe. The caller is responsible to hold proper locks. -func (s *specAggregator) updateOpenAPISpec() error { - if s.openAPIService == nil || s.openAPIVersionedService == nil { - // openAPIVersionedService and deprecated openAPIService should be initialized together - if !(s.openAPIService == nil && s.openAPIVersionedService == nil) { - return fmt.Errorf("unexpected openapi service initialization error") - } - return nil - } - specToServe, err := s.buildOpenAPISpec() - if err != nil { - return err - } - // openAPIService.UpdateSpec and openAPIVersionedService.UpdateSpec read the same swagger spec - // serially and update their local caches separately. Both endpoints will have same spec in - // their caches if the caller is holding proper locks. - err = s.openAPIService.UpdateSpec(specToServe) - if err != nil { - return err - } - return s.openAPIVersionedService.UpdateSpec(specToServe) -} - -// tryUpdatingServiceSpecs tries updating openAPISpecs map with specified specInfo, and keeps the map intact -// if the update fails. -func (s *specAggregator) tryUpdatingServiceSpecs(specInfo *openAPISpecInfo) error { - orgSpecInfo, exists := s.openAPISpecs[specInfo.name] - s.openAPISpecs[specInfo.name] = specInfo - if err := s.updateOpenAPISpec(); err != nil { - if exists { - s.openAPISpecs[specInfo.name] = orgSpecInfo - } else { - delete(s.openAPISpecs, specInfo.name) - } - return err - } - return nil -} - -// tryDeleteServiceSpecs tries delete specified specInfo from openAPISpecs map, and keeps the map intact -// if the update fails. -func (s *specAggregator) tryDeleteServiceSpecs(apiServiceName string) error { - orgSpecInfo, exists := s.openAPISpecs[apiServiceName] - if !exists { - return nil - } - delete(s.openAPISpecs, apiServiceName) - if err := s.updateOpenAPISpec(); err != nil { - s.openAPISpecs[apiServiceName] = orgSpecInfo - return err - } - return nil -} - -// AddUpdateLocalAPIService allows adding/updating local API service with nil handler and -// nil Spec.Service. This function can be used for local dynamic OpenAPI spec aggregation -// management (e.g. CRD) -func (s *specAggregator) AddUpdateLocalAPIServiceSpec(name string, spec *spec.Swagger, etag string) error { - s.rwMutex.Lock() - defer s.rwMutex.Unlock() - - return s.tryUpdatingServiceSpecs(&openAPISpecInfo{ - name: name, - spec: spec, - etag: etag, - }) -} - -// RemoveAPIServiceSpec removes an api service from OpenAPI aggregation. If it does not exist, no error is returned. -// It is thread safe. -func (s *specAggregator) RemoveAPIServiceSpec(apiServiceName string) error { - s.rwMutex.Lock() - defer s.rwMutex.Unlock() - - if _, existingService := s.openAPISpecs[apiServiceName]; !existingService { - return nil - } - - return s.tryDeleteServiceSpecs(apiServiceName) -} - -// mergeSpecs simply adds source openapi spec to dest and ignores any path/definition -// conflicts because CRD openapi spec should not have conflict -func mergeSpecs(dest, source *spec.Swagger) { - // Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). - if source.Paths == nil { - // If Path is nil, none of the model defined in Definitions is used and we - // should not do anything. - // NOTE: this should not happen for CRD specs, because we automatically construct - // the Paths for CRD specs. We use utilruntime.HandleError to log this impossible - // case - utilruntime.HandleError(fmt.Errorf("unexpected CRD spec with empty Path: %v", *source)) - return - } - if dest.Paths == nil { - dest.Paths = &spec.Paths{} - } - for k, v := range source.Definitions { - if dest.Definitions == nil { - dest.Definitions = spec.Definitions{} - } - dest.Definitions[k] = v - } - for k, v := range source.Paths.Paths { - // PathItem may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). - if dest.Paths.Paths == nil { - dest.Paths.Paths = map[string]spec.PathItem{} - } - dest.Paths.Paths[k] = v - } -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go deleted file mode 100644 index 804681da1f3..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/construction.go +++ /dev/null @@ -1,371 +0,0 @@ -/* -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 openapi - -import ( - "fmt" - "strings" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" -) - -// ResourceKind determines the scope of an API object: if it's the parent resource, -// scale subresource or status subresource. -type ResourceKind string - -const ( - // Resource specifies an object of custom resource kind - Resource ResourceKind = "Resource" - // Scale specifies an object of custom resource's scale subresource kind - Scale ResourceKind = "Scale" - // Status specifies an object of custom resource's status subresource kind - Status ResourceKind = "Status" - - scaleSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.Scale" - statusSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Status" - listMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" - patchSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.Patch" -) - -// SwaggerConstructor takes in CRD OpenAPI schema and CustomResourceDefinitionSpec, and -// constructs the OpenAPI swagger that an apiserver serves. -type SwaggerConstructor struct { - // schema is the CRD's OpenAPI v2 schema - schema *spec.Schema - - status, scale bool - - group string - version string - kind string - listKind string - plural string - scope apiextensions.ResourceScope -} - -// NewSwaggerConstructor creates a new SwaggerConstructor using the CRD OpenAPI schema -// and CustomResourceDefinitionSpec -func NewSwaggerConstructor(schema *spec.Schema, crdSpec *apiextensions.CustomResourceDefinitionSpec, version string) (*SwaggerConstructor, error) { - ret := &SwaggerConstructor{ - schema: schema, - group: crdSpec.Group, - version: version, - kind: crdSpec.Names.Kind, - listKind: crdSpec.Names.ListKind, - plural: crdSpec.Names.Plural, - scope: crdSpec.Scope, - } - - sub, err := getSubresourcesForVersion(crdSpec, version) - if err != nil { - return nil, err - } - if sub != nil { - ret.status = sub.Status != nil - ret.scale = sub.Scale != nil - } - - return ret, nil -} - -// ConstructCRDOpenAPISpec constructs the complete OpenAPI swagger (spec). -func (c *SwaggerConstructor) ConstructCRDOpenAPISpec() *spec.Swagger { - basePath := fmt.Sprintf("/apis/%s/%s/%s", c.group, c.version, c.plural) - if c.scope == apiextensions.NamespaceScoped { - basePath = fmt.Sprintf("/apis/%s/%s/namespaces/{namespace}/%s", c.group, c.version, c.plural) - } - - model := fmt.Sprintf("%s.%s.%s", c.group, c.version, c.kind) - listModel := fmt.Sprintf("%s.%s.%s", c.group, c.version, c.listKind) - - var schema spec.Schema - if c.schema != nil { - schema = *c.schema - } - - ret := &spec.Swagger{ - SwaggerProps: spec.SwaggerProps{ - Paths: &spec.Paths{ - Paths: map[string]spec.PathItem{ - basePath: { - PathItemProps: spec.PathItemProps{ - Get: c.listOperation(), - Post: c.createOperation(), - Delete: c.deleteCollectionOperation(), - Parameters: pathParameters(), - }, - }, - fmt.Sprintf("%s/{name}", basePath): { - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Resource), - Put: c.replaceOperation(Resource), - Delete: c.deleteOperation(), - Patch: c.patchOperation(Resource), - Parameters: pathParameters(), - }, - }, - }, - }, - Definitions: spec.Definitions{ - model: schema, - listModel: *c.listSchema(), - }, - }, - } - - if c.status { - ret.SwaggerProps.Paths.Paths[fmt.Sprintf("%s/{name}/status", basePath)] = spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Status), - Put: c.replaceOperation(Status), - Patch: c.patchOperation(Status), - Parameters: pathParameters(), - }, - } - } - - if c.scale { - ret.SwaggerProps.Paths.Paths[fmt.Sprintf("%s/{name}/scale", basePath)] = spec.PathItem{ - PathItemProps: spec.PathItemProps{ - Get: c.readOperation(Scale), - Put: c.replaceOperation(Scale), - Patch: c.patchOperation(Scale), - Parameters: pathParameters(), - }, - } - // TODO(roycaihw): this is a hack to let apiExtension apiserver and generic kube-apiserver - // to have the same io.k8s.api.autoscaling.v1.Scale definition, so that aggregator server won't - // detect name conflict and create a duplicate io.k8s.api.autoscaling.v1.Scale_V2 schema - // when aggregating the openapi spec. It would be better if apiExtension apiserver serves - // identical definition through the same code path (using routes) as generic kube-apiserver. - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.Scale"] = *scaleSchema() - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.ScaleSpec"] = *scaleSpecSchema() - ret.SwaggerProps.Definitions["io.k8s.api.autoscaling.v1.ScaleStatus"] = *scaleStatusSchema() - } - - return ret -} - -// baseOperation initializes a base operation that all operations build upon -func (c *SwaggerConstructor) baseOperation(kind ResourceKind, action string) *spec.Operation { - op := spec.NewOperation(c.operationID(kind, action)). - WithConsumes( - "application/json", - "application/yaml", - ). - WithProduces( - "application/json", - "application/yaml", - ). - WithTags(fmt.Sprintf("%s_%s", c.group, c.version)). - RespondsWith(401, unauthorizedResponse()) - op.Schemes = []string{"https"} - op.AddExtension("x-kubernetes-action", action) - - // Add x-kubernetes-group-version-kind extension - // For CRD scale subresource, the x-kubernetes-group-version-kind is autoscaling.v1.Scale - switch kind { - case Scale: - op.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": "autoscaling", - "kind": "Scale", - "version": "v1", - }, - }) - default: - op.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": c.group, - "kind": c.kind, - "version": c.version, - }, - }) - } - return op -} - -// listOperation constructs a list operation for a CRD -func (c *SwaggerConstructor) listOperation() *spec.Operation { - op := c.baseOperation(Resource, "list"). - WithDescription(fmt.Sprintf("list or watch objects of kind %s", c.kind)). - RespondsWith(200, okResponse(fmt.Sprintf("#/definitions/%s.%s.%s", c.group, c.version, c.listKind))) - return addCollectionOperationParameters(op) -} - -// createOperation constructs a create operation for a CRD -func (c *SwaggerConstructor) createOperation() *spec.Operation { - ref := c.constructSchemaRef(Resource) - return c.baseOperation(Resource, "create"). - WithDescription(fmt.Sprintf("create a %s", c.kind)). - RespondsWith(200, okResponse(ref)). - RespondsWith(201, createdResponse(ref)). - RespondsWith(202, acceptedResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(ref)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// deleteOperation constructs a delete operation for a CRD -func (c *SwaggerConstructor) deleteOperation() *spec.Operation { - op := c.baseOperation(Resource, "delete"). - WithDescription(fmt.Sprintf("delete a %s", c.kind)). - RespondsWith(200, okResponse(statusSchemaRef)). - RespondsWith(202, acceptedResponse(statusSchemaRef)) - return addDeleteOperationParameters(op) -} - -// deleteCollectionOperation constructs a deletecollection operation for a CRD -func (c *SwaggerConstructor) deleteCollectionOperation() *spec.Operation { - op := c.baseOperation(Resource, "deletecollection"). - WithDescription(fmt.Sprintf("delete collection of %s", c.kind)) - return addCollectionOperationParameters(op) -} - -// readOperation constructs a read operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) readOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "read" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, action)). - RespondsWith(200, okResponse(ref)) -} - -// replaceOperation constructs a replace operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) replaceOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "replace" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, action)). - RespondsWith(200, okResponse(ref)). - RespondsWith(201, createdResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(ref)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// patchOperation constructs a patch operation for a CRD, CRD's scale subresource -// or CRD's status subresource -func (c *SwaggerConstructor) patchOperation(kind ResourceKind) *spec.Operation { - ref := c.constructSchemaRef(kind) - action := "patch" - return c.baseOperation(kind, action). - WithDescription(c.constructDescription(kind, "partially update")). - RespondsWith(200, okResponse(ref)). - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(patchSchemaRef)}}). - Named("body"). - WithLocation("body"). - AsRequired()) -} - -// listSchema constructs the OpenAPI schema for a list of CRD objects -func (c *SwaggerConstructor) listSchema() *spec.Schema { - ref := c.constructSchemaRef(Resource) - s := new(spec.Schema). - WithDescription(fmt.Sprintf("%s is a list of %s", c.listKind, c.kind)). - WithRequired("items"). - SetProperty("apiVersion", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["apiVersion"])). - SetProperty("items", *spec.ArrayProperty(spec.RefSchema(ref)). - WithDescription(fmt.Sprintf("List of %s. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md", c.plural))). - SetProperty("kind", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["kind"])). - SetProperty("metadata", *spec.RefSchema(listMetaSchemaRef). - WithDescription(swaggerListDescriptions["metadata"])) - s.AddExtension("x-kubernetes-group-version-kind", map[string]string{ - "group": c.group, - "kind": c.listKind, - "version": c.version, - }) - return s -} - -// operationID generates the ID for an operation -func (c *SwaggerConstructor) operationID(kind ResourceKind, action string) string { - var collectionTemplate, namespacedTemplate, subresourceTemplate string - if action == "deletecollection" { - action = "delete" - collectionTemplate = "Collection" - } - if c.scope == apiextensions.NamespaceScoped { - namespacedTemplate = "Namespaced" - } - switch kind { - case Status: - subresourceTemplate = "Status" - case Scale: - subresourceTemplate = "Scale" - } - return fmt.Sprintf("%s%s%s%s%s%s%s", action, strings.Title(c.group), strings.Title(c.version), collectionTemplate, namespacedTemplate, c.kind, subresourceTemplate) -} - -// constructSchemaRef generates a reference to an object schema, based on the ResourceKind -// used by an operation -func (c *SwaggerConstructor) constructSchemaRef(kind ResourceKind) string { - var ref string - switch kind { - case Scale: - ref = scaleSchemaRef - default: - ref = fmt.Sprintf("#/definitions/%s.%s.%s", c.group, c.version, c.kind) - } - return ref -} - -// constructDescription generates a description for READ, REPLACE and PATCH operations, based on -// the ResourceKind used by the operation -func (c *SwaggerConstructor) constructDescription(kind ResourceKind, action string) string { - var descriptionTemplate string - switch kind { - case Status: - descriptionTemplate = "status of " - case Scale: - descriptionTemplate = "scale of " - } - return fmt.Sprintf("%s %sthe specified %s", action, descriptionTemplate, c.kind) -} - -// hasPerVersionSubresources returns true if a CRD spec uses per-version subresources. -func hasPerVersionSubresources(versions []apiextensions.CustomResourceDefinitionVersion) bool { - for _, v := range versions { - if v.Subresources != nil { - return true - } - } - return false -} - -// getSubresourcesForVersion returns the subresources for given version in given CRD spec. -func getSubresourcesForVersion(spec *apiextensions.CustomResourceDefinitionSpec, version string) (*apiextensions.CustomResourceSubresources, error) { - if !hasPerVersionSubresources(spec.Versions) { - return spec.Subresources, nil - } - if spec.Subresources != nil { - return nil, fmt.Errorf("malformed CustomResourceDefinitionSpec version %s: top-level and per-version subresources must be mutual exclusive", version) - } - for _, v := range spec.Versions { - if version == v.Name { - return v.Subresources, nil - } - } - return nil, fmt.Errorf("version %s not found in CustomResourceDefinitionSpec", version) -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go deleted file mode 100644 index 61853acb2ff..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -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 openapi - -import ( - "encoding/json" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" -) - -// ConvertJSONSchemaPropsToOpenAPIv2Schema converts our internal OpenAPI v3 schema -// (*apiextensions.JSONSchemaProps) to an OpenAPI v2 schema (*spec.Schema). -// NOTE: we use versioned type (v1beta1) here so that we can properly marshal the object -// using the JSON tags -func ConvertJSONSchemaPropsToOpenAPIv2Schema(in *v1beta1.JSONSchemaProps) (*spec.Schema, error) { - if in == nil { - return nil, nil - } - - // Marshal JSONSchemaProps into JSON and unmarshal the data into spec.Schema - data, err := json.Marshal(*in) - if err != nil { - return nil, err - } - out := new(spec.Schema) - if err := out.UnmarshalJSON(data); err != nil { - return nil, err - } - // Remove unsupported fields in OpenAPI v2 - out.OneOf = nil - out.AnyOf = nil - out.Not = nil - return out, nil -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go deleted file mode 100644 index d8c330280b6..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/conversion_test.go +++ /dev/null @@ -1,448 +0,0 @@ -/* -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 openapi - -import ( - "encoding/json" - "reflect" - "testing" - - "github.com/go-openapi/spec" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" -) - -func Test_ConvertJSONSchemaPropsToOpenAPIv2Schema(t *testing.T) { - testStr := "test" - testStr2 := "test2" - testFloat64 := float64(6.4) - testInt64 := int64(64) - raw, _ := json.Marshal(testStr) - raw2, _ := json.Marshal(testStr2) - testApiextensionsJSON := v1beta1.JSON{Raw: raw} - - tests := []struct { - name string - in *v1beta1.JSONSchemaProps - expected *spec.Schema - }{ - { - name: "id", - in: &v1beta1.JSONSchemaProps{ - ID: testStr, - }, - expected: new(spec.Schema). - WithID(testStr), - }, - { - name: "$schema", - in: &v1beta1.JSONSchemaProps{ - Schema: "test", - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Schema: "test", - }, - }, - }, - { - name: "$ref", - in: &v1beta1.JSONSchemaProps{ - Ref: &testStr, - }, - expected: spec.RefSchema(testStr), - }, - { - name: "description", - in: &v1beta1.JSONSchemaProps{ - Description: testStr, - }, - expected: new(spec.Schema). - WithDescription(testStr), - }, - { - name: "type and format", - in: &v1beta1.JSONSchemaProps{ - Type: testStr, - Format: testStr2, - }, - expected: new(spec.Schema). - Typed(testStr, testStr2), - }, - { - name: "title", - in: &v1beta1.JSONSchemaProps{ - Title: testStr, - }, - expected: new(spec.Schema). - WithTitle(testStr), - }, - { - name: "default", - in: &v1beta1.JSONSchemaProps{ - Default: &testApiextensionsJSON, - }, - expected: new(spec.Schema). - WithDefault(testStr), - }, - { - name: "maximum and exclusiveMaximum", - in: &v1beta1.JSONSchemaProps{ - Maximum: &testFloat64, - ExclusiveMaximum: true, - }, - expected: new(spec.Schema). - WithMaximum(testFloat64, true), - }, - { - name: "minimum and exclusiveMinimum", - in: &v1beta1.JSONSchemaProps{ - Minimum: &testFloat64, - ExclusiveMinimum: true, - }, - expected: new(spec.Schema). - WithMinimum(testFloat64, true), - }, - { - name: "maxLength", - in: &v1beta1.JSONSchemaProps{ - MaxLength: &testInt64, - }, - expected: new(spec.Schema). - WithMaxLength(testInt64), - }, - { - name: "minLength", - in: &v1beta1.JSONSchemaProps{ - MinLength: &testInt64, - }, - expected: new(spec.Schema). - WithMinLength(testInt64), - }, - { - name: "pattern", - in: &v1beta1.JSONSchemaProps{ - Pattern: testStr, - }, - expected: new(spec.Schema). - WithPattern(testStr), - }, - { - name: "maxItems", - in: &v1beta1.JSONSchemaProps{ - MaxItems: &testInt64, - }, - expected: new(spec.Schema). - WithMaxItems(testInt64), - }, - { - name: "minItems", - in: &v1beta1.JSONSchemaProps{ - MinItems: &testInt64, - }, - expected: new(spec.Schema). - WithMinItems(testInt64), - }, - { - name: "uniqueItems", - in: &v1beta1.JSONSchemaProps{ - UniqueItems: true, - }, - expected: new(spec.Schema). - UniqueValues(), - }, - { - name: "multipleOf", - in: &v1beta1.JSONSchemaProps{ - MultipleOf: &testFloat64, - }, - expected: new(spec.Schema). - WithMultipleOf(testFloat64), - }, - { - name: "enum", - in: &v1beta1.JSONSchemaProps{ - Enum: []v1beta1.JSON{{Raw: raw}, {Raw: raw2}}, - }, - expected: new(spec.Schema). - WithEnum(testStr, testStr2), - }, - { - name: "maxProperties", - in: &v1beta1.JSONSchemaProps{ - MaxProperties: &testInt64, - }, - expected: new(spec.Schema). - WithMaxProperties(testInt64), - }, - { - name: "minProperties", - in: &v1beta1.JSONSchemaProps{ - MinProperties: &testInt64, - }, - expected: new(spec.Schema). - WithMinProperties(testInt64), - }, - { - name: "required", - in: &v1beta1.JSONSchemaProps{ - Required: []string{testStr, testStr2}, - }, - expected: new(spec.Schema). - WithRequired(testStr, testStr2), - }, - { - name: "items single props", - in: &v1beta1.JSONSchemaProps{ - Items: &v1beta1.JSONSchemaPropsOrArray{ - Schema: &v1beta1.JSONSchemaProps{ - Type: "boolean", - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Items: &spec.SchemaOrArray{ - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "items array props", - in: &v1beta1.JSONSchemaProps{ - Items: &v1beta1.JSONSchemaPropsOrArray{ - JSONSchemas: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Items: &spec.SchemaOrArray{ - Schemas: []spec.Schema{ - *spec.BooleanProperty(), - *spec.StringProperty(), - }, - }, - }, - }, - }, - { - name: "allOf", - in: &v1beta1.JSONSchemaProps{ - AllOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema). - WithAllOf(*spec.BooleanProperty(), *spec.StringProperty()), - }, - { - name: "oneOf", - in: &v1beta1.JSONSchemaProps{ - OneOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // OneOf: []spec.Schema{ - // *spec.BooleanProperty(), - // *spec.StringProperty(), - // }, - // }, - // }, - }, - { - name: "anyOf", - in: &v1beta1.JSONSchemaProps{ - AnyOf: []v1beta1.JSONSchemaProps{ - {Type: "boolean"}, - {Type: "string"}, - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // AnyOf: []spec.Schema{ - // *spec.BooleanProperty(), - // *spec.StringProperty(), - // }, - // }, - // }, - }, - { - name: "not", - in: &v1beta1.JSONSchemaProps{ - Not: &v1beta1.JSONSchemaProps{ - Type: "boolean", - }, - }, - expected: new(spec.Schema), - // expected: &spec.Schema{ - // SchemaProps: spec.SchemaProps{ - // Not: spec.BooleanProperty(), - // }, - // }, - }, - { - name: "properties", - in: &v1beta1.JSONSchemaProps{ - Properties: map[string]v1beta1.JSONSchemaProps{ - testStr: {Type: "boolean"}, - }, - }, - expected: new(spec.Schema). - SetProperty(testStr, *spec.BooleanProperty()), - }, - { - name: "additionalProperties", - in: &v1beta1.JSONSchemaProps{ - AdditionalProperties: &v1beta1.JSONSchemaPropsOrBool{ - Allows: true, - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - AdditionalProperties: &spec.SchemaOrBool{ - Allows: true, - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "patternProperties", - in: &v1beta1.JSONSchemaProps{ - PatternProperties: map[string]v1beta1.JSONSchemaProps{ - testStr: {Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - PatternProperties: map[string]spec.Schema{ - testStr: *spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "dependencies schema", - in: &v1beta1.JSONSchemaProps{ - Dependencies: v1beta1.JSONSchemaDependencies{ - testStr: v1beta1.JSONSchemaPropsOrStringArray{ - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Dependencies: spec.Dependencies{ - testStr: spec.SchemaOrStringArray{ - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - }, - { - name: "dependencies string array", - in: &v1beta1.JSONSchemaProps{ - Dependencies: v1beta1.JSONSchemaDependencies{ - testStr: v1beta1.JSONSchemaPropsOrStringArray{ - Property: []string{testStr2}, - }, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Dependencies: spec.Dependencies{ - testStr: spec.SchemaOrStringArray{ - Property: []string{testStr2}, - }, - }, - }, - }, - }, - { - name: "additionalItems", - in: &v1beta1.JSONSchemaProps{ - AdditionalItems: &v1beta1.JSONSchemaPropsOrBool{ - Allows: true, - Schema: &v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - AdditionalItems: &spec.SchemaOrBool{ - Allows: true, - Schema: spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "definitions", - in: &v1beta1.JSONSchemaProps{ - Definitions: v1beta1.JSONSchemaDefinitions{ - testStr: v1beta1.JSONSchemaProps{Type: "boolean"}, - }, - }, - expected: &spec.Schema{ - SchemaProps: spec.SchemaProps{ - Definitions: spec.Definitions{ - testStr: *spec.BooleanProperty(), - }, - }, - }, - }, - { - name: "externalDocs", - in: &v1beta1.JSONSchemaProps{ - ExternalDocs: &v1beta1.ExternalDocumentation{ - Description: testStr, - URL: testStr2, - }, - }, - expected: new(spec.Schema). - WithExternalDocs(testStr, testStr2), - }, - { - name: "example", - in: &v1beta1.JSONSchemaProps{ - Example: &testApiextensionsJSON, - }, - expected: new(spec.Schema). - WithExample(testStr), - }, - } - - for _, test := range tests { - out, err := ConvertJSONSchemaPropsToOpenAPIv2Schema(test.in) - if err != nil { - t.Errorf("unexpected error in converting openapi schema: %v", test.name) - } - if !reflect.DeepEqual(out, test.expected) { - t.Errorf("result of conversion test '%v' didn't match, want: %v; got: %v", test.name, *test.expected, *out) - } - } -} diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go deleted file mode 100644 index 797fbeddc3a..00000000000 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/openapi/swagger_util.go +++ /dev/null @@ -1,228 +0,0 @@ -/* -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 openapi - -import ( - "crypto/sha512" - "encoding/json" - "fmt" - - "github.com/go-openapi/spec" - autoscalingv1 "k8s.io/api/autoscaling/v1" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - deleteOptionsSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.DeleteOptions" - objectMetaSchemaRef = "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" - scaleSpecSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.ScaleSpec" - scaleStatusSchemaRef = "#/definitions/io.k8s.api.autoscaling.v1.ScaleStatus" -) - -var swaggerTypeMetaDescriptions = metav1.TypeMeta{}.SwaggerDoc() -var swaggerDeleteOptionsDescriptions = metav1.DeleteOptions{}.SwaggerDoc() -var swaggerListDescriptions = metav1.List{}.SwaggerDoc() -var swaggerListOptionsDescriptions = metav1.ListOptions{}.SwaggerDoc() -var swaggerScaleDescriptions = autoscalingv1.Scale{}.SwaggerDoc() -var swaggerScaleSpecDescriptions = autoscalingv1.ScaleSpec{}.SwaggerDoc() -var swaggerScaleStatusDescriptions = autoscalingv1.ScaleStatus{}.SwaggerDoc() - -// calcSwaggerEtag calculates an etag of the OpenAPI swagger (spec) -func calcSwaggerEtag(openAPISpec *spec.Swagger) (string, error) { - specBytes, err := json.MarshalIndent(openAPISpec, " ", " ") - if err != nil { - return "", err - } - return fmt.Sprintf("\"%X\"", sha512.Sum512(specBytes)), nil -} - -// pathParameters constructs the Parameter used by all paths in the CRD swagger (spec) -func pathParameters() []spec.Parameter { - return []spec.Parameter{ - *spec.QueryParam("pretty"). - Typed("string", ""). - UniqueValues(). - WithDescription("If 'true', then the output is pretty printed."), - } -} - -// addDeleteOperationParameters add the body&query parameters used by a delete operation -func addDeleteOperationParameters(op *spec.Operation) *spec.Operation { - return op. - AddParam((&spec.Parameter{ParamProps: spec.ParamProps{Schema: spec.RefSchema(deleteOptionsSchemaRef)}}). - Named("body"). - WithLocation("body"). - AsRequired()). - AddParam(spec.QueryParam("dryRun"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["dryRun"])). - AddParam(spec.QueryParam("gracePeriodSeconds"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["gracePeriodSeconds"])). - AddParam(spec.QueryParam("orphanDependents"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["orphanDependents"])). - AddParam(spec.QueryParam("propagationPolicy"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerDeleteOptionsDescriptions["propagationPolicy"])) -} - -// addCollectionOperationParameters adds the query parameters used by list and deletecollection -// operations -func addCollectionOperationParameters(op *spec.Operation) *spec.Operation { - return op. - AddParam(spec.QueryParam("continue"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["continue"])). - AddParam(spec.QueryParam("fieldSelector"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["fieldSelector"])). - AddParam(spec.QueryParam("includeUninitialized"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["includeUninitialized"])). - AddParam(spec.QueryParam("labelSelector"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["labelSelector"])). - AddParam(spec.QueryParam("limit"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["limit"])). - AddParam(spec.QueryParam("resourceVersion"). - Typed("string", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["resourceVersion"])). - AddParam(spec.QueryParam("timeoutSeconds"). - Typed("integer", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["timeoutSeconds"])). - AddParam(spec.QueryParam("watch"). - Typed("boolean", ""). - UniqueValues(). - WithDescription(swaggerListOptionsDescriptions["watch"])) -} - -// okResponse constructs a 200 OK response with the input object schema reference -func okResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("OK"). - WithSchema(spec.RefSchema(ref)) -} - -// createdResponse constructs a 201 Created response with the input object schema reference -func createdResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("Created"). - WithSchema(spec.RefSchema(ref)) -} - -// acceptedResponse constructs a 202 Accepted response with the input object schema reference -func acceptedResponse(ref string) *spec.Response { - return spec.NewResponse(). - WithDescription("Accepted"). - WithSchema(spec.RefSchema(ref)) -} - -// unauthorizedResponse constructs a 401 Unauthorized response -func unauthorizedResponse() *spec.Response { - return spec.NewResponse(). - WithDescription("Unauthorized") -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.Scale objects -// TODO(roycaihw): this is a hack to let apiExtension apiserver and generic kube-apiserver -// to have the same io.k8s.api.autoscaling.v1.Scale definition, so that aggregator server won't -// detect name conflict and create a duplicate io.k8s.api.autoscaling.v1.Scale_V2 schema -// when aggregating the openapi spec. It would be better if apiExtension apiserver serves -// identical definition through the same code path (using routes) as generic kube-apiserver. -func scaleSchema() *spec.Schema { - s := new(spec.Schema). - WithDescription(swaggerScaleDescriptions[""]). - SetProperty("apiVersion", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["apiVersion"])). - SetProperty("kind", *spec.StringProperty(). - WithDescription(swaggerTypeMetaDescriptions["kind"])). - SetProperty("metadata", *spec.RefSchema(objectMetaSchemaRef). - WithDescription(swaggerScaleDescriptions["metadata"])). - SetProperty("spec", *spec.RefSchema(scaleSpecSchemaRef). - WithDescription(swaggerScaleDescriptions["spec"])). - SetProperty("status", *spec.RefSchema(scaleStatusSchemaRef). - WithDescription(swaggerScaleDescriptions["status"])) - - s.AddExtension("x-kubernetes-group-version-kind", []map[string]string{ - { - "group": "autoscaling", - "kind": "Scale", - "version": "v1", - }, - }) - return s -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.ScaleSpec objects -func scaleSpecSchema() *spec.Schema { - return new(spec.Schema). - WithDescription(swaggerScaleSpecDescriptions[""]). - SetProperty("replicas", *spec.Int32Property(). - WithDescription(swaggerScaleSpecDescriptions["replicas"])) -} - -// scaleSchema constructs the OpenAPI schema for io.k8s.api.autoscaling.v1.ScaleStatus objects -func scaleStatusSchema() *spec.Schema { - return new(spec.Schema). - WithDescription(swaggerScaleStatusDescriptions[""]). - WithRequired("replicas"). - SetProperty("replicas", *spec.Int32Property(). - WithDescription(swaggerScaleStatusDescriptions["replicas"])). - SetProperty("selector", *spec.StringProperty(). - WithDescription(swaggerScaleStatusDescriptions["selector"])) -} - -// CustomResourceDefinitionOpenAPISpec constructs the OpenAPI spec (swagger) and calculates -// etag for a given CustomResourceDefinitionSpec. -// NOTE: in apiserver we general operates on internal types. We are using versioned (v1beta1) -// validation schema here because we need the json tags to properly marshal the object to -// JSON. -func CustomResourceDefinitionOpenAPISpec(crdSpec *apiextensions.CustomResourceDefinitionSpec, version string, validationSchema *v1beta1.CustomResourceValidation) (*spec.Swagger, string, error) { - schema := &spec.Schema{} - if validationSchema != nil && validationSchema.OpenAPIV3Schema != nil { - var err error - schema, err = ConvertJSONSchemaPropsToOpenAPIv2Schema(validationSchema.OpenAPIV3Schema) - if err != nil { - return nil, "", err - } - } - crdSwaggerConstructor, err := NewSwaggerConstructor(schema, crdSpec, version) - if err != nil { - return nil, "", err - } - crdOpenAPISpec := crdSwaggerConstructor.ConstructCRDOpenAPISpec() - etag, err := calcSwaggerEtag(crdOpenAPISpec) - if err != nil { - return nil, "", err - } - return crdOpenAPISpec, etag, nil -} diff --git a/staging/src/k8s.io/apiserver/pkg/server/BUILD b/staging/src/k8s.io/apiserver/pkg/server/BUILD index 1d71c60a7d0..68588c5a83c 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/BUILD @@ -116,7 +116,6 @@ go_library( "//vendor/k8s.io/klog:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", ], diff --git a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go index fac46af815e..4efc82427e2 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go +++ b/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go @@ -25,8 +25,7 @@ import ( "time" systemd "github.com/coreos/go-systemd/daemon" - swagger "github.com/emicklei/go-restful-swagger12" - "github.com/go-openapi/spec" + "github.com/emicklei/go-restful-swagger12" "k8s.io/klog" "k8s.io/apimachinery/pkg/api/meta" @@ -48,7 +47,6 @@ import ( restclient "k8s.io/client-go/rest" openapibuilder "k8s.io/kube-openapi/pkg/builder" openapicommon "k8s.io/kube-openapi/pkg/common" - "k8s.io/kube-openapi/pkg/handler" openapiutil "k8s.io/kube-openapi/pkg/util" openapiproto "k8s.io/kube-openapi/pkg/util/proto" ) @@ -126,11 +124,6 @@ type GenericAPIServer struct { swaggerConfig *swagger.Config openAPIConfig *openapicommon.Config - // Expose the registered OpenAPI Services and built static OpenAPI spec if openAPIConfig is non-nil - OpenAPIService *handler.OpenAPIService // for endpoint /swagger.json - OpenAPIVersionedService *handler.OpenAPIService // for endpoint /openapi/v2 - StaticOpenAPISpec *spec.Swagger - // PostStartHooks are each called after the server has started listening, in a separate go func for each // with no guarantee of ordering between them. The map key is a name used for error reporting. // It may kill the process with a panic if it wishes to by returning an error. @@ -247,7 +240,7 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer { routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer) } if s.openAPIConfig != nil { - s.OpenAPIService, s.OpenAPIVersionedService, s.StaticOpenAPISpec = routes.OpenAPI{ + routes.OpenAPI{ Config: s.openAPIConfig, }.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux) } diff --git a/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD b/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD index a2fc6075cb7..0b81332a3a4 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD +++ b/staging/src/k8s.io/apiserver/pkg/server/routes/BUILD @@ -32,10 +32,8 @@ go_library( "//vendor/github.com/elazarl/go-bindata-assetfs:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful-swagger12:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/k8s.io/klog:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library", ], diff --git a/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go b/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go index d080e471061..934bbf84a04 100644 --- a/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go +++ b/staging/src/k8s.io/apiserver/pkg/server/routes/openapi.go @@ -18,11 +18,9 @@ package routes import ( restful "github.com/emicklei/go-restful" - "github.com/go-openapi/spec" "k8s.io/klog" "k8s.io/apiserver/pkg/server/mux" - "k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/handler" ) @@ -32,27 +30,17 @@ type OpenAPI struct { Config *common.Config } -// Install adds the SwaggerUI webservice to the given mux. This function returns -// the built static OpenAPI spec and the registered OpenAPI services to allow -// further OpenAPI spec aggregation. -func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) (openAPIService, openAPIVersionedService *handler.OpenAPIService, spec *spec.Swagger) { - var err error - // Record the static OpenAPI spec to allow further OpenAPI spec aggregation - // with this static spec on the registered OpenAPI services - spec, err = builder.BuildOpenAPISpec(c.RegisteredWebServices(), oa.Config) - if err != nil { - klog.Fatalf("Failed to build open api spec for root: %v", err) - } +// Install adds the SwaggerUI webservice to the given mux. +func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) { // NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec, // and switch to a single /openapi/v2 endpoint in Kubernetes 1.10. The design doc and deprecation process // are tracked at: https://docs.google.com/document/d/19lEqE9lc4yHJ3WJAJxS_G7TcORIJXGHyq3wpwcH28nU. - openAPIService, err = handler.RegisterOpenAPIService(spec, "/swagger.json", mux) + _, err := handler.BuildAndRegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux) if err != nil { klog.Fatalf("Failed to register open api spec for root: %v", err) } - openAPIVersionedService, err = handler.RegisterOpenAPIVersionedService(spec, "/openapi/v2", mux) + _, err = handler.BuildAndRegisterOpenAPIVersionedService("/openapi/v2", c.RegisteredWebServices(), oa.Config, mux) if err != nil { klog.Fatalf("Failed to register versioned open api spec for root: %v", err) } - return openAPIService, openAPIVersionedService, spec } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go index e236f27d890..4c1f75bd6d9 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/aggregator.go @@ -53,19 +53,12 @@ type specAggregator struct { // provided for dynamic OpenAPI spec openAPIService *handler.OpenAPIService openAPIVersionedService *handler.OpenAPIService - - // initialized is set to be true at the end of startup. All local specs - // must be registered before initialized is set, we panic otherwise. - initialized bool } var _ AggregationManager = &specAggregator{} // This function is not thread safe as it only being called on startup. func (s *specAggregator) addLocalSpec(spec *spec.Swagger, localHandler http.Handler, name, etag string) { - if s.initialized { - panic("Local spec must not be added after startup") - } localAPIService := apiregistration.APIService{} localAPIService.Name = name s.openAPISpecs[name] = &openAPISpecInfo{ @@ -76,17 +69,6 @@ func (s *specAggregator) addLocalSpec(spec *spec.Swagger, localHandler http.Hand } } -// GetAPIServicesName returns the names of APIServices recorded in specAggregator.openAPISpecs. -// We use this function to pass the names of local APIServices to the controller in this package, -// so that the controller can periodically sync the OpenAPI spec from delegation API servers. -func (s *specAggregator) GetAPIServiceNames() []string { - names := make([]string, len(s.openAPISpecs)) - for key := range s.openAPISpecs { - names = append(names, key) - } - return names -} - // BuildAndRegisterAggregator registered OpenAPI aggregator handler. This function is not thread safe as it only being called on startup. func BuildAndRegisterAggregator(downloader *Downloader, delegationTarget server.DelegationTarget, webServices []*restful.WebService, config *common.Config, pathHandler common.PathHandler) (AggregationManager, error) { @@ -142,9 +124,6 @@ func BuildAndRegisterAggregator(downloader *Downloader, delegationTarget server. return nil, err } - // We set initialized to be true to forbid any future local spec addition - s.initialized = true - return s, nil } diff --git a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go index a349deea761..49d190e9020 100644 --- a/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go +++ b/staging/src/k8s.io/kube-aggregator/pkg/controllers/openapi/controller.go @@ -49,9 +49,6 @@ type AggregationManager interface { UpdateAPIServiceSpec(apiServiceName string, spec *spec.Swagger, etag string) error RemoveAPIServiceSpec(apiServiceName string) error GetAPIServiceInfo(apiServiceName string) (handler http.Handler, etag string, exists bool) - - // GetAPIServicesName returns the names of APIServices recorded in AggregationManager. - GetAPIServiceNames() []string } // AggregationController periodically check for changes in OpenAPI specs of APIServices and update/remove @@ -75,18 +72,6 @@ func NewAggregationController(downloader *Downloader, openAPIAggregationManager } c.syncHandler = c.sync - // During initialization, openAPIAggregationManager only has record of local APIServices. There must be - // no aggregated APIService recorded, because aggregated APIServices only get added to openAPIAggregationManager - // by calling AggregationController.AddAPIService or AggregationController.UpdateAPIService after the - // controller is initialized. - // Here we add delegation target API services to queue, to periodically sync dynamic OpenAPI spec from - // delegation target. - // NOTE: openAPIAggregationManager.GetAPIServiceNames() will also return the APIService of non-name spec - // for aggregator, which has no http.Handler. The first time sync (when popping off from queue) for - // this APIService will be a no-op, and the controller will drop the APIService from queue. - for _, name := range openAPIAggregationManager.GetAPIServiceNames() { - c.queue.AddAfter(name, time.Second) - } return c } diff --git a/test/e2e/apimachinery/custom_resource_definition.go b/test/e2e/apimachinery/custom_resource_definition.go index b282bc184cf..fb0469487e6 100644 --- a/test/e2e/apimachinery/custom_resource_definition.go +++ b/test/e2e/apimachinery/custom_resource_definition.go @@ -17,22 +17,16 @@ limitations under the License. package apimachinery import ( - "fmt" - "strings" - "time" - "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" "k8s.io/apiextensions-apiserver/test/integration/fixtures" utilversion "k8s.io/apimachinery/pkg/util/version" - "k8s.io/apimachinery/pkg/util/wait" "k8s.io/kubernetes/test/e2e/framework" . "github.com/onsi/ginkgo" ) var crdVersion = utilversion.MustParseSemantic("v1.7.0") -var crdOpenAPIVersion = utilversion.MustParseSemantic("v1.13.0") var _ = SIGDescribe("CustomResourceDefinition resources", func() { @@ -73,58 +67,5 @@ var _ = SIGDescribe("CustomResourceDefinition resources", func() { } }() }) - - It("has OpenAPI spec served with CRD Validation chema", func() { - framework.SkipUnlessServerVersionGTE(crdOpenAPIVersion, f.ClientSet.Discovery()) - - config, err := framework.LoadConfig() - if err != nil { - framework.Failf("failed to load config: %v", err) - } - - apiExtensionClient, err := clientset.NewForConfig(config) - if err != nil { - framework.Failf("failed to initialize apiExtensionClient: %v", err) - } - - randomDefinition := fixtures.NewRandomNameCustomResourceDefinition(v1beta1.ClusterScoped) - - //create CRD and waits for the resource to be recognized and available. - randomDefinition, err = fixtures.CreateNewCustomResourceDefinition(randomDefinition, apiExtensionClient, f.DynamicClient) - if err != nil { - framework.Failf("failed to create CustomResourceDefinition: %v", err) - } - - // TODO(roycaihw): think about tweaking feature gates in e2e test (is it possible/easy - // to do so?) and have CRD use top-level/per-version schema - // Also need to test NamespaceScoped CRDs - - // We use a wait.Poll block here because the kube-aggregator openapi - // controller takes time to rotate the queue and resync apiextensions-apiserver's spec - if err := wait.Poll(5*time.Second, 120*time.Second, func() (bool, error) { - data, err := f.ClientSet.CoreV1().RESTClient().Get(). - AbsPath("/swagger.json"). - DoRaw() - - if err != nil { - return false, err - } - // TODO(roycaihw): verify more Paths and List Definitions, also for multiple versions - baseDefinition := fmt.Sprintf("%s.%s.%s", randomDefinition.Spec.Group, randomDefinition.Spec.Version, randomDefinition.Spec.Names.Kind) - basePath := fmt.Sprintf("/apis/%s/%s/%s", randomDefinition.Spec.Group, randomDefinition.Spec.Version, randomDefinition.Spec.Names.Plural) - return strings.Contains(string(data), basePath) && - strings.Contains(string(data), baseDefinition), nil - }); err != nil { - framework.Failf("failed to wait for apiserver to serve openapi spec for registered CRD: %v", err) - } - - defer func() { - err = fixtures.DeleteCustomResourceDefinition(randomDefinition, apiExtensionClient) - if err != nil { - framework.Failf("failed to delete CustomResourceDefinition: %v", err) - } - }() - }) - }) }) diff --git a/test/integration/master/BUILD b/test/integration/master/BUILD index ff8f048bb6a..87c9afda8fe 100644 --- a/test/integration/master/BUILD +++ b/test/integration/master/BUILD @@ -66,7 +66,6 @@ go_test( "//test/integration/framework:go_default_library", "//test/utils:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", - "//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/sigs.k8s.io/yaml:go_default_library", ] + select({ "@io_bazel_rules_go//go/platform:android": [ diff --git a/test/integration/master/crd_test.go b/test/integration/master/crd_test.go index 56e4a037ed2..ac28b037c30 100644 --- a/test/integration/master/crd_test.go +++ b/test/integration/master/crd_test.go @@ -18,12 +18,9 @@ package master import ( "encoding/json" - "fmt" "testing" "time" - "github.com/go-openapi/spec" - admissionregistrationv1alpha1 "k8s.io/api/admissionregistration/v1alpha1" networkingv1 "k8s.io/api/networking/v1" apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" @@ -275,91 +272,6 @@ func TestCRD(t *testing.T) { } } -func TestCRDOpenAPI(t *testing.T) { - defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.Initializers, true)() - result := kubeapiservertesting.StartTestServerOrDie(t, nil, nil, framework.SharedEtcd()) - defer result.TearDownFn() - kubeclient, err := kubernetes.NewForConfig(result.ClientConfig) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - apiextensionsclient, err := apiextensionsclientset.NewForConfig(result.ClientConfig) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - t.Logf("Trying to create a custom resource without conflict") - crd := &apiextensionsv1beta1.CustomResourceDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: "foos.cr.bar.com", - }, - Spec: apiextensionsv1beta1.CustomResourceDefinitionSpec{ - Group: "cr.bar.com", - Version: "v1", - Scope: apiextensionsv1beta1.NamespaceScoped, - Names: apiextensionsv1beta1.CustomResourceDefinitionNames{ - Plural: "foos", - Kind: "Foo", - }, - Validation: &apiextensionsv1beta1.CustomResourceValidation{ - OpenAPIV3Schema: &apiextensionsv1beta1.JSONSchemaProps{ - Properties: map[string]apiextensionsv1beta1.JSONSchemaProps{ - "foo": {Type: "string"}, - }, - }, - }, - }, - } - etcd.CreateTestCRDs(t, apiextensionsclient, false, crd) - waitForSpec := func(expectedType string) { - t.Logf(`Waiting for {properties: {"foo": {"type":"%s"}}} to show up in schema`, expectedType) - lastMsg := "" - if err := wait.PollImmediate(500*time.Millisecond, 120*time.Second, func() (bool, error) { - lastMsg = "" - bs, err := kubeclient.RESTClient().Get().AbsPath("openapi", "v2").DoRaw() - if err != nil { - return false, err - } - spec := spec.Swagger{} - if err := json.Unmarshal(bs, &spec); err != nil { - return false, err - } - if spec.SwaggerProps.Paths == nil { - lastMsg = "spec.SwaggerProps.Paths is nil" - return false, nil - } - d, ok := spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"] - if !ok { - lastMsg = `spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"] not found` - return false, nil - } - p, ok := d.Properties["foo"] - if !ok { - lastMsg = `spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"].Properties["foo"] not found` - return false, nil - } - if !p.Type.Contains(expectedType) { - lastMsg = fmt.Sprintf(`spec.SwaggerProps.Definitions["cr.bar.com.v1.Foo"].Properties["foo"].Type should be %q, but got: %q`, expectedType, p.Type) - return false, nil - } - return true, nil - }); err != nil { - t.Fatalf("Failed to see %s OpenAPI spec in discovery: %v, last message: %s", crd.Name, err, lastMsg) - } - } - waitForSpec("string") - crd, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(crd.Name, metav1.GetOptions{}) - if err != nil { - t.Fatal(err) - } - prop := crd.Spec.Validation.OpenAPIV3Schema.Properties["foo"] - prop.Type = "boolean" - crd.Spec.Validation.OpenAPIV3Schema.Properties["foo"] = prop - if _, err = apiextensionsclient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(crd); err != nil { - t.Fatal(err) - } - waitForSpec("boolean") -} - type Foo struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`