Commit 9067d359 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #48861 from mbohlool/openapi_aggr

Automatic merge from submit-queue (batch tested with PRs 49992, 48861, 49267, 49356, 49886) Reintegrate aggregation support for OpenAPI Reintegrating changes of #46734 Changes summary: - Extracted all OpenAPI specs to new repo `kube-openapi` - Make OpenAPI spec aggregator to copy and rename any non-requal model (even with documentation change only). - Load specs when adding APIServices and retry on failure until successful spec retrieval or a 404. - Assumes all Specs except aggregator's Spec are static - A re-register of any APIService will result in updating the spec for that service (Suggestion for TPR: they should be registered to aggregator API Server, Open for discussion if any more changes needed for another PR.) fixes #48548
parents 35c3a51e 10d9a475
...@@ -3033,6 +3033,30 @@ ...@@ -3033,6 +3033,30 @@
"Rev": "c2ac40f1adf8c42a79badddb2a2acd673cae3bcb" "Rev": "c2ac40f1adf8c42a79badddb2a2acd673cae3bcb"
}, },
{ {
"ImportPath": "k8s.io/kube-openapi/pkg/aggregator",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/builder",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/common",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/generators",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/handler",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/kube-openapi/pkg/util",
"Rev": "80f07ef71bb4f781233c65aa8d0369e4ecafab87"
},
{
"ImportPath": "k8s.io/utils/exec", "ImportPath": "k8s.io/utils/exec",
"Rev": "9fdc871a36f37980dd85f96d576b20d564cc0784" "Rev": "9fdc871a36f37980dd85f96d576b20d564cc0784"
}, },
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -81,7 +81,6 @@ go_library( ...@@ -81,7 +81,6 @@ go_library(
"//vendor/k8s.io/apiextensions-apiserver/pkg/cmd/server:go_default_library", "//vendor/k8s.io/apiextensions-apiserver/pkg/cmd/server:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
...@@ -103,6 +102,7 @@ go_library( ...@@ -103,6 +102,7 @@ go_library(
"//vendor/k8s.io/kube-aggregator/pkg/apiserver:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/apiserver:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/typed/apiregistration/internalversion:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/typed/apiregistration/internalversion:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/controllers/autoregister:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/controllers/autoregister:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
......
...@@ -38,7 +38,6 @@ import ( ...@@ -38,7 +38,6 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
...@@ -53,6 +52,7 @@ import ( ...@@ -53,6 +52,7 @@ import (
serverstorage "k8s.io/apiserver/pkg/server/storage" serverstorage "k8s.io/apiserver/pkg/server/storage"
aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver" aggregatorapiserver "k8s.io/kube-aggregator/pkg/apiserver"
//aggregatorinformers "k8s.io/kube-aggregator/pkg/client/informers/internalversion" //aggregatorinformers "k8s.io/kube-aggregator/pkg/client/informers/internalversion"
openapi "k8s.io/kube-openapi/pkg/common"
clientgoinformers "k8s.io/client-go/informers" clientgoinformers "k8s.io/client-go/informers"
clientgoclientset "k8s.io/client-go/kubernetes" clientgoclientset "k8s.io/client-go/kubernetes"
......
...@@ -73,7 +73,6 @@ go_library( ...@@ -73,7 +73,6 @@ go_library(
"//vendor/k8s.io/api/batch/v1:go_default_library", "//vendor/k8s.io/api/batch/v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
...@@ -84,6 +83,7 @@ go_library( ...@@ -84,6 +83,7 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//vendor/k8s.io/client-go/kubernetes:go_default_library", "//vendor/k8s.io/client-go/kubernetes:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
......
...@@ -32,7 +32,6 @@ import ( ...@@ -32,7 +32,6 @@ import (
apiv1 "k8s.io/api/core/v1" apiv1 "k8s.io/api/core/v1"
extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1" extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1"
apimachineryopenapi "k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
...@@ -40,6 +39,7 @@ import ( ...@@ -40,6 +39,7 @@ import (
"k8s.io/apiserver/pkg/server/filters" "k8s.io/apiserver/pkg/server/filters"
serverstorage "k8s.io/apiserver/pkg/server/storage" serverstorage "k8s.io/apiserver/pkg/server/storage"
clientset "k8s.io/client-go/kubernetes" clientset "k8s.io/client-go/kubernetes"
openapicommon "k8s.io/kube-openapi/pkg/common"
federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1" federationv1beta1 "k8s.io/kubernetes/federation/apis/federation/v1beta1"
"k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options" "k8s.io/kubernetes/federation/cmd/federation-apiserver/app/options"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
...@@ -452,7 +452,7 @@ func postProcessOpenAPISpecForBackwardCompatibility(s *spec.Swagger) (*spec.Swag ...@@ -452,7 +452,7 @@ func postProcessOpenAPISpecForBackwardCompatibility(s *spec.Swagger) (*spec.Swag
} }
s.Definitions[k] = spec.Schema{ s.Definitions[k] = spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef("#/definitions/" + apimachineryopenapi.EscapeJsonPointer(v)), Ref: spec.MustCreateRef("#/definitions/" + openapicommon.EscapeJsonPointer(v)),
Description: fmt.Sprintf("Deprecated. Please use %s instead.", v), Description: fmt.Sprintf("Deprecated. Please use %s instead.", v),
}, },
} }
......
...@@ -582,7 +582,6 @@ staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1 ...@@ -582,7 +582,6 @@ staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1
staging/src/k8s.io/apimachinery/pkg/conversion staging/src/k8s.io/apimachinery/pkg/conversion
staging/src/k8s.io/apimachinery/pkg/conversion/unstructured staging/src/k8s.io/apimachinery/pkg/conversion/unstructured
staging/src/k8s.io/apimachinery/pkg/labels staging/src/k8s.io/apimachinery/pkg/labels
staging/src/k8s.io/apimachinery/pkg/openapi
staging/src/k8s.io/apimachinery/pkg/runtime/schema staging/src/k8s.io/apimachinery/pkg/runtime/schema
staging/src/k8s.io/apimachinery/pkg/runtime/serializer staging/src/k8s.io/apimachinery/pkg/runtime/serializer
staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf
...@@ -654,7 +653,6 @@ staging/src/k8s.io/apiserver/pkg/registry/rest/resttest ...@@ -654,7 +653,6 @@ staging/src/k8s.io/apiserver/pkg/registry/rest/resttest
staging/src/k8s.io/apiserver/pkg/server staging/src/k8s.io/apiserver/pkg/server
staging/src/k8s.io/apiserver/pkg/server/healthz staging/src/k8s.io/apiserver/pkg/server/healthz
staging/src/k8s.io/apiserver/pkg/server/httplog staging/src/k8s.io/apiserver/pkg/server/httplog
staging/src/k8s.io/apiserver/pkg/server/openapi
staging/src/k8s.io/apiserver/pkg/server/options staging/src/k8s.io/apiserver/pkg/server/options
staging/src/k8s.io/apiserver/pkg/server/routes/data/swagger staging/src/k8s.io/apiserver/pkg/server/routes/data/swagger
staging/src/k8s.io/apiserver/pkg/server/storage staging/src/k8s.io/apiserver/pkg/server/storage
...@@ -674,7 +672,6 @@ staging/src/k8s.io/apiserver/pkg/util/feature ...@@ -674,7 +672,6 @@ staging/src/k8s.io/apiserver/pkg/util/feature
staging/src/k8s.io/apiserver/pkg/util/flag staging/src/k8s.io/apiserver/pkg/util/flag
staging/src/k8s.io/apiserver/pkg/util/proxy staging/src/k8s.io/apiserver/pkg/util/proxy
staging/src/k8s.io/apiserver/pkg/util/trace staging/src/k8s.io/apiserver/pkg/util/trace
staging/src/k8s.io/apiserver/pkg/util/trie
staging/src/k8s.io/apiserver/pkg/util/webhook staging/src/k8s.io/apiserver/pkg/util/webhook
staging/src/k8s.io/apiserver/pkg/util/wsstream staging/src/k8s.io/apiserver/pkg/util/wsstream
staging/src/k8s.io/apiserver/plugin/pkg/audit/log staging/src/k8s.io/apiserver/plugin/pkg/audit/log
...@@ -782,7 +779,6 @@ staging/src/k8s.io/kube-gen/cmd/conversion-gen/generators ...@@ -782,7 +779,6 @@ staging/src/k8s.io/kube-gen/cmd/conversion-gen/generators
staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf staging/src/k8s.io/kube-gen/cmd/go-to-protobuf/protobuf
staging/src/k8s.io/kube-gen/cmd/informer-gen/generators staging/src/k8s.io/kube-gen/cmd/informer-gen/generators
staging/src/k8s.io/kube-gen/cmd/lister-gen/generators staging/src/k8s.io/kube-gen/cmd/lister-gen/generators
staging/src/k8s.io/kube-gen/cmd/openapi-gen/generators
staging/src/k8s.io/metrics/pkg/apis/custom_metrics staging/src/k8s.io/metrics/pkg/apis/custom_metrics
staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1alpha1 staging/src/k8s.io/metrics/pkg/apis/custom_metrics/v1alpha1
staging/src/k8s.io/metrics/pkg/apis/metrics staging/src/k8s.io/metrics/pkg/apis/metrics
......
...@@ -31,7 +31,7 @@ for i in $(find vendor/ -type d); do ...@@ -31,7 +31,7 @@ for i in $(find vendor/ -type d); do
echo "Package ${i} has a cyclic dependency on the main repository." echo "Package ${i} has a cyclic dependency on the main repository."
failed=true failed=true
fi fi
deps_on_staging=$(echo "${deps}" | grep "k8s.io/kubernetes/vendor/k8s.io" | grep -E ${staging_repos_pattern} || echo "") deps_on_staging=$(echo "${deps}" | grep "k8s.io/kubernetes/vendor/k8s.io" | grep -E "k8s.io\/${staging_repos_pattern}\>" || echo "")
if [ -n "${deps_on_staging}" ]; then if [ -n "${deps_on_staging}" ]; then
echo "Package ${i} has a cyclic dependency on staging repository packages: ${deps_on_staging}" echo "Package ${i} has a cyclic dependency on staging repository packages: ${deps_on_staging}"
failed=true failed=true
......
...@@ -65,15 +65,16 @@ function print_forbidden_imports () { ...@@ -65,15 +65,16 @@ function print_forbidden_imports () {
} }
RC=0 RC=0
print_forbidden_imports apimachinery should_be_leaf || RC=1 print_forbidden_imports apimachinery k8s.io/kube-openapi || RC=1
print_forbidden_imports api k8s.io/apimachinery || RC=1 print_forbidden_imports api k8s.io/apimachinery || RC=1
print_forbidden_imports kube-gen k8s.io/apimachinery k8s.io/client-go k8s.io/gengo || RC=1 print_forbidden_imports kube-gen k8s.io/apimachinery k8s.io/client-go k8s.io/gengo k8s.io/kube-openapi || RC=1
print_forbidden_imports client-go k8s.io/apimachinery k8s.io/api || RC=1 print_forbidden_imports client-go k8s.io/apimachinery k8s.io/api || RC=1
print_forbidden_imports apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/api || RC=1 print_forbidden_imports apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/api k8s.io/kube-openapi || RC=1
print_forbidden_imports metrics k8s.io/apimachinery k8s.io/client-go k8s.io/api || RC=1 print_forbidden_imports metrics k8s.io/apimachinery k8s.io/client-go k8s.io/api || RC=1
print_forbidden_imports kube-aggregator k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api || RC=1 print_forbidden_imports kube-aggregator k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api k8s.io/kube-openapi || RC=1
print_forbidden_imports sample-apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api || RC=1 print_forbidden_imports sample-apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api || RC=1
print_forbidden_imports apiextensions-apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api || RC=1 print_forbidden_imports apiextensions-apiserver k8s.io/apimachinery k8s.io/client-go k8s.io/apiserver k8s.io/api || RC=1
print_forbidden_imports kube-openapi k8s.io/gengo || RC=1
if [ ${RC} != 0 ]; then if [ ${RC} != 0 ]; then
exit ${RC} exit ${RC}
fi fi
......
...@@ -18,7 +18,7 @@ go_library( ...@@ -18,7 +18,7 @@ go_library(
deps = [ deps = [
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/google/gofuzz" "github.com/google/gofuzz"
......
...@@ -14,7 +14,7 @@ go_library( ...@@ -14,7 +14,7 @@ go_library(
deps = [ deps = [
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/google/gofuzz" "github.com/google/gofuzz"
......
...@@ -4,7 +4,7 @@ load("@io_kubernetes_build//defs:go.bzl", "go_genrule") ...@@ -4,7 +4,7 @@ load("@io_kubernetes_build//defs:go.bzl", "go_genrule")
def openapi_library(name, tags, srcs, openapi_targets=[], vendor_targets=[]): def openapi_library(name, tags, srcs, openapi_targets=[], vendor_targets=[]):
deps = [ deps = [
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
] + ["//%s:go_default_library" % target for target in openapi_targets] + ["//vendor/%s:go_default_library" % target for target in vendor_targets] ] + ["//%s:go_default_library" % target for target in openapi_targets] + ["//vendor/%s:go_default_library" % target for target in vendor_targets]
go_library( go_library(
name=name, name=name,
......
...@@ -43,7 +43,7 @@ go_library( ...@@ -43,7 +43,7 @@ go_library(
"//vendor/github.com/gogo/protobuf/proto:go_default_library", "//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/gopkg.in/inf.v0:go_default_library", "//vendor/gopkg.in/inf.v0:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
......
...@@ -29,7 +29,7 @@ import ( ...@@ -29,7 +29,7 @@ import (
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
inf "gopkg.in/inf.v0" inf "gopkg.in/inf.v0"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
) )
// Quantity is a fixed-point representation of a number. // Quantity is a fixed-point representation of a number.
......
...@@ -63,12 +63,12 @@ go_library( ...@@ -63,12 +63,12 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/selection:go_default_library", "//vendor/k8s.io/apimachinery/pkg/selection:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/google/gofuzz" "github.com/google/gofuzz"
......
...@@ -20,7 +20,7 @@ import ( ...@@ -20,7 +20,7 @@ import (
"encoding/json" "encoding/json"
"time" "time"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/google/gofuzz" "github.com/google/gofuzz"
......
...@@ -28,6 +28,6 @@ go_library( ...@@ -28,6 +28,6 @@ go_library(
"//vendor/github.com/gogo/protobuf/proto:go_default_library", "//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
...@@ -24,7 +24,7 @@ import ( ...@@ -24,7 +24,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"k8s.io/apimachinery/pkg/openapi" openapi "k8s.io/kube-openapi/pkg/common"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/golang/glog" "github.com/golang/glog"
......
...@@ -31,6 +31,6 @@ go_library( ...@@ -31,6 +31,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/trie:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library",
], ],
) )
...@@ -30,10 +30,10 @@ import ( ...@@ -30,10 +30,10 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/util/trie" "k8s.io/kube-openapi/pkg/util"
) )
var verbs = trie.New([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"}) var verbs = util.NewTrie([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"})
const ( const (
extensionGVK = "x-kubernetes-group-version-kind" extensionGVK = "x-kubernetes-group-version-kind"
......
...@@ -22,7 +22,6 @@ go_test( ...@@ -22,7 +22,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
...@@ -40,6 +39,7 @@ go_test( ...@@ -40,6 +39,7 @@ go_test(
"//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library", "//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
...@@ -69,7 +69,6 @@ go_library( ...@@ -69,7 +69,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
...@@ -101,11 +100,11 @@ go_library( ...@@ -101,11 +100,11 @@ go_library(
"//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/routes:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/routes:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library",
"//vendor/k8s.io/client-go/util/cert:go_default_library", "//vendor/k8s.io/client-go/util/cert:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
...@@ -33,7 +33,6 @@ import ( ...@@ -33,7 +33,6 @@ import (
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/pborman/uuid" "github.com/pborman/uuid"
openapicommon "k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
...@@ -61,6 +60,7 @@ import ( ...@@ -61,6 +60,7 @@ import (
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
openapicommon "k8s.io/kube-openapi/pkg/common"
_ "k8s.io/apiserver/pkg/apis/apiserver/install" _ "k8s.io/apiserver/pkg/apis/apiserver/install"
) )
......
...@@ -27,11 +27,9 @@ import ( ...@@ -27,11 +27,9 @@ import (
"github.com/emicklei/go-restful-swagger12" "github.com/emicklei/go-restful-swagger12"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/apimachinery" "k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/apimachinery/registered" "k8s.io/apimachinery/pkg/apimachinery/registered"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
openapicommon "k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
...@@ -43,9 +41,9 @@ import ( ...@@ -43,9 +41,9 @@ import (
apirequest "k8s.io/apiserver/pkg/endpoints/request" apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/openapi"
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
openapicommon "k8s.io/kube-openapi/pkg/common"
) )
// Info about an API group. // Info about an API group.
...@@ -131,9 +129,6 @@ type GenericAPIServer struct { ...@@ -131,9 +129,6 @@ type GenericAPIServer struct {
swaggerConfig *swagger.Config swaggerConfig *swagger.Config
openAPIConfig *openapicommon.Config openAPIConfig *openapicommon.Config
// Enables updating OpenAPI spec using update method.
OpenAPIService *openapi.OpenAPIService
// PostStartHooks are each called after the server has started listening, in a separate go func for each // PostStartHooks are each called after the server has started listening, in a separate go func for each
// with no guarantee of ordering between them. The map key is a name used for error reporting. // with no guarantee of ordering between them. The map key is a name used for error reporting.
// It may kill the process with a panic if it wishes to by returning an error. // It may kill the process with a panic if it wishes to by returning an error.
...@@ -173,9 +168,6 @@ type DelegationTarget interface { ...@@ -173,9 +168,6 @@ type DelegationTarget interface {
// ListedPaths returns the paths for supporting an index // ListedPaths returns the paths for supporting an index
ListedPaths() []string ListedPaths() []string
// OpenAPISpec returns the OpenAPI spec of the delegation target if exists, nil otherwise.
OpenAPISpec() *spec.Swagger
} }
func (s *GenericAPIServer) UnprotectedHandler() http.Handler { func (s *GenericAPIServer) UnprotectedHandler() http.Handler {
...@@ -191,9 +183,6 @@ func (s *GenericAPIServer) HealthzChecks() []healthz.HealthzChecker { ...@@ -191,9 +183,6 @@ func (s *GenericAPIServer) HealthzChecks() []healthz.HealthzChecker {
func (s *GenericAPIServer) ListedPaths() []string { func (s *GenericAPIServer) ListedPaths() []string {
return s.listedPathProvider.ListedPaths() return s.listedPathProvider.ListedPaths()
} }
func (s *GenericAPIServer) OpenAPISpec() *spec.Swagger {
return s.OpenAPIService.GetSpec()
}
var EmptyDelegate = emptyDelegate{ var EmptyDelegate = emptyDelegate{
requestContextMapper: apirequest.NewRequestContextMapper(), requestContextMapper: apirequest.NewRequestContextMapper(),
...@@ -218,9 +207,6 @@ func (s emptyDelegate) ListedPaths() []string { ...@@ -218,9 +207,6 @@ func (s emptyDelegate) ListedPaths() []string {
func (s emptyDelegate) RequestContextMapper() apirequest.RequestContextMapper { func (s emptyDelegate) RequestContextMapper() apirequest.RequestContextMapper {
return s.requestContextMapper return s.requestContextMapper
} }
func (s emptyDelegate) OpenAPISpec() *spec.Swagger {
return nil
}
// RequestContextMapper is exposed so that third party resource storage can be build in a different location. // RequestContextMapper is exposed so that third party resource storage can be build in a different location.
// TODO refactor third party resource storage // TODO refactor third party resource storage
...@@ -243,22 +229,17 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer { ...@@ -243,22 +229,17 @@ func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
if s.swaggerConfig != nil { if s.swaggerConfig != nil {
routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer) routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer)
} }
s.PrepareOpenAPIService() if s.openAPIConfig != nil {
routes.OpenAPI{
Config: s.openAPIConfig,
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
}
s.installHealthz() s.installHealthz()
return preparedGenericAPIServer{s} return preparedGenericAPIServer{s}
} }
// PrepareOpenAPIService installs OpenAPI handler if it does not exists.
func (s *GenericAPIServer) PrepareOpenAPIService() {
if s.openAPIConfig != nil && s.OpenAPIService == nil {
s.OpenAPIService = routes.OpenAPI{
Config: s.openAPIConfig,
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
}
}
// Run spawns the secure http server. It only returns if stopCh is closed // Run spawns the secure http server. It only returns if stopCh is closed
// or the secure port cannot be listened on initially. // or the secure port cannot be listened on initially.
func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error { func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error {
......
...@@ -34,7 +34,6 @@ import ( ...@@ -34,7 +34,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apimachinery" "k8s.io/apimachinery/pkg/apimachinery"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/runtime/serializer"
...@@ -51,6 +50,7 @@ import ( ...@@ -51,6 +50,7 @@ import (
"k8s.io/client-go/informers" "k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
openapi "k8s.io/kube-openapi/pkg/common"
) )
const ( const (
......
reviewers:
- yujuhong
- gmarek
- mbohlool
- philips
approvers:
- mbohlool
...@@ -27,14 +27,14 @@ go_library( ...@@ -27,14 +27,14 @@ go_library(
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library", "//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library", "//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/routes/data/swagger:go_default_library", "//vendor/k8s.io/apiserver/pkg/server/routes/data/swagger:go_default_library",
"//vendor/k8s.io/apiserver/pkg/storage/etcd/metrics:go_default_library", "//vendor/k8s.io/apiserver/pkg/storage/etcd/metrics:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library",
], ],
) )
...@@ -18,30 +18,22 @@ package routes ...@@ -18,30 +18,22 @@ package routes
import ( import (
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/mux"
apiserveropenapi "k8s.io/apiserver/pkg/server/openapi" "k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/handler"
"github.com/golang/glog"
) )
// OpenAPI installs spec endpoints for each web service. // OpenAPI installs spec endpoints for each web service.
type OpenAPI struct { type OpenAPI struct {
Config *openapi.Config Config *common.Config
} }
// Install adds the SwaggerUI webservice to the given mux. // Install adds the SwaggerUI webservice to the given mux.
func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) *apiserveropenapi.OpenAPIService { func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) {
openapiSpec, err := apiserveropenapi.BuildSwaggerSpec(c.RegisteredWebServices(), oa.Config) _, err := handler.BuildAndRegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux)
if err != nil {
glog.Fatalf("Failed to register open api spec for root: %v", err)
return nil
}
service, err := apiserveropenapi.RegisterOpenAPIService(openapiSpec, "/swagger.json", mux)
if err != nil { if err != nil {
glog.Fatalf("Failed to register open api spec for root: %v", err) glog.Fatalf("Failed to register open api spec for root: %v", err)
return nil
} }
return service
} }
...@@ -13,10 +13,12 @@ go_test( ...@@ -13,10 +13,12 @@ go_test(
srcs = [ srcs = [
"handler_apis_test.go", "handler_apis_test.go",
"handler_proxy_test.go", "handler_proxy_test.go",
"openapi_aggregator_test.go",
], ],
library = ":go_default_library", library = ":go_default_library",
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/golang.org/x/net/websocket:go_default_library", "//vendor/golang.org/x/net/websocket:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
...@@ -38,13 +40,14 @@ go_library( ...@@ -38,13 +40,14 @@ go_library(
"apiservice_controller.go", "apiservice_controller.go",
"handler_apis.go", "handler_apis.go",
"handler_proxy.go", "handler_proxy.go",
"openapi_aggregator.go",
"resolvers.go", "resolvers.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/pkg/errors:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library",
...@@ -61,12 +64,12 @@ go_library( ...@@ -61,12 +64,12 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library", "//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
"//vendor/k8s.io/apiserver/pkg/features:go_default_library", "//vendor/k8s.io/apiserver/pkg/features:go_default_library",
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library", "//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server:go_default_library", "//vendor/k8s.io/apiserver/pkg/server:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/proxy:go_default_library", "//vendor/k8s.io/apiserver/pkg/util/proxy:go_default_library",
"//vendor/k8s.io/client-go/informers:go_default_library", "//vendor/k8s.io/client-go/informers:go_default_library",
...@@ -87,5 +90,9 @@ go_library( ...@@ -87,5 +90,9 @@ go_library(
"//vendor/k8s.io/kube-aggregator/pkg/controllers:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/controllers:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/controllers/status:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/controllers/status:go_default_library",
"//vendor/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd:go_default_library", "//vendor/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/aggregator:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/builder:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library",
], ],
) )
...@@ -39,7 +39,7 @@ import ( ...@@ -39,7 +39,7 @@ import (
) )
type APIHandlerManager interface { type APIHandlerManager interface {
AddAPIService(apiService *apiregistration.APIService) AddAPIService(apiService *apiregistration.APIService) error
RemoveAPIService(apiServiceName string) RemoveAPIService(apiServiceName string)
} }
...@@ -102,8 +102,7 @@ func (c *APIServiceRegistrationController) sync(key string) error { ...@@ -102,8 +102,7 @@ func (c *APIServiceRegistrationController) sync(key string) error {
return nil return nil
} }
c.apiHandlerManager.AddAPIService(apiService) return c.apiHandlerManager.AddAPIService(apiService)
return nil
} }
func (c *APIServiceRegistrationController) Run(stopCh <-chan struct{}) { func (c *APIServiceRegistrationController) Run(stopCh <-chan struct{}) {
......
/*
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 apiserver
import (
"encoding/json"
"fmt"
"net/http"
"sort"
"time"
"github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
"k8s.io/kube-openapi/pkg/aggregator"
"k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/handler"
)
const (
aggregatorUser = "system:aggregator"
specDownloadTimeout = 60 * time.Second
)
type openAPIAggregator struct {
// Map of API Services' OpenAPI specs by their name
openAPISpecs map[string]*openAPISpecInfo
// provided for dynamic OpenAPI spec
openAPIService *handler.OpenAPIService
// Aggregator's OpenAPI spec (holds apiregistration group).
aggregatorOpenAPISpec *spec.Swagger
// Local (in process) delegate's OpenAPI spec.
inProcessDelegatesOpenAPISpec *spec.Swagger
contextMapper request.RequestContextMapper
}
func buildAndRegisterOpenAPIAggregator(delegateHandler http.Handler, webServices []*restful.WebService, config *common.Config, pathHandler common.PathHandler, contextMapper request.RequestContextMapper) (s *openAPIAggregator, err error) {
s = &openAPIAggregator{
openAPISpecs: map[string]*openAPISpecInfo{},
contextMapper: contextMapper,
}
// Get Local delegate's Spec
s.inProcessDelegatesOpenAPISpec, err = s.downloadOpenAPISpec(delegateHandler)
if err != nil {
return nil, err
}
// Build Aggregator's spec
s.aggregatorOpenAPISpec, err = builder.BuildOpenAPISpec(
webServices, config)
if err != nil {
return nil, err
}
// Remove any non-API endpoints from aggregator's spec. aggregatorOpenAPISpec
// is the source of truth for all non-api endpoints.
aggregator.FilterSpecByPaths(s.aggregatorOpenAPISpec, []string{"/apis/"})
// Build initial spec to serve.
specToServe, err := s.buildOpenAPISpec()
if err != nil {
return nil, err
}
// Install handler
s.openAPIService, err = handler.RegisterOpenAPIService(
specToServe, "/swagger.json", pathHandler)
if err != nil {
return nil, err
}
return s, nil
}
// openAPISpecInfo is used to store OpenAPI spec with its priority.
// It can be used to sort specs with their priorities.
type openAPISpecInfo struct {
apiService apiregistration.APIService
spec *spec.Swagger
}
// byPriority can be used in sort.Sort to sort specs with their priorities.
type byPriority struct {
specs []openAPISpecInfo
groupPriorities map[string]int32
}
func (a byPriority) Len() int { return len(a.specs) }
func (a byPriority) Swap(i, j int) { a.specs[i], a.specs[j] = a.specs[j], a.specs[i] }
func (a byPriority) Less(i, j int) bool {
var iPriority, jPriority int32
if a.specs[i].apiService.Spec.Group == a.specs[j].apiService.Spec.Group {
iPriority = a.specs[i].apiService.Spec.VersionPriority
jPriority = a.specs[i].apiService.Spec.VersionPriority
} else {
iPriority = a.groupPriorities[a.specs[i].apiService.Spec.Group]
jPriority = a.groupPriorities[a.specs[j].apiService.Spec.Group]
}
if iPriority != jPriority {
// Sort by priority, higher first
return iPriority > jPriority
}
// Sort by service name.
return a.specs[i].apiService.Name < a.specs[j].apiService.Name
}
func sortByPriority(specs []openAPISpecInfo) {
b := byPriority{
specs: specs,
groupPriorities: map[string]int32{},
}
for _, spec := range specs {
if pr, found := b.groupPriorities[spec.apiService.Spec.Group]; !found || spec.apiService.Spec.GroupPriorityMinimum > pr {
b.groupPriorities[spec.apiService.Spec.Group] = spec.apiService.Spec.GroupPriorityMinimum
}
}
sort.Sort(b)
}
// buildOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe.
func (s *openAPIAggregator) buildOpenAPISpec() (specToReturn *spec.Swagger, err error) {
specToReturn, err = aggregator.CloneSpec(s.inProcessDelegatesOpenAPISpec)
if err != nil {
return nil, err
}
if err := aggregator.MergeSpecs(specToReturn, s.aggregatorOpenAPISpec); err != nil {
return nil, fmt.Errorf("cannot merge local delegate spec with aggregator spec: %s", err.Error())
}
specs := []openAPISpecInfo{}
for _, specInfo := range s.openAPISpecs {
specs = append(specs, openAPISpecInfo{specInfo.apiService, specInfo.spec})
}
sortByPriority(specs)
for _, specInfo := range specs {
if err := aggregator.MergeSpecs(specToReturn, specInfo.spec); err != nil {
return nil, err
}
}
return specToReturn, nil
}
// updateOpenAPISpec aggregates all OpenAPI specs. It is not thread-safe.
func (s *openAPIAggregator) updateOpenAPISpec() error {
if s.openAPIService == nil {
return nil
}
specToServe, err := s.buildOpenAPISpec()
if err != nil {
return err
}
return s.openAPIService.UpdateSpec(specToServe)
}
// inMemoryResponseWriter is a http.Writer that keep the response in memory.
type inMemoryResponseWriter struct {
header http.Header
respCode int
data []byte
}
func newInMemoryResponseWriter() *inMemoryResponseWriter {
return &inMemoryResponseWriter{header: http.Header{}}
}
func (r *inMemoryResponseWriter) Header() http.Header {
return r.header
}
func (r *inMemoryResponseWriter) WriteHeader(code int) {
r.respCode = code
}
func (r *inMemoryResponseWriter) Write(in []byte) (int, error) {
r.data = append(r.data, in...)
return len(in), nil
}
func (r *inMemoryResponseWriter) String() string {
s := fmt.Sprintf("ResponseCode: %d", r.respCode)
if r.data != nil {
s += fmt.Sprintf(", Body: %s", string(r.data))
}
if r.header != nil {
s += fmt.Sprintf(", Header: %s", r.header)
}
return s
}
func (s *openAPIAggregator) handlerWithUser(handler http.Handler, info user.Info) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if ctx, ok := s.contextMapper.Get(req); ok {
s.contextMapper.Update(req, request.WithUser(ctx, info))
}
handler.ServeHTTP(w, req)
})
}
// downloadOpenAPISpec downloads openAPI spec from /swagger.json endpoint of the given handler.
func (s *openAPIAggregator) downloadOpenAPISpec(handler http.Handler) (*spec.Swagger, error) {
handler = s.handlerWithUser(handler, &user.DefaultInfo{Name: aggregatorUser})
handler = request.WithRequestContext(handler, s.contextMapper)
handler = http.TimeoutHandler(handler, specDownloadTimeout, "request timed out")
req, err := http.NewRequest("GET", "/swagger.json", nil)
if err != nil {
return nil, err
}
writer := newInMemoryResponseWriter()
handler.ServeHTTP(writer, req)
switch writer.respCode {
case http.StatusOK:
openApiSpec := &spec.Swagger{}
if err := json.Unmarshal(writer.data, openApiSpec); err != nil {
return nil, err
}
return openApiSpec, nil
default:
return nil, fmt.Errorf("failed to retrive openAPI spec, http error: %s", writer.String())
}
}
// loadApiServiceSpec loads OpenAPI spec for the given API Service and then updates aggregator's spec.
func (s *openAPIAggregator) loadApiServiceSpec(handler http.Handler, apiService *apiregistration.APIService) error {
// Ignore local services
if apiService.Spec.Service == nil {
return nil
}
openApiSpec, err := s.downloadOpenAPISpec(handler)
if err != nil {
return err
}
aggregator.FilterSpecByPaths(openApiSpec, []string{"/apis/" + apiService.Spec.Group + "/"})
s.openAPISpecs[apiService.Name] = &openAPISpecInfo{
apiService: *apiService,
spec: openApiSpec,
}
err = s.updateOpenAPISpec()
if err != nil {
delete(s.openAPISpecs, apiService.Name)
return err
}
return nil
}
/*
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 apiserver
import (
"reflect"
"testing"
"github.com/go-openapi/spec"
"k8s.io/kube-aggregator/pkg/apis/apiregistration"
)
func newApiServiceForTest(name, group string, minGroupPriority, versionPriority int32) apiregistration.APIService {
r := apiregistration.APIService{}
r.Spec.Group = group
r.Spec.GroupPriorityMinimum = minGroupPriority
r.Spec.VersionPriority = versionPriority
r.Name = name
return r
}
func assertSortedServices(t *testing.T, actual []openAPISpecInfo, expectedNames []string) {
actualNames := []string{}
for _, a := range actual {
actualNames = append(actualNames, a.apiService.Name)
}
if !reflect.DeepEqual(actualNames, expectedNames) {
t.Errorf("Expected %s got %s.", expectedNames, actualNames)
}
}
func TestApiServiceSort(t *testing.T) {
list := []openAPISpecInfo{
{
apiService: newApiServiceForTest("FirstService", "Group1", 10, 5),
spec: &spec.Swagger{},
},
{
apiService: newApiServiceForTest("SecondService", "Group2", 15, 3),
spec: &spec.Swagger{},
},
{
apiService: newApiServiceForTest("FirstServiceInternal", "Group1", 16, 3),
spec: &spec.Swagger{},
},
{
apiService: newApiServiceForTest("ThirdService", "Group3", 15, 3),
spec: &spec.Swagger{},
},
}
sortByPriority(list)
assertSortedServices(t, list, []string{"FirstService", "FirstServiceInternal", "SecondService", "ThirdService"})
}
...@@ -207,18 +207,10 @@ ...@@ -207,18 +207,10 @@
"Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0" "Rev": "d5b7844b561a7bc640052f1b935f7b800330d7e0"
}, },
{ {
"ImportPath": "github.com/pmezard/go-difflib/difflib",
"Rev": "d8ed2627bdf02c080bf22230dbb337003b7aba2d"
},
{
"ImportPath": "github.com/spf13/pflag", "ImportPath": "github.com/spf13/pflag",
"Rev": "9ff6c6923cfffbcd502984b8e0c80539a94968b7" "Rev": "9ff6c6923cfffbcd502984b8e0c80539a94968b7"
}, },
{ {
"ImportPath": "github.com/stretchr/testify/assert",
"Rev": "f6abca593680b2315d2075e0f5e2a9751e3f431a"
},
{
"ImportPath": "github.com/ugorji/go/codec", "ImportPath": "github.com/ugorji/go/codec",
"Rev": "ded73eae5db7e7a0ef6f55aace87a2873c5d2b74" "Rev": "ded73eae5db7e7a0ef6f55aace87a2873c5d2b74"
}, },
......
...@@ -21,6 +21,6 @@ go_library( ...@@ -21,6 +21,6 @@ go_library(
deps = [ deps = [
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/gengo/args:go_default_library", "//vendor/k8s.io/gengo/args:go_default_library",
"//vendor/k8s.io/kube-gen/cmd/openapi-gen/generators:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/generators:go_default_library",
], ],
) )
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package generators
import (
"bytes"
"fmt"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"k8s.io/gengo/generator"
"k8s.io/gengo/namer"
"k8s.io/gengo/parser"
"k8s.io/gengo/types"
)
func construct(t *testing.T, files map[string]string, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
b := parser.New()
for name, src := range files {
if err := b.AddFileForTest(filepath.Dir(name), name, []byte(src)); err != nil {
t.Fatal(err)
}
}
u, err := b.FindTypes()
if err != nil {
t.Fatal(err)
}
orderer := namer.Orderer{Namer: testNamer}
o := orderer.OrderUniverse(u)
return b, u, o
}
func testOpenAPITypeWritter(t *testing.T, code string) (error, *assert.Assertions, *bytes.Buffer) {
assert := assert.New(t)
var testFiles = map[string]string{
"base/foo/bar.go": code,
}
rawNamer := namer.NewRawNamer("o", nil)
namers := namer.NameSystems{
"raw": namer.NewRawNamer("", nil),
}
builder, universe, _ := construct(t, testFiles, rawNamer)
context, err := generator.NewContext(builder, namers, "raw")
if err != nil {
t.Fatal(err)
}
buffer := &bytes.Buffer{}
sw := generator.NewSnippetWriter(buffer, context, "$", "$")
blahT := universe.Type(types.Name{Package: "base/foo", Name: "Blah"})
return newOpenAPITypeWriter(sw).generate(blahT), assert, buffer
}
func TestSimple(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
// Blah is a test.
// +k8s:openapi-gen=true
// +k8s:openapi-gen=x-kubernetes-type-tag:type_test
type Blah struct {
// A simple string
String string
// A simple int
Int int `+"`"+`json:",omitempty"`+"`"+`
// An int considered string simple int
IntString int `+"`"+`json:",string"`+"`"+`
// A simple int64
Int64 int64
// A simple int32
Int32 int32
// A simple int16
Int16 int16
// A simple int8
Int8 int8
// A simple int
Uint uint
// A simple int64
Uint64 uint64
// A simple int32
Uint32 uint32
// A simple int16
Uint16 uint16
// A simple int8
Uint8 uint8
// A simple byte
Byte byte
// A simple boolean
Bool bool
// A simple float64
Float64 float64
// A simple float32
Float32 float32
// a base64 encoded characters
ByteArray []byte
// a member with an extension
// +k8s:openapi-gen=x-kubernetes-member-tag:member_test
WithExtension string
// a member with struct tag as extension
// +patchStrategy=ps
// +patchMergeKey=pmk
WithStructTagExtension string `+"`"+`patchStrategy:"ps" patchMergeKey:"pmk"`+"`"+`
}
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Blah is a test.",
Properties: map[string]spec.Schema{
"String": {
SchemaProps: spec.SchemaProps{
Description: "A simple string",
Type: []string{"string"},
Format: "",
},
},
"Int64": {
SchemaProps: spec.SchemaProps{
Description: "A simple int64",
Type: []string{"integer"},
Format: "int64",
},
},
"Int32": {
SchemaProps: spec.SchemaProps{
Description: "A simple int32",
Type: []string{"integer"},
Format: "int32",
},
},
"Int16": {
SchemaProps: spec.SchemaProps{
Description: "A simple int16",
Type: []string{"integer"},
Format: "int32",
},
},
"Int8": {
SchemaProps: spec.SchemaProps{
Description: "A simple int8",
Type: []string{"integer"},
Format: "byte",
},
},
"Uint": {
SchemaProps: spec.SchemaProps{
Description: "A simple int",
Type: []string{"integer"},
Format: "int32",
},
},
"Uint64": {
SchemaProps: spec.SchemaProps{
Description: "A simple int64",
Type: []string{"integer"},
Format: "int64",
},
},
"Uint32": {
SchemaProps: spec.SchemaProps{
Description: "A simple int32",
Type: []string{"integer"},
Format: "int64",
},
},
"Uint16": {
SchemaProps: spec.SchemaProps{
Description: "A simple int16",
Type: []string{"integer"},
Format: "int32",
},
},
"Uint8": {
SchemaProps: spec.SchemaProps{
Description: "A simple int8",
Type: []string{"integer"},
Format: "byte",
},
},
"Byte": {
SchemaProps: spec.SchemaProps{
Description: "A simple byte",
Type: []string{"integer"},
Format: "byte",
},
},
"Bool": {
SchemaProps: spec.SchemaProps{
Description: "A simple boolean",
Type: []string{"boolean"},
Format: "",
},
},
"Float64": {
SchemaProps: spec.SchemaProps{
Description: "A simple float64",
Type: []string{"number"},
Format: "double",
},
},
"Float32": {
SchemaProps: spec.SchemaProps{
Description: "A simple float32",
Type: []string{"number"},
Format: "float",
},
},
"ByteArray": {
SchemaProps: spec.SchemaProps{
Description: "a base64 encoded characters",
Type: []string{"string"},
Format: "byte",
},
},
"WithExtension": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-member-tag": "member_test",
},
},
SchemaProps: spec.SchemaProps{
Description: "a member with an extension",
Type: []string{"string"},
Format: "",
},
},
"WithStructTagExtension": {
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-patch-merge-key": "pmk",
"x-kubernetes-patch-strategy": "ps",
},
},
SchemaProps: spec.SchemaProps{
Description: "a member with struct tag as extension",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"String","Int64","Int32","Int16","Int8","Uint","Uint64","Uint32","Uint16","Uint8","Byte","Bool","Float64","Float32","ByteArray","WithExtension","WithStructTagExtension"},
},
VendorExtensible: spec.VendorExtensible{
Extensions: spec.Extensions{
"x-kubernetes-type-tag": "type_test",
},
},
},
Dependencies: []string{
},
},
`, buffer.String())
}
func TestFailingSample1(t *testing.T) {
err, assert, _ := testOpenAPITypeWritter(t, `
package foo
// Map sample tests openAPIGen.generateMapProperty method.
type Blah struct {
// A sample String to String map
StringToArray map[string]map[string]string
}
`)
if assert.Error(err, "An error was expected") {
assert.Equal(err, fmt.Errorf("map Element kind Map is not supported in map[string]map[string]string"))
}
}
func TestFailingSample2(t *testing.T) {
err, assert, _ := testOpenAPITypeWritter(t, `
package foo
// Map sample tests openAPIGen.generateMapProperty method.
type Blah struct {
// A sample String to String map
StringToArray map[int]string
} `)
if assert.Error(err, "An error was expected") {
assert.Equal(err, fmt.Errorf("map with non-string keys are not supported by OpenAPI in map[int]string"))
}
}
func TestPointer(t *testing.T) {
err, assert, buffer := testOpenAPITypeWritter(t, `
package foo
// PointerSample demonstrate pointer's properties
type Blah struct {
// A string pointer
StringPointer *string
// A struct pointer
StructPointer *Blah
// A slice pointer
SlicePointer *[]string
// A map pointer
MapPointer *map[string]string
}
`)
if err != nil {
t.Fatal(err)
}
assert.Equal(`"base/foo.Blah": {
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "PointerSample demonstrate pointer's properties",
Properties: map[string]spec.Schema{
"StringPointer": {
SchemaProps: spec.SchemaProps{
Description: "A string pointer",
Type: []string{"string"},
Format: "",
},
},
"StructPointer": {
SchemaProps: spec.SchemaProps{
Description: "A struct pointer",
Ref: ref("base/foo.Blah"),
},
},
"SlicePointer": {
SchemaProps: spec.SchemaProps{
Description: "A slice pointer",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
"MapPointer": {
SchemaProps: spec.SchemaProps{
Description: "A map pointer",
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
Required: []string{"StringPointer","StructPointer","SlicePointer","MapPointer"},
},
},
Dependencies: []string{
"base/foo.Blah",},
},
`, buffer.String())
}
...@@ -23,7 +23,7 @@ import ( ...@@ -23,7 +23,7 @@ import (
"path/filepath" "path/filepath"
"k8s.io/gengo/args" "k8s.io/gengo/args"
"k8s.io/kube-gen/cmd/openapi-gen/generators" "k8s.io/kube-openapi/pkg/generators"
"github.com/golang/glog" "github.com/golang/glog"
) )
......
...@@ -383,6 +383,12 @@ filegroup( ...@@ -383,6 +383,12 @@ filegroup(
"//vendor/k8s.io/gengo/parser:all-srcs", "//vendor/k8s.io/gengo/parser:all-srcs",
"//vendor/k8s.io/gengo/types:all-srcs", "//vendor/k8s.io/gengo/types:all-srcs",
"//vendor/k8s.io/heapster/metrics/api/v1/types:all-srcs", "//vendor/k8s.io/heapster/metrics/api/v1/types:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/aggregator:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/builder:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/common:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/generators:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/handler:all-srcs",
"//vendor/k8s.io/kube-openapi/pkg/util:all-srcs",
"//vendor/k8s.io/utils/exec:all-srcs", "//vendor/k8s.io/utils/exec:all-srcs",
"//vendor/vbom.ml/util/sortorder:all-srcs", "//vendor/vbom.ml/util/sortorder:all-srcs",
], ],
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["aggregator.go"],
tags = ["automanaged"],
deps = [
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/util:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -5,24 +5,6 @@ licenses(["notice"]) ...@@ -5,24 +5,6 @@ licenses(["notice"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"openapi_aggregator_test.go",
"openapi_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/ghodss/yaml:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
],
) )
go_library( go_library(
...@@ -30,22 +12,26 @@ go_library( ...@@ -30,22 +12,26 @@ go_library(
srcs = [ srcs = [
"doc.go", "doc.go",
"openapi.go", "openapi.go",
"openapi_aggregator.go",
"openapi_handler.go",
"util.go", "util.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/NYTimes/gziphandler:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library", "//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
"//vendor/github.com/golang/protobuf/proto:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library", "//vendor/k8s.io/kube-openapi/pkg/util:go_default_library",
"//vendor/github.com/googleapis/gnostic/compiler:go_default_library",
"//vendor/gopkg.in/yaml.v2:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/trie:go_default_library",
], ],
) )
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// Package openapi contains code to generate OpenAPI discovery spec (which // Package builder contains code to generate OpenAPI discovery spec (which
// initial version of it also known as Swagger 2.0). // initial version of it also known as Swagger 2.0).
// For more details: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md // For more details: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
package openapi package builder
...@@ -14,10 +14,9 @@ See the License for the specific language governing permissions and ...@@ -14,10 +14,9 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package openapi package builder
import ( import (
"crypto/sha512"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
...@@ -27,34 +26,25 @@ import ( ...@@ -27,34 +26,25 @@ import (
restful "github.com/emicklei/go-restful" restful "github.com/emicklei/go-restful"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"k8s.io/apimachinery/pkg/openapi" "k8s.io/kube-openapi/pkg/common"
"k8s.io/apiserver/pkg/util/trie" "k8s.io/kube-openapi/pkg/util"
) )
const ( const (
OpenAPIVersion = "2.0" OpenAPIVersion = "2.0"
// TODO: Make this configurable.
extensionPrefix = "x-kubernetes-" extensionPrefix = "x-kubernetes-"
JSON_EXT = ".json"
MIME_JSON = "application/json"
// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
MIME_PB = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
MIME_PB_GZ = "application/x-gzip"
) )
type openAPI struct { type openAPI struct {
config *openapi.Config config *common.Config
swagger *spec.Swagger swagger *spec.Swagger
protocolList []string protocolList []string
definitions map[string]openapi.OpenAPIDefinition definitions map[string]common.OpenAPIDefinition
}
func computeEtag(data []byte) string {
return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
} }
func BuildSwaggerSpec(webServices []*restful.WebService, config *openapi.Config) (*spec.Swagger, error) { // BuildOpenAPISpec builds OpenAPI spec given a list of webservices (containing routes) and common.Config to customize it.
func BuildOpenAPISpec(webServices []*restful.WebService, config *common.Config) (*spec.Swagger, error) {
o := openAPI{ o := openAPI{
config: config, config: config,
swagger: &spec.Swagger{ swagger: &spec.Swagger{
...@@ -88,7 +78,7 @@ func (o *openAPI) init(webServices []*restful.WebService) error { ...@@ -88,7 +78,7 @@ func (o *openAPI) init(webServices []*restful.WebService) error {
} }
o.definitions = o.config.GetDefinitions(func(name string) spec.Ref { o.definitions = o.config.GetDefinitions(func(name string) spec.Ref {
defName, _ := o.config.GetDefinitionName(name) defName, _ := o.config.GetDefinitionName(name)
return spec.MustCreateRef(DEFINITION_PREFIX + openapi.EscapeJsonPointer(defName)) return spec.MustCreateRef("#/definitions/" + common.EscapeJsonPointer(defName))
}) })
if o.config.CommonResponses == nil { if o.config.CommonResponses == nil {
o.config.CommonResponses = map[int]spec.Response{} o.config.CommonResponses = map[int]spec.Response{}
...@@ -148,14 +138,14 @@ func (o *openAPI) buildDefinitionRecursively(name string) error { ...@@ -148,14 +138,14 @@ func (o *openAPI) buildDefinitionRecursively(name string) error {
} }
} }
} else { } else {
return fmt.Errorf("cannot find model definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run code-gen again.", name) return fmt.Errorf("cannot find model definition for %v. If you added a new type, you may need to add +k8s:openapi-gen=true to the package or type and run code-gen again", name)
} }
return nil return nil
} }
// buildDefinitionForType build a definition for a given type and return a referable name to it's definition. // buildDefinitionForType build a definition for a given type and return a referable name to it's definition.
// This is the main function that keep track of definitions used in this spec and is depend on code generated // This is the main function that keep track of definitions used in this spec and is depend on code generated
// by k8s.io/kube-gen/cmd/openapi-gen. // by k8s.io/kubernetes/cmd/libs/go2idl/openapi-gen.
func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) { func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) {
t := reflect.TypeOf(sample) t := reflect.TypeOf(sample)
if t.Kind() == reflect.Ptr { if t.Kind() == reflect.Ptr {
...@@ -166,12 +156,12 @@ func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) { ...@@ -166,12 +156,12 @@ func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) {
return "", err return "", err
} }
defName, _ := o.config.GetDefinitionName(name) defName, _ := o.config.GetDefinitionName(name)
return DEFINITION_PREFIX + openapi.EscapeJsonPointer(defName), nil return "#/definitions/" + common.EscapeJsonPointer(defName), nil
} }
// buildPaths builds OpenAPI paths using go-restful's web services. // buildPaths builds OpenAPI paths using go-restful's web services.
func (o *openAPI) buildPaths(webServices []*restful.WebService) error { func (o *openAPI) buildPaths(webServices []*restful.WebService) error {
pathsToIgnore := trie.New(o.config.IgnorePrefixes) pathsToIgnore := util.NewTrie(o.config.IgnorePrefixes)
duplicateOpId := make(map[string]string) duplicateOpId := make(map[string]string)
for _, w := range webServices { for _, w := range webServices {
rootPath := w.RootPath() rootPath := w.RootPath()
...@@ -219,7 +209,7 @@ func (o *openAPI) buildPaths(webServices []*restful.WebService) error { ...@@ -219,7 +209,7 @@ func (o *openAPI) buildPaths(webServices []*restful.WebService) error {
} }
dpath, exists := duplicateOpId[op.ID] dpath, exists := duplicateOpId[op.ID]
if exists { if exists {
return fmt.Errorf("Duplicate Operation ID %v for path %v and %v.", op.ID, dpath, path) return fmt.Errorf("duplicate Operation ID %v for path %v and %v", op.ID, dpath, path)
} else { } else {
duplicateOpId[op.ID] = path duplicateOpId[op.ID] = path
} }
...@@ -337,7 +327,7 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}] ...@@ -337,7 +327,7 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]
key := mapKeyFromParam(param) key := mapKeyFromParam(param)
if routeParamDuplicateMap[key] { if routeParamDuplicateMap[key] {
msg, _ := json.Marshal(route.ParameterDocs) msg, _ := json.Marshal(route.ParameterDocs)
return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v.", param.Data().Name, string(msg), s) return commonParamsMap, fmt.Errorf("duplicate parameter %v for route %v, %v", param.Data().Name, string(msg), s)
} }
routeParamDuplicateMap[key] = true routeParamDuplicateMap[key] = true
paramOpsCountByName[key]++ paramOpsCountByName[key]++
...@@ -358,7 +348,7 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}] ...@@ -358,7 +348,7 @@ func (o *openAPI) findCommonParameters(routes []restful.Route) (map[interface{}]
} }
func (o *openAPI) toSchema(model interface{}) (_ *spec.Schema, err error) { func (o *openAPI) toSchema(model interface{}) (_ *spec.Schema, err error) {
if openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(getCanonicalizeTypeName(reflect.TypeOf(model))); openAPIType != "" { if openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(getCanonicalizeTypeName(reflect.TypeOf(model))); openAPIType != "" {
return &spec.Schema{ return &spec.Schema{
SchemaProps: spec.SchemaProps{ SchemaProps: spec.SchemaProps{
Type: []string{openAPIType}, Type: []string{openAPIType},
...@@ -412,7 +402,7 @@ func (o *openAPI) buildParameter(restParam restful.ParameterData, bodySample int ...@@ -412,7 +402,7 @@ func (o *openAPI) buildParameter(restParam restful.ParameterData, bodySample int
default: default:
return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind) return ret, fmt.Errorf("unknown restful operation kind : %v", restParam.Kind)
} }
openAPIType, openAPIFormat := openapi.GetOpenAPITypeFormat(restParam.DataType) openAPIType, openAPIFormat := common.GetOpenAPITypeFormat(restParam.DataType)
if openAPIType == "" { if openAPIType == "" {
return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType) return ret, fmt.Errorf("non-body Restful parameter type should be a simple type, but got : %v", restParam.DataType)
} }
......
...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package openapi package builder
import ( import (
"sort" "sort"
......
...@@ -19,3 +19,16 @@ go_library( ...@@ -19,3 +19,16 @@ go_library(
"//vendor/github.com/go-openapi/spec:go_default_library", "//vendor/github.com/go-openapi/spec:go_default_library",
], ],
) )
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and ...@@ -14,12 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package openapi package common
import ( import (
"net/http"
"strings"
"github.com/emicklei/go-restful" "github.com/emicklei/go-restful"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"strings"
) )
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi. // OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
...@@ -41,6 +43,10 @@ type OpenAPIDefinitionGetter interface { ...@@ -41,6 +43,10 @@ type OpenAPIDefinitionGetter interface {
OpenAPIDefinition() *OpenAPIDefinition OpenAPIDefinition() *OpenAPIDefinition
} }
type PathHandler interface {
Handle(path string, handler http.Handler)
}
// Config is set of configuration for openAPI spec generation. // Config is set of configuration for openAPI spec generation.
type Config struct { type Config struct {
// List of supported protocols such as https, http, etc. // List of supported protocols such as https, http, etc.
......
...@@ -14,5 +14,6 @@ See the License for the specific language governing permissions and ...@@ -14,5 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
// package openapi holds shared codes and types between open API code generator and spec generator. // package common holds shared code and types between open API code
package openapi // generator and spec generator.
package common
...@@ -5,7 +5,6 @@ licenses(["notice"]) ...@@ -5,7 +5,6 @@ licenses(["notice"])
load( load(
"@io_bazel_rules_go//go:def.bzl", "@io_bazel_rules_go//go:def.bzl",
"go_library", "go_library",
"go_test",
) )
go_library( go_library(
...@@ -14,24 +13,23 @@ go_library( ...@@ -14,24 +13,23 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/openapi:go_default_library",
"//vendor/k8s.io/gengo/args:go_default_library", "//vendor/k8s.io/gengo/args:go_default_library",
"//vendor/k8s.io/gengo/generator:go_default_library", "//vendor/k8s.io/gengo/generator:go_default_library",
"//vendor/k8s.io/gengo/namer:go_default_library", "//vendor/k8s.io/gengo/namer:go_default_library",
"//vendor/k8s.io/gengo/types:go_default_library", "//vendor/k8s.io/gengo/types:go_default_library",
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
], ],
) )
go_test( filegroup(
name = "go_default_test", name = "package-srcs",
srcs = ["openapi_test.go"], srcs = glob(["**"]),
library = ":go_default_library", tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"], tags = ["automanaged"],
deps = [
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/k8s.io/gengo/generator:go_default_library",
"//vendor/k8s.io/gengo/namer:go_default_library",
"//vendor/k8s.io/gengo/parser:go_default_library",
"//vendor/k8s.io/gengo/types:go_default_library",
],
) )
# Generate OpenAPI definitions
- To generate definition for a specific type or package add "+k8s:openapi-gen=true" tag to the type/package comment lines.
- To exclude a type or a member from a tagged package/type, add "+k8s:openapi-gen=false" tag to the comment lines.
# OpenAPI Extensions
OpenAPI spec can have extensions on types. To define one or more extensions on a type or its member
add `+k8s:openapi-gen=x-kubernetes-$NAME:`$VALUE`` to the comment lines before type/member. A type/member can
have multiple extensions. The rest of the line in the comment will be used as $VALUE so there is no need to
escape or quote the value string. Extensions can be used to pass more information to client generators or
documentation generators. For example a type might have a friendly name to be displayed in documentation or
being used in a client's fluent interface.
TODO(mehdy): Make k8s:openapi-gen a parameter to the generator now that OpenAPI has its own repo.
...@@ -25,11 +25,11 @@ import ( ...@@ -25,11 +25,11 @@ import (
"sort" "sort"
"strings" "strings"
"k8s.io/apimachinery/pkg/openapi"
"k8s.io/gengo/args" "k8s.io/gengo/args"
"k8s.io/gengo/generator" "k8s.io/gengo/generator"
"k8s.io/gengo/namer" "k8s.io/gengo/namer"
"k8s.io/gengo/types" "k8s.io/gengo/types"
openapi "k8s.io/kube-openapi/pkg/common"
"github.com/golang/glog" "github.com/golang/glog"
) )
...@@ -59,7 +59,7 @@ func getSingleTagsValue(comments []string, tag string) (string, error) { ...@@ -59,7 +59,7 @@ func getSingleTagsValue(comments []string, tag string) (string, error) {
return "", nil return "", nil
} }
if len(tags) > 1 { if len(tags) > 1 {
return "", fmt.Errorf("Multiple values are not allowed for tag %s", tag) return "", fmt.Errorf("multiple values are not allowed for tag %s", tag)
} }
return tags[0], nil return tags[0], nil
} }
...@@ -153,7 +153,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat ...@@ -153,7 +153,7 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
const ( const (
specPackagePath = "github.com/go-openapi/spec" specPackagePath = "github.com/go-openapi/spec"
openAPICommonPackagePath = "k8s.io/apimachinery/pkg/openapi" openAPICommonPackagePath = "k8s.io/kube-openapi/pkg/common"
) )
// openApiGen produces a file with auto-generated OpenAPI functions. // openApiGen produces a file with auto-generated OpenAPI functions.
...@@ -391,7 +391,7 @@ func (g openAPITypeWriter) generateExtensions(CommentLines []string) error { ...@@ -391,7 +391,7 @@ func (g openAPITypeWriter) generateExtensions(CommentLines []string) error {
if strings.HasPrefix(val, tagExtensionPrefix) { if strings.HasPrefix(val, tagExtensionPrefix) {
parts := strings.SplitN(val, ":", 2) parts := strings.SplitN(val, ":", 2)
if len(parts) != 2 { if len(parts) != 2 {
return fmt.Errorf("Invalid extension value: %v", val) return fmt.Errorf("invalid extension value: %v", val)
} }
extensions = append(extensions, NameValue{parts[0], parts[1]}) extensions = append(extensions, NameValue{parts[0], parts[1]})
} }
...@@ -577,7 +577,7 @@ func (g openAPITypeWriter) generateMapProperty(t *types.Type) error { ...@@ -577,7 +577,7 @@ func (g openAPITypeWriter) generateMapProperty(t *types.Type) error {
} }
switch elemType.Kind { switch elemType.Kind {
case types.Builtin: case types.Builtin:
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function.", elemType) return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
case types.Struct: case types.Struct:
g.generateReferenceProperty(elemType) g.generateReferenceProperty(elemType)
case types.Slice, types.Array: case types.Slice, types.Array:
...@@ -601,7 +601,7 @@ func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error { ...@@ -601,7 +601,7 @@ func (g openAPITypeWriter) generateSliceProperty(t *types.Type) error {
} }
switch elemType.Kind { switch elemType.Kind {
case types.Builtin: case types.Builtin:
return fmt.Errorf("please add type %v to getOpenAPITypeFormat function.", elemType) return fmt.Errorf("please add type %v to getOpenAPITypeFormat function", elemType)
case types.Struct: case types.Struct:
g.generateReferenceProperty(elemType) g.generateReferenceProperty(elemType)
default: default:
......
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["handler.go"],
tags = ["automanaged"],
deps = [
"//vendor/github.com/NYTimes/gziphandler:go_default_library",
"//vendor/github.com/emicklei/go-restful:go_default_library",
"//vendor/github.com/go-openapi/spec: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/compiler: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/common:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -14,56 +14,97 @@ See the License for the specific language governing permissions and ...@@ -14,56 +14,97 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package openapi package handler
import ( import (
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"crypto/sha512"
"encoding/json" "encoding/json"
"fmt" "fmt"
"gopkg.in/yaml.v2"
"mime" "mime"
"net/http" "net/http"
"strings" "strings"
"sync"
"time" "time"
"github.com/NYTimes/gziphandler" "github.com/NYTimes/gziphandler"
"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"
"github.com/googleapis/gnostic/OpenAPIv2" "github.com/googleapis/gnostic/OpenAPIv2"
"github.com/googleapis/gnostic/compiler" "github.com/googleapis/gnostic/compiler"
"gopkg.in/yaml.v2"
genericmux "k8s.io/apiserver/pkg/server/mux" "k8s.io/kube-openapi/pkg/builder"
"k8s.io/kube-openapi/pkg/common"
)
const (
jsonExt = ".json"
mimeJson = "application/json"
// TODO(mehdy): change @68f4ded to a version tag when gnostic add version tags.
mimePb = "application/com.github.googleapis.gnostic.OpenAPIv2@68f4ded+protobuf"
mimePbGz = "application/x-gzip"
) )
// OpenAPIService is the service responsible for serving OpenAPI spec. It has
// the ability to safely change the spec while serving it.
type OpenAPIService struct { type OpenAPIService struct {
// rwMutex protects All members of this service.
rwMutex sync.RWMutex
orgSpec *spec.Swagger orgSpec *spec.Swagger
specBytes []byte
specPb []byte
specPbGz []byte
lastModified time.Time lastModified time.Time
updateHooks []func(*http.Request)
specBytes []byte
specPb []byte
specPbGz []byte
specBytesETag string
specPbETag string
specPbGzETag string
}
func init() {
mime.AddExtensionType(".json", mimeJson)
mime.AddExtensionType(".pb-v1", mimePb)
mime.AddExtensionType(".gz", mimePbGz)
}
func computeETag(data []byte) string {
return fmt.Sprintf("\"%X\"", sha512.Sum512(data))
}
// BuildAndRegisterOpenAPIService builds the spec and registers a handler to provides access to it.
// Use this method if your OpenAPI spec is static. If you want to update the spec, use BuildOpenAPISpec then RegisterOpenAPIService.
func BuildAndRegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, handler common.PathHandler) (*OpenAPIService, error) {
spec, err := builder.BuildOpenAPISpec(webServices, config)
if err != nil {
return nil, err
}
return RegisterOpenAPIService(spec, servePath, handler)
} }
// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification. // RegisterOpenAPIService registers a handler to provides access to provided swagger spec.
func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, mux *genericmux.PathRecorderMux) (*OpenAPIService, error) { // Note: servePath should end with ".json" as the RegisterOpenAPIService assume it is serving a
if !strings.HasSuffix(servePath, JSON_EXT) { // json file and will also serve .pb and .gz files.
return nil, fmt.Errorf("Serving path must ends with \"%s\".", JSON_EXT) func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, handler common.PathHandler) (*OpenAPIService, error) {
if !strings.HasSuffix(servePath, jsonExt) {
return nil, fmt.Errorf("serving path must end with \"%s\"", jsonExt)
} }
servePathBase := servePath[:len(servePath)-len(JSON_EXT)] servePathBase := strings.TrimSuffix(servePath, jsonExt)
o := OpenAPIService{} o := OpenAPIService{}
if err := o.UpdateSpec(openapiSpec); err != nil { if err := o.UpdateSpec(openapiSpec); err != nil {
return nil, err return nil, err
} }
mime.AddExtensionType(".json", MIME_JSON)
mime.AddExtensionType(".pb-v1", MIME_PB)
mime.AddExtensionType(".gz", MIME_PB_GZ)
type fileInfo struct { type fileInfo struct {
ext string ext string
getData func() []byte getDataAndETag func() ([]byte, string, time.Time)
} }
files := []fileInfo{ files := []fileInfo{
...@@ -75,21 +116,14 @@ func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, mux *ge ...@@ -75,21 +116,14 @@ func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, mux *ge
for _, file := range files { for _, file := range files {
path := servePathBase + file.ext path := servePathBase + file.ext
getData := file.getData getDataAndETag := file.getDataAndETag
mux.Handle(path, gziphandler.GzipHandler(http.HandlerFunc( handler.Handle(path, gziphandler.GzipHandler(http.HandlerFunc(
func(w http.ResponseWriter, r *http.Request) { func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != path { data, etag, lastModified := getDataAndETag()
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("Path not found!"))
return
}
o.update(r)
data := getData()
etag := computeEtag(data)
w.Header().Set("Etag", etag) w.Header().Set("Etag", etag)
// ServeContent will take care of caching using eTag. // ServeContent will take care of caching using eTag.
http.ServeContent(w, r, path, o.lastModified, bytes.NewReader(data)) http.ServeContent(w, r, path, lastModified, bytes.NewReader(data))
}), }),
)) ))
} }
...@@ -97,34 +131,53 @@ func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, mux *ge ...@@ -97,34 +131,53 @@ func RegisterOpenAPIService(openapiSpec *spec.Swagger, servePath string, mux *ge
return &o, nil return &o, nil
} }
func (o *OpenAPIService) getSwaggerBytes() []byte { func (o *OpenAPIService) getSwaggerBytes() ([]byte, string, time.Time) {
return o.specBytes o.rwMutex.RLock()
defer o.rwMutex.RUnlock()
return o.specBytes, o.specBytesETag, o.lastModified
} }
func (o *OpenAPIService) getSwaggerPbBytes() []byte { func (o *OpenAPIService) getSwaggerPbBytes() ([]byte, string, time.Time) {
return o.specPb o.rwMutex.RLock()
defer o.rwMutex.RUnlock()
return o.specPb, o.specPbETag, o.lastModified
} }
func (o *OpenAPIService) getSwaggerPbGzBytes() []byte { func (o *OpenAPIService) getSwaggerPbGzBytes() ([]byte, string, time.Time) {
return o.specPbGz o.rwMutex.RLock()
} defer o.rwMutex.RUnlock()
return o.specPbGz, o.specPbGzETag, o.lastModified
func (o *OpenAPIService) GetSpec() *spec.Swagger {
return o.orgSpec
} }
func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) { func (o *OpenAPIService) UpdateSpec(openapiSpec *spec.Swagger) (err error) {
o.orgSpec = openapiSpec orgSpec := openapiSpec
o.specBytes, err = json.MarshalIndent(openapiSpec, " ", " ") specBytes, err := json.MarshalIndent(openapiSpec, " ", " ")
if err != nil { if err != nil {
return err return err
} }
o.specPb, err = toProtoBinary(o.specBytes) specPb, err := toProtoBinary(specBytes)
if err != nil { if err != nil {
return err return err
} }
o.specPbGz = toGzip(o.specPb) specPbGz := toGzip(specPb)
o.lastModified = time.Now()
specBytesETag := computeETag(specBytes)
specPbETag := computeETag(specPb)
specPbGzETag := computeETag(specPbGz)
lastModified := time.Now()
o.rwMutex.Lock()
defer o.rwMutex.Unlock()
o.orgSpec = orgSpec
o.specBytes = specBytes
o.specPb = specPb
o.specPbGz = specPbGz
o.specBytesETag = specBytesETag
o.specPbETag = specPbETag
o.specPbGzETag = specPbGzETag
o.lastModified = lastModified
return nil return nil
} }
...@@ -149,15 +202,3 @@ func toGzip(data []byte) []byte { ...@@ -149,15 +202,3 @@ func toGzip(data []byte) []byte {
zw.Close() zw.Close()
return buf.Bytes() return buf.Bytes()
} }
// Adds an update hook to be called on each spec request. The hook is responsible
// to call UpdateSpec method.
func (o *OpenAPIService) AddUpdateHook(hook func(*http.Request)) {
o.updateHooks = append(o.updateHooks, hook)
}
func (o *OpenAPIService) update(r *http.Request) {
for _, h := range o.updateHooks {
h(r)
}
}
...@@ -12,3 +12,16 @@ go_library( ...@@ -12,3 +12,16 @@ go_library(
srcs = ["trie.go"], srcs = ["trie.go"],
tags = ["automanaged"], tags = ["automanaged"],
) )
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
...@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and ...@@ -14,17 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package trie package util
// A simple trie implementation with Add an HasPrefix methods only. // A simple trie implementation with Add and HasPrefix methods only.
type Trie struct { type Trie struct {
children map[byte]*Trie children map[byte]*Trie
wordTail bool wordTail bool
word string word string
} }
// New creates a Trie and add all strings in the provided list to it. // NewTrie creates a Trie and add all strings in the provided list to it.
func New(list []string) Trie { func NewTrie(list []string) Trie {
ret := Trie{ ret := Trie{
children: make(map[byte]*Trie), children: make(map[byte]*Trie),
wordTail: false, wordTail: false,
......
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