mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-01-04 23:17:50 +00:00
Make Kubernetes OpenAPI operation IDs unique
This commit is contained in:
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user