Merge pull request #40124 from mbohlool/separation

Automatic merge from submit-queue

Use full package path for definition name in OpenAPI spec

We were using short package name (last part of package name) plus type name for OpenAPI spec definition name. That can result in duplicate names and make the spec invalid. To be sure we will always have unique names, we are going to use full package name as definition name. Also "x-kubernetes-tag" custom field is added to definitions to list Group/Version/Kind for the definitions that has it. This will help clients to discover definitions easier.
Lastly, we've added a reference from old definition names to the new ones to keep backward compatibilities. The list of old definitions will not be updated.

**Release note**:
- Rename OpenAPI definition names to type's full package names to prevent duplicates
- Create OpenAPI extension "x-kubernetes-group-version-kind" for definitions to store Group/Version/Kind
- Deprecate old definition names and create a reference to the new definitions. Old definitions will be removed in the next release.
pull/6/head
Kubernetes Submit Queue 2017-02-01 12:06:39 -08:00 committed by GitHub
commit c523476d6f
17 changed files with 21529 additions and 20101 deletions

File diff suppressed because it is too large Load Diff

View File

@ -295,7 +295,7 @@ func Run(s *options.ServerRunOptions) error {
genericConfig.Authenticator = apiAuthenticator genericConfig.Authenticator = apiAuthenticator
genericConfig.Authorizer = apiAuthorizer genericConfig.Authorizer = apiAuthorizer
genericConfig.AdmissionControl = admissionController genericConfig.AdmissionControl = admissionController
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.OpenAPIDefinitions) genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions)
genericConfig.OpenAPIConfig.SecurityDefinitions = securityDefinitions genericConfig.OpenAPIConfig.SecurityDefinitions = securityDefinitions
genericConfig.OpenAPIConfig.Info.Title = "Kubernetes" genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
genericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig() genericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()

View File

@ -133,7 +133,7 @@ const (
// openApiGen produces a file with auto-generated OpenAPI functions. // openApiGen produces a file with auto-generated OpenAPI functions.
type openAPIGen struct { type openAPIGen struct {
generator.DefaultGen generator.DefaultGen
// TargetPackage is the package that will get OpenAPIDefinitions variable contains all open API definitions. // TargetPackage is the package that will get GetOpenAPIDefinitions function returns all open API definitions.
targetPackage *types.Package targetPackage *types.Package
imports namer.ImportTracker imports namer.ImportTracker
context *generator.Context context *generator.Context
@ -186,7 +186,7 @@ func (g *openAPIGen) Imports(c *generator.Context) []string {
func argsFromType(t *types.Type) generator.Args { func argsFromType(t *types.Type) generator.Args {
return generator.Args{ return generator.Args{
"type": t, "type": t,
"OpenAPIDefinitions": types.Ref(openAPICommonPackagePath, "OpenAPIDefinitions"), "ReferenceCallback": types.Ref(openAPICommonPackagePath, "ReferenceCallback"),
"OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"), "OpenAPIDefinition": types.Ref(openAPICommonPackagePath, "OpenAPIDefinition"),
"SpecSchemaType": types.Ref(specPackagePath, "Schema"), "SpecSchemaType": types.Ref(specPackagePath, "Schema"),
} }
@ -194,14 +194,15 @@ func argsFromType(t *types.Type) generator.Args {
func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error { func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("var OpenAPIDefinitions *$.OpenAPIDefinitions|raw$ = ", argsFromType(nil)) sw.Do("func GetOpenAPIDefinitions(ref $.ReferenceCallback|raw$) map[string]$.OpenAPIDefinition|raw$ {\n", argsFromType(nil))
sw.Do("&$.OpenAPIDefinitions|raw${\n", argsFromType(nil)) sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
return sw.Error() return sw.Error()
} }
func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error { func (g *openAPIGen) Finalize(c *generator.Context, w io.Writer) error {
sw := generator.NewSnippetWriter(w, c, "$", "$") sw := generator.NewSnippetWriter(w, c, "$", "$")
sw.Do("}\n", nil) sw.Do("}\n", nil)
sw.Do("}\n", nil)
return sw.Error() return sw.Error()
} }
@ -308,9 +309,9 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
switch t.Kind { switch t.Kind {
case types.Struct: case types.Struct:
args := argsFromType(t) args := argsFromType(t)
g.Do("\"$.$\": ", typeShortName(t)) g.Do("\"$.$\": ", t.Name)
if hasOpenAPIDefinitionMethod(t) { if hasOpenAPIDefinitionMethod(t) {
g.Do("$.type|raw${}.OpenAPIDefinition(),", args) g.Do("$.type|raw${}.OpenAPIDefinition(),\n", args)
return nil return nil
} }
g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) g.Do("{\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil)
@ -437,14 +438,8 @@ func (g openAPITypeWriter) generateSimpleProperty(typeString, format string) {
} }
func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) { func (g openAPITypeWriter) generateReferenceProperty(t *types.Type) {
var name string g.refTypes[t.Name.String()] = t
if t.Name.Package == "" { g.Do("Ref: ref(\"$.$\"),\n", t.Name.String())
name = t.Name.Name
} else {
name = filepath.Base(t.Name.Package) + "." + t.Name.Name
}
g.refTypes[name] = t
g.Do("Ref: spec.MustCreateRef(\"#/definitions/$.$\"),\n", name)
} }
func resolveAliasAndPtrType(t *types.Type) *types.Type { func resolveAliasAndPtrType(t *types.Type) *types.Type {

View File

@ -112,7 +112,7 @@ type Blah struct {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(`"foo.Blah": { assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{ Schema: spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "Blah is a test.", Description: "Blah is a test.",
@ -280,7 +280,7 @@ type Blah struct {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assert.Equal(`"foo.Blah": { assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{ Schema: spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "PointerSample demonstrate pointer's properties", Description: "PointerSample demonstrate pointer's properties",
@ -295,7 +295,7 @@ Format: "",
"StructPointer": { "StructPointer": {
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Description: "A struct pointer", Description: "A struct pointer",
Ref: spec.MustCreateRef("#/definitions/foo.Blah"), Ref: ref("base/foo.Blah"),
}, },
}, },
"SlicePointer": { "SlicePointer": {
@ -331,7 +331,7 @@ Required: []string{"StringPointer","StructPointer","SlicePointer","MapPointer"},
}, },
}, },
Dependencies: []string{ Dependencies: []string{
"foo.Blah",}, "base/foo.Blah",},
}, },
`, buffer.String()) `, buffer.String())
} }

File diff suppressed because it is too large Load Diff

View File

@ -175,7 +175,7 @@ func Run(s *options.ServerRunOptions) error {
genericConfig.Authenticator = apiAuthenticator genericConfig.Authenticator = apiAuthenticator
genericConfig.Authorizer = apiAuthorizer genericConfig.Authorizer = apiAuthorizer
genericConfig.AdmissionControl = admissionController genericConfig.AdmissionControl = admissionController
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.OpenAPIDefinitions) genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions)
genericConfig.OpenAPIConfig.SecurityDefinitions = securityDefinitions genericConfig.OpenAPIConfig.SecurityDefinitions = securityDefinitions
genericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig() genericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()
genericConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck( genericConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck(

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,7 @@ licenses(["notice"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
) )
go_library( go_library(
@ -13,6 +14,11 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor:github.com/emicklei/go-restful", "//vendor:github.com/emicklei/go-restful",
"//vendor:github.com/go-openapi/spec",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/openapi",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apiserver/pkg/util/trie", "//vendor:k8s.io/apiserver/pkg/util/trie",
], ],
) )
@ -29,3 +35,16 @@ filegroup(
srcs = [":package-srcs"], srcs = [":package-srcs"],
tags = ["automanaged"], tags = ["automanaged"],
) )
go_test(
name = "go_default_test",
srcs = ["openapi_test.go"],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//vendor:github.com/go-openapi/spec",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
],
)

View File

@ -19,16 +19,27 @@ package openapi
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"reflect"
"strings" "strings"
"unicode" "unicode"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/util/trie" "k8s.io/apiserver/pkg/util/trie"
"sort"
) )
var verbs = trie.New([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"}) var verbs = trie.New([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"})
const (
extensionGVK = "x-kubernetes-group-version-kind"
)
// ToValidOperationID makes an string a valid op ID (e.g. removing punctuations and whitespaces and make it camel case) // ToValidOperationID makes an string a valid op ID (e.g. removing punctuations and whitespaces and make it camel case)
func ToValidOperationID(s string, capitalizeFirstLetter bool) string { func ToValidOperationID(s string, capitalizeFirstLetter bool) string {
var buffer bytes.Buffer var buffer bytes.Buffer
@ -85,3 +96,382 @@ func GetOperationIDAndTags(servePath string, r *restful.Route) (string, []string
return op, tags, nil return op, tags, nil
} }
} }
type groupVersionKinds []v1.GroupVersionKind
func (s groupVersionKinds) Len() int {
return len(s)
}
func (s groupVersionKinds) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s groupVersionKinds) Less(i, j int) bool {
if s[i].Group == s[j].Group {
if s[i].Version == s[j].Version {
return s[i].Kind < s[j].Kind
}
return s[i].Version < s[j].Version
}
return s[i].Group < s[j].Group
}
// DefinitionNamer is the type to customize OpenAPI definition name.
type DefinitionNamer struct {
typeGroupVersionKinds map[string]groupVersionKinds
}
func gvkConvert(gvk schema.GroupVersionKind) v1.GroupVersionKind {
return v1.GroupVersionKind{
Group: gvk.Group,
Version: gvk.Version,
Kind: gvk.Kind,
}
}
func friendlyName(name string) string {
nameParts := strings.Split(name, "/")
// Reverse first part. e.g., io.k8s... instead of k8s.io...
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
parts := strings.Split(nameParts[0], ".")
for i, j := 0, len(parts)-1; i < j; i, j = i+1, j-1 {
parts[i], parts[j] = parts[j], parts[i]
}
nameParts[0] = strings.Join(parts, ".")
}
return strings.Join(nameParts, ".")
}
func typeName(t reflect.Type) string {
return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
}
// NewDefinitionNamer constructs a new DefinitionNamer to be used to customize OpenAPI spec.
func NewDefinitionNamer(s *runtime.Scheme) DefinitionNamer {
ret := DefinitionNamer{
typeGroupVersionKinds: map[string]groupVersionKinds{},
}
for gvk, rtype := range s.AllKnownTypes() {
ret.typeGroupVersionKinds[typeName(rtype)] = append(ret.typeGroupVersionKinds[typeName(rtype)], gvkConvert(gvk))
}
for _, gvk := range ret.typeGroupVersionKinds {
sort.Sort(gvk)
}
return ret
}
// GetDefinitionName returns the name and tags for a given definition
func (d *DefinitionNamer) GetDefinitionName(servePath string, name string) (string, spec.Extensions) {
if groupVersionKinds, ok := d.typeGroupVersionKinds[name]; ok {
return friendlyName(name), spec.Extensions{
extensionGVK: []v1.GroupVersionKind(groupVersionKinds),
}
}
return friendlyName(name), nil
}
// PostProcessSpec finalize OpenAPI spec and add removed definition for backward compatibility
func PostProcessSpec(s *spec.Swagger) (*spec.Swagger, error) {
compatibilityMap := map[string]string{
"v1beta1.DeploymentStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentStatus",
"v1beta1.ReplicaSetList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ReplicaSetList",
"v1beta1.Eviction": "k8s.io/kubernetes/pkg/apis/policy/v1beta1.Eviction",
"v1beta1.StatefulSetList": "k8s.io/kubernetes/pkg/apis/apps/v1beta1.StatefulSetList",
"v1beta1.RoleBinding": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.RoleBinding",
"v1beta1.PodSecurityPolicyList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.PodSecurityPolicyList",
"v1.NodeSpec": "k8s.io/kubernetes/pkg/api/v1.NodeSpec",
"v1.FlockerVolumeSource": "k8s.io/kubernetes/pkg/api/v1.FlockerVolumeSource",
"v1.ContainerState": "k8s.io/kubernetes/pkg/api/v1.ContainerState",
"v1beta1.ClusterRole": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.ClusterRole",
"v1beta1.StorageClass": "k8s.io/kubernetes/pkg/apis/storage/v1beta1.StorageClass",
"v1.FlexVolumeSource": "k8s.io/kubernetes/pkg/api/v1.FlexVolumeSource",
"v1.SecretKeySelector": "k8s.io/kubernetes/pkg/api/v1.SecretKeySelector",
"v1.DeleteOptions": "k8s.io/kubernetes/pkg/api/v1.DeleteOptions",
"v1.PodStatus": "k8s.io/kubernetes/pkg/api/v1.PodStatus",
"v1.NodeStatus": "k8s.io/kubernetes/pkg/api/v1.NodeStatus",
"v1.ServiceSpec": "k8s.io/kubernetes/pkg/api/v1.ServiceSpec",
"v1.AttachedVolume": "k8s.io/kubernetes/pkg/api/v1.AttachedVolume",
"v1.PersistentVolume": "k8s.io/kubernetes/pkg/api/v1.PersistentVolume",
"v1.LimitRangeList": "k8s.io/kubernetes/pkg/api/v1.LimitRangeList",
"v1alpha1.Role": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.Role",
"v1.Affinity": "k8s.io/kubernetes/pkg/api/v1.Affinity",
"v1beta1.PodDisruptionBudget": "k8s.io/kubernetes/pkg/apis/policy/v1beta1.PodDisruptionBudget",
"v1alpha1.RoleBindingList": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.RoleBindingList",
"v1.PodAffinity": "k8s.io/kubernetes/pkg/api/v1.PodAffinity",
"v1beta1.SELinuxStrategyOptions": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.SELinuxStrategyOptions",
"v1.ResourceQuotaList": "k8s.io/kubernetes/pkg/api/v1.ResourceQuotaList",
"v1.PodList": "k8s.io/kubernetes/pkg/api/v1.PodList",
"v1.EnvVarSource": "k8s.io/kubernetes/pkg/api/v1.EnvVarSource",
"v1beta1.TokenReviewStatus": "k8s.io/kubernetes/pkg/apis/authentication/v1beta1.TokenReviewStatus",
"v1.PersistentVolumeClaimList": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeClaimList",
"v1beta1.RoleList": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.RoleList",
"v1.ListMeta": "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta",
"v1.ObjectMeta": "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta",
"v1.APIGroupList": "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroupList",
"v2alpha1.Job": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.Job",
"v1.EnvFromSource": "k8s.io/kubernetes/pkg/api/v1.EnvFromSource",
"v1beta1.IngressStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressStatus",
"v1.Service": "k8s.io/kubernetes/pkg/api/v1.Service",
"v1beta1.DaemonSetStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DaemonSetStatus",
"v1alpha1.Subject": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.Subject",
"v1.HorizontalPodAutoscaler": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.HorizontalPodAutoscaler",
"v1.StatusCause": "k8s.io/apimachinery/pkg/apis/meta/v1.StatusCause",
"v1.NodeSelectorRequirement": "k8s.io/kubernetes/pkg/api/v1.NodeSelectorRequirement",
"v1beta1.NetworkPolicyIngressRule": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicyIngressRule",
"v1beta1.ThirdPartyResource": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ThirdPartyResource",
"v1beta1.PodSecurityPolicy": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.PodSecurityPolicy",
"v1beta1.StatefulSet": "k8s.io/kubernetes/pkg/apis/apps/v1beta1.StatefulSet",
"v1.LabelSelector": "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector",
"v1.ScaleSpec": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.ScaleSpec",
"v1.DownwardAPIVolumeFile": "k8s.io/kubernetes/pkg/api/v1.DownwardAPIVolumeFile",
"v1beta1.HorizontalPodAutoscaler": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HorizontalPodAutoscaler",
"v1.AWSElasticBlockStoreVolumeSource": "k8s.io/kubernetes/pkg/api/v1.AWSElasticBlockStoreVolumeSource",
"v1.ComponentStatus": "k8s.io/kubernetes/pkg/api/v1.ComponentStatus",
"v2alpha1.JobSpec": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.JobSpec",
"v1.ContainerImage": "k8s.io/kubernetes/pkg/api/v1.ContainerImage",
"v1.ReplicationControllerStatus": "k8s.io/kubernetes/pkg/api/v1.ReplicationControllerStatus",
"v1.ResourceQuota": "k8s.io/kubernetes/pkg/api/v1.ResourceQuota",
"v1beta1.NetworkPolicyList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicyList",
"v1beta1.NonResourceAttributes": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.NonResourceAttributes",
"v1.JobCondition": "k8s.io/kubernetes/pkg/apis/batch/v1.JobCondition",
"v1.LabelSelectorRequirement": "k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelectorRequirement",
"v1beta1.Deployment": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.Deployment",
"v1.LoadBalancerIngress": "k8s.io/kubernetes/pkg/api/v1.LoadBalancerIngress",
"v1.SecretList": "k8s.io/kubernetes/pkg/api/v1.SecretList",
"v1beta1.ReplicaSetSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ReplicaSetSpec",
"v1beta1.RoleBindingList": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.RoleBindingList",
"v1.ServicePort": "k8s.io/kubernetes/pkg/api/v1.ServicePort",
"v1.Namespace": "k8s.io/kubernetes/pkg/api/v1.Namespace",
"v1beta1.NetworkPolicyPeer": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicyPeer",
"v1.ReplicationControllerList": "k8s.io/kubernetes/pkg/api/v1.ReplicationControllerList",
"v1beta1.ReplicaSetCondition": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ReplicaSetCondition",
"v1.ReplicationControllerCondition": "k8s.io/kubernetes/pkg/api/v1.ReplicationControllerCondition",
"v1.DaemonEndpoint": "k8s.io/kubernetes/pkg/api/v1.DaemonEndpoint",
"v1beta1.NetworkPolicyPort": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicyPort",
"v1.NodeSystemInfo": "k8s.io/kubernetes/pkg/api/v1.NodeSystemInfo",
"v1.LimitRangeItem": "k8s.io/kubernetes/pkg/api/v1.LimitRangeItem",
"v1.ConfigMapVolumeSource": "k8s.io/kubernetes/pkg/api/v1.ConfigMapVolumeSource",
"v1beta1.ClusterRoleList": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.ClusterRoleList",
"v1beta1.ResourceAttributes": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.ResourceAttributes",
"v1.Pod": "k8s.io/kubernetes/pkg/api/v1.Pod",
"v1.FCVolumeSource": "k8s.io/kubernetes/pkg/api/v1.FCVolumeSource",
"v1beta1.SubresourceReference": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.SubresourceReference",
"v1.ResourceQuotaStatus": "k8s.io/kubernetes/pkg/api/v1.ResourceQuotaStatus",
"v1alpha1.RoleBinding": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.RoleBinding",
"v1.PodCondition": "k8s.io/kubernetes/pkg/api/v1.PodCondition",
"v1.GroupVersionForDiscovery": "k8s.io/apimachinery/pkg/apis/meta/v1.GroupVersionForDiscovery",
"v1.NamespaceStatus": "k8s.io/kubernetes/pkg/api/v1.NamespaceStatus",
"v1.Job": "k8s.io/kubernetes/pkg/apis/batch/v1.Job",
"v1.PersistentVolumeClaimVolumeSource": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeClaimVolumeSource",
"v1.Handler": "k8s.io/kubernetes/pkg/api/v1.Handler",
"v1.ComponentStatusList": "k8s.io/kubernetes/pkg/api/v1.ComponentStatusList",
"v1.ServerAddressByClientCIDR": "k8s.io/apimachinery/pkg/apis/meta/v1.ServerAddressByClientCIDR",
"v1.PodAntiAffinity": "k8s.io/kubernetes/pkg/api/v1.PodAntiAffinity",
"v1.ISCSIVolumeSource": "k8s.io/kubernetes/pkg/api/v1.ISCSIVolumeSource",
"v1.ContainerStateRunning": "k8s.io/kubernetes/pkg/api/v1.ContainerStateRunning",
"v1.WeightedPodAffinityTerm": "k8s.io/kubernetes/pkg/api/v1.WeightedPodAffinityTerm",
"v1beta1.HostPortRange": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HostPortRange",
"v1.HorizontalPodAutoscalerSpec": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.HorizontalPodAutoscalerSpec",
"v1.HorizontalPodAutoscalerList": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.HorizontalPodAutoscalerList",
"v1beta1.RoleRef": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.RoleRef",
"v1.Probe": "k8s.io/kubernetes/pkg/api/v1.Probe",
"v1beta1.IngressTLS": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressTLS",
"v1beta1.ThirdPartyResourceList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ThirdPartyResourceList",
"v1beta1.DaemonSet": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DaemonSet",
"v1.APIGroup": "k8s.io/apimachinery/pkg/apis/meta/v1.APIGroup",
"v1beta1.Subject": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.Subject",
"v1beta1.DeploymentList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentList",
"v1.NodeAffinity": "k8s.io/kubernetes/pkg/api/v1.NodeAffinity",
"v1beta1.RollingUpdateDeployment": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.RollingUpdateDeployment",
"v1beta1.APIVersion": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.APIVersion",
"v1alpha1.CertificateSigningRequest": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1.CertificateSigningRequest",
"v1.CinderVolumeSource": "k8s.io/kubernetes/pkg/api/v1.CinderVolumeSource",
"v1.NamespaceSpec": "k8s.io/kubernetes/pkg/api/v1.NamespaceSpec",
"v1beta1.PodDisruptionBudgetSpec": "k8s.io/kubernetes/pkg/apis/policy/v1beta1.PodDisruptionBudgetSpec",
"v1.Patch": "k8s.io/apimachinery/pkg/apis/meta/v1.Patch",
"v1beta1.ClusterRoleBinding": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.ClusterRoleBinding",
"v1beta1.HorizontalPodAutoscalerSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HorizontalPodAutoscalerSpec",
"v1.PersistentVolumeClaimSpec": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeClaimSpec",
"v1.Secret": "k8s.io/kubernetes/pkg/api/v1.Secret",
"v1.NodeCondition": "k8s.io/kubernetes/pkg/api/v1.NodeCondition",
"v1.LocalObjectReference": "k8s.io/kubernetes/pkg/api/v1.LocalObjectReference",
"runtime.RawExtension": "k8s.io/apimachinery/pkg/runtime.RawExtension",
"v1.PreferredSchedulingTerm": "k8s.io/kubernetes/pkg/api/v1.PreferredSchedulingTerm",
"v1.RBDVolumeSource": "k8s.io/kubernetes/pkg/api/v1.RBDVolumeSource",
"v1.KeyToPath": "k8s.io/kubernetes/pkg/api/v1.KeyToPath",
"v1.ScaleStatus": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.ScaleStatus",
"v1alpha1.PolicyRule": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.PolicyRule",
"v1.EndpointPort": "k8s.io/kubernetes/pkg/api/v1.EndpointPort",
"v1beta1.IngressList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressList",
"v1.EndpointAddress": "k8s.io/kubernetes/pkg/api/v1.EndpointAddress",
"v1.NodeSelector": "k8s.io/kubernetes/pkg/api/v1.NodeSelector",
"v1beta1.StorageClassList": "k8s.io/kubernetes/pkg/apis/storage/v1beta1.StorageClassList",
"v1.ServiceList": "k8s.io/kubernetes/pkg/api/v1.ServiceList",
"v2alpha1.CronJobSpec": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.CronJobSpec",
"v1.ContainerStateTerminated": "k8s.io/kubernetes/pkg/api/v1.ContainerStateTerminated",
"v1beta1.TokenReview": "k8s.io/kubernetes/pkg/apis/authentication/v1beta1.TokenReview",
"v1beta1.IngressBackend": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressBackend",
"v1.Time": "k8s.io/apimachinery/pkg/apis/meta/v1.Time",
"v1beta1.IngressSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressSpec",
"v2alpha1.JobTemplateSpec": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.JobTemplateSpec",
"v1.LimitRange": "k8s.io/kubernetes/pkg/api/v1.LimitRange",
"v1beta1.UserInfo": "k8s.io/kubernetes/pkg/apis/authentication/v1beta1.UserInfo",
"v1.ResourceQuotaSpec": "k8s.io/kubernetes/pkg/api/v1.ResourceQuotaSpec",
"v1.ContainerPort": "k8s.io/kubernetes/pkg/api/v1.ContainerPort",
"v1beta1.HTTPIngressRuleValue": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HTTPIngressRuleValue",
"v1.AzureFileVolumeSource": "k8s.io/kubernetes/pkg/api/v1.AzureFileVolumeSource",
"v1beta1.NetworkPolicySpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicySpec",
"v1.PodTemplateSpec": "k8s.io/kubernetes/pkg/api/v1.PodTemplateSpec",
"v1.SecretVolumeSource": "k8s.io/kubernetes/pkg/api/v1.SecretVolumeSource",
"v1.PodSpec": "k8s.io/kubernetes/pkg/api/v1.PodSpec",
"v1.CephFSVolumeSource": "k8s.io/kubernetes/pkg/api/v1.CephFSVolumeSource",
"v1beta1.CPUTargetUtilization": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.CPUTargetUtilization",
"v1.Volume": "k8s.io/kubernetes/pkg/api/v1.Volume",
"v1beta1.Ingress": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.Ingress",
"v1beta1.HorizontalPodAutoscalerList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HorizontalPodAutoscalerList",
"v1.PersistentVolumeStatus": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeStatus",
"v1beta1.IDRange": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IDRange",
"v2alpha1.JobCondition": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.JobCondition",
"v1beta1.IngressRule": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.IngressRule",
"v1alpha1.RoleRef": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.RoleRef",
"v1.PodAffinityTerm": "k8s.io/kubernetes/pkg/api/v1.PodAffinityTerm",
"v1.ObjectReference": "k8s.io/kubernetes/pkg/api/v1.ObjectReference",
"v1.ServiceStatus": "k8s.io/kubernetes/pkg/api/v1.ServiceStatus",
"v1.APIResource": "k8s.io/apimachinery/pkg/apis/meta/v1.APIResource",
"v1beta1.Scale": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.Scale",
"v1.AzureDiskVolumeSource": "k8s.io/kubernetes/pkg/api/v1.AzureDiskVolumeSource",
"v1beta1.SubjectAccessReviewStatus": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.SubjectAccessReviewStatus",
"v1.ConfigMap": "k8s.io/kubernetes/pkg/api/v1.ConfigMap",
"v1.CrossVersionObjectReference": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.CrossVersionObjectReference",
"v1.APIVersions": "k8s.io/apimachinery/pkg/apis/meta/v1.APIVersions",
"v1alpha1.ClusterRoleList": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.ClusterRoleList",
"v1.Node": "k8s.io/kubernetes/pkg/api/v1.Node",
"resource.Quantity": "k8s.io/kubernetes/pkg/api/resource.Quantity",
"v1.Event": "k8s.io/kubernetes/pkg/api/v1.Event",
"v1.JobStatus": "k8s.io/kubernetes/pkg/apis/batch/v1.JobStatus",
"v1.PersistentVolumeSpec": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeSpec",
"v1beta1.SubjectAccessReviewSpec": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.SubjectAccessReviewSpec",
"v1.ResourceFieldSelector": "k8s.io/kubernetes/pkg/api/v1.ResourceFieldSelector",
"v1.EndpointSubset": "k8s.io/kubernetes/pkg/api/v1.EndpointSubset",
"v1alpha1.CertificateSigningRequestSpec": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1.CertificateSigningRequestSpec",
"v1.HostPathVolumeSource": "k8s.io/kubernetes/pkg/api/v1.HostPathVolumeSource",
"v1.LoadBalancerStatus": "k8s.io/kubernetes/pkg/api/v1.LoadBalancerStatus",
"v1beta1.HTTPIngressPath": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HTTPIngressPath",
"v1beta1.Role": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.Role",
"v1beta1.DeploymentStrategy": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentStrategy",
"v1beta1.RunAsUserStrategyOptions": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.RunAsUserStrategyOptions",
"v1beta1.DeploymentSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentSpec",
"v1.ExecAction": "k8s.io/kubernetes/pkg/api/v1.ExecAction",
"v1beta1.PodSecurityPolicySpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.PodSecurityPolicySpec",
"v1.HorizontalPodAutoscalerStatus": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.HorizontalPodAutoscalerStatus",
"v1.PersistentVolumeList": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeList",
"v1alpha1.ClusterRole": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.ClusterRole",
"v1.JobSpec": "k8s.io/kubernetes/pkg/apis/batch/v1.JobSpec",
"v1beta1.DaemonSetSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DaemonSetSpec",
"v2alpha1.CronJobList": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.CronJobList",
"v1.Endpoints": "k8s.io/kubernetes/pkg/api/v1.Endpoints",
"v1.SELinuxOptions": "k8s.io/kubernetes/pkg/api/v1.SELinuxOptions",
"v1beta1.SelfSubjectAccessReviewSpec": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.SelfSubjectAccessReviewSpec",
"v1beta1.ScaleStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ScaleStatus",
"v1.NodeSelectorTerm": "k8s.io/kubernetes/pkg/api/v1.NodeSelectorTerm",
"v1alpha1.CertificateSigningRequestStatus": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1.CertificateSigningRequestStatus",
"v1.StatusDetails": "k8s.io/apimachinery/pkg/apis/meta/v1.StatusDetails",
"v2alpha1.JobStatus": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.JobStatus",
"v1beta1.DeploymentRollback": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentRollback",
"v1.GlusterfsVolumeSource": "k8s.io/kubernetes/pkg/api/v1.GlusterfsVolumeSource",
"v1.ServiceAccountList": "k8s.io/kubernetes/pkg/api/v1.ServiceAccountList",
"v1.JobList": "k8s.io/kubernetes/pkg/apis/batch/v1.JobList",
"v1.EventList": "k8s.io/kubernetes/pkg/api/v1.EventList",
"v1.ContainerStateWaiting": "k8s.io/kubernetes/pkg/api/v1.ContainerStateWaiting",
"v1.APIResourceList": "k8s.io/apimachinery/pkg/apis/meta/v1.APIResourceList",
"v1.ContainerStatus": "k8s.io/kubernetes/pkg/api/v1.ContainerStatus",
"v2alpha1.JobList": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.JobList",
"v1.ConfigMapKeySelector": "k8s.io/kubernetes/pkg/api/v1.ConfigMapKeySelector",
"v1.PhotonPersistentDiskVolumeSource": "k8s.io/kubernetes/pkg/api/v1.PhotonPersistentDiskVolumeSource",
"v1.PodTemplateList": "k8s.io/kubernetes/pkg/api/v1.PodTemplateList",
"v1.PersistentVolumeClaimStatus": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeClaimStatus",
"v1.ServiceAccount": "k8s.io/kubernetes/pkg/api/v1.ServiceAccount",
"v1alpha1.CertificateSigningRequestList": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1.CertificateSigningRequestList",
"v1beta1.SupplementalGroupsStrategyOptions": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.SupplementalGroupsStrategyOptions",
"v1.HTTPHeader": "k8s.io/kubernetes/pkg/api/v1.HTTPHeader",
"version.Info": "k8s.io/apimachinery/pkg/version.Info",
"v1.EventSource": "k8s.io/kubernetes/pkg/api/v1.EventSource",
"v1alpha1.ClusterRoleBindingList": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.ClusterRoleBindingList",
"v1.OwnerReference": "k8s.io/apimachinery/pkg/apis/meta/v1.OwnerReference",
"v1beta1.ClusterRoleBindingList": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.ClusterRoleBindingList",
"v1beta1.ScaleSpec": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ScaleSpec",
"v1.GitRepoVolumeSource": "k8s.io/kubernetes/pkg/api/v1.GitRepoVolumeSource",
"v1beta1.NetworkPolicy": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.NetworkPolicy",
"v1.ConfigMapEnvSource": "k8s.io/kubernetes/pkg/api/v1.ConfigMapEnvSource",
"v1.PodTemplate": "k8s.io/kubernetes/pkg/api/v1.PodTemplate",
"v1beta1.DeploymentCondition": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DeploymentCondition",
"v1beta1.PodDisruptionBudgetStatus": "k8s.io/kubernetes/pkg/apis/policy/v1beta1.PodDisruptionBudgetStatus",
"v1.EnvVar": "k8s.io/kubernetes/pkg/api/v1.EnvVar",
"v1.LimitRangeSpec": "k8s.io/kubernetes/pkg/api/v1.LimitRangeSpec",
"v1.DownwardAPIVolumeSource": "k8s.io/kubernetes/pkg/api/v1.DownwardAPIVolumeSource",
"v1.NodeDaemonEndpoints": "k8s.io/kubernetes/pkg/api/v1.NodeDaemonEndpoints",
"v1.ComponentCondition": "k8s.io/kubernetes/pkg/api/v1.ComponentCondition",
"v1alpha1.CertificateSigningRequestCondition": "k8s.io/kubernetes/pkg/apis/certificates/v1alpha1.CertificateSigningRequestCondition",
"v1.SecurityContext": "k8s.io/kubernetes/pkg/api/v1.SecurityContext",
"v1beta1.LocalSubjectAccessReview": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.LocalSubjectAccessReview",
"v1beta1.StatefulSetSpec": "k8s.io/kubernetes/pkg/apis/apps/v1beta1.StatefulSetSpec",
"v1.NodeAddress": "k8s.io/kubernetes/pkg/api/v1.NodeAddress",
"v1.QuobyteVolumeSource": "k8s.io/kubernetes/pkg/api/v1.QuobyteVolumeSource",
"v1.Capabilities": "k8s.io/kubernetes/pkg/api/v1.Capabilities",
"v1.GCEPersistentDiskVolumeSource": "k8s.io/kubernetes/pkg/api/v1.GCEPersistentDiskVolumeSource",
"v1beta1.ReplicaSet": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ReplicaSet",
"v1beta1.HorizontalPodAutoscalerStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.HorizontalPodAutoscalerStatus",
"v1beta1.PolicyRule": "k8s.io/kubernetes/pkg/apis/rbac/v1beta1.PolicyRule",
"v1.ConfigMapList": "k8s.io/kubernetes/pkg/api/v1.ConfigMapList",
"v1.Lifecycle": "k8s.io/kubernetes/pkg/api/v1.Lifecycle",
"v1beta1.SelfSubjectAccessReview": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.SelfSubjectAccessReview",
"v2alpha1.CronJob": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.CronJob",
"v2alpha1.CronJobStatus": "k8s.io/kubernetes/pkg/apis/batch/v2alpha1.CronJobStatus",
"v1beta1.SubjectAccessReview": "k8s.io/kubernetes/pkg/apis/authorization/v1beta1.SubjectAccessReview",
"v1.Preconditions": "k8s.io/kubernetes/pkg/api/v1.Preconditions",
"v1beta1.DaemonSetList": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.DaemonSetList",
"v1.PersistentVolumeClaim": "k8s.io/kubernetes/pkg/api/v1.PersistentVolumeClaim",
"v1.Scale": "k8s.io/kubernetes/pkg/apis/autoscaling/v1.Scale",
"v1beta1.StatefulSetStatus": "k8s.io/kubernetes/pkg/apis/apps/v1beta1.StatefulSetStatus",
"v1.NFSVolumeSource": "k8s.io/kubernetes/pkg/api/v1.NFSVolumeSource",
"v1.ObjectFieldSelector": "k8s.io/kubernetes/pkg/api/v1.ObjectFieldSelector",
"v1.ResourceRequirements": "k8s.io/kubernetes/pkg/api/v1.ResourceRequirements",
"v1.WatchEvent": "k8s.io/apimachinery/pkg/apis/meta/v1.WatchEvent",
"v1.ReplicationControllerSpec": "k8s.io/kubernetes/pkg/api/v1.ReplicationControllerSpec",
"v1.HTTPGetAction": "k8s.io/kubernetes/pkg/api/v1.HTTPGetAction",
"v1beta1.RollbackConfig": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.RollbackConfig",
"v1beta1.TokenReviewSpec": "k8s.io/kubernetes/pkg/apis/authentication/v1beta1.TokenReviewSpec",
"v1.PodSecurityContext": "k8s.io/kubernetes/pkg/api/v1.PodSecurityContext",
"v1beta1.PodDisruptionBudgetList": "k8s.io/kubernetes/pkg/apis/policy/v1beta1.PodDisruptionBudgetList",
"v1.VolumeMount": "k8s.io/kubernetes/pkg/api/v1.VolumeMount",
"v1.ReplicationController": "k8s.io/kubernetes/pkg/api/v1.ReplicationController",
"v1.NamespaceList": "k8s.io/kubernetes/pkg/api/v1.NamespaceList",
"v1alpha1.ClusterRoleBinding": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.ClusterRoleBinding",
"v1.TCPSocketAction": "k8s.io/kubernetes/pkg/api/v1.TCPSocketAction",
"v1.Binding": "k8s.io/kubernetes/pkg/api/v1.Binding",
"v1beta1.ReplicaSetStatus": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.ReplicaSetStatus",
"intstr.IntOrString": "k8s.io/kubernetes/pkg/util/intstr.IntOrString",
"v1.EndpointsList": "k8s.io/kubernetes/pkg/api/v1.EndpointsList",
"v1.Container": "k8s.io/kubernetes/pkg/api/v1.Container",
"v1alpha1.RoleList": "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1.RoleList",
"v1.VsphereVirtualDiskVolumeSource": "k8s.io/kubernetes/pkg/api/v1.VsphereVirtualDiskVolumeSource",
"v1.NodeList": "k8s.io/kubernetes/pkg/api/v1.NodeList",
"v1.EmptyDirVolumeSource": "k8s.io/kubernetes/pkg/api/v1.EmptyDirVolumeSource",
"v1beta1.FSGroupStrategyOptions": "k8s.io/kubernetes/pkg/apis/extensions/v1beta1.FSGroupStrategyOptions",
"v1.Status": "k8s.io/apimachinery/pkg/apis/meta/v1.Status",
}
for k, v := range compatibilityMap {
if _, found := s.Definitions[v]; !found {
continue
}
s.Definitions[k] = spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef("#/definitions/" + openapi.EscapeJsonPointer(v)),
Description: fmt.Sprintf("Deprecated. Please use %s instead.", v),
},
}
}
return s, nil
}

View File

@ -0,0 +1,86 @@
/*
Copyright 2016 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 (
"reflect"
"strings"
"testing"
"github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type TestType struct {
}
func (t TestType) GetObjectKind() schema.ObjectKind {
return t
}
func (t TestType) SetGroupVersionKind(kind schema.GroupVersionKind) {
}
func (t TestType) GroupVersionKind() schema.GroupVersionKind {
return schema.GroupVersionKind{
Group: "test",
Version: "v1",
Kind: "TestType",
}
}
func assertEqual(t *testing.T, expected, actual interface{}) {
var equal bool
if expected == nil || actual == nil {
equal = expected == actual
} else {
equal = reflect.DeepEqual(expected, actual)
}
if !equal {
t.Errorf("%v != %v", expected, actual)
}
}
func TestGetDefinitionName(t *testing.T) {
testType := TestType{}
typePkgName := "k8s.io/kubernetes/pkg/genericapiserver/endpoints/openapi.TestType"
typeFriendlyName := "io.k8s.kubernetes.pkg.genericapiserver.endpoints.openapi.TestType"
if strings.HasSuffix(reflect.TypeOf(testType).PkgPath(), "go_default_test") {
// the test is running inside bazel where the package name is changed and
// "go_default_test" will add to package path.
typePkgName = "k8s.io/kubernetes/pkg/genericapiserver/endpoints/openapi/go_default_test.TestType"
typeFriendlyName = "io.k8s.kubernetes.pkg.genericapiserver.endpoints.openapi.go_default_test.TestType"
}
s := runtime.NewScheme()
s.AddKnownTypeWithName(testType.GroupVersionKind(), &testType)
namer := NewDefinitionNamer(s)
n, e := namer.GetDefinitionName("", typePkgName)
assertEqual(t, typeFriendlyName, n)
assertEqual(t, e["x-kubernetes-group-version-kind"], []v1.GroupVersionKind{
{
Group: "test",
Version: "v1",
Kind: "TestType",
},
})
n, e2 := namer.GetDefinitionName("", "test.com/another.Type")
assertEqual(t, "com.test.another.Type", n)
assertEqual(t, e2, spec.Extensions(nil))
}

View File

@ -213,7 +213,8 @@ func NewConfig() *Config {
return config.ApplyOptions(defaultOptions) return config.ApplyOptions(defaultOptions)
} }
func DefaultOpenAPIConfig(definitions *openapicommon.OpenAPIDefinitions) *openapicommon.Config { func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions) *openapicommon.Config {
defNamer := apiopenapi.NewDefinitionNamer(api.Scheme)
return &openapicommon.Config{ return &openapicommon.Config{
ProtocolList: []string{"https"}, ProtocolList: []string{"https"},
IgnorePrefixes: []string{"/swaggerapi"}, IgnorePrefixes: []string{"/swaggerapi"},
@ -228,7 +229,9 @@ func DefaultOpenAPIConfig(definitions *openapicommon.OpenAPIDefinitions) *openap
}, },
}, },
GetOperationIDAndTags: apiopenapi.GetOperationIDAndTags, GetOperationIDAndTags: apiopenapi.GetOperationIDAndTags,
Definitions: definitions, GetDefinitionName: defNamer.GetDefinitionName,
GetDefinitions: getDefinitions,
PostProcessSpec: apiopenapi.PostProcessSpec,
} }
} }

View File

@ -82,7 +82,7 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
config.RequestContextMapper = genericapirequest.NewRequestContextMapper() config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
config.LegacyAPIGroupPrefixes = sets.NewString("/api") config.LegacyAPIGroupPrefixes = sets.NewString("/api")
config.OpenAPIConfig = DefaultOpenAPIConfig(openapigen.OpenAPIDefinitions) config.OpenAPIConfig = DefaultOpenAPIConfig(openapigen.GetOpenAPIDefinitions)
config.OpenAPIConfig.Info = &spec.Info{ config.OpenAPIConfig.Info = &spec.Info{
InfoProps: spec.InfoProps{ InfoProps: spec.InfoProps{
Title: "Kubernetes", Title: "Kubernetes",

View File

@ -40,6 +40,7 @@ type openAPI struct {
swagger *spec.Swagger swagger *spec.Swagger
protocolList []string protocolList []string
servePath string servePath string
definitions map[string]openapi.OpenAPIDefinition
} }
// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification. // RegisterOpenAPIService registers a handler to provides standard OpenAPI specification.
@ -79,6 +80,15 @@ func (o *openAPI) init(webServices []*restful.WebService) error {
return r.Operation, nil, nil return r.Operation, nil, nil
} }
} }
if o.config.GetDefinitionName == nil {
o.config.GetDefinitionName = func(_, name string) (string, spec.Extensions) {
return name[strings.LastIndex(name, "/")+1:], nil
}
}
o.definitions = o.config.GetDefinitions(func(name string) spec.Ref {
defName, _ := o.config.GetDefinitionName(o.servePath, name)
return spec.MustCreateRef("#/definitions/" + openapi.EscapeJsonPointer(defName))
})
if o.config.CommonResponses == nil { if o.config.CommonResponses == nil {
o.config.CommonResponses = map[int]spec.Response{} o.config.CommonResponses = map[int]spec.Response{}
} }
@ -90,15 +100,43 @@ func (o *openAPI) init(webServices []*restful.WebService) error {
o.swagger.SecurityDefinitions = *o.config.SecurityDefinitions o.swagger.SecurityDefinitions = *o.config.SecurityDefinitions
o.swagger.Security = o.config.DefaultSecurity o.swagger.Security = o.config.DefaultSecurity
} }
if o.config.PostProcessSpec != nil {
o.swagger, err = o.config.PostProcessSpec(o.swagger)
if err != nil {
return err
}
}
return nil return nil
} }
func getCanonicalizeTypeName(t reflect.Type) string {
if t.PkgPath() == "" {
return t.Name()
}
path := t.PkgPath()
if strings.Contains(path, "/vendor/") {
path = path[strings.Index(path, "/vendor/")+len("/vendor/"):]
}
return path + "." + t.Name()
}
func (o *openAPI) buildDefinitionRecursively(name string) error { func (o *openAPI) buildDefinitionRecursively(name string) error {
if _, ok := o.swagger.Definitions[name]; ok { uniqueName, extensions := o.config.GetDefinitionName(o.servePath, name)
if _, ok := o.swagger.Definitions[uniqueName]; ok {
return nil return nil
} }
if item, ok := (*o.config.Definitions)[name]; ok { if item, ok := o.definitions[name]; ok {
o.swagger.Definitions[name] = item.Schema schema := spec.Schema{
SchemaProps: item.Schema.SchemaProps,
SwaggerSchemaProps: item.Schema.SwaggerSchemaProps,
}
if extensions != nil {
schema.Extensions = spec.Extensions{}
for k, v := range extensions {
schema.Extensions[k] = v
}
}
o.swagger.Definitions[uniqueName] = schema
for _, v := range item.Dependencies { for _, v := range item.Dependencies {
if err := o.buildDefinitionRecursively(v); err != nil { if err := o.buildDefinitionRecursively(v); err != nil {
return err return err
@ -118,11 +156,12 @@ func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) {
if t.Kind() == reflect.Ptr { if t.Kind() == reflect.Ptr {
t = t.Elem() t = t.Elem()
} }
name := t.String() name := getCanonicalizeTypeName(t)
if err := o.buildDefinitionRecursively(name); err != nil { if err := o.buildDefinitionRecursively(name); err != nil {
return "", err return "", err
} }
return "#/definitions/" + name, nil defName, _ := o.config.GetDefinitionName(o.servePath, name)
return "#/definitions/" + openapi.EscapeJsonPointer(defName), nil
} }
// buildPaths builds OpenAPI paths using go-restful's web services. // buildPaths builds OpenAPI paths using go-restful's web services.
@ -244,18 +283,12 @@ func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map
if len(ret.Responses.StatusCodeResponses) == 0 { if len(ret.Responses.StatusCodeResponses) == 0 {
ret.Responses.Default = o.config.DefaultResponse ret.Responses.Default = o.config.DefaultResponse
} }
// If there is a read sample, there will be a body param referring to it.
if route.ReadSample != nil {
if _, err := o.toSchema(reflect.TypeOf(route.ReadSample).String(), route.ReadSample); err != nil {
return ret, err
}
}
// Build non-common Parameters // Build non-common Parameters
ret.Parameters = make([]spec.Parameter, 0) ret.Parameters = make([]spec.Parameter, 0)
for _, param := range route.ParameterDocs { for _, param := range route.ParameterDocs {
if _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]; !isCommon { if _, isCommon := inPathCommonParamsMap[mapKeyFromParam(param)]; !isCommon {
openAPIParam, err := o.buildParameter(param.Data()) openAPIParam, err := o.buildParameter(param.Data(), route.ReadSample)
if err != nil { if err != nil {
return ret, err return ret, err
} }
@ -266,8 +299,7 @@ func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map
} }
func (o *openAPI) buildResponse(model interface{}, description string) (spec.Response, error) { func (o *openAPI) buildResponse(model interface{}, description string) (spec.Response, error) {
typeName := reflect.TypeOf(model).String() schema, err := o.toSchema(model)
schema, err := o.toSchema(typeName, model)
if err != nil { if err != nil {
return spec.Response{}, err return spec.Response{}, err
} }
@ -300,8 +332,9 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]
} }
} }
for key, count := range paramOpsCountByName { for key, count := range paramOpsCountByName {
if count == len(routes) { paramData := paramNameKindToDataMap[key]
openAPIParam, err := o.buildParameter(paramNameKindToDataMap[key]) if count == len(routes) && paramData.Kind != restful.BodyParameterKind {
openAPIParam, err := o.buildParameter(paramData, nil)
if err != nil { if err != nil {
return commonParamsMap, err return commonParamsMap, err
} }
@ -311,8 +344,8 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]
return commonParamsMap, nil return commonParamsMap, nil
} }
func (o *openAPI) toSchema(typeName string, model interface{}) (_ *spec.Schema, err error) { func (o *openAPI) toSchema(model interface{}) (_ *spec.Schema, err error) {
if openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(typeName); openAPIType != "" { if openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(getCanonicalizeTypeName(reflect.TypeOf(model))); openAPIType != "" {
return &spec.Schema{ return &spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Type: []string{openAPIType}, Type: []string{openAPIType},
@ -320,13 +353,10 @@ func (o *openAPI) toSchema(typeName string, model interface{}) (_ *spec.Schema,
}, },
}, nil }, nil
} else { } else {
ref := "#/definitions/" + typeName ref, err := o.buildDefinitionForType(model)
if model != nil {
ref, err = o.buildDefinitionForType(model)
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
return &spec.Schema{ return &spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(ref), Ref: spec.MustCreateRef(ref),
@ -335,7 +365,7 @@ func (o *openAPI) toSchema(typeName string, model interface{}) (_ *spec.Schema,
} }
} }
func (o *openAPI) buildParameter(restParam restful.ParameterData) (ret spec.Parameter, err error) { func (o *openAPI) buildParameter(restParam restful.ParameterData, bodySample interface{}) (ret spec.Parameter, err error) {
ret = spec.Parameter{ ret = spec.Parameter{
ParamProps: spec.ParamProps{ ParamProps: spec.ParamProps{
Name: restParam.Name, Name: restParam.Name,
@ -345,9 +375,16 @@ func (o *openAPI) buildParameter(restParam restful.ParameterData) (ret spec.Para
} }
switch restParam.Kind { switch restParam.Kind {
case restful.BodyParameterKind: case restful.BodyParameterKind:
if bodySample != nil {
ret.In = "body" ret.In = "body"
ret.Schema, err = o.toSchema(restParam.DataType, nil) ret.Schema, err = o.toSchema(bodySample)
return ret, err return ret, err
} else {
// There is not enough information in the body parameter to build the definition.
// Body parameter has a data type that is a short name but we need full package name
// of the type to create a definition.
return ret, fmt.Errorf("restful body parameters are not supported: %v", restParam.DataType)
}
case restful.PathParameterKind: case restful.PathParameterKind:
ret.In = "path" ret.In = "path"
if !restParam.Required { if !restParam.Required {
@ -375,7 +412,7 @@ func (o *openAPI) buildParameter(restParam restful.ParameterData) (ret spec.Para
func (o *openAPI) buildParameters(restParam []*restful.Parameter) (ret []spec.Parameter, err error) { func (o *openAPI) buildParameters(restParam []*restful.Parameter) (ret []spec.Parameter, err error) {
ret = make([]spec.Parameter, len(restParam)) ret = make([]spec.Parameter, len(restParam))
for i, v := range restParam { for i, v := range restParam {
ret[i], err = o.buildParameter(v.Data()) ret[i], err = o.buildParameter(v.Data(), nil)
if err != nil { if err != nil {
return ret, err return ret, err
} }

View File

@ -19,6 +19,7 @@ package openapi
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"testing" "testing"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
@ -186,9 +187,22 @@ func getConfig(fullMethods bool) (*openapi.Config, *restful.Container) {
Description: "Test API", Description: "Test API",
}, },
}, },
Definitions: &openapi.OpenAPIDefinitions{ GetDefinitions: func(_ openapi.ReferenceCallback) map[string]openapi.OpenAPIDefinition {
"openapi.TestInput": *TestInput{}.OpenAPIDefinition(), return map[string]openapi.OpenAPIDefinition{
"openapi.TestOutput": *TestOutput{}.OpenAPIDefinition(), "k8s.io/kubernetes/pkg/genericapiserver/server/openapi.TestInput": *TestInput{}.OpenAPIDefinition(),
"k8s.io/kubernetes/pkg/genericapiserver/server/openapi.TestOutput": *TestOutput{}.OpenAPIDefinition(),
// Bazel changes the package name, this is ok for testing, but we need to fix it if it happened
// in the main code.
"k8s.io/kubernetes/pkg/genericapiserver/server/openapi/go_default_test.TestInput": *TestInput{}.OpenAPIDefinition(),
"k8s.io/kubernetes/pkg/genericapiserver/server/openapi/go_default_test.TestOutput": *TestOutput{}.OpenAPIDefinition(),
}
},
GetDefinitionName: func(_ string, name string) (string, spec.Extensions) {
friendlyName := name[strings.LastIndex(name, "/")+1:]
if strings.HasPrefix(friendlyName, "go_default_test") {
friendlyName = "openapi" + friendlyName[len("go_default_test"):]
}
return friendlyName, nil
}, },
}, container }, container
} }
@ -216,12 +230,18 @@ func getTestPathItem(allMethods bool, opPrefix string) spec.PathItem {
} }
ret.Get.Parameters = getAdditionalTestParameters() ret.Get.Parameters = getAdditionalTestParameters()
if allMethods { if allMethods {
ret.PathItemProps.Put = getTestOperation("put", opPrefix) ret.Put = getTestOperation("put", opPrefix)
ret.PathItemProps.Post = getTestOperation("post", opPrefix) ret.Put.Parameters = getTestParameters()
ret.PathItemProps.Head = getTestOperation("head", opPrefix) ret.Post = getTestOperation("post", opPrefix)
ret.PathItemProps.Patch = getTestOperation("patch", opPrefix) ret.Post.Parameters = getTestParameters()
ret.PathItemProps.Delete = getTestOperation("delete", opPrefix) ret.Head = getTestOperation("head", opPrefix)
ret.PathItemProps.Options = getTestOperation("options", opPrefix) ret.Head.Parameters = getTestParameters()
ret.Patch = getTestOperation("patch", opPrefix)
ret.Patch.Parameters = getTestParameters()
ret.Delete = getTestOperation("delete", opPrefix)
ret.Delete.Parameters = getTestParameters()
ret.Options = getTestOperation("options", opPrefix)
ret.Options.Parameters = getTestParameters()
} }
return ret return ret
} }
@ -250,16 +270,8 @@ func getTestResponses() *spec.Responses {
} }
func getTestCommonParameters() []spec.Parameter { func getTestCommonParameters() []spec.Parameter {
ret := make([]spec.Parameter, 3) ret := make([]spec.Parameter, 2)
ret[0] = spec.Parameter{ ret[0] = spec.Parameter{
ParamProps: spec.ParamProps{
Name: "body",
In: "body",
Required: true,
Schema: getRefSchema("#/definitions/openapi.TestInput"),
},
}
ret[1] = spec.Parameter{
SimpleSchema: spec.SimpleSchema{ SimpleSchema: spec.SimpleSchema{
Type: "string", Type: "string",
}, },
@ -273,7 +285,7 @@ func getTestCommonParameters() []spec.Parameter {
UniqueItems: true, UniqueItems: true,
}, },
} }
ret[2] = spec.Parameter{ ret[1] = spec.Parameter{
SimpleSchema: spec.SimpleSchema{ SimpleSchema: spec.SimpleSchema{
Type: "string", Type: "string",
}, },
@ -289,9 +301,30 @@ func getTestCommonParameters() []spec.Parameter {
return ret return ret
} }
func getAdditionalTestParameters() []spec.Parameter { func getTestParameters() []spec.Parameter {
ret := make([]spec.Parameter, 2) ret := make([]spec.Parameter, 1)
ret[0] = spec.Parameter{ ret[0] = spec.Parameter{
ParamProps: spec.ParamProps{
Name: "body",
In: "body",
Required: true,
Schema: getRefSchema("#/definitions/openapi.TestInput"),
},
}
return ret
}
func getAdditionalTestParameters() []spec.Parameter {
ret := make([]spec.Parameter, 3)
ret[0] = spec.Parameter{
ParamProps: spec.ParamProps{
Name: "body",
In: "body",
Required: true,
Schema: getRefSchema("#/definitions/openapi.TestInput"),
},
}
ret[1] = spec.Parameter{
ParamProps: spec.ParamProps{ ParamProps: spec.ParamProps{
Name: "fparam", Name: "fparam",
Description: "a test form parameter", Description: "a test form parameter",
@ -304,7 +337,7 @@ func getAdditionalTestParameters() []spec.Parameter {
UniqueItems: true, UniqueItems: true,
}, },
} }
ret[1] = spec.Parameter{ ret[2] = spec.Parameter{
SimpleSchema: spec.SimpleSchema{ SimpleSchema: spec.SimpleSchema{
Type: "integer", Type: "integer",
}, },

View File

@ -43,7 +43,7 @@ func TestValidOpenAPISpec(t *testing.T) {
defer etcdserver.Terminate(t) defer etcdserver.Terminate(t)
config.GenericConfig.EnableIndex = true config.GenericConfig.EnableIndex = true
config.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapigen.OpenAPIDefinitions) config.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapigen.GetOpenAPIDefinitions)
config.GenericConfig.OpenAPIConfig.Info = &spec.Info{ config.GenericConfig.OpenAPIConfig.Info = &spec.Info{
InfoProps: spec.InfoProps{ InfoProps: spec.InfoProps{
Title: "Kubernetes", Title: "Kubernetes",

View File

@ -19,6 +19,7 @@ package openapi
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"strings"
) )
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi. // OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
@ -27,8 +28,10 @@ type OpenAPIDefinition struct {
Dependencies []string Dependencies []string
} }
type ReferenceCallback func(path string) spec.Ref
// OpenAPIDefinitions is collection of all definitions. // OpenAPIDefinitions is collection of all definitions.
type OpenAPIDefinitions map[string]OpenAPIDefinition type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface, // OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See // the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
@ -59,11 +62,18 @@ type Config struct {
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map // OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
// or any of the models will result in spec generation failure. // or any of the models will result in spec generation failure.
Definitions *OpenAPIDefinitions GetDefinitions GetOpenAPIDefinitions
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs. // GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error) GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error)
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
// It is an optional function to customize model names.
GetDefinitionName func(servePath string, name string) (string, spec.Extensions)
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config // SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses. // is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
SecurityDefinitions *spec.SecurityDefinitions SecurityDefinitions *spec.SecurityDefinitions
@ -141,3 +151,10 @@ func GetOpenAPITypeFormat(typeName string) (string, string) {
} }
return mapped[0], mapped[1] return mapped[0], mapped[1]
} }
func EscapeJsonPointer(p string) string {
// Escaping reference name using rfc6901
p = strings.Replace(p, "~", "~0", -1)
p = strings.Replace(p, "/", "~1", -1)
return p
}

View File

@ -183,7 +183,7 @@ func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Serv
masterConfig = NewMasterConfig() masterConfig = NewMasterConfig()
masterConfig.GenericConfig.EnableProfiling = true masterConfig.GenericConfig.EnableProfiling = true
masterConfig.GenericConfig.EnableMetrics = true masterConfig.GenericConfig.EnableMetrics = true
masterConfig.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.OpenAPIDefinitions) masterConfig.GenericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(openapi.GetOpenAPIDefinitions)
masterConfig.GenericConfig.OpenAPIConfig.Info = &spec.Info{ masterConfig.GenericConfig.OpenAPIConfig.Info = &spec.Info{
InfoProps: spec.InfoProps{ InfoProps: spec.InfoProps{
Title: "Kubernetes", Title: "Kubernetes",
@ -195,7 +195,7 @@ func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Serv
Description: "Default Response.", Description: "Default Response.",
}, },
} }
masterConfig.GenericConfig.OpenAPIConfig.Definitions = openapi.OpenAPIDefinitions masterConfig.GenericConfig.OpenAPIConfig.GetDefinitions = openapi.GetOpenAPIDefinitions
masterConfig.GenericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig() masterConfig.GenericConfig.SwaggerConfig = genericapiserver.DefaultSwaggerConfig()
} }