diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 58faf3f3f9..c32fdcc6d1 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -4099,59 +4099,59 @@ }, { "ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen/args", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/aggregator", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/builder", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/common", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/generators", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/generators/rules", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/handler", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/schemaconv", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto/testing", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/proto/validation", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/kube-openapi/pkg/util/sets", - "Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" + "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e" }, { "ImportPath": "k8s.io/repo-infra/kazel", diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD b/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD index 9a3f174b28..f4e30b50b3 100644 --- a/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/BUILD @@ -2,7 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") go_library( name = "go_default_library", - srcs = ["aggregator.go"], + srcs = [ + "aggregator.go", + "mutating_walker.go", + "walker.go", + ], importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-openapi/pkg/aggregator", importpath = "k8s.io/kube-openapi/pkg/aggregator", visibility = ["//visibility:public"], diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go index cbeb3ef8a5..95f7588871 100644 --- a/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/aggregator.go @@ -17,7 +17,6 @@ limitations under the License. package aggregator import ( - "encoding/json" "fmt" "reflect" "strings" @@ -27,154 +26,14 @@ import ( "k8s.io/kube-openapi/pkg/util" ) -const ( - definitionPrefix = "#/definitions/" -) - -// Run a walkRefCallback method on all references of an OpenAPI spec -type referenceWalker struct { - // walkRefCallback will be called on each reference and the return value - // will replace that reference. This will allow the callers to change - // all/some references of an spec (e.g. useful in renaming definitions). - walkRefCallback func(ref spec.Ref) spec.Ref - - // The spec to walk through. - root *spec.Swagger - - // Keep track of visited references - alreadyVisited map[string]bool -} - -func walkOnAllReferences(walkRef func(ref spec.Ref) spec.Ref, sp *spec.Swagger) { - walker := &referenceWalker{walkRefCallback: walkRef, root: sp, alreadyVisited: map[string]bool{}} - walker.Start() -} - -func (s *referenceWalker) walkRef(ref spec.Ref) spec.Ref { - refStr := ref.String() - // References that start with #/definitions/ has a definition - // inside the same spec file. If that is the case, walk through - // those definitions too. - // We do not support external references yet. - if !s.alreadyVisited[refStr] && strings.HasPrefix(refStr, definitionPrefix) { - s.alreadyVisited[refStr] = true - k := refStr[len(definitionPrefix):] - def := s.root.Definitions[k] - s.walkSchema(&def) - // Make sure we don't assign to nil map - if s.root.Definitions == nil { - s.root.Definitions = spec.Definitions{} - } - s.root.Definitions[k] = def - } - return s.walkRefCallback(ref) -} - -func (s *referenceWalker) walkSchema(schema *spec.Schema) { - if schema == nil { - return - } - schema.Ref = s.walkRef(schema.Ref) - for k, v := range schema.Definitions { - s.walkSchema(&v) - schema.Definitions[k] = v - } - for k, v := range schema.Properties { - s.walkSchema(&v) - schema.Properties[k] = v - } - for k, v := range schema.PatternProperties { - s.walkSchema(&v) - schema.PatternProperties[k] = v - } - for i := range schema.AllOf { - s.walkSchema(&schema.AllOf[i]) - } - for i := range schema.AnyOf { - s.walkSchema(&schema.AnyOf[i]) - } - for i := range schema.OneOf { - s.walkSchema(&schema.OneOf[i]) - } - if schema.Not != nil { - s.walkSchema(schema.Not) - } - if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { - s.walkSchema(schema.AdditionalProperties.Schema) - } - if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { - s.walkSchema(schema.AdditionalItems.Schema) - } - if schema.Items != nil { - if schema.Items.Schema != nil { - s.walkSchema(schema.Items.Schema) - } - for i := range schema.Items.Schemas { - s.walkSchema(&schema.Items.Schemas[i]) - } - } -} - -func (s *referenceWalker) walkParams(params []spec.Parameter) { - if params == nil { - return - } - for _, param := range params { - param.Ref = s.walkRef(param.Ref) - s.walkSchema(param.Schema) - if param.Items != nil { - param.Items.Ref = s.walkRef(param.Items.Ref) - } - } -} - -func (s *referenceWalker) walkResponse(resp *spec.Response) { - if resp == nil { - return - } - resp.Ref = s.walkRef(resp.Ref) - s.walkSchema(resp.Schema) -} - -func (s *referenceWalker) walkOperation(op *spec.Operation) { - if op == nil { - return - } - s.walkParams(op.Parameters) - if op.Responses == nil { - return - } - s.walkResponse(op.Responses.Default) - for _, r := range op.Responses.StatusCodeResponses { - s.walkResponse(&r) - } -} - -func (s *referenceWalker) Start() { - if s.root.Paths == nil { - return - } - for _, pathItem := range s.root.Paths.Paths { - s.walkParams(pathItem.Parameters) - s.walkOperation(pathItem.Delete) - s.walkOperation(pathItem.Get) - s.walkOperation(pathItem.Head) - s.walkOperation(pathItem.Options) - s.walkOperation(pathItem.Patch) - s.walkOperation(pathItem.Post) - s.walkOperation(pathItem.Put) - } -} - // usedDefinitionForSpec returns a map with all used definitions in the provided spec as keys and true as values. -func usedDefinitionForSpec(sp *spec.Swagger) map[string]bool { +func usedDefinitionForSpec(root *spec.Swagger) map[string]bool { usedDefinitions := map[string]bool{} - walkOnAllReferences(func(ref spec.Ref) spec.Ref { + walkOnAllReferences(func(ref *spec.Ref) { if refStr := ref.String(); refStr != "" && strings.HasPrefix(refStr, definitionPrefix) { usedDefinitions[refStr[len(definitionPrefix):]] = true } - return ref - }, sp) + }, root) return usedDefinitions } @@ -182,6 +41,18 @@ func usedDefinitionForSpec(sp *spec.Swagger) map[string]bool { // i.e. if a Path removed by this function, all definitions used by it and not used // anywhere else will also be removed. func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) { + *sp = *FilterSpecByPathsWithoutSideEffects(sp, keepPathPrefixes) +} + +// FilterSpecByPathsWithoutSideEffects removes unnecessary paths and definitions used by those paths. +// i.e. if a Path removed by this function, all definitions used by it and not used +// anywhere else will also be removed. +// It does not modify the input, but the output shares data structures with the input. +func FilterSpecByPathsWithoutSideEffects(sp *spec.Swagger, keepPathPrefixes []string) *spec.Swagger { + if sp.Paths == nil { + return sp + } + // Walk all references to find all used definitions. This function // want to only deal with unused definitions resulted from filtering paths. // Thus a definition will be removed only if it has been used before but @@ -190,71 +61,100 @@ func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) { // First remove unwanted paths prefixes := util.NewTrie(keepPathPrefixes) - orgPaths := sp.Paths - if orgPaths == nil { - return - } - sp.Paths = &spec.Paths{ - VendorExtensible: orgPaths.VendorExtensible, + ret := *sp + ret.Paths = &spec.Paths{ + VendorExtensible: sp.Paths.VendorExtensible, Paths: map[string]spec.PathItem{}, } - for path, pathItem := range orgPaths.Paths { + for path, pathItem := range sp.Paths.Paths { if !prefixes.HasPrefix(path) { continue } - sp.Paths.Paths[path] = pathItem + ret.Paths.Paths[path] = pathItem } // Walk all references to find all definition references. - usedDefinitions := usedDefinitionForSpec(sp) + usedDefinitions := usedDefinitionForSpec(&ret) // Remove unused definitions - orgDefinitions := sp.Definitions - sp.Definitions = spec.Definitions{} - for k, v := range orgDefinitions { + ret.Definitions = spec.Definitions{} + for k, v := range sp.Definitions { if usedDefinitions[k] || !initialUsedDefinitions[k] { - sp.Definitions[k] = v + ret.Definitions[k] = v } } + + return &ret } -func renameDefinition(s *spec.Swagger, old, new string) { - oldRef := definitionPrefix + old - newRef := definitionPrefix + new - walkOnAllReferences(func(ref spec.Ref) spec.Ref { - if ref.String() == oldRef { - return spec.MustCreateRef(newRef) +type rename struct { + from, to string +} + +// renameDefinition renames references, without mutating the input. +// The output might share data structures with the input. +func renameDefinition(s *spec.Swagger, renames map[string]string) *spec.Swagger { + refRenames := make(map[string]string, len(renames)) + foundOne := false + for k, v := range renames { + refRenames[definitionPrefix+k] = definitionPrefix + v + if _, ok := s.Definitions[k]; ok { + foundOne = true + } + } + + if !foundOne { + return s + } + + ret := &spec.Swagger{} + *ret = *s + + ret = replaceReferences(func(ref *spec.Ref) *spec.Ref { + refName := ref.String() + if newRef, found := refRenames[refName]; found { + ret := spec.MustCreateRef(newRef) + return &ret } return ref - }, s) - // Make sure we don't assign to nil map - if s.Definitions == nil { - s.Definitions = spec.Definitions{} + }, ret) + + renamedDefinitions := make(spec.Definitions, len(ret.Definitions)) + for k, v := range ret.Definitions { + if newRef, found := renames[k]; found { + k = newRef + } + renamedDefinitions[k] = v } - s.Definitions[new] = s.Definitions[old] - delete(s.Definitions, old) + ret.Definitions = renamedDefinitions + + return ret } // MergeSpecsIgnorePathConflict is the same as MergeSpecs except it will ignore any path // conflicts by keeping the paths of destination. It will rename definition conflicts. +// The source is not mutated. func MergeSpecsIgnorePathConflict(dest, source *spec.Swagger) error { return mergeSpecs(dest, source, true, true) } // MergeSpecsFailOnDefinitionConflict is differ from MergeSpecs as it fails if there is // a definition conflict. +// The source is not mutated. func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error { return mergeSpecs(dest, source, false, false) } // MergeSpecs copies paths and definitions from source to dest, rename definitions if needed. // dest will be mutated, and source will not be changed. It will fail on path conflicts. +// The source is not mutated. func MergeSpecs(dest, source *spec.Swagger) error { return mergeSpecs(dest, source, true, false) } +// mergeSpecs merged source into dest while resolving conflicts. +// The source is not mutated. func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConflicts bool) (err error) { - specCloned := false // Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering). if source.Paths == nil { // When a source spec does not have any path, that means none of the definitions @@ -279,12 +179,7 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf return nil } if hasConflictingPath { - source, err = CloneSpec(source) - if err != nil { - return err - } - specCloned = true - FilterSpecByPaths(source, keepPaths) + source = FilterSpecByPathsWithoutSideEffects(source, keepPaths) } } // Check for model conflicts @@ -301,21 +196,11 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf } if conflicts { - if !specCloned { - source, err = CloneSpec(source) - if err != nil { - return err - } - } - specCloned = true usedNames := map[string]bool{} for k := range dest.Definitions { usedNames[k] = true } - type Rename struct { - from, to string - } - renames := []Rename{} + renames := map[string]string{} OUTERLOOP: for k, v := range source.Definitions { @@ -334,7 +219,7 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf newName = fmt.Sprintf("%s_v%d", k, i) v2, found = dest.Definitions[newName] if found && reflect.DeepEqual(v, v2) { - renames = append(renames, Rename{from: k, to: newName}) + renames[k] = newName continue OUTERLOOP } } @@ -345,13 +230,11 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf newName = fmt.Sprintf("%s_v%d", k, i) _, foundInSource = source.Definitions[newName] } - renames = append(renames, Rename{from: k, to: newName}) + renames[k] = newName usedNames[newName] = true } } - for _, r := range renames { - renameDefinition(source, r.from, r.to) - } + source = renameDefinition(source, renames) } for k, v := range source.Definitions { if _, found := dest.Definitions[k]; !found { @@ -374,18 +257,3 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf } return nil } - -// CloneSpec clones OpenAPI spec -func CloneSpec(source *spec.Swagger) (*spec.Swagger, error) { - // TODO(mehdy): Find a faster way to clone an spec - bytes, err := json.Marshal(source) - if err != nil { - return nil, err - } - var ret spec.Swagger - err = json.Unmarshal(bytes, &ret) - if err != nil { - return nil, err - } - return &ret, nil -} diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/mutating_walker.go b/vendor/k8s.io/kube-openapi/pkg/aggregator/mutating_walker.go new file mode 100644 index 0000000000..7c35f26126 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/mutating_walker.go @@ -0,0 +1,498 @@ +/* +Copyright 2017 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 aggregator + +import ( + _ "net/http/pprof" + + "github.com/go-openapi/spec" +) + +// Run a walkRefCallback method on all references of an OpenAPI spec, replacing the values. +type mutatingReferenceWalker struct { + // walkRefCallback will be called on each reference. Do not mutate the input, always create a copy first and return that. + walkRefCallback func(ref *spec.Ref) *spec.Ref +} + +// replaceReferences rewrites the references without mutating the input. +// The output might share data with the input. +func replaceReferences(walkRef func(ref *spec.Ref) *spec.Ref, sp *spec.Swagger) *spec.Swagger { + walker := &mutatingReferenceWalker{walkRefCallback: walkRef} + return walker.Start(sp) +} + +func (w *mutatingReferenceWalker) walkSchema(schema *spec.Schema) *spec.Schema { + if schema == nil { + return nil + } + + orig := schema + clone := func() { + if orig == schema { + schema = &spec.Schema{} + *schema = *orig + } + } + + if r := w.walkRefCallback(&schema.Ref); r != &schema.Ref { + clone() + schema.Ref = *r + } + + definitionsCloned := false + for k, v := range schema.Definitions { + if s := w.walkSchema(&v); s != &v { + if !definitionsCloned { + definitionsCloned = true + clone() + schema.Definitions = make(spec.Definitions, len(orig.Definitions)) + for k2, v2 := range orig.Definitions { + schema.Definitions[k2] = v2 + } + } + schema.Definitions[k] = *s + } + } + + propertiesCloned := false + for k, v := range schema.Properties { + if s := w.walkSchema(&v); s != &v { + if !propertiesCloned { + propertiesCloned = true + clone() + schema.Properties = make(map[string]spec.Schema, len(orig.Properties)) + for k2, v2 := range orig.Properties { + schema.Properties[k2] = v2 + } + } + schema.Properties[k] = *s + } + } + + patternPropertiesCloned := false + for k, v := range schema.PatternProperties { + if s := w.walkSchema(&v); s != &v { + if !patternPropertiesCloned { + patternPropertiesCloned = true + clone() + schema.PatternProperties = make(map[string]spec.Schema, len(orig.PatternProperties)) + for k2, v2 := range orig.PatternProperties { + schema.PatternProperties[k2] = v2 + } + } + schema.PatternProperties[k] = *s + } + } + + allOfCloned := false + for i := range schema.AllOf { + if s := w.walkSchema(&schema.AllOf[i]); s != &schema.AllOf[i] { + if !allOfCloned { + allOfCloned = true + clone() + schema.AllOf = make([]spec.Schema, len(orig.AllOf)) + copy(schema.AllOf, orig.AllOf) + } + schema.AllOf[i] = *s + } + } + + anyOfCloned := false + for i := range schema.AnyOf { + if s := w.walkSchema(&schema.AnyOf[i]); s != &schema.AnyOf[i] { + if !anyOfCloned { + anyOfCloned = true + clone() + schema.AnyOf = make([]spec.Schema, len(orig.AnyOf)) + copy(schema.AnyOf, orig.AnyOf) + } + schema.AnyOf[i] = *s + } + } + + oneOfCloned := false + for i := range schema.OneOf { + if s := w.walkSchema(&schema.OneOf[i]); s != &schema.OneOf[i] { + if !oneOfCloned { + oneOfCloned = true + clone() + schema.OneOf = make([]spec.Schema, len(orig.OneOf)) + copy(schema.OneOf, orig.OneOf) + } + schema.OneOf[i] = *s + } + } + + if schema.Not != nil { + if s := w.walkSchema(schema.Not); s != schema.Not { + clone() + schema.Not = s + } + } + + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + if s := w.walkSchema(schema.AdditionalProperties.Schema); s != schema.AdditionalProperties.Schema { + clone() + schema.AdditionalProperties = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalProperties.Allows} + } + } + + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + if s := w.walkSchema(schema.AdditionalItems.Schema); s != schema.AdditionalItems.Schema { + clone() + schema.AdditionalItems = &spec.SchemaOrBool{Schema: s, Allows: schema.AdditionalItems.Allows} + } + } + + if schema.Items != nil { + if schema.Items.Schema != nil { + if s := w.walkSchema(schema.Items.Schema); s != schema.Items.Schema { + clone() + schema.Items = &spec.SchemaOrArray{Schema: s} + } + } else { + itemsCloned := false + for i := range schema.Items.Schemas { + if s := w.walkSchema(&schema.Items.Schemas[i]); s != &schema.Items.Schemas[i] { + if !itemsCloned { + clone() + schema.Items = &spec.SchemaOrArray{ + Schemas: make([]spec.Schema, len(orig.Items.Schemas)), + } + itemsCloned = true + copy(schema.Items.Schemas, orig.Items.Schemas) + } + schema.Items.Schemas[i] = *s + } + } + } + } + + return schema +} + +func (w *mutatingReferenceWalker) walkParameter(param *spec.Parameter) *spec.Parameter { + if param == nil { + return nil + } + + orig := param + cloned := false + clone := func() { + if !cloned { + cloned = true + param = &spec.Parameter{} + *param = *orig + } + } + + if r := w.walkRefCallback(¶m.Ref); r != ¶m.Ref { + clone() + param.Ref = *r + } + if s := w.walkSchema(param.Schema); s != param.Schema { + clone() + param.Schema = s + } + if param.Items != nil { + if r := w.walkRefCallback(¶m.Items.Ref); r != ¶m.Items.Ref { + param.Items.Ref = *r + } + } + + return param +} + +func (w *mutatingReferenceWalker) walkParameters(params []spec.Parameter) ([]spec.Parameter, bool) { + if params == nil { + return nil, false + } + + orig := params + cloned := false + clone := func() { + if !cloned { + cloned = true + params = make([]spec.Parameter, len(params)) + copy(params, orig) + } + } + + for i := range params { + if s := w.walkParameter(¶ms[i]); s != ¶ms[i] { + clone() + params[i] = *s + } + } + + return params, cloned +} + +func (w *mutatingReferenceWalker) walkResponse(resp *spec.Response) *spec.Response { + if resp == nil { + return nil + } + + orig := resp + cloned := false + clone := func() { + if !cloned { + cloned = true + resp = &spec.Response{} + *resp = *orig + } + } + + if r := w.walkRefCallback(&resp.Ref); r != &resp.Ref { + clone() + resp.Ref = *r + } + if s := w.walkSchema(resp.Schema); s != resp.Schema { + clone() + resp.Schema = s + } + + return resp +} + +func (w *mutatingReferenceWalker) walkResponses(resps *spec.Responses) *spec.Responses { + if resps == nil { + return nil + } + + orig := resps + cloned := false + clone := func() { + if !cloned { + cloned = true + resps = &spec.Responses{} + *resps = *orig + } + } + + if r := w.walkResponse(resps.ResponsesProps.Default); r != resps.ResponsesProps.Default { + clone() + resps.Default = r + } + + responsesCloned := false + for k, v := range resps.ResponsesProps.StatusCodeResponses { + if r := w.walkResponse(&v); r != &v { + if !responsesCloned { + responsesCloned = true + clone() + resps.ResponsesProps.StatusCodeResponses = make(map[int]spec.Response, len(orig.StatusCodeResponses)) + for k2, v2 := range orig.StatusCodeResponses { + resps.ResponsesProps.StatusCodeResponses[k2] = v2 + } + } + resps.ResponsesProps.StatusCodeResponses[k] = *r + } + } + + return resps +} + +func (w *mutatingReferenceWalker) walkOperation(op *spec.Operation) *spec.Operation { + if op == nil { + return nil + } + + orig := op + cloned := false + clone := func() { + if !cloned { + cloned = true + op = &spec.Operation{} + *op = *orig + } + } + + parametersCloned := false + for i := range op.Parameters { + if s := w.walkParameter(&op.Parameters[i]); s != &op.Parameters[i] { + if !parametersCloned { + parametersCloned = true + clone() + op.Parameters = make([]spec.Parameter, len(orig.Parameters)) + copy(op.Parameters, orig.Parameters) + } + op.Parameters[i] = *s + } + } + + if r := w.walkResponses(op.Responses); r != op.Responses { + clone() + op.Responses = r + } + + return op +} + +func (w *mutatingReferenceWalker) walkPathItem(pathItem *spec.PathItem) *spec.PathItem { + if pathItem == nil { + return nil + } + + orig := pathItem + cloned := false + clone := func() { + if !cloned { + cloned = true + pathItem = &spec.PathItem{} + *pathItem = *orig + } + } + + if p, changed := w.walkParameters(pathItem.Parameters); changed { + clone() + pathItem.Parameters = p + } + if op := w.walkOperation(pathItem.Get); op != pathItem.Get { + clone() + pathItem.Get = op + } + if op := w.walkOperation(pathItem.Head); op != pathItem.Head { + clone() + pathItem.Head = op + } + if op := w.walkOperation(pathItem.Delete); op != pathItem.Delete { + clone() + pathItem.Delete = op + } + if op := w.walkOperation(pathItem.Options); op != pathItem.Options { + clone() + pathItem.Options = op + } + if op := w.walkOperation(pathItem.Patch); op != pathItem.Patch { + clone() + pathItem.Patch = op + } + if op := w.walkOperation(pathItem.Post); op != pathItem.Post { + clone() + pathItem.Post = op + } + if op := w.walkOperation(pathItem.Put); op != pathItem.Put { + clone() + pathItem.Put = op + } + + return pathItem +} + +func (w *mutatingReferenceWalker) walkPaths(paths *spec.Paths) *spec.Paths { + if paths == nil { + return nil + } + + orig := paths + cloned := false + clone := func() { + if !cloned { + cloned = true + paths = &spec.Paths{} + *paths = *orig + } + } + + pathsCloned := false + for k, v := range paths.Paths { + if p := w.walkPathItem(&v); p != &v { + if !pathsCloned { + pathsCloned = true + clone() + paths.Paths = make(map[string]spec.PathItem, len(orig.Paths)) + for k2, v2 := range orig.Paths { + paths.Paths[k2] = v2 + } + } + paths.Paths[k] = *p + } + } + + return paths +} + +func (w *mutatingReferenceWalker) Start(swagger *spec.Swagger) *spec.Swagger { + if swagger == nil { + return nil + } + + orig := swagger + cloned := false + clone := func() { + if !cloned { + cloned = true + swagger = &spec.Swagger{} + *swagger = *orig + } + } + + parametersCloned := false + for k, v := range swagger.Parameters { + if p := w.walkParameter(&v); p != &v { + if !parametersCloned { + parametersCloned = true + clone() + swagger.Parameters = make(map[string]spec.Parameter, len(orig.Parameters)) + for k2, v2 := range orig.Parameters { + swagger.Parameters[k2] = v2 + } + } + swagger.Parameters[k] = *p + } + } + + responsesCloned := false + for k, v := range swagger.Responses { + if r := w.walkResponse(&v); r != &v { + if !responsesCloned { + responsesCloned = true + clone() + swagger.Responses = make(map[string]spec.Response, len(orig.Responses)) + for k2, v2 := range orig.Responses { + swagger.Responses[k2] = v2 + } + } + swagger.Responses[k] = *r + } + } + + definitionsCloned := false + for k, v := range swagger.Definitions { + if s := w.walkSchema(&v); s != &v { + if !definitionsCloned { + definitionsCloned = true + clone() + swagger.Definitions = make(spec.Definitions, len(orig.Definitions)) + for k2, v2 := range orig.Definitions { + swagger.Definitions[k2] = v2 + } + } + swagger.Definitions[k] = *s + } + } + + if swagger.Paths != nil { + if p := w.walkPaths(swagger.Paths); p != swagger.Paths { + clone() + swagger.Paths = p + } + } + + return swagger +} diff --git a/vendor/k8s.io/kube-openapi/pkg/aggregator/walker.go b/vendor/k8s.io/kube-openapi/pkg/aggregator/walker.go new file mode 100644 index 0000000000..81dc1395e1 --- /dev/null +++ b/vendor/k8s.io/kube-openapi/pkg/aggregator/walker.go @@ -0,0 +1,162 @@ +/* +Copyright 2017 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 aggregator + +import ( + "strings" + + "github.com/go-openapi/spec" +) + +const ( + definitionPrefix = "#/definitions/" +) + +// Run a readonlyReferenceWalker method on all references of an OpenAPI spec +type readonlyReferenceWalker struct { + // walkRefCallback will be called on each reference. The input will never be nil. + walkRefCallback func(ref *spec.Ref) + + // The spec to walk through. + root *spec.Swagger +} + +// walkOnAllReferences recursively walks on all references, while following references into definitions. +// it calls walkRef on each found reference. +func walkOnAllReferences(walkRef func(ref *spec.Ref), root *spec.Swagger) { + alreadyVisited := map[string]bool{} + + walker := &readonlyReferenceWalker{ + root: root, + } + walker.walkRefCallback = func(ref *spec.Ref) { + walkRef(ref) + + refStr := ref.String() + if refStr == "" || !strings.HasPrefix(refStr, definitionPrefix) { + return + } + defName := refStr[len(definitionPrefix):] + + if _, found := root.Definitions[defName]; found && !alreadyVisited[refStr] { + alreadyVisited[refStr] = true + def := root.Definitions[defName] + walker.walkSchema(&def) + } + } + walker.Start() +} + +func (s *readonlyReferenceWalker) walkSchema(schema *spec.Schema) { + if schema == nil { + return + } + s.walkRefCallback(&schema.Ref) + var v *spec.Schema + if len(schema.Definitions)+len(schema.Properties)+len(schema.PatternProperties) > 0 { + v = &spec.Schema{} + } + for k := range schema.Definitions { + *v = schema.Definitions[k] + s.walkSchema(v) + } + for k := range schema.Properties { + *v = schema.Properties[k] + s.walkSchema(v) + } + for k := range schema.PatternProperties { + *v = schema.PatternProperties[k] + s.walkSchema(v) + } + for i := range schema.AllOf { + s.walkSchema(&schema.AllOf[i]) + } + for i := range schema.AnyOf { + s.walkSchema(&schema.AnyOf[i]) + } + for i := range schema.OneOf { + s.walkSchema(&schema.OneOf[i]) + } + if schema.Not != nil { + s.walkSchema(schema.Not) + } + if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { + s.walkSchema(schema.AdditionalProperties.Schema) + } + if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { + s.walkSchema(schema.AdditionalItems.Schema) + } + if schema.Items != nil { + if schema.Items.Schema != nil { + s.walkSchema(schema.Items.Schema) + } + for i := range schema.Items.Schemas { + s.walkSchema(&schema.Items.Schemas[i]) + } + } +} + +func (s *readonlyReferenceWalker) walkParams(params []spec.Parameter) { + if params == nil { + return + } + for _, param := range params { + s.walkRefCallback(¶m.Ref) + s.walkSchema(param.Schema) + if param.Items != nil { + s.walkRefCallback(¶m.Items.Ref) + } + } +} + +func (s *readonlyReferenceWalker) walkResponse(resp *spec.Response) { + if resp == nil { + return + } + s.walkRefCallback(&resp.Ref) + s.walkSchema(resp.Schema) +} + +func (s *readonlyReferenceWalker) walkOperation(op *spec.Operation) { + if op == nil { + return + } + s.walkParams(op.Parameters) + if op.Responses == nil { + return + } + s.walkResponse(op.Responses.Default) + for _, r := range op.Responses.StatusCodeResponses { + s.walkResponse(&r) + } +} + +func (s *readonlyReferenceWalker) Start() { + if s.root.Paths == nil { + return + } + for _, pathItem := range s.root.Paths.Paths { + s.walkParams(pathItem.Parameters) + s.walkOperation(pathItem.Delete) + s.walkOperation(pathItem.Get) + s.walkOperation(pathItem.Head) + s.walkOperation(pathItem.Options) + s.walkOperation(pathItem.Patch) + s.walkOperation(pathItem.Post) + s.walkOperation(pathItem.Put) + } +} diff --git a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go index 97278fe6f0..774886a940 100644 --- a/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go +++ b/vendor/k8s.io/kube-openapi/pkg/generators/openapi.go @@ -171,7 +171,7 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error { sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil)) for _, t := range c.Order { - err := newOpenAPITypeWriter(sw).generateCall(t) + err := newOpenAPITypeWriter(sw, c).generateCall(t) if err != nil { return err } @@ -186,7 +186,7 @@ func (g *openAPIGen) Init(c *generator.Context, w io.Writer) error { func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error { klog.V(5).Infof("generating for type %v", t) sw := generator.NewSnippetWriter(w, c, "$", "$") - err := newOpenAPITypeWriter(sw).generate(t) + err := newOpenAPITypeWriter(sw, c).generate(t) if err != nil { return err } @@ -221,13 +221,15 @@ func shouldInlineMembers(m *types.Member) bool { type openAPITypeWriter struct { *generator.SnippetWriter + context *generator.Context refTypes map[string]*types.Type GetDefinitionInterface *types.Type } -func newOpenAPITypeWriter(sw *generator.SnippetWriter) openAPITypeWriter { +func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context) openAPITypeWriter { return openAPITypeWriter{ SnippetWriter: sw, + context: c, refTypes: map[string]*types.Type{}, } } @@ -337,12 +339,22 @@ func (g openAPITypeWriter) generate(t *types.Type) error { g.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args) g.generateDescription(t.CommentLines) g.Do("Type: []string{\"object\"},\n", nil) - g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args) - required, err := g.generateMembers(t, []string{}) + + // write members into a temporary buffer, in order to postpone writing out the Properties field. We only do + // that if it is not empty. + propertiesBuf := bytes.Buffer{} + bsw := g + bsw.SnippetWriter = generator.NewSnippetWriter(&propertiesBuf, g.context, "$", "$") + required, err := bsw.generateMembers(t, []string{}) if err != nil { return err } - g.Do("},\n", nil) + if propertiesBuf.Len() > 0 { + g.Do("Properties: map[string]$.SpecSchemaType|raw${\n", args) + g.Do(strings.Replace(propertiesBuf.String(), "$", "$\"$\"$", -1), nil) // escape $ (used as delimiter of the templates) + g.Do("},\n", nil) + } + if len(required) > 0 { g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\"")) } @@ -351,13 +363,14 @@ func (g openAPITypeWriter) generate(t *types.Type) error { return err } g.Do("},\n", nil) - g.Do("Dependencies: []string{\n", args) + // Map order is undefined, sort them or we may get a different file generated each time. keys := []string{} for k := range g.refTypes { keys = append(keys, k) } sort.Strings(keys) + deps := []string{} for _, k := range keys { v := g.refTypes[k] if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" { @@ -365,9 +378,16 @@ func (g openAPITypeWriter) generate(t *types.Type) error { // Will eliminate special case of time.Time continue } - g.Do("\"$.$\",", k) + deps = append(deps, k) } - g.Do("},\n}\n}\n\n", nil) + if len(deps) > 0 { + g.Do("Dependencies: []string{\n", args) + for _, k := range deps { + g.Do("\"$.$\",", k) + } + g.Do("},\n", nil) + } + g.Do("}\n}\n\n", nil) } return nil } @@ -409,7 +429,7 @@ func (g openAPITypeWriter) emitExtensions(extensions []extension) { for _, extension := range extensions { g.Do("\"$.$\": ", extension.xName) if extension.hasMultipleValues() { - g.Do("[]string{\n", nil) + g.Do("[]interface{}{\n", nil) } for _, value := range extension.values { g.Do("\"$.$\",\n", value) @@ -562,7 +582,7 @@ func (g openAPITypeWriter) generateMapProperty(t *types.Type) error { return fmt.Errorf("map with non-string keys are not supported by OpenAPI in %v", t) } g.Do("Type: []string{\"object\"},\n", nil) - g.Do("AdditionalProperties: &spec.SchemaOrBool{\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) + g.Do("AdditionalProperties: &spec.SchemaOrBool{\nAllows: true,\nSchema: &spec.Schema{\nSchemaProps: spec.SchemaProps{\n", nil) typeString, format := openapi.GetOpenAPITypeFormat(elemType.String()) if typeString != "" { g.generateSimpleProperty(typeString, format) diff --git a/vendor/k8s.io/kube-openapi/pkg/handler/BUILD b/vendor/k8s.io/kube-openapi/pkg/handler/BUILD index 18993e30e9..b2fb7dcce9 100644 --- a/vendor/k8s.io/kube-openapi/pkg/handler/BUILD +++ b/vendor/k8s.io/kube-openapi/pkg/handler/BUILD @@ -13,6 +13,7 @@ go_library( "//vendor/github.com/golang/protobuf/proto:go_default_library", "//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/github.com/googleapis/gnostic/compiler:go_default_library", + "//vendor/github.com/json-iterator/go:go_default_library", "//vendor/github.com/munnerz/goautoneg:go_default_library", "//vendor/gopkg.in/yaml.v2:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library", diff --git a/vendor/k8s.io/kube-openapi/pkg/handler/handler.go b/vendor/k8s.io/kube-openapi/pkg/handler/handler.go index f4e64766a6..6b3887e585 100644 --- a/vendor/k8s.io/kube-openapi/pkg/handler/handler.go +++ b/vendor/k8s.io/kube-openapi/pkg/handler/handler.go @@ -20,7 +20,6 @@ import ( "bytes" "compress/gzip" "crypto/sha512" - "encoding/json" "fmt" "mime" "net/http" @@ -28,15 +27,15 @@ import ( "sync" "time" - yaml "gopkg.in/yaml.v2" - "github.com/NYTimes/gziphandler" restful "github.com/emicklei/go-restful" "github.com/go-openapi/spec" "github.com/golang/protobuf/proto" openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2" "github.com/googleapis/gnostic/compiler" + "github.com/json-iterator/go" "github.com/munnerz/goautoneg" + yaml "gopkg.in/yaml.v2" "k8s.io/kube-openapi/pkg/builder" "k8s.io/kube-openapi/pkg/common" @@ -78,6 +77,15 @@ func computeETag(data []byte) string { return fmt.Sprintf("\"%X\"", sha512.Sum512(data)) } +// NewOpenAPIService builds an OpenAPIService starting with the given spec. +func NewOpenAPIService(spec *spec.Swagger) (*OpenAPIService, error) { + o := &OpenAPIService{} + if err := o.UpdateSpec(spec); err != nil { + return nil, err + } + return o, nil +} + // 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. @@ -89,7 +97,11 @@ func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.Web if err != nil { return nil, err } - return RegisterOpenAPIService(spec, servePath, handler) + o, err := NewOpenAPIService(spec) + if err != nil { + return nil, err + } + return o, o.RegisterOpenAPIService(servePath, handler) } // NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec, @@ -99,18 +111,30 @@ func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.Web // RegisterOpenAPIService registers a handler to provide access to provided swagger spec. // Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a // json file and will also serve .pb and .gz files. -func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { +// +// Deprecated: use OpenAPIService.RegisterOpenAPIService instead. +func RegisterOpenAPIService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { + o, err := NewOpenAPIService(spec) + if err != nil { + return nil, err + } + return o, o.RegisterOpenAPIService(servePath, handler) +} + +// 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. +// +// RegisterOpenAPIService registers a handler to provide access to provided swagger spec. +// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a +// json file and will also serve .pb and .gz files. +func (o *OpenAPIService) RegisterOpenAPIService(servePath string, handler common.PathHandler) error { if !strings.HasSuffix(servePath, jsonExt) { - return nil, fmt.Errorf("serving path must end with \"%s\"", jsonExt) + return fmt.Errorf("serving path must end with \"%s\"", jsonExt) } servePathBase := strings.TrimSuffix(servePath, jsonExt) - o := OpenAPIService{} - if err := o.UpdateSpec(openapiSpec); err != nil { - return nil, err - } - type fileInfo struct { ext string getDataAndETag func() ([]byte, string, time.Time) @@ -137,7 +161,7 @@ func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler )) } - return &o, nil + return nil } func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) { @@ -159,11 +183,15 @@ func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) { } func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) { - specBytes, err := json.MarshalIndent(openapiSpec, " ", " ") + specBytes, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec) if err != nil { return err } - specPb, err := toProtoBinary(specBytes) + var json map[string]interface{} + if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(specBytes, &json); err != nil { + return err + } + specPb, err := ToProtoBinary(json) if err != nil { return err } @@ -189,13 +217,33 @@ func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) { return nil } -func toProtoBinary(spec []byte) ([]byte, error) { - var info yaml.MapSlice - err := yaml.Unmarshal(spec, &info) - if err != nil { - return nil, err +func jsonToYAML(j map[string]interface{}) yaml.MapSlice { + if j == nil { + return nil } - document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil)) + ret := make(yaml.MapSlice, 0, len(j)) + for k, v := range j { + ret = append(ret, yaml.MapItem{k, jsonToYAMLValue(v)}) + } + return ret +} + +func jsonToYAMLValue(j interface{}) interface{} { + switch j := j.(type) { + case map[string]interface{}: + return jsonToYAML(j) + case []interface{}: + ret := make([]interface{}, len(j)) + for i := range j { + ret[i] = jsonToYAMLValue(j[i]) + } + return ret + } + return j +} + +func ToProtoBinary(json map[string]interface{}) ([]byte, error) { + document, err := openapi_v2.NewDocument(jsonToYAML(json), compiler.NewContext("$root", nil)) if err != nil { return nil, err } @@ -211,12 +259,18 @@ func toGzip(data []byte) []byte { } // RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec. -func RegisterOpenAPIVersionedService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { - o := OpenAPIService{} - if err := o.UpdateSpec(openapiSpec); err != nil { +// +// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead. +func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) { + o, err := NewOpenAPIService(spec) + if err != nil { return nil, err } + return o, o.RegisterOpenAPIVersionedService(servePath, handler) +} +// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec. +func (o *OpenAPIService) RegisterOpenAPIVersionedService(servePath string, handler common.PathHandler) error { accepted := []struct { Type string SubType string @@ -257,7 +311,7 @@ func RegisterOpenAPIVersionedService(openapiSpec *spec.Swagger, servePath string }), )) - return &o, nil + return nil } // BuildAndRegisterOpenAPIVersionedService builds the spec and registers a handler to provide access to it. @@ -267,5 +321,9 @@ func BuildAndRegisterOpenAPIVersionedService(servePath string, webServices []*re if err != nil { return nil, err } - return RegisterOpenAPIVersionedService(spec, servePath, handler) + o, err := NewOpenAPIService(spec) + if err != nil { + return nil, err + } + return o, o.RegisterOpenAPIVersionedService(servePath, handler) } diff --git a/vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go b/vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go index a885527c72..df0e22da78 100644 --- a/vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go +++ b/vendor/k8s.io/kube-openapi/pkg/schemaconv/smd.go @@ -98,6 +98,13 @@ func (c *convert) insertTypeDef(name string, model proto.Schema) { func (c *convert) makeRef(model proto.Schema) schema.TypeRef { var tr schema.TypeRef if r, ok := model.(*proto.Ref); ok { + if r.Reference() == "io.k8s.apimachinery.pkg.runtime.RawExtension" { + return schema.TypeRef{ + Inlined: schema.Atom{ + Untyped: &schema.Untyped{}, + }, + } + } // reference a named type _, n := path.Split(r.Reference()) tr.NamedType = &n diff --git a/vendor/k8s.io/kube-openapi/pkg/util/util.go b/vendor/k8s.io/kube-openapi/pkg/util/util.go index c5c42cd44c..1eb674eea0 100644 --- a/vendor/k8s.io/kube-openapi/pkg/util/util.go +++ b/vendor/k8s.io/kube-openapi/pkg/util/util.go @@ -21,14 +21,39 @@ import ( "strings" ) -// ToCanonicalName converts Golang package/type name into canonical OpenAPI name. -// Examples: +// [DEPRECATED] ToCanonicalName converts Golang package/type canonical name into REST friendly OpenAPI name. +// This method is deprecated because it has a misleading name. Please use ToRESTFriendlyName +// instead +// +// NOTE: actually the "canonical name" in this method should be named "REST friendly OpenAPI name", +// which is different from "canonical name" defined in GetCanonicalTypeName. The "canonical name" defined +// in GetCanonicalTypeName means Go type names with full package path. +// +// Examples of REST friendly OpenAPI name: // Input: k8s.io/api/core/v1.Pod // Output: io.k8s.api.core.v1.Pod // // Input: k8s.io/api/core/v1 // Output: io.k8s.api.core.v1 +// +// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo +// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo func ToCanonicalName(name string) string { + return ToRESTFriendlyName(name) +} + +// ToRESTFriendlyName converts Golang package/type canonical name into REST friendly OpenAPI name. +// +// Examples of REST friendly OpenAPI name: +// Input: k8s.io/api/core/v1.Pod +// Output: io.k8s.api.core.v1.Pod +// +// Input: k8s.io/api/core/v1 +// Output: io.k8s.api.core.v1 +// +// Input: csi.storage.k8s.io/v1alpha1.CSINodeInfo +// Output: io.k8s.storage.csi.v1alpha1.CSINodeInfo +func ToRESTFriendlyName(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], ".") { @@ -41,9 +66,30 @@ func ToCanonicalName(name string) string { return strings.Join(nameParts, ".") } +// OpenAPICanonicalTypeNamer is an interface for models without Go type to seed model name. +// +// OpenAPI canonical names are Go type names with full package path, for uniquely indentifying +// a model / Go type. If a Go type is vendored from another package, only the path after "/vendor/" +// should be used. For custom resource definition (CRD), the canonical name is expected to be +// group/version.kind +// +// Examples of canonical name: +// Go type: k8s.io/kubernetes/pkg/apis/core.Pod +// CRD: csi.storage.k8s.io/v1alpha1.CSINodeInfo +// +// Example for vendored Go type: +// Original full path: k8s.io/kubernetes/vendor/k8s.io/api/core/v1.Pod +// Canonical name: k8s.io/api/core/v1.Pod +type OpenAPICanonicalTypeNamer interface { + OpenAPICanonicalTypeName() string +} + // GetCanonicalTypeName will find the canonical type name of a sample object, removing // the "vendor" part of the path func GetCanonicalTypeName(model interface{}) string { + if namer, ok := model.(OpenAPICanonicalTypeNamer); ok { + return namer.OpenAPICanonicalTypeName() + } t := reflect.TypeOf(model) if t.Kind() == reflect.Ptr { t = t.Elem()