mirror of https://github.com/k3s-io/k3s
Merge pull request #71223 from sttts/sttts-openapi-aggreation-without-clone
openapi-aggregation: speed up merging from 1 sec to 50-100 mspull/564/head
commit
6912bbb153
|
@ -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",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -339,10 +339,9 @@ kube::util::create-fake-git-tree() {
|
|||
kube::util::godep_restored() {
|
||||
local -r godeps_json=${1:-Godeps/Godeps.json}
|
||||
local -r gopath=${2:-${GOPATH%:*}}
|
||||
if ! which jq &>/dev/null; then
|
||||
echo "jq not found. Please install." 1>&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
kube::util::require-jq
|
||||
|
||||
local root
|
||||
local old_rev=""
|
||||
while read path rev; do
|
||||
|
@ -461,7 +460,7 @@ kube::util::base_ref() {
|
|||
fi
|
||||
|
||||
full_branch="$(kube::util::git_upstream_remote_name)/${git_branch}"
|
||||
|
||||
|
||||
# make sure the branch is valid, otherwise the check will pass erroneously.
|
||||
if ! git describe "${full_branch}" >/dev/null; then
|
||||
# abort!
|
||||
|
@ -479,7 +478,7 @@ kube::util::has_changes() {
|
|||
local -r git_branch=$1
|
||||
local -r pattern=$2
|
||||
local -r not_pattern=${3:-totallyimpossiblepattern}
|
||||
|
||||
|
||||
local base_ref=$(kube::util::base_ref "${git_branch}")
|
||||
echo "Checking for '${pattern}' changes against '${base_ref}'"
|
||||
|
||||
|
@ -792,6 +791,15 @@ function kube::util::check-file-in-alphabetical-order {
|
|||
fi
|
||||
}
|
||||
|
||||
# kube::util::require-jq
|
||||
# Checks whether jq is installed.
|
||||
function kube::util::require-jq {
|
||||
if ! which jq &>/dev/null; then
|
||||
echo "jq not found. Please install." 1>&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Some useful colors.
|
||||
if [[ -z "${color_start-}" ]]; then
|
||||
declare -r color_start="\033["
|
||||
|
|
|
@ -25,6 +25,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
|
|||
OPENAPI_ROOT_DIR="${KUBE_ROOT}/api/openapi-spec"
|
||||
source "${KUBE_ROOT}/hack/lib/init.sh"
|
||||
|
||||
kube::util::require-jq
|
||||
kube::golang::setup_env
|
||||
|
||||
make -C "${KUBE_ROOT}" WHAT=cmd/kube-apiserver
|
||||
|
@ -83,7 +84,7 @@ fi
|
|||
|
||||
kube::log::status "Updating " ${OPENAPI_ROOT_DIR}
|
||||
|
||||
curl -w "\n" -fs "${API_HOST}:${API_PORT}/openapi/v2" > "${OPENAPI_ROOT_DIR}/swagger.json"
|
||||
curl -w "\n" -fs "${API_HOST}:${API_PORT}/openapi/v2" | jq -S . > "${OPENAPI_ROOT_DIR}/swagger.json"
|
||||
|
||||
kube::log::status "SUCCESS"
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load("//build:code_generation.bzl", "gen_openapi", "openapi_deps")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library")
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
gen_openapi(
|
||||
outs = ["zz_generated.openapi.go"],
|
||||
|
@ -30,3 +30,13 @@ filegroup(
|
|||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["openapi_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||
],
|
||||
)
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package openapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
)
|
||||
|
||||
func TestOpenAPIRoundtrip(t *testing.T) {
|
||||
dummyRef := func(name string) spec.Ref { return spec.MustCreateRef("#/definitions/dummy") }
|
||||
for name, value := range GetOpenAPIDefinitions(dummyRef) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
data, err := json.Marshal(value.Schema)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
roundTripped := spec.Schema{}
|
||||
if err := json.Unmarshal(data, &roundTripped); err != nil {
|
||||
t.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(value.Schema, roundTripped) {
|
||||
t.Errorf("unexpected diff (a=expected,b=roundtripped):\n%s", diff.ObjectReflectDiff(value.Schema, roundTripped))
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -2252,27 +2252,27 @@
|
|||
},
|
||||
{
|
||||
"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/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/apimachinery/pkg/api/apitesting/fuzzer",
|
||||
|
|
|
@ -68,7 +68,7 @@ type JSON struct {
|
|||
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
|
||||
func (_ JSON) OpenAPISchemaType() []string {
|
||||
// TODO: return actual types when anyOf is supported
|
||||
return []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
|
||||
|
@ -91,7 +91,7 @@ type JSONSchemaPropsOrArray struct {
|
|||
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
|
||||
func (_ JSONSchemaPropsOrArray) OpenAPISchemaType() []string {
|
||||
// TODO: return actual types when anyOf is supported
|
||||
return []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
|
||||
|
@ -111,7 +111,7 @@ type JSONSchemaPropsOrBool struct {
|
|||
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
|
||||
func (_ JSONSchemaPropsOrBool) OpenAPISchemaType() []string {
|
||||
// TODO: return actual types when anyOf is supported
|
||||
return []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
|
||||
|
@ -133,7 +133,7 @@ type JSONSchemaPropsOrStringArray struct {
|
|||
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
|
||||
func (_ JSONSchemaPropsOrStringArray) OpenAPISchemaType() []string {
|
||||
// TODO: return actual types when anyOf is supported
|
||||
return []string{}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
|
||||
|
|
|
@ -176,7 +176,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath",
|
||||
|
|
|
@ -1944,31 +1944,31 @@
|
|||
},
|
||||
{
|
||||
"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/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/client-go/discovery",
|
||||
|
|
|
@ -11,7 +11,6 @@ go_test(
|
|||
srcs = ["openapi_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi/testing:go_default_library",
|
||||
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||
|
|
|
@ -108,6 +108,18 @@ func (s groupVersionKinds) Less(i, j int) bool {
|
|||
return s[i].Group < s[j].Group
|
||||
}
|
||||
|
||||
func (s groupVersionKinds) JSON() []interface{} {
|
||||
j := []interface{}{}
|
||||
for _, gvk := range s {
|
||||
j = append(j, map[string]interface{}{
|
||||
"group": gvk.Group,
|
||||
"version": gvk.Version,
|
||||
"kind": gvk.Kind,
|
||||
})
|
||||
}
|
||||
return j
|
||||
}
|
||||
|
||||
// DefinitionNamer is the type to customize OpenAPI definition name.
|
||||
type DefinitionNamer struct {
|
||||
typeGroupVersionKinds map[string]groupVersionKinds
|
||||
|
@ -172,7 +184,7 @@ func NewDefinitionNamer(schemes ...*runtime.Scheme) *DefinitionNamer {
|
|||
func (d *DefinitionNamer) GetDefinitionName(name string) (string, spec.Extensions) {
|
||||
if groupVersionKinds, ok := d.typeGroupVersionKinds[name]; ok {
|
||||
return friendlyName(name), spec.Extensions{
|
||||
extensionGVK: []v1.GroupVersionKind(groupVersionKinds),
|
||||
extensionGVK: groupVersionKinds.JSON(),
|
||||
}
|
||||
}
|
||||
return friendlyName(name), nil
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
openapitesting "k8s.io/apiserver/pkg/endpoints/openapi/testing"
|
||||
)
|
||||
|
@ -58,13 +57,13 @@ func TestGetDefinitionName(t *testing.T) {
|
|||
namer := NewDefinitionNamer(s)
|
||||
n, e := namer.GetDefinitionName(typePkgName)
|
||||
assertEqual(t, typeFriendlyName, n)
|
||||
assertEqual(t, e["x-kubernetes-group-version-kind"], []v1.GroupVersionKind{
|
||||
{
|
||||
Group: "test",
|
||||
Version: "v1",
|
||||
Kind: "TestType",
|
||||
assertEqual(t, []interface{}{
|
||||
map[string]interface{}{
|
||||
"group": "test",
|
||||
"version": "v1",
|
||||
"kind": "TestType",
|
||||
},
|
||||
})
|
||||
}, e["x-kubernetes-group-version-kind"])
|
||||
n, e2 := namer.GetDefinitionName("test.com/another.Type")
|
||||
assertEqual(t, "com.test.another.Type", n)
|
||||
assertEqual(t, e2, spec.Extensions(nil))
|
||||
|
|
|
@ -89,18 +89,18 @@ func buildTestOpenAPIDefinition() kubeopenapi.OpenAPIDefinition {
|
|||
},
|
||||
VendorExtensible: openapi.VendorExtensible{
|
||||
Extensions: openapi.Extensions{
|
||||
"x-kubernetes-group-version-kind": []map[string]string{
|
||||
{
|
||||
"x-kubernetes-group-version-kind": []interface{}{
|
||||
map[string]interface{}{
|
||||
"group": "",
|
||||
"version": "v1",
|
||||
"kind": "Getter",
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"group": "batch",
|
||||
"version": "v1",
|
||||
"kind": "Getter",
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"group": "extensions",
|
||||
"version": "v1",
|
||||
"kind": "Getter",
|
||||
|
|
|
@ -44,8 +44,8 @@ func TestOpenAPIDefinitionsToProtoModels(t *testing.T) {
|
|||
},
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
Extensions: spec.Extensions{
|
||||
"x-kubernetes-group-version-kind": []map[string]string{
|
||||
{
|
||||
"x-kubernetes-group-version-kind": []interface{}{
|
||||
map[string]interface{}{
|
||||
"group": "testgroup.k8s.io",
|
||||
"version": "v1",
|
||||
"kind": "Foo",
|
||||
|
|
|
@ -612,7 +612,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/buffer",
|
||||
|
|
|
@ -976,7 +976,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/buffer",
|
||||
|
|
|
@ -544,7 +544,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/buffer",
|
||||
|
|
|
@ -1816,31 +1816,31 @@
|
|||
},
|
||||
{
|
||||
"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/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/utils/buffer",
|
||||
|
|
|
@ -196,13 +196,12 @@ func (s *specAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err err
|
|||
}
|
||||
sortByPriority(specs)
|
||||
for _, specInfo := range specs {
|
||||
// TODO: Make kube-openapi.MergeSpec(s) accept nil or empty spec as destination and just clone the spec in that case.
|
||||
if specToReturn == nil {
|
||||
specToReturn, err = aggregator.CloneSpec(specInfo.spec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
specToReturn = &spec.Swagger{}
|
||||
*specToReturn = *specInfo.spec
|
||||
// Paths and Definitions are set by MergeSpecsIgnorePathConflict
|
||||
specToReturn.Paths = nil
|
||||
specToReturn.Definitions = nil
|
||||
}
|
||||
if err := aggregator.MergeSpecsIgnorePathConflict(specToReturn, specInfo.spec); err != nil {
|
||||
return nil, err
|
||||
|
@ -267,7 +266,7 @@ func (s *specAggregator) UpdateAPIServiceSpec(apiServiceName string, spec *spec.
|
|||
// For APIServices (non-local) specs, only merge their /apis/ prefixed endpoint as it is the only paths
|
||||
// proxy handler delegates.
|
||||
if specInfo.apiService.Spec.Service != nil {
|
||||
aggregator.FilterSpecByPaths(spec, []string{"/apis/"})
|
||||
spec = aggregator.FilterSpecByPathsWithoutSideEffects(spec, []string{"/apis/"})
|
||||
}
|
||||
|
||||
return s.tryUpdatingServiceSpecs(&openAPISpecInfo{
|
||||
|
|
|
@ -520,7 +520,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/integer",
|
||||
|
|
|
@ -544,7 +544,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/buffer",
|
||||
|
|
|
@ -1772,27 +1772,27 @@
|
|||
},
|
||||
{
|
||||
"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/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/utils/buffer",
|
||||
|
|
|
@ -1152,7 +1152,7 @@
|
|||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
|
||||
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03"
|
||||
"Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "k8s.io/utils/buffer",
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
||||
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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue