mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
Make Kubernetes OpenAPI operation IDs unique
This commit is contained in:
parent
221a620a14
commit
5ba06cf2bc
@ -42,13 +42,14 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
"k8s.io/kubernetes/pkg/apiserver/authenticator"
|
||||
"k8s.io/kubernetes/pkg/apiserver/openapi"
|
||||
authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
"k8s.io/kubernetes/pkg/capabilities"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
"k8s.io/kubernetes/pkg/controller/informers"
|
||||
serviceaccountcontroller "k8s.io/kubernetes/pkg/controller/serviceaccount"
|
||||
"k8s.io/kubernetes/pkg/generated/openapi"
|
||||
generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver/authorizer"
|
||||
genericoptions "k8s.io/kubernetes/pkg/genericapiserver/options"
|
||||
@ -334,8 +335,9 @@ func Run(s *options.APIServer) error {
|
||||
genericConfig.ProxyDialer = proxyDialerFn
|
||||
genericConfig.ProxyTLSClientConfig = proxyTLSClientConfig
|
||||
genericConfig.Serializer = api.Codecs
|
||||
genericConfig.OpenAPIInfo.Title = "Kubernetes"
|
||||
genericConfig.OpenAPIDefinitions = openapi.OpenAPIDefinitions
|
||||
genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
|
||||
genericConfig.OpenAPIConfig.Definitions = generatedopenapi.OpenAPIDefinitions
|
||||
genericConfig.OpenAPIConfig.GetOperationID = openapi.GetOperationID
|
||||
genericConfig.EnableOpenAPISupport = true
|
||||
|
||||
config := &master.Config{
|
||||
|
@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apiserver/authenticator"
|
||||
apiserveropenapi "k8s.io/kubernetes/pkg/apiserver/openapi"
|
||||
authorizerunion "k8s.io/kubernetes/pkg/auth/authorizer/union"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
"k8s.io/kubernetes/pkg/controller/informers"
|
||||
@ -221,7 +222,10 @@ func Run(s *options.ServerRunOptions) error {
|
||||
genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
|
||||
genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
|
||||
genericConfig.Serializer = api.Codecs
|
||||
genericConfig.OpenAPIDefinitions = openapi.OpenAPIDefinitions
|
||||
genericConfig.OpenAPIConfig.Definitions = openapi.OpenAPIDefinitions
|
||||
// Reusing api-server's GetOperationID function. if federation and api-server spec diverge and
|
||||
// this method does not provide good operation IDs for federation, we should create federation's own GetOperationID.
|
||||
genericConfig.OpenAPIConfig.GetOperationID = apiserveropenapi.GetOperationID
|
||||
genericConfig.EnableOpenAPISupport = true
|
||||
|
||||
// TODO: Move this to generic api server (Need to move the command line flag).
|
||||
|
@ -76,6 +76,7 @@ pkg/apis/rbac/install
|
||||
pkg/apis/storage/install
|
||||
pkg/apis/storage/validation
|
||||
pkg/apiserver/audit
|
||||
pkg/apiserver/openapi
|
||||
pkg/auth/authenticator
|
||||
pkg/auth/authorizer/union
|
||||
pkg/client/metrics
|
||||
|
81
pkg/apiserver/openapi/openapi.go
Normal file
81
pkg/apiserver/openapi/openapi.go
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
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 openapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
var verbs = util.CreateTrie([]string{"get", "log", "read", "replace", "patch", "delete", "deletecollection", "watch", "connect", "proxy", "list", "create", "patch"})
|
||||
|
||||
// ToValidOperationID makes an string a valid op ID (e.g. removing punctuations and whitespaces and make it camel case)
|
||||
func ToValidOperationID(s string, capitalizeFirstLetter bool) string {
|
||||
var buffer bytes.Buffer
|
||||
capitalize := capitalizeFirstLetter
|
||||
for i, r := range s {
|
||||
if unicode.IsLetter(r) || r == '_' || (i != 0 && unicode.IsDigit(r)) {
|
||||
if capitalize {
|
||||
buffer.WriteRune(unicode.ToUpper(r))
|
||||
capitalize = false
|
||||
} else {
|
||||
buffer.WriteRune(r)
|
||||
}
|
||||
} else {
|
||||
capitalize = true
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// GetOperationID returns a customize operation ID for kubernetes API server's OpenAPI spec to prevent duplicate IDs.
|
||||
func GetOperationID(servePath string, r *restful.Route) (string, error) {
|
||||
op := r.Operation
|
||||
path := r.Path
|
||||
// TODO: This is hacky, figure out where this name conflict is created and fix it at the root.
|
||||
if strings.HasPrefix(path, "/apis/extensions/v1beta1/namespaces/{namespace}/") && strings.HasSuffix(op, "ScaleScale") {
|
||||
op = op[:len(op)-10] + strings.Title(strings.Split(path[48:], "/")[0]) + "Scale"
|
||||
}
|
||||
switch servePath {
|
||||
case "/swagger.json":
|
||||
prefix, exists := verbs.GetPrefix(op)
|
||||
if !exists {
|
||||
return op, fmt.Errorf("operation names should start with a verb. Cannot determine operation verb from %v", op)
|
||||
}
|
||||
op = op[len(prefix):]
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
// Assume /api is /apis/core, remove this when we actually server /api/... on /apis/core/...
|
||||
if len(parts) >= 1 && parts[0] == "api" {
|
||||
parts = append([]string{"apis", "core"}, parts[1:]...)
|
||||
}
|
||||
if len(parts) >= 2 && parts[0] == "apis" {
|
||||
prefix = prefix + ToValidOperationID(strings.TrimSuffix(parts[1], ".k8s.io"), prefix != "")
|
||||
if len(parts) > 2 {
|
||||
prefix = prefix + ToValidOperationID(parts[2], prefix != "")
|
||||
}
|
||||
}
|
||||
return prefix + ToValidOperationID(op, prefix != ""), nil
|
||||
default:
|
||||
return op, nil
|
||||
}
|
||||
}
|
@ -156,15 +156,8 @@ type Config struct {
|
||||
// EnableOpenAPISupport enables OpenAPI support. Allow downstream customers to disable OpenAPI spec.
|
||||
EnableOpenAPISupport bool
|
||||
|
||||
// OpenAPIInfo will be directly available as Info section of Open API spec.
|
||||
OpenAPIInfo spec.Info
|
||||
|
||||
// OpenAPIDefaultResponse will be used if an web service operation does not have any responses listed.
|
||||
OpenAPIDefaultResponse spec.Response
|
||||
|
||||
// OpenAPIDefinitions is a map of type to OpenAPI spec for all types used in this API server. Failure to provide
|
||||
// this map or any of the models used by the server APIs will result in spec generation failure.
|
||||
OpenAPIDefinitions *common.OpenAPIDefinitions
|
||||
// OpenAPIConfig will be used in generating OpenAPI spec.
|
||||
OpenAPIConfig *common.Config
|
||||
|
||||
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
|
||||
// request has to wait.
|
||||
@ -253,13 +246,19 @@ func NewConfig(options *options.ServerRunOptions) *Config {
|
||||
ReadWritePort: options.SecurePort,
|
||||
ServiceClusterIPRange: &options.ServiceClusterIPRange,
|
||||
ServiceNodePortRange: options.ServiceNodePortRange,
|
||||
OpenAPIDefaultResponse: spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Default Response."}},
|
||||
OpenAPIInfo: spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Generic API Server",
|
||||
Version: "unversioned",
|
||||
OpenAPIConfig: &common.Config{
|
||||
ProtocolList: []string{"https"},
|
||||
IgnorePrefixes: []string{"/swaggerapi"},
|
||||
Info: &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Generic API Server",
|
||||
Version: "unversioned",
|
||||
},
|
||||
},
|
||||
DefaultResponse: &spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Default Response.",
|
||||
},
|
||||
},
|
||||
},
|
||||
MaxRequestsInFlight: options.MaxRequestsInFlight,
|
||||
@ -386,10 +385,8 @@ func (c completedConfig) New() (*GenericAPIServer, error) {
|
||||
KubernetesServiceNodePort: c.KubernetesServiceNodePort,
|
||||
apiGroupsForDiscovery: map[string]unversioned.APIGroup{},
|
||||
|
||||
enableOpenAPISupport: c.EnableOpenAPISupport,
|
||||
openAPIInfo: c.OpenAPIInfo,
|
||||
openAPIDefaultResponse: c.OpenAPIDefaultResponse,
|
||||
openAPIDefinitions: c.OpenAPIDefinitions,
|
||||
enableOpenAPISupport: c.EnableOpenAPISupport,
|
||||
openAPIConfig: c.OpenAPIConfig,
|
||||
}
|
||||
|
||||
s.HandlerContainer = mux.NewAPIContainer(http.NewServeMux(), c.Serializer)
|
||||
|
@ -31,9 +31,9 @@ import (
|
||||
systemd "github.com/coreos/go-systemd/daemon"
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful/swagger"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"k8s.io/kubernetes/pkg/admission"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
@ -147,6 +147,11 @@ type GenericAPIServer struct {
|
||||
apiGroupsForDiscoveryLock sync.RWMutex
|
||||
apiGroupsForDiscovery map[string]unversioned.APIGroup
|
||||
|
||||
// See Config.$name for documentation of these flags
|
||||
|
||||
enableOpenAPISupport bool
|
||||
openAPIConfig *common.Config
|
||||
|
||||
// PostStartHooks are each called after the server has started listening, in a separate go func for each
|
||||
// with no guaranteee 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
|
||||
@ -156,7 +161,6 @@ type GenericAPIServer struct {
|
||||
|
||||
// See Config.$name for documentation of these flags:
|
||||
|
||||
enableOpenAPISupport bool
|
||||
openAPIInfo spec.Info
|
||||
openAPIDefaultResponse spec.Response
|
||||
openAPIDefinitions *common.OpenAPIDefinitions
|
||||
@ -462,33 +466,20 @@ func (s *GenericAPIServer) InstallOpenAPI() {
|
||||
// Install one spec per web service, an ideal client will have a ClientSet containing one client
|
||||
// per each of these specs.
|
||||
for _, w := range s.HandlerContainer.RegisteredWebServices() {
|
||||
if w.RootPath() == "/swaggerapi" {
|
||||
if strings.HasPrefix(w.RootPath(), "/swaggerapi") {
|
||||
continue
|
||||
}
|
||||
info := s.openAPIInfo
|
||||
info.Title = info.Title + " " + w.RootPath()
|
||||
err := openapi.RegisterOpenAPIService(&openapi.Config{
|
||||
OpenAPIServePath: w.RootPath() + "/swagger.json",
|
||||
WebServices: []*restful.WebService{w},
|
||||
ProtocolList: []string{"https"},
|
||||
IgnorePrefixes: []string{"/swaggerapi"},
|
||||
Info: &info,
|
||||
DefaultResponse: &s.openAPIDefaultResponse,
|
||||
OpenAPIDefinitions: s.openAPIDefinitions,
|
||||
}, s.HandlerContainer.Container)
|
||||
config := *s.openAPIConfig
|
||||
config.Info = new(spec.Info)
|
||||
*config.Info = *s.openAPIConfig.Info
|
||||
config.Info.Title = config.Info.Title + " " + w.RootPath()
|
||||
err := openapi.RegisterOpenAPIService(w.RootPath()+"/swagger.json", []*restful.WebService{w}, &config, s.HandlerContainer.Container)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to register open api spec for %v: %v", w.RootPath(), err)
|
||||
}
|
||||
}
|
||||
err := openapi.RegisterOpenAPIService(&openapi.Config{
|
||||
OpenAPIServePath: "/swagger.json",
|
||||
WebServices: s.HandlerContainer.RegisteredWebServices(),
|
||||
ProtocolList: []string{"https"},
|
||||
IgnorePrefixes: []string{"/swaggerapi"},
|
||||
Info: &s.openAPIInfo,
|
||||
DefaultResponse: &s.openAPIDefaultResponse,
|
||||
OpenAPIDefinitions: s.openAPIDefinitions,
|
||||
}, s.HandlerContainer.Container)
|
||||
err := openapi.RegisterOpenAPIService("/swagger.json", s.HandlerContainer.RegisteredWebServices(), s.openAPIConfig, s.HandlerContainer.Container)
|
||||
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to register open api spec for root: %v", err)
|
||||
}
|
||||
|
@ -16,7 +16,10 @@ limitations under the License.
|
||||
|
||||
package common
|
||||
|
||||
import "github.com/go-openapi/spec"
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/go-openapi/spec"
|
||||
)
|
||||
|
||||
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
|
||||
type OpenAPIDefinition struct {
|
||||
@ -35,6 +38,27 @@ type OpenAPIDefinitionGetter interface {
|
||||
OpenAPIDefinition() *OpenAPIDefinition
|
||||
}
|
||||
|
||||
// Config is set of configuration for openAPI spec generation.
|
||||
type Config struct {
|
||||
// List of supported protocols such as https, http, etc.
|
||||
ProtocolList []string
|
||||
|
||||
// Info is general information about the API.
|
||||
Info *spec.Info
|
||||
// DefaultResponse will be used if an operation does not have any responses listed. It
|
||||
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
|
||||
DefaultResponse *spec.Response
|
||||
// List of webservice's path prefixes to ignore
|
||||
IgnorePrefixes []string
|
||||
|
||||
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
|
||||
// or any of the models will result in spec generation failure.
|
||||
Definitions *OpenAPIDefinitions
|
||||
|
||||
// GetOperationID returns operation id for a restful route. It is an optional function to customize operation IDs.
|
||||
GetOperationID func(servePath string, r *restful.Route) (string, error)
|
||||
}
|
||||
|
||||
// This function is a reference for converting go (or any custom type) to a simple open API type,format pair. There are
|
||||
// two ways to customize spec for a type. If you add it here, a type will be converted to a simple type and the type
|
||||
// comment (the comment that is added before type definition) will be lost. The spec will still have the property
|
||||
|
@ -26,47 +26,26 @@ import (
|
||||
"github.com/go-openapi/spec"
|
||||
|
||||
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
|
||||
"k8s.io/kubernetes/pkg/util/json"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/json"
|
||||
)
|
||||
|
||||
const (
|
||||
OpenAPIVersion = "2.0"
|
||||
)
|
||||
|
||||
// Config is set of configuration for openAPI spec generation.
|
||||
type Config struct {
|
||||
// Path to the spec file. by convention, it should name [.*/]*/swagger.json
|
||||
OpenAPIServePath string
|
||||
// List of web services for this API spec
|
||||
WebServices []*restful.WebService
|
||||
|
||||
// List of supported protocols such as https, http, etc.
|
||||
ProtocolList []string
|
||||
|
||||
// Info is general information about the API.
|
||||
Info *spec.Info
|
||||
// DefaultResponse will be used if an operation does not have any responses listed. It
|
||||
// will show up as ... "responses" : {"default" : $DefaultResponse} in the spec.
|
||||
DefaultResponse *spec.Response
|
||||
// List of webservice's path prefixes to ignore
|
||||
IgnorePrefixes []string
|
||||
|
||||
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
|
||||
// or any of the models will result in spec generation failure.
|
||||
OpenAPIDefinitions *common.OpenAPIDefinitions
|
||||
}
|
||||
|
||||
type openAPI struct {
|
||||
config *Config
|
||||
config *common.Config
|
||||
swagger *spec.Swagger
|
||||
protocolList []string
|
||||
servePath string
|
||||
}
|
||||
|
||||
// RegisterOpenAPIService registers a handler to provides standard OpenAPI specification.
|
||||
func RegisterOpenAPIService(config *Config, containers *restful.Container) (err error) {
|
||||
func RegisterOpenAPIService(servePath string, webServices []*restful.WebService, config *common.Config, containers *restful.Container) (err error) {
|
||||
o := openAPI{
|
||||
config: config,
|
||||
config: config,
|
||||
servePath: servePath,
|
||||
swagger: &spec.Swagger{
|
||||
SwaggerProps: spec.SwaggerProps{
|
||||
Swagger: OpenAPIVersion,
|
||||
@ -77,14 +56,14 @@ func RegisterOpenAPIService(config *Config, containers *restful.Container) (err
|
||||
},
|
||||
}
|
||||
|
||||
err = o.init()
|
||||
err = o.init(webServices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
containers.ServeMux.HandleFunc(config.OpenAPIServePath, func(w http.ResponseWriter, r *http.Request) {
|
||||
containers.ServeMux.HandleFunc(servePath, func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := restful.NewResponse(w)
|
||||
if r.URL.Path != config.OpenAPIServePath {
|
||||
if r.URL.Path != servePath {
|
||||
resp.WriteErrorString(http.StatusNotFound, "Path not found!")
|
||||
}
|
||||
// TODO: we can cache json string and return it here.
|
||||
@ -93,8 +72,13 @@ func RegisterOpenAPIService(config *Config, containers *restful.Container) (err
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *openAPI) init() error {
|
||||
err := o.buildPaths()
|
||||
func (o *openAPI) init(webServices []*restful.WebService) error {
|
||||
if o.config.GetOperationID == nil {
|
||||
o.config.GetOperationID = func(_ string, r *restful.Route) (string, error) {
|
||||
return r.Operation, nil
|
||||
}
|
||||
}
|
||||
err := o.buildPaths(webServices)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -105,7 +89,7 @@ func (o *openAPI) buildDefinitionRecursively(name string) error {
|
||||
if _, ok := o.swagger.Definitions[name]; ok {
|
||||
return nil
|
||||
}
|
||||
if item, ok := (*o.config.OpenAPIDefinitions)[name]; ok {
|
||||
if item, ok := (*o.config.Definitions)[name]; ok {
|
||||
o.swagger.Definitions[name] = item.Schema
|
||||
for _, v := range item.Dependencies {
|
||||
if err := o.buildDefinitionRecursively(v); err != nil {
|
||||
@ -134,20 +118,10 @@ func (o *openAPI) buildDefinitionForType(sample interface{}) (string, error) {
|
||||
}
|
||||
|
||||
// buildPaths builds OpenAPI paths using go-restful's web services.
|
||||
func (o *openAPI) buildPaths() error {
|
||||
func (o *openAPI) buildPaths(webServices []*restful.WebService) error {
|
||||
pathsToIgnore := util.CreateTrie(o.config.IgnorePrefixes)
|
||||
duplicateOpId := make(map[string]bool)
|
||||
// Find duplicate operation IDs.
|
||||
for _, service := range o.config.WebServices {
|
||||
if pathsToIgnore.HasPrefix(service.RootPath()) {
|
||||
continue
|
||||
}
|
||||
for _, route := range service.Routes() {
|
||||
_, exists := duplicateOpId[route.Operation]
|
||||
duplicateOpId[route.Operation] = exists
|
||||
}
|
||||
}
|
||||
for _, w := range o.config.WebServices {
|
||||
duplicateOpId := make(map[string]string)
|
||||
for _, w := range webServices {
|
||||
rootPath := w.RootPath()
|
||||
if pathsToIgnore.HasPrefix(rootPath) {
|
||||
continue
|
||||
@ -191,11 +165,11 @@ func (o *openAPI) buildPaths() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if duplicateOpId[op.ID] {
|
||||
// Repeated Operation IDs are not allowed in OpenAPI spec but if
|
||||
// an OperationID is empty, client generators will infer the ID
|
||||
// from the path and method of operation.
|
||||
op.ID = ""
|
||||
dpath, exists := duplicateOpId[op.ID]
|
||||
if exists {
|
||||
return fmt.Errorf("Duplicate Operation ID %v for path %v and %v.", op.ID, dpath, path)
|
||||
} else {
|
||||
duplicateOpId[op.ID] = path
|
||||
}
|
||||
switch strings.ToUpper(route.Method) {
|
||||
case "GET":
|
||||
@ -227,7 +201,6 @@ func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map
|
||||
Description: route.Doc,
|
||||
Consumes: route.Consumes,
|
||||
Produces: route.Produces,
|
||||
ID: route.Operation,
|
||||
Schemes: o.config.ProtocolList,
|
||||
Responses: &spec.Responses{
|
||||
ResponsesProps: spec.ResponsesProps{
|
||||
@ -236,6 +209,9 @@ func (o *openAPI) buildOperations(route restful.Route, inPathCommonParamsMap map
|
||||
},
|
||||
},
|
||||
}
|
||||
if ret.ID, err = o.config.GetOperationID(o.servePath, &route); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Build responses
|
||||
for _, resp := range route.ResponseErrors {
|
||||
|
@ -28,9 +28,9 @@ import (
|
||||
)
|
||||
|
||||
// setUp is a convenience function for setting up for (most) tests.
|
||||
func setUp(t *testing.T, fullMethods bool) (openAPI, *assert.Assertions) {
|
||||
func setUp(t *testing.T, fullMethods bool) (openAPI, *restful.Container, *assert.Assertions) {
|
||||
assert := assert.New(t)
|
||||
config := getConfig(fullMethods)
|
||||
config, container := getConfig(fullMethods)
|
||||
return openAPI{
|
||||
config: config,
|
||||
swagger: &spec.Swagger{
|
||||
@ -41,7 +41,7 @@ func setUp(t *testing.T, fullMethods bool) (openAPI, *assert.Assertions) {
|
||||
Info: config.Info,
|
||||
},
|
||||
},
|
||||
}, assert
|
||||
}, container, assert
|
||||
}
|
||||
|
||||
func noOp(request *restful.Request, response *restful.Response) {}
|
||||
@ -130,11 +130,11 @@ func (_ TestOutput) OpenAPIDefinition() *common.OpenAPIDefinition {
|
||||
var _ common.OpenAPIDefinitionGetter = TestInput{}
|
||||
var _ common.OpenAPIDefinitionGetter = TestOutput{}
|
||||
|
||||
func getTestRoute(ws *restful.WebService, method string, additionalParams bool) *restful.RouteBuilder {
|
||||
func getTestRoute(ws *restful.WebService, method string, additionalParams bool, opPrefix string) *restful.RouteBuilder {
|
||||
ret := ws.Method(method).
|
||||
Path("/test/{path:*}").
|
||||
Doc(fmt.Sprintf("%s test input", method)).
|
||||
Operation(fmt.Sprintf("%sTestInput", method)).
|
||||
Operation(fmt.Sprintf("%s%sTestInput", method, opPrefix)).
|
||||
Produces(restful.MIME_JSON).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Param(ws.PathParameter("path", "path to the resource").DataType("string")).
|
||||
@ -150,52 +150,50 @@ func getTestRoute(ws *restful.WebService, method string, additionalParams bool)
|
||||
return ret
|
||||
}
|
||||
|
||||
func getConfig(fullMethods bool) *Config {
|
||||
func getConfig(fullMethods bool) (*common.Config, *restful.Container) {
|
||||
mux := http.NewServeMux()
|
||||
container := restful.NewContainer()
|
||||
container.ServeMux = mux
|
||||
ws := new(restful.WebService)
|
||||
ws.Path("/foo")
|
||||
ws.Route(getTestRoute(ws, "get", true))
|
||||
ws.Route(getTestRoute(ws, "get", true, "foo"))
|
||||
if fullMethods {
|
||||
ws.Route(getTestRoute(ws, "post", false)).
|
||||
Route(getTestRoute(ws, "put", false)).
|
||||
Route(getTestRoute(ws, "head", false)).
|
||||
Route(getTestRoute(ws, "patch", false)).
|
||||
Route(getTestRoute(ws, "options", false)).
|
||||
Route(getTestRoute(ws, "delete", false))
|
||||
ws.Route(getTestRoute(ws, "post", false, "foo")).
|
||||
Route(getTestRoute(ws, "put", false, "foo")).
|
||||
Route(getTestRoute(ws, "head", false, "foo")).
|
||||
Route(getTestRoute(ws, "patch", false, "foo")).
|
||||
Route(getTestRoute(ws, "options", false, "foo")).
|
||||
Route(getTestRoute(ws, "delete", false, "foo"))
|
||||
|
||||
}
|
||||
ws.Path("/bar")
|
||||
ws.Route(getTestRoute(ws, "get", true))
|
||||
ws.Route(getTestRoute(ws, "get", true, "bar"))
|
||||
if fullMethods {
|
||||
ws.Route(getTestRoute(ws, "post", false)).
|
||||
Route(getTestRoute(ws, "put", false)).
|
||||
Route(getTestRoute(ws, "head", false)).
|
||||
Route(getTestRoute(ws, "patch", false)).
|
||||
Route(getTestRoute(ws, "options", false)).
|
||||
Route(getTestRoute(ws, "delete", false))
|
||||
ws.Route(getTestRoute(ws, "post", false, "bar")).
|
||||
Route(getTestRoute(ws, "put", false, "bar")).
|
||||
Route(getTestRoute(ws, "head", false, "bar")).
|
||||
Route(getTestRoute(ws, "patch", false, "bar")).
|
||||
Route(getTestRoute(ws, "options", false, "bar")).
|
||||
Route(getTestRoute(ws, "delete", false, "bar"))
|
||||
|
||||
}
|
||||
container.Add(ws)
|
||||
return &Config{
|
||||
WebServices: container.RegisteredWebServices(),
|
||||
ProtocolList: []string{"https"},
|
||||
OpenAPIServePath: "/swagger.json",
|
||||
return &common.Config{
|
||||
ProtocolList: []string{"https"},
|
||||
Info: &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "TestAPI",
|
||||
Description: "Test API",
|
||||
},
|
||||
},
|
||||
OpenAPIDefinitions: &common.OpenAPIDefinitions{
|
||||
Definitions: &common.OpenAPIDefinitions{
|
||||
"openapi.TestInput": *TestInput{}.OpenAPIDefinition(),
|
||||
"openapi.TestOutput": *TestOutput{}.OpenAPIDefinition(),
|
||||
},
|
||||
}
|
||||
}, container
|
||||
}
|
||||
|
||||
func getTestOperation(method string) *spec.Operation {
|
||||
func getTestOperation(method string, opPrefix string) *spec.Operation {
|
||||
return &spec.Operation{
|
||||
OperationProps: spec.OperationProps{
|
||||
Description: fmt.Sprintf("%s test input", method),
|
||||
@ -204,25 +202,26 @@ func getTestOperation(method string) *spec.Operation {
|
||||
Schemes: []string{"https"},
|
||||
Parameters: []spec.Parameter{},
|
||||
Responses: getTestResponses(),
|
||||
ID: fmt.Sprintf("%s%sTestInput", method, opPrefix),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getTestPathItem(allMethods bool) spec.PathItem {
|
||||
func getTestPathItem(allMethods bool, opPrefix string) spec.PathItem {
|
||||
ret := spec.PathItem{
|
||||
PathItemProps: spec.PathItemProps{
|
||||
Get: getTestOperation("get"),
|
||||
Get: getTestOperation("get", opPrefix),
|
||||
Parameters: getTestCommonParameters(),
|
||||
},
|
||||
}
|
||||
ret.Get.Parameters = getAdditionalTestParameters()
|
||||
if allMethods {
|
||||
ret.PathItemProps.Put = getTestOperation("put")
|
||||
ret.PathItemProps.Post = getTestOperation("post")
|
||||
ret.PathItemProps.Head = getTestOperation("head")
|
||||
ret.PathItemProps.Patch = getTestOperation("patch")
|
||||
ret.PathItemProps.Delete = getTestOperation("delete")
|
||||
ret.PathItemProps.Options = getTestOperation("options")
|
||||
ret.PathItemProps.Put = getTestOperation("put", opPrefix)
|
||||
ret.PathItemProps.Post = getTestOperation("post", opPrefix)
|
||||
ret.PathItemProps.Head = getTestOperation("head", opPrefix)
|
||||
ret.PathItemProps.Patch = getTestOperation("patch", opPrefix)
|
||||
ret.PathItemProps.Delete = getTestOperation("delete", opPrefix)
|
||||
ret.PathItemProps.Options = getTestOperation("options", opPrefix)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
@ -380,7 +379,7 @@ func getTestOutputDefinition() spec.Schema {
|
||||
}
|
||||
|
||||
func TestBuildSwaggerSpec(t *testing.T) {
|
||||
o, assert := setUp(t, true)
|
||||
o, container, assert := setUp(t, true)
|
||||
expected := &spec.Swagger{
|
||||
SwaggerProps: spec.SwaggerProps{
|
||||
Info: &spec.Info{
|
||||
@ -392,8 +391,8 @@ func TestBuildSwaggerSpec(t *testing.T) {
|
||||
Swagger: "2.0",
|
||||
Paths: &spec.Paths{
|
||||
Paths: map[string]spec.PathItem{
|
||||
"/foo/test/{path}": getTestPathItem(true),
|
||||
"/bar/test/{path}": getTestPathItem(true),
|
||||
"/foo/test/{path}": getTestPathItem(true, "foo"),
|
||||
"/bar/test/{path}": getTestPathItem(true, "bar"),
|
||||
},
|
||||
},
|
||||
Definitions: spec.Definitions{
|
||||
@ -402,7 +401,7 @@ func TestBuildSwaggerSpec(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
err := o.init()
|
||||
err := o.init(container.RegisteredWebServices())
|
||||
if assert.NoError(err) {
|
||||
assert.Equal(expected, o.swagger)
|
||||
}
|
||||
|
@ -44,10 +44,12 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apiserver/openapi"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/generated/openapi"
|
||||
openapigen "k8s.io/kubernetes/pkg/generated/openapi"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
ipallocator "k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
|
||||
"k8s.io/kubernetes/pkg/registry/registrytest"
|
||||
@ -69,7 +71,9 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
|
||||
server, storageConfig := etcdtesting.NewUnsecuredEtcd3TestClientServer(t)
|
||||
|
||||
config := &Config{
|
||||
GenericConfig: &genericapiserver.Config{},
|
||||
GenericConfig: &genericapiserver.Config{
|
||||
OpenAPIConfig: &common.Config{},
|
||||
},
|
||||
}
|
||||
|
||||
resourceEncoding := genericapiserver.NewDefaultResourceEncodingConfig()
|
||||
@ -499,15 +503,16 @@ func TestValidOpenAPISpec(t *testing.T) {
|
||||
_, etcdserver, config, assert := setUp(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
config.GenericConfig.OpenAPIDefinitions = openapi.OpenAPIDefinitions
|
||||
config.GenericConfig.OpenAPIConfig.Definitions = openapigen.OpenAPIDefinitions
|
||||
config.GenericConfig.EnableOpenAPISupport = true
|
||||
config.GenericConfig.EnableIndex = true
|
||||
config.GenericConfig.OpenAPIInfo = spec.Info{
|
||||
config.GenericConfig.OpenAPIConfig.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Kubernetes",
|
||||
Version: "unversioned",
|
||||
},
|
||||
}
|
||||
config.GenericConfig.OpenAPIConfig.GetOperationID = openapi.GetOperationID
|
||||
master, err := config.Complete().New()
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the master: %v", err)
|
||||
@ -592,7 +597,7 @@ func TestValidOpenAPISpec(t *testing.T) {
|
||||
t.Logf("Open API spec on %v has some warnings : %v", path, warns)
|
||||
}
|
||||
} else {
|
||||
t.Logf("Validation is disabled because it is timing out on jenkins put passing locally.")
|
||||
t.Logf("Validation is disabled because it is timing out on jenkins but passing locally.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,10 @@ package util
|
||||
type Trie struct {
|
||||
children map[byte]*Trie
|
||||
wordTail bool
|
||||
word string
|
||||
}
|
||||
|
||||
// CreateTrie creates a Trie and add all strings in the provided list to it.
|
||||
func CreateTrie(list []string) Trie {
|
||||
ret := Trie{
|
||||
children: make(map[byte]*Trie),
|
||||
@ -33,6 +35,7 @@ func CreateTrie(list []string) Trie {
|
||||
return ret
|
||||
}
|
||||
|
||||
// Add adds a word to this trie
|
||||
func (t *Trie) Add(v string) {
|
||||
root := t
|
||||
for _, b := range []byte(v) {
|
||||
@ -47,24 +50,30 @@ func (t *Trie) Add(v string) {
|
||||
root = child
|
||||
}
|
||||
root.wordTail = true
|
||||
root.word = v
|
||||
}
|
||||
|
||||
// HasPrefix returns true of v has any of the prefixes stored in this trie.
|
||||
func (t *Trie) HasPrefix(v string) bool {
|
||||
_, has := t.GetPrefix(v)
|
||||
return has
|
||||
}
|
||||
|
||||
// GetPrefix is like HasPrefix but return the prefix in case of match or empty string otherwise.
|
||||
func (t *Trie) GetPrefix(v string) (string, bool) {
|
||||
root := t
|
||||
if root.wordTail {
|
||||
return true
|
||||
return root.word, true
|
||||
}
|
||||
for _, b := range []byte(v) {
|
||||
child, exists := root.children[b]
|
||||
if !exists {
|
||||
return false
|
||||
return "", false
|
||||
}
|
||||
if child.wordTail {
|
||||
return true
|
||||
return child.word, true
|
||||
}
|
||||
root = child
|
||||
}
|
||||
return false
|
||||
return "", false
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,6 +68,7 @@ import (
|
||||
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/pborman/uuid"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver/openapi/common"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -184,17 +185,18 @@ func startMasterOrDie(masterConfig *master.Config, incomingServer *httptest.Serv
|
||||
masterConfig.GenericConfig.EnableProfiling = true
|
||||
masterConfig.GenericConfig.EnableSwaggerSupport = true
|
||||
masterConfig.GenericConfig.EnableOpenAPISupport = true
|
||||
masterConfig.GenericConfig.OpenAPIInfo = spec.Info{
|
||||
masterConfig.GenericConfig.OpenAPIConfig.Info = &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Kubernetes",
|
||||
Version: "unversioned",
|
||||
},
|
||||
}
|
||||
masterConfig.GenericConfig.OpenAPIDefaultResponse = spec.Response{
|
||||
masterConfig.GenericConfig.OpenAPIConfig.DefaultResponse = &spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Default Response.",
|
||||
},
|
||||
}
|
||||
masterConfig.GenericConfig.OpenAPIConfig.Definitions = openapi.OpenAPIDefinitions
|
||||
}
|
||||
|
||||
// set the loopback client config
|
||||
@ -355,8 +357,8 @@ func NewMasterConfig() *master.Config {
|
||||
ServiceClusterIPRange: parseCIDROrDie("10.0.0.0/24"),
|
||||
ServiceNodePortRange: utilnet.PortRange{Base: 30000, Size: 2768},
|
||||
EnableVersion: true,
|
||||
OpenAPIDefinitions: openapi.OpenAPIDefinitions,
|
||||
EnableOpenAPISupport: true,
|
||||
OpenAPIConfig: &common.Config{},
|
||||
},
|
||||
StorageFactory: storageFactory,
|
||||
EnableCoreControllers: true,
|
||||
|
Loading…
Reference in New Issue
Block a user