From d6c480122f36beeb8ce85b2c8cbaa1eb6265684d Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 7 Jun 2019 00:36:12 -0400 Subject: [PATCH] apiextensions: unfold x-kubernetes-embedded-resource in publishing Co-authored-by: Haowei Cai --- .../pkg/controller/openapi/BUILD | 1 + .../pkg/controller/openapi/builder.go | 42 +++++++++++++++ .../pkg/controller/openapi/builder_test.go | 51 +++++++++++++++++-- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/BUILD b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/BUILD index c534778ae01..ef711c98ec8 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/BUILD +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/BUILD @@ -24,6 +24,7 @@ go_library( "//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library", diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder.go index ee31526cda5..a239d726f5e 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder.go @@ -30,6 +30,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apiserver/pkg/endpoints" "k8s.io/apiserver/pkg/endpoints/openapi" openapibuilder "k8s.io/kube-openapi/pkg/builder" @@ -310,6 +311,7 @@ func (b *builder) buildKubeNative(schema *structuralschema.Structural, v2 bool) ret.SetProperty("metadata", *spec.RefSchema(objectMetaSchemaRef). WithDescription(swaggerPartialObjectMetadataDescriptions["metadata"])) addTypeMetaProperties(ret) + addEmbeddedProperties(ret) } ret.AddExtension(endpoints.ROUTE_META_GVK, []interface{}{ map[string]interface{}{ @@ -322,6 +324,42 @@ func (b *builder) buildKubeNative(schema *structuralschema.Structural, v2 bool) return ret } +func addEmbeddedProperties(s *spec.Schema) { + if s == nil { + return + } + + for k := range s.Properties { + v := s.Properties[k] + addEmbeddedProperties(&v) + s.Properties[k] = v + } + if s.Items != nil { + addEmbeddedProperties(s.Items.Schema) + } + if s.AdditionalProperties != nil { + addEmbeddedProperties(s.AdditionalProperties.Schema) + } + + if isTrue, ok := s.VendorExtensible.Extensions.GetBool("x-kubernetes-embedded-resource"); ok && isTrue { + s.SetProperty("apiVersion", withDescription(getDefinition(typeMetaType).SchemaProps.Properties["apiVersion"], + "apiVersion defines the versioned schema of this representation of an object. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + )) + s.SetProperty("kind", withDescription(getDefinition(typeMetaType).SchemaProps.Properties["kind"], + "kind is a string value representing the type of this object. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + )) + s.SetProperty("metadata", *spec.RefSchema(objectMetaSchemaRef).WithDescription(swaggerPartialObjectMetadataDescriptions["metadata"])) + + req := sets.NewString(s.Required...) + if !req.Has("kind") { + s.Required = append(s.Required, "kind") + } + if !req.Has("apiVersion") { + s.Required = append(s.Required, "apiVersion") + } + } +} + // getDefinition gets definition for given Kubernetes type. This function is extracted from // kube-openapi builder logic func getDefinition(name string) spec.Schema { @@ -329,6 +367,10 @@ func getDefinition(name string) spec.Schema { return definitions[name].Schema } +func withDescription(s spec.Schema, desc string) spec.Schema { + return *s.WithDescription(desc) +} + func buildDefinitionsFunc() { namer = openapi.NewDefinitionNamer(runtime.NewScheme()) definitions = generatedopenapi.GetOpenAPIDefinitions(func(name string) spec.Ref { diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder_test.go index 3b175cf282e..5c8da1bfde3 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/openapi/builder_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/go-openapi/spec" + "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" @@ -154,7 +155,22 @@ func TestNewBuilder(t *testing.T) { "embedded-object": { "x-kubernetes-embedded-resource": true, "x-kubernetes-preserve-unknown-fields": true, - "type": "object" + "type": "object", + "required":["kind","apiVersion"], + "properties":{ + "apiVersion":{ + "description":"apiVersion defines the versioned schema of this representation of an object. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type":"string" + }, + "kind":{ + "description":"kind is a string value representing the type of this object. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type":"string" + }, + "metadata":{ + "description":"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + } } }, "x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}] @@ -305,7 +321,22 @@ func TestNewBuilder(t *testing.T) { "embedded-object": { "x-kubernetes-embedded-resource": true, "x-kubernetes-preserve-unknown-fields": true, - "type": "object" + "type": "object", + "required":["kind","apiVersion"], + "properties":{ + "apiVersion":{ + "description":"apiVersion defines the versioned schema of this representation of an object. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", + "type":"string" + }, + "kind":{ + "description":"kind is a string value representing the type of this object. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds", + "type":"string" + }, + "metadata":{ + "description":"Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata", + "$ref":"#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" + } + } } }, "x-kubernetes-group-version-kind":[{"group":"bar.k8s.io","kind":"Foo","version":"v1"}] @@ -373,7 +404,7 @@ func TestNewBuilder(t *testing.T) { } if !reflect.DeepEqual(&wantedSchema, got.schema) { - t.Errorf("unexpected schema: %s\nwant = %#v\ngot = %#v", diff.ObjectDiff(&wantedSchema, got.schema), &wantedSchema, got.schema) + t.Errorf("unexpected schema: %s\nwant = %#v\ngot = %#v", schemaDiff(&wantedSchema, got.schema), &wantedSchema, got.schema) } gotListProperties := properties(got.listSchema.Properties) @@ -383,7 +414,7 @@ func TestNewBuilder(t *testing.T) { gotListSchema := got.listSchema.Properties["items"].Items.Schema if !reflect.DeepEqual(&wantedItemsSchema, gotListSchema) { - t.Errorf("unexpected list schema: %s (want/got)", diff.ObjectDiff(&wantedItemsSchema, &gotListSchema)) + t.Errorf("unexpected list schema: %s (want/got)", schemaDiff(&wantedItemsSchema, gotListSchema)) } }) } @@ -396,3 +427,15 @@ func properties(p map[string]spec.Schema) sets.String { } return ret } + +func schemaDiff(a, b *spec.Schema) string { + as, err := json.Marshal(a) + if err != nil { + panic(err) + } + bs, err := json.Marshal(b) + if err != nil { + panic(err) + } + return diff.StringDiff(string(as), string(bs)) +}