Unverified Commit 6912bbb1 authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #71223 from sttts/sttts-openapi-aggreation-without-clone

openapi-aggregation: speed up merging from 1 sec to 50-100 ms
parents 986399b8 233178ee
...@@ -4099,59 +4099,59 @@ ...@@ -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",
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -339,10 +339,9 @@ kube::util::create-fake-git-tree() { ...@@ -339,10 +339,9 @@ kube::util::create-fake-git-tree() {
kube::util::godep_restored() { kube::util::godep_restored() {
local -r godeps_json=${1:-Godeps/Godeps.json} local -r godeps_json=${1:-Godeps/Godeps.json}
local -r gopath=${2:-${GOPATH%:*}} local -r gopath=${2:-${GOPATH%:*}}
if ! which jq &>/dev/null; then
echo "jq not found. Please install." 1>&2 kube::util::require-jq
return 1
fi
local root local root
local old_rev="" local old_rev=""
while read path rev; do while read path rev; do
...@@ -461,7 +460,7 @@ kube::util::base_ref() { ...@@ -461,7 +460,7 @@ kube::util::base_ref() {
fi fi
full_branch="$(kube::util::git_upstream_remote_name)/${git_branch}" full_branch="$(kube::util::git_upstream_remote_name)/${git_branch}"
# make sure the branch is valid, otherwise the check will pass erroneously. # make sure the branch is valid, otherwise the check will pass erroneously.
if ! git describe "${full_branch}" >/dev/null; then if ! git describe "${full_branch}" >/dev/null; then
# abort! # abort!
...@@ -479,7 +478,7 @@ kube::util::has_changes() { ...@@ -479,7 +478,7 @@ kube::util::has_changes() {
local -r git_branch=$1 local -r git_branch=$1
local -r pattern=$2 local -r pattern=$2
local -r not_pattern=${3:-totallyimpossiblepattern} local -r not_pattern=${3:-totallyimpossiblepattern}
local base_ref=$(kube::util::base_ref "${git_branch}") local base_ref=$(kube::util::base_ref "${git_branch}")
echo "Checking for '${pattern}' changes against '${base_ref}'" echo "Checking for '${pattern}' changes against '${base_ref}'"
...@@ -792,6 +791,15 @@ function kube::util::check-file-in-alphabetical-order { ...@@ -792,6 +791,15 @@ function kube::util::check-file-in-alphabetical-order {
fi 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. # Some useful colors.
if [[ -z "${color_start-}" ]]; then if [[ -z "${color_start-}" ]]; then
declare -r color_start="\033[" declare -r color_start="\033["
......
...@@ -25,6 +25,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/.. ...@@ -25,6 +25,7 @@ KUBE_ROOT=$(dirname "${BASH_SOURCE}")/..
OPENAPI_ROOT_DIR="${KUBE_ROOT}/api/openapi-spec" OPENAPI_ROOT_DIR="${KUBE_ROOT}/api/openapi-spec"
source "${KUBE_ROOT}/hack/lib/init.sh" source "${KUBE_ROOT}/hack/lib/init.sh"
kube::util::require-jq
kube::golang::setup_env kube::golang::setup_env
make -C "${KUBE_ROOT}" WHAT=cmd/kube-apiserver make -C "${KUBE_ROOT}" WHAT=cmd/kube-apiserver
...@@ -83,7 +84,7 @@ fi ...@@ -83,7 +84,7 @@ fi
kube::log::status "Updating " ${OPENAPI_ROOT_DIR} 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" kube::log::status "SUCCESS"
......
package(default_visibility = ["//visibility:public"]) package(default_visibility = ["//visibility:public"])
load("//build:code_generation.bzl", "gen_openapi", "openapi_deps") 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( gen_openapi(
outs = ["zz_generated.openapi.go"], outs = ["zz_generated.openapi.go"],
...@@ -30,3 +30,13 @@ filegroup( ...@@ -30,3 +30,13 @@ filegroup(
srcs = [":package-srcs"], srcs = [":package-srcs"],
tags = ["automanaged"], 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",
],
)
/*
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 @@ ...@@ -2252,27 +2252,27 @@
}, },
{ {
"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/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/apimachinery/pkg/api/apitesting/fuzzer", "ImportPath": "k8s.io/apimachinery/pkg/api/apitesting/fuzzer",
......
...@@ -68,7 +68,7 @@ type JSON struct { ...@@ -68,7 +68,7 @@ type JSON struct {
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSON) OpenAPISchemaType() []string { func (_ JSON) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported // TODO: return actual types when anyOf is supported
return []string{} return nil
} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
...@@ -91,7 +91,7 @@ type JSONSchemaPropsOrArray struct { ...@@ -91,7 +91,7 @@ type JSONSchemaPropsOrArray struct {
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrArray) OpenAPISchemaType() []string { func (_ JSONSchemaPropsOrArray) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported // TODO: return actual types when anyOf is supported
return []string{} return nil
} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
...@@ -111,7 +111,7 @@ type JSONSchemaPropsOrBool struct { ...@@ -111,7 +111,7 @@ type JSONSchemaPropsOrBool struct {
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrBool) OpenAPISchemaType() []string { func (_ JSONSchemaPropsOrBool) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported // TODO: return actual types when anyOf is supported
return []string{} return nil
} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
...@@ -133,7 +133,7 @@ type JSONSchemaPropsOrStringArray struct { ...@@ -133,7 +133,7 @@ type JSONSchemaPropsOrStringArray struct {
// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators // See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
func (_ JSONSchemaPropsOrStringArray) OpenAPISchemaType() []string { func (_ JSONSchemaPropsOrStringArray) OpenAPISchemaType() []string {
// TODO: return actual types when anyOf is supported // TODO: return actual types when anyOf is supported
return []string{} return nil
} }
// OpenAPISchemaFormat is used by the kube-openapi generator when constructing // OpenAPISchemaFormat is used by the kube-openapi generator when constructing
......
...@@ -176,7 +176,7 @@ ...@@ -176,7 +176,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath",
......
...@@ -1944,31 +1944,31 @@ ...@@ -1944,31 +1944,31 @@
}, },
{ {
"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/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/client-go/discovery", "ImportPath": "k8s.io/client-go/discovery",
......
...@@ -11,7 +11,6 @@ go_test( ...@@ -11,7 +11,6 @@ go_test(
srcs = ["openapi_test.go"], srcs = ["openapi_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ 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/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi/testing:go_default_library", "//staging/src/k8s.io/apiserver/pkg/endpoints/openapi/testing:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
......
...@@ -108,6 +108,18 @@ func (s groupVersionKinds) Less(i, j int) bool { ...@@ -108,6 +108,18 @@ func (s groupVersionKinds) Less(i, j int) bool {
return s[i].Group < s[j].Group 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. // DefinitionNamer is the type to customize OpenAPI definition name.
type DefinitionNamer struct { type DefinitionNamer struct {
typeGroupVersionKinds map[string]groupVersionKinds typeGroupVersionKinds map[string]groupVersionKinds
...@@ -172,7 +184,7 @@ func NewDefinitionNamer(schemes ...*runtime.Scheme) *DefinitionNamer { ...@@ -172,7 +184,7 @@ func NewDefinitionNamer(schemes ...*runtime.Scheme) *DefinitionNamer {
func (d *DefinitionNamer) GetDefinitionName(name string) (string, spec.Extensions) { func (d *DefinitionNamer) GetDefinitionName(name string) (string, spec.Extensions) {
if groupVersionKinds, ok := d.typeGroupVersionKinds[name]; ok { if groupVersionKinds, ok := d.typeGroupVersionKinds[name]; ok {
return friendlyName(name), spec.Extensions{ return friendlyName(name), spec.Extensions{
extensionGVK: []v1.GroupVersionKind(groupVersionKinds), extensionGVK: groupVersionKinds.JSON(),
} }
} }
return friendlyName(name), nil return friendlyName(name), nil
......
...@@ -23,7 +23,6 @@ import ( ...@@ -23,7 +23,6 @@ import (
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
openapitesting "k8s.io/apiserver/pkg/endpoints/openapi/testing" openapitesting "k8s.io/apiserver/pkg/endpoints/openapi/testing"
) )
...@@ -58,13 +57,13 @@ func TestGetDefinitionName(t *testing.T) { ...@@ -58,13 +57,13 @@ func TestGetDefinitionName(t *testing.T) {
namer := NewDefinitionNamer(s) namer := NewDefinitionNamer(s)
n, e := namer.GetDefinitionName(typePkgName) n, e := namer.GetDefinitionName(typePkgName)
assertEqual(t, typeFriendlyName, n) assertEqual(t, typeFriendlyName, n)
assertEqual(t, e["x-kubernetes-group-version-kind"], []v1.GroupVersionKind{ assertEqual(t, []interface{}{
{ map[string]interface{}{
Group: "test", "group": "test",
Version: "v1", "version": "v1",
Kind: "TestType", "kind": "TestType",
}, },
}) }, e["x-kubernetes-group-version-kind"])
n, e2 := namer.GetDefinitionName("test.com/another.Type") n, e2 := namer.GetDefinitionName("test.com/another.Type")
assertEqual(t, "com.test.another.Type", n) assertEqual(t, "com.test.another.Type", n)
assertEqual(t, e2, spec.Extensions(nil)) assertEqual(t, e2, spec.Extensions(nil))
......
...@@ -89,18 +89,18 @@ func buildTestOpenAPIDefinition() kubeopenapi.OpenAPIDefinition { ...@@ -89,18 +89,18 @@ func buildTestOpenAPIDefinition() kubeopenapi.OpenAPIDefinition {
}, },
VendorExtensible: openapi.VendorExtensible{ VendorExtensible: openapi.VendorExtensible{
Extensions: openapi.Extensions{ Extensions: openapi.Extensions{
"x-kubernetes-group-version-kind": []map[string]string{ "x-kubernetes-group-version-kind": []interface{}{
{ map[string]interface{}{
"group": "", "group": "",
"version": "v1", "version": "v1",
"kind": "Getter", "kind": "Getter",
}, },
{ map[string]interface{}{
"group": "batch", "group": "batch",
"version": "v1", "version": "v1",
"kind": "Getter", "kind": "Getter",
}, },
{ map[string]interface{}{
"group": "extensions", "group": "extensions",
"version": "v1", "version": "v1",
"kind": "Getter", "kind": "Getter",
......
...@@ -44,8 +44,8 @@ func TestOpenAPIDefinitionsToProtoModels(t *testing.T) { ...@@ -44,8 +44,8 @@ func TestOpenAPIDefinitionsToProtoModels(t *testing.T) {
}, },
VendorExtensible: spec.VendorExtensible{ VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{ Extensions: spec.Extensions{
"x-kubernetes-group-version-kind": []map[string]string{ "x-kubernetes-group-version-kind": []interface{}{
{ map[string]interface{}{
"group": "testgroup.k8s.io", "group": "testgroup.k8s.io",
"version": "v1", "version": "v1",
"kind": "Foo", "kind": "Foo",
......
...@@ -612,7 +612,7 @@ ...@@ -612,7 +612,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -976,7 +976,7 @@ ...@@ -976,7 +976,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -544,7 +544,7 @@ ...@@ -544,7 +544,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -1816,31 +1816,31 @@ ...@@ -1816,31 +1816,31 @@
}, },
{ {
"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/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/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -196,13 +196,12 @@ func (s *specAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err err ...@@ -196,13 +196,12 @@ func (s *specAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err err
} }
sortByPriority(specs) sortByPriority(specs)
for _, specInfo := range 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 { if specToReturn == nil {
specToReturn, err = aggregator.CloneSpec(specInfo.spec) specToReturn = &spec.Swagger{}
if err != nil { *specToReturn = *specInfo.spec
return nil, err // Paths and Definitions are set by MergeSpecsIgnorePathConflict
} specToReturn.Paths = nil
continue specToReturn.Definitions = nil
} }
if err := aggregator.MergeSpecsIgnorePathConflict(specToReturn, specInfo.spec); err != nil { if err := aggregator.MergeSpecsIgnorePathConflict(specToReturn, specInfo.spec); err != nil {
return nil, err return nil, err
...@@ -267,7 +266,7 @@ func (s *specAggregator) UpdateAPIServiceSpec(apiServiceName string, spec *spec. ...@@ -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 // For APIServices (non-local) specs, only merge their /apis/ prefixed endpoint as it is the only paths
// proxy handler delegates. // proxy handler delegates.
if specInfo.apiService.Spec.Service != nil { if specInfo.apiService.Spec.Service != nil {
aggregator.FilterSpecByPaths(spec, []string{"/apis/"}) spec = aggregator.FilterSpecByPathsWithoutSideEffects(spec, []string{"/apis/"})
} }
return s.tryUpdatingServiceSpecs(&openAPISpecInfo{ return s.tryUpdatingServiceSpecs(&openAPISpecInfo{
......
...@@ -520,7 +520,7 @@ ...@@ -520,7 +520,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/integer", "ImportPath": "k8s.io/utils/integer",
......
...@@ -544,7 +544,7 @@ ...@@ -544,7 +544,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -1772,27 +1772,27 @@ ...@@ -1772,27 +1772,27 @@
}, },
{ {
"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/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/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -1152,7 +1152,7 @@ ...@@ -1152,7 +1152,7 @@
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/util/proto", "ImportPath": "k8s.io/kube-openapi/pkg/util/proto",
"Rev": "ced9eb3070a5f1c548ef46e8dfe2a97c208d9f03" "Rev": "d7c86cdc46e3a4fcf892b32dd7bc3aa775e0870e"
}, },
{ {
"ImportPath": "k8s.io/utils/buffer", "ImportPath": "k8s.io/utils/buffer",
......
...@@ -2,7 +2,11 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library") ...@@ -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"],
......
/*
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(&param.Ref)
s.walkSchema(param.Schema)
if param.Items != nil {
s.walkRefCallback(&param.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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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)
}
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\n", nil) g.Do("}\n}\n\n", nil)
} }
return nil return nil
} }
...@@ -409,7 +429,7 @@ func (g openAPITypeWriter) emitExtensions(extensions []extension) { ...@@ -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 { ...@@ -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( ...@@ -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 ( ...@@ -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 ( ...@@ -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 { ...@@ -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,28 @@ func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.Web ...@@ -89,7 +97,28 @@ 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,
// 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.
//
// 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, // NOTE: [DEPRECATION] We will announce deprecation for format-separated endpoints for OpenAPI spec,
...@@ -99,18 +128,13 @@ func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.Web ...@@ -99,18 +128,13 @@ 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) { 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 ...@@ -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) { ...@@ -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) { ...@@ -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 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
} }
document, err := openapi_v2.NewDocument(info, compiler.NewContext("$root", nil)) 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 { ...@@ -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 ...@@ -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 ...@@ -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) { ...@@ -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 ( ...@@ -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 { ...@@ -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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment