mirror of https://github.com/k3s-io/k3s
bump(k8s.io/kube-openapi): c12348ce28de40eed0136aa2b644d0ee0650e56c
parent
2fe54a144b
commit
84f0629e95
|
@ -4099,59 +4099,59 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen",
|
"ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen/args",
|
"ImportPath": "k8s.io/kube-openapi/cmd/openapi-gen/args",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/aggregator",
|
"ImportPath": "k8s.io/kube-openapi/pkg/aggregator",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
|
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/common",
|
"ImportPath": "k8s.io/kube-openapi/pkg/common",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
|
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/generators/rules",
|
"ImportPath": "k8s.io/kube-openapi/pkg/generators/rules",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/handler",
|
"ImportPath": "k8s.io/kube-openapi/pkg/handler",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/schemaconv",
|
"ImportPath": "k8s.io/kube-openapi/pkg/schemaconv",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/util",
|
"ImportPath": "k8s.io/kube-openapi/pkg/util",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto/testing",
|
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto/testing",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto/validation",
|
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto/validation",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/sets",
|
"ImportPath": "k8s.io/kube-openapi/pkg/util/sets",
|
||||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ImportPath": "k8s.io/repo-infra/kazel",
|
"ImportPath": "k8s.io/repo-infra/kazel",
|
||||||
|
|
|
@ -2,7 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||||
|
|
||||||
go_library(
|
go_library(
|
||||||
name = "go_default_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",
|
importmap = "k8s.io/kubernetes/vendor/k8s.io/kube-openapi/pkg/aggregator",
|
||||||
importpath = "k8s.io/kube-openapi/pkg/aggregator",
|
importpath = "k8s.io/kube-openapi/pkg/aggregator",
|
||||||
visibility = ["//visibility:public"],
|
visibility = ["//visibility:public"],
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
package aggregator
|
package aggregator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -27,154 +26,14 @@ import (
|
||||||
"k8s.io/kube-openapi/pkg/util"
|
"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.
|
// 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{}
|
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) {
|
if refStr := ref.String(); refStr != "" && strings.HasPrefix(refStr, definitionPrefix) {
|
||||||
usedDefinitions[refStr[len(definitionPrefix):]] = true
|
usedDefinitions[refStr[len(definitionPrefix):]] = true
|
||||||
}
|
}
|
||||||
return ref
|
}, root)
|
||||||
}, sp)
|
|
||||||
return usedDefinitions
|
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
|
// i.e. if a Path removed by this function, all definitions used by it and not used
|
||||||
// anywhere else will also be removed.
|
// anywhere else will also be removed.
|
||||||
func FilterSpecByPaths(sp *spec.Swagger, keepPathPrefixes []string) {
|
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
|
// Walk all references to find all used definitions. This function
|
||||||
// want to only deal with unused definitions resulted from filtering paths.
|
// 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
|
// 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
|
// First remove unwanted paths
|
||||||
prefixes := util.NewTrie(keepPathPrefixes)
|
prefixes := util.NewTrie(keepPathPrefixes)
|
||||||
orgPaths := sp.Paths
|
ret := *sp
|
||||||
if orgPaths == nil {
|
ret.Paths = &spec.Paths{
|
||||||
return
|
VendorExtensible: sp.Paths.VendorExtensible,
|
||||||
}
|
|
||||||
sp.Paths = &spec.Paths{
|
|
||||||
VendorExtensible: orgPaths.VendorExtensible,
|
|
||||||
Paths: map[string]spec.PathItem{},
|
Paths: map[string]spec.PathItem{},
|
||||||
}
|
}
|
||||||
for path, pathItem := range orgPaths.Paths {
|
for path, pathItem := range sp.Paths.Paths {
|
||||||
if !prefixes.HasPrefix(path) {
|
if !prefixes.HasPrefix(path) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sp.Paths.Paths[path] = pathItem
|
ret.Paths.Paths[path] = pathItem
|
||||||
}
|
}
|
||||||
|
|
||||||
// Walk all references to find all definition references.
|
// Walk all references to find all definition references.
|
||||||
usedDefinitions := usedDefinitionForSpec(sp)
|
usedDefinitions := usedDefinitionForSpec(&ret)
|
||||||
|
|
||||||
// Remove unused definitions
|
// Remove unused definitions
|
||||||
orgDefinitions := sp.Definitions
|
ret.Definitions = spec.Definitions{}
|
||||||
sp.Definitions = spec.Definitions{}
|
for k, v := range sp.Definitions {
|
||||||
for k, v := range orgDefinitions {
|
|
||||||
if usedDefinitions[k] || !initialUsedDefinitions[k] {
|
if usedDefinitions[k] || !initialUsedDefinitions[k] {
|
||||||
sp.Definitions[k] = v
|
ret.Definitions[k] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &ret
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameDefinition(s *spec.Swagger, old, new string) {
|
type rename struct {
|
||||||
oldRef := definitionPrefix + old
|
from, to string
|
||||||
newRef := definitionPrefix + new
|
}
|
||||||
walkOnAllReferences(func(ref spec.Ref) spec.Ref {
|
|
||||||
if ref.String() == oldRef {
|
// renameDefinition renames references, without mutating the input.
|
||||||
return spec.MustCreateRef(newRef)
|
// 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
|
return ref
|
||||||
}, s)
|
}, ret)
|
||||||
// Make sure we don't assign to nil map
|
|
||||||
if s.Definitions == nil {
|
renamedDefinitions := make(spec.Definitions, len(ret.Definitions))
|
||||||
s.Definitions = spec.Definitions{}
|
for k, v := range ret.Definitions {
|
||||||
|
if newRef, found := renames[k]; found {
|
||||||
|
k = newRef
|
||||||
|
}
|
||||||
|
renamedDefinitions[k] = v
|
||||||
}
|
}
|
||||||
s.Definitions[new] = s.Definitions[old]
|
ret.Definitions = renamedDefinitions
|
||||||
delete(s.Definitions, old)
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeSpecsIgnorePathConflict is the same as MergeSpecs except it will ignore any path
|
// MergeSpecsIgnorePathConflict is the same as MergeSpecs except it will ignore any path
|
||||||
// conflicts by keeping the paths of destination. It will rename definition conflicts.
|
// conflicts by keeping the paths of destination. It will rename definition conflicts.
|
||||||
|
// The source is not mutated.
|
||||||
func MergeSpecsIgnorePathConflict(dest, source *spec.Swagger) error {
|
func MergeSpecsIgnorePathConflict(dest, source *spec.Swagger) error {
|
||||||
return mergeSpecs(dest, source, true, true)
|
return mergeSpecs(dest, source, true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeSpecsFailOnDefinitionConflict is differ from MergeSpecs as it fails if there is
|
// MergeSpecsFailOnDefinitionConflict is differ from MergeSpecs as it fails if there is
|
||||||
// a definition conflict.
|
// a definition conflict.
|
||||||
|
// The source is not mutated.
|
||||||
func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error {
|
func MergeSpecsFailOnDefinitionConflict(dest, source *spec.Swagger) error {
|
||||||
return mergeSpecs(dest, source, false, false)
|
return mergeSpecs(dest, source, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeSpecs copies paths and definitions from source to dest, rename definitions if needed.
|
// 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.
|
// 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 {
|
func MergeSpecs(dest, source *spec.Swagger) error {
|
||||||
return mergeSpecs(dest, source, true, false)
|
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) {
|
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).
|
// Paths may be empty, due to [ACL constraints](http://goo.gl/8us55a#securityFiltering).
|
||||||
if source.Paths == nil {
|
if source.Paths == nil {
|
||||||
// When a source spec does not have any path, that means none of the definitions
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
if hasConflictingPath {
|
if hasConflictingPath {
|
||||||
source, err = CloneSpec(source)
|
source = FilterSpecByPathsWithoutSideEffects(source, keepPaths)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
specCloned = true
|
|
||||||
FilterSpecByPaths(source, keepPaths)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check for model conflicts
|
// Check for model conflicts
|
||||||
|
@ -301,21 +196,11 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||||
}
|
}
|
||||||
|
|
||||||
if conflicts {
|
if conflicts {
|
||||||
if !specCloned {
|
|
||||||
source, err = CloneSpec(source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
specCloned = true
|
|
||||||
usedNames := map[string]bool{}
|
usedNames := map[string]bool{}
|
||||||
for k := range dest.Definitions {
|
for k := range dest.Definitions {
|
||||||
usedNames[k] = true
|
usedNames[k] = true
|
||||||
}
|
}
|
||||||
type Rename struct {
|
renames := map[string]string{}
|
||||||
from, to string
|
|
||||||
}
|
|
||||||
renames := []Rename{}
|
|
||||||
|
|
||||||
OUTERLOOP:
|
OUTERLOOP:
|
||||||
for k, v := range source.Definitions {
|
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)
|
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||||
v2, found = dest.Definitions[newName]
|
v2, found = dest.Definitions[newName]
|
||||||
if found && reflect.DeepEqual(v, v2) {
|
if found && reflect.DeepEqual(v, v2) {
|
||||||
renames = append(renames, Rename{from: k, to: newName})
|
renames[k] = newName
|
||||||
continue OUTERLOOP
|
continue OUTERLOOP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,13 +230,11 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||||
newName = fmt.Sprintf("%s_v%d", k, i)
|
newName = fmt.Sprintf("%s_v%d", k, i)
|
||||||
_, foundInSource = source.Definitions[newName]
|
_, foundInSource = source.Definitions[newName]
|
||||||
}
|
}
|
||||||
renames = append(renames, Rename{from: k, to: newName})
|
renames[k] = newName
|
||||||
usedNames[newName] = true
|
usedNames[newName] = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, r := range renames {
|
source = renameDefinition(source, renames)
|
||||||
renameDefinition(source, r.from, r.to)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for k, v := range source.Definitions {
|
for k, v := range source.Definitions {
|
||||||
if _, found := dest.Definitions[k]; !found {
|
if _, found := dest.Definitions[k]; !found {
|
||||||
|
@ -374,18 +257,3 @@ func mergeSpecs(dest, source *spec.Swagger, renameModelConflicts, ignorePathConf
|
||||||
}
|
}
|
||||||
return nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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))
|
sw.Do("return map[string]$.OpenAPIDefinition|raw${\n", argsFromType(nil))
|
||||||
|
|
||||||
for _, t := range c.Order {
|
for _, t := range c.Order {
|
||||||
err := newOpenAPITypeWriter(sw).generateCall(t)
|
err := newOpenAPITypeWriter(sw, c).generateCall(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
func (g *openAPIGen) GenerateType(c *generator.Context, t *types.Type, w io.Writer) error {
|
||||||
klog.V(5).Infof("generating for type %v", t)
|
klog.V(5).Infof("generating for type %v", t)
|
||||||
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
sw := generator.NewSnippetWriter(w, c, "$", "$")
|
||||||
err := newOpenAPITypeWriter(sw).generate(t)
|
err := newOpenAPITypeWriter(sw, c).generate(t)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -221,13 +221,15 @@ func shouldInlineMembers(m *types.Member) bool {
|
||||||
|
|
||||||
type openAPITypeWriter struct {
|
type openAPITypeWriter struct {
|
||||||
*generator.SnippetWriter
|
*generator.SnippetWriter
|
||||||
|
context *generator.Context
|
||||||
refTypes map[string]*types.Type
|
refTypes map[string]*types.Type
|
||||||
GetDefinitionInterface *types.Type
|
GetDefinitionInterface *types.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func newOpenAPITypeWriter(sw *generator.SnippetWriter) openAPITypeWriter {
|
func newOpenAPITypeWriter(sw *generator.SnippetWriter, c *generator.Context) openAPITypeWriter {
|
||||||
return openAPITypeWriter{
|
return openAPITypeWriter{
|
||||||
SnippetWriter: sw,
|
SnippetWriter: sw,
|
||||||
|
context: c,
|
||||||
refTypes: map[string]*types.Type{},
|
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.Do("return $.OpenAPIDefinition|raw${\nSchema: spec.Schema{\nSchemaProps: spec.SchemaProps{\n", args)
|
||||||
g.generateDescription(t.CommentLines)
|
g.generateDescription(t.CommentLines)
|
||||||
g.Do("Type: []string{\"object\"},\n", nil)
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if len(required) > 0 {
|
||||||
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
|
g.Do("Required: []string{\"$.$\"},\n", strings.Join(required, "\",\""))
|
||||||
}
|
}
|
||||||
|
@ -351,13 +363,14 @@ func (g openAPITypeWriter) generate(t *types.Type) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
g.Do("},\n", nil)
|
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.
|
// Map order is undefined, sort them or we may get a different file generated each time.
|
||||||
keys := []string{}
|
keys := []string{}
|
||||||
for k := range g.refTypes {
|
for k := range g.refTypes {
|
||||||
keys = append(keys, k)
|
keys = append(keys, k)
|
||||||
}
|
}
|
||||||
sort.Strings(keys)
|
sort.Strings(keys)
|
||||||
|
deps := []string{}
|
||||||
for _, k := range keys {
|
for _, k := range keys {
|
||||||
v := g.refTypes[k]
|
v := g.refTypes[k]
|
||||||
if t, _ := openapi.GetOpenAPITypeFormat(v.String()); t != "" {
|
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
|
// Will eliminate special case of time.Time
|
||||||
continue
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -409,7 +429,7 @@ func (g openAPITypeWriter) emitExtensions(extensions []extension) {
|
||||||
for _, extension := range extensions {
|
for _, extension := range extensions {
|
||||||
g.Do("\"$.$\": ", extension.xName)
|
g.Do("\"$.$\": ", extension.xName)
|
||||||
if extension.hasMultipleValues() {
|
if extension.hasMultipleValues() {
|
||||||
g.Do("[]string{\n", nil)
|
g.Do("[]interface{}{\n", nil)
|
||||||
}
|
}
|
||||||
for _, value := range extension.values {
|
for _, value := range extension.values {
|
||||||
g.Do("\"$.$\",\n", value)
|
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)
|
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("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())
|
typeString, format := openapi.GetOpenAPITypeFormat(elemType.String())
|
||||||
if typeString != "" {
|
if typeString != "" {
|
||||||
g.generateSimpleProperty(typeString, format)
|
g.generateSimpleProperty(typeString, format)
|
||||||
|
|
|
@ -13,6 +13,7 @@ go_library(
|
||||||
"//vendor/github.com/golang/protobuf/proto:go_default_library",
|
"//vendor/github.com/golang/protobuf/proto:go_default_library",
|
||||||
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
|
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
|
||||||
"//vendor/github.com/googleapis/gnostic/compiler: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/github.com/munnerz/goautoneg:go_default_library",
|
||||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||||
"//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library",
|
"//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library",
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto/sha512"
|
"crypto/sha512"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -28,15 +27,15 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
yaml "gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/NYTimes/gziphandler"
|
"github.com/NYTimes/gziphandler"
|
||||||
restful "github.com/emicklei/go-restful"
|
restful "github.com/emicklei/go-restful"
|
||||||
"github.com/go-openapi/spec"
|
"github.com/go-openapi/spec"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
|
openapi_v2 "github.com/googleapis/gnostic/OpenAPIv2"
|
||||||
"github.com/googleapis/gnostic/compiler"
|
"github.com/googleapis/gnostic/compiler"
|
||||||
|
"github.com/json-iterator/go"
|
||||||
"github.com/munnerz/goautoneg"
|
"github.com/munnerz/goautoneg"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
|
||||||
"k8s.io/kube-openapi/pkg/builder"
|
"k8s.io/kube-openapi/pkg/builder"
|
||||||
"k8s.io/kube-openapi/pkg/common"
|
"k8s.io/kube-openapi/pkg/common"
|
||||||
|
@ -78,6 +77,15 @@ func computeETag(data []byte) string {
|
||||||
return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
|
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,
|
// 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
|
// 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.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
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,
|
// 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.
|
// 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
|
// Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
|
||||||
// json file and will also serve .pb and .gz files.
|
// 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) {
|
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)
|
servePathBase := strings.TrimSuffix(servePath, jsonExt)
|
||||||
|
|
||||||
o := OpenAPIService{}
|
|
||||||
if err := o.UpdateSpec(openapiSpec); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type fileInfo struct {
|
type fileInfo struct {
|
||||||
ext string
|
ext string
|
||||||
getDataAndETag func() ([]byte, string, time.Time)
|
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) {
|
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) {
|
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
|
||||||
specBytes, err := json.MarshalIndent(openapiSpec, " ", " ")
|
specBytes, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(openapiSpec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -189,13 +217,33 @@ func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toProtoBinary(spec []byte) ([]byte, error) {
|
func jsonToYAML(j map[string]interface{}) yaml.MapSlice {
|
||||||
var info yaml.MapSlice
|
if j == nil {
|
||||||
err := yaml.Unmarshal(spec, &info)
|
return nil
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -211,12 +259,18 @@ func toGzip(data []byte) []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterOpenAPIVersionedService registers a handler to provide access to provided swagger spec.
|
// 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{}
|
// Deprecated: use OpenAPIService.RegisterOpenAPIVersionedService instead.
|
||||||
if err := o.UpdateSpec(openapiSpec); err != nil {
|
func RegisterOpenAPIVersionedService(spec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
|
||||||
|
o, err := NewOpenAPIService(spec)
|
||||||
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
accepted := []struct {
|
||||||
Type string
|
Type string
|
||||||
SubType 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.
|
// 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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return RegisterOpenAPIVersionedService(spec, servePath, handler)
|
o, err := NewOpenAPIService(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return o, o.RegisterOpenAPIVersionedService(servePath, handler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,6 +98,13 @@ func (c *convert) insertTypeDef(name string, model proto.Schema) {
|
||||||
func (c *convert) makeRef(model proto.Schema) schema.TypeRef {
|
func (c *convert) makeRef(model proto.Schema) schema.TypeRef {
|
||||||
var tr schema.TypeRef
|
var tr schema.TypeRef
|
||||||
if r, ok := model.(*proto.Ref); ok {
|
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
|
// reference a named type
|
||||||
_, n := path.Split(r.Reference())
|
_, n := path.Split(r.Reference())
|
||||||
tr.NamedType = &n
|
tr.NamedType = &n
|
||||||
|
|
|
@ -21,14 +21,39 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ToCanonicalName converts Golang package/type name into canonical OpenAPI name.
|
// [DEPRECATED] ToCanonicalName converts Golang package/type canonical name into REST friendly OpenAPI name.
|
||||||
// Examples:
|
// 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
|
// Input: k8s.io/api/core/v1.Pod
|
||||||
// Output: io.k8s.api.core.v1.Pod
|
// Output: io.k8s.api.core.v1.Pod
|
||||||
//
|
//
|
||||||
// Input: k8s.io/api/core/v1
|
// Input: k8s.io/api/core/v1
|
||||||
// Output: io.k8s.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 {
|
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, "/")
|
nameParts := strings.Split(name, "/")
|
||||||
// Reverse first part. e.g., io.k8s... instead of k8s.io...
|
// Reverse first part. e.g., io.k8s... instead of k8s.io...
|
||||||
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
|
if len(nameParts) > 0 && strings.Contains(nameParts[0], ".") {
|
||||||
|
@ -41,9 +66,30 @@ func ToCanonicalName(name string) string {
|
||||||
return strings.Join(nameParts, ".")
|
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
|
// GetCanonicalTypeName will find the canonical type name of a sample object, removing
|
||||||
// the "vendor" part of the path
|
// the "vendor" part of the path
|
||||||
func GetCanonicalTypeName(model interface{}) string {
|
func GetCanonicalTypeName(model interface{}) string {
|
||||||
|
if namer, ok := model.(OpenAPICanonicalTypeNamer); ok {
|
||||||
|
return namer.OpenAPICanonicalTypeName()
|
||||||
|
}
|
||||||
t := reflect.TypeOf(model)
|
t := reflect.TypeOf(model)
|
||||||
if t.Kind() == reflect.Ptr {
|
if t.Kind() == reflect.Ptr {
|
||||||
t = t.Elem()
|
t = t.Elem()
|
||||||
|
|
Loading…
Reference in New Issue