From d228cc34f33b7464dfb429e29a4386838ec22014 Mon Sep 17 00:00:00 2001 From: mbohlool Date: Thu, 22 Sep 2016 05:43:56 -0700 Subject: [PATCH] Fix API Installer to generate unique Operation IDs --- pkg/apiserver/api_installer.go | 114 ++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 06b6c323a5f..317081b6f8c 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -47,10 +47,11 @@ type APIInstaller struct { // Struct capturing information about an action ("GET", "POST", "WATCH", PROXY", etc). type action struct { - Verb string // Verb identifying the action ("GET", "POST", "WATCH", PROXY", etc). - Path string // The path of the action - Params []*restful.Parameter // List of parameters associated with the action. - Namer ScopeNamer + Verb string // Verb identifying the action ("GET", "POST", "WATCH", PROXY", etc). + Path string // The path of the action + Params []*restful.Parameter // List of parameters associated with the action. + Namer ScopeNamer + AllNamespaces bool // true iff the action is namespaced but works on aggregate result for all namespaces } // An interface to see if an object supports swagger documentation as a method @@ -367,29 +368,29 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag // Handler for standard REST verbs (GET, PUT, POST and DELETE). // Add actions at the resource path: /api/apiVersion/resource - actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister) - actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater) - actions = appendIf(actions, action{"DELETECOLLECTION", resourcePath, resourceParams, namer}, isCollectionDeleter) + actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister) + actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater) + actions = appendIf(actions, action{"DELETECOLLECTION", resourcePath, resourceParams, namer, false}, isCollectionDeleter) // DEPRECATED - actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer}, allowWatchList) + actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer, false}, allowWatchList) // Add actions at the item path: /api/apiVersion/resource/{name} - actions = appendIf(actions, action{"GET", itemPath, nameParams, namer}, isGetter) + actions = appendIf(actions, action{"GET", itemPath, nameParams, namer, false}, isGetter) if getSubpath { - actions = appendIf(actions, action{"GET", itemPath + "/{path:*}", proxyParams, namer}, isGetter) + actions = appendIf(actions, action{"GET", itemPath + "/{path:*}", proxyParams, namer, false}, isGetter) } - actions = appendIf(actions, action{"PUT", itemPath, nameParams, namer}, isUpdater) - actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher) - actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter) - actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer}, isWatcher) + actions = appendIf(actions, action{"PUT", itemPath, nameParams, namer, false}, isUpdater) + actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer, false}, isPatcher) + actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer, false}, isDeleter) + actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer, false}, isWatcher) // We add "proxy" subresource to remove the need for the generic top level prefix proxy. // The generic top level prefix proxy is deprecated in v1.2, and will be removed in 1.3, or 1.4 at the latest. // TODO: DEPRECATED in v1.2. - actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer}, isRedirector) + actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer, false}, isRedirector) // TODO: DEPRECATED in v1.2. - actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer}, isRedirector) - actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer}, isConnecter) - actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer}, isConnecter && connectSubpath) + actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer, false}, isRedirector) + actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer, false}, isConnecter) + actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer, false}, isConnecter && connectSubpath) break case meta.RESTScopeNameNamespace: // Handler for standard REST verbs (GET, PUT, POST and DELETE). @@ -420,36 +421,36 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag } namer := scopeNaming{scope, a.group.Linker, itemPathFn, false} - actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister) - actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater) - actions = appendIf(actions, action{"DELETECOLLECTION", resourcePath, resourceParams, namer}, isCollectionDeleter) + actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer, false}, isLister) + actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer, false}, isCreater) + actions = appendIf(actions, action{"DELETECOLLECTION", resourcePath, resourceParams, namer, false}, isCollectionDeleter) // DEPRECATED - actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer}, allowWatchList) + actions = appendIf(actions, action{"WATCHLIST", "watch/" + resourcePath, resourceParams, namer, false}, allowWatchList) - actions = appendIf(actions, action{"GET", itemPath, nameParams, namer}, isGetter) + actions = appendIf(actions, action{"GET", itemPath, nameParams, namer, false}, isGetter) if getSubpath { - actions = appendIf(actions, action{"GET", itemPath + "/{path:*}", proxyParams, namer}, isGetter) + actions = appendIf(actions, action{"GET", itemPath + "/{path:*}", proxyParams, namer, false}, isGetter) } - actions = appendIf(actions, action{"PUT", itemPath, nameParams, namer}, isUpdater) - actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer}, isPatcher) - actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer}, isDeleter) - actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer}, isWatcher) + actions = appendIf(actions, action{"PUT", itemPath, nameParams, namer, false}, isUpdater) + actions = appendIf(actions, action{"PATCH", itemPath, nameParams, namer, false}, isPatcher) + actions = appendIf(actions, action{"DELETE", itemPath, nameParams, namer, false}, isDeleter) + actions = appendIf(actions, action{"WATCH", "watch/" + itemPath, nameParams, namer, false}, isWatcher) // We add "proxy" subresource to remove the need for the generic top level prefix proxy. // The generic top level prefix proxy is deprecated in v1.2, and will be removed in 1.3, or 1.4 at the latest. // TODO: DEPRECATED in v1.2. - actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer}, isRedirector) + actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath + "/{path:*}", proxyParams, namer, false}, isRedirector) // TODO: DEPRECATED in v1.2. - actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer}, isRedirector) - actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer}, isConnecter) - actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer}, isConnecter && connectSubpath) + actions = appendIf(actions, action{"PROXY", "proxy/" + itemPath, nameParams, namer, false}, isRedirector) + actions = appendIf(actions, action{"CONNECT", itemPath, nameParams, namer, false}, isConnecter) + actions = appendIf(actions, action{"CONNECT", itemPath + "/{path:*}", proxyParams, namer, false}, isConnecter && connectSubpath) // list or post across namespace. // For ex: LIST all pods in all namespaces by sending a LIST request at /api/apiVersion/pods. // TODO: more strongly type whether a resource allows these actions on "all namespaces" (bulk delete) if !hasSubresource { namer = scopeNaming{scope, a.group.Linker, itemPathFn, true} - actions = appendIf(actions, action{"LIST", resource, params, namer}, isLister) - actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer}, allowWatchList) + actions = appendIf(actions, action{"LIST", resource, params, namer, true}, isLister) + actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer, true}, allowWatchList) } break default: @@ -492,6 +493,15 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag if apiResource.Namespaced { namespaced = "Namespaced" } + operationSuffix := "" + if strings.HasSuffix(action.Path, "/{path:*}") { + operationSuffix = operationSuffix + "WithPath" + } + if action.AllNamespaces { + operationSuffix = operationSuffix + "ForAllNamespaces" + namespaced = "" + } + switch action.Verb { case "GET": // Get a resource. var handler restful.RouteFunction @@ -508,7 +518,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.GET(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("read"+namespaced+kind+strings.Title(subresource)). + Operation("read"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Writes(versionedObject) @@ -533,7 +543,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.GET(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("list"+namespaced+kind+strings.Title(subresource)). + Operation("list"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedList). Writes(versionedList) @@ -565,7 +575,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.PUT(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("replace"+namespaced+kind+strings.Title(subresource)). + Operation("replace"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). @@ -582,7 +592,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). Consumes(string(api.JSONPatchType), string(api.MergePatchType), string(api.StrategicMergePatchType)). - Operation("patch"+namespaced+kind+strings.Title(subresource)). + Operation("patch"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(unversioned.Patch{}). @@ -604,7 +614,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.POST(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("create"+namespaced+kind+strings.Title(subresource)). + Operation("create"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Returns(http.StatusOK, "OK", versionedObject). Reads(versionedObject). @@ -620,7 +630,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.DELETE(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("delete"+namespaced+kind+strings.Title(subresource)). + Operation("delete"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) @@ -638,7 +648,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.DELETE(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("deletecollection"+namespaced+kind+strings.Title(subresource)). + Operation("deletecollection"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(append(storageMeta.ProducesMIMETypes(action.Verb), a.group.Serializer.SupportedMediaTypes()...)...). Writes(versionedStatus). Returns(http.StatusOK, "OK", versionedStatus) @@ -657,7 +667,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.GET(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("watch"+namespaced+kind+strings.Title(subresource)). + Operation("watch"+namespaced+kind+strings.Title(subresource)+operationSuffix). Produces(a.group.Serializer.SupportedStreamingMediaTypes()...). Returns(http.StatusOK, "OK", versionedWatchEvent). Writes(versionedWatchEvent) @@ -676,7 +686,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.GET(action.Path).To(handler). Doc(doc). Param(ws.QueryParameter("pretty", "If 'true', then the output is pretty printed.")). - Operation("watch"+namespaced+kind+strings.Title(subresource)+"List"). + Operation("watch"+namespaced+kind+strings.Title(subresource)+"List"+operationSuffix). Produces(a.group.Serializer.SupportedStreamingMediaTypes()...). Returns(http.StatusOK, "OK", versionedWatchEvent). Writes(versionedWatchEvent) @@ -690,12 +700,12 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag // TODO: DEPRECATED in v1.2. case "PROXY": // Proxy requests to a resource. // Accept all methods as per http://issue.k8s.io/3996 - addProxyRoute(ws, "GET", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) - addProxyRoute(ws, "PUT", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) - addProxyRoute(ws, "POST", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) - addProxyRoute(ws, "DELETE", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) - addProxyRoute(ws, "HEAD", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) - addProxyRoute(ws, "OPTIONS", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params) + addProxyRoute(ws, "GET", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) + addProxyRoute(ws, "PUT", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) + addProxyRoute(ws, "POST", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) + addProxyRoute(ws, "DELETE", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) + addProxyRoute(ws, "HEAD", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) + addProxyRoute(ws, "OPTIONS", a.prefix, action.Path, proxyHandler, namespaced, kind, resource, subresource, hasSubresource, action.Params, operationSuffix) case "CONNECT": for _, method := range connecter.ConnectMethods() { doc := "connect " + method + " requests to " + kind @@ -706,7 +716,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag route := ws.Method(method).Path(action.Path). To(handler). Doc(doc). - Operation("connect" + strings.Title(strings.ToLower(method)) + namespaced + kind + strings.Title(subresource)). + Operation("connect" + strings.Title(strings.ToLower(method)) + namespaced + kind + strings.Title(subresource) + operationSuffix). Produces("*/*"). Consumes("*/*"). Writes("string") @@ -884,7 +894,7 @@ func routeFunction(handler http.Handler) restful.RouteFunction { } } -func addProxyRoute(ws *restful.WebService, method string, prefix string, path string, proxyHandler http.Handler, namespaced, kind, resource, subresource string, hasSubresource bool, params []*restful.Parameter) { +func addProxyRoute(ws *restful.WebService, method string, prefix string, path string, proxyHandler http.Handler, namespaced, kind, resource, subresource string, hasSubresource bool, params []*restful.Parameter, operationSuffix string) { doc := "proxy " + method + " requests to " + kind if hasSubresource { doc = "proxy " + method + " requests to " + subresource + " of " + kind @@ -892,7 +902,7 @@ func addProxyRoute(ws *restful.WebService, method string, prefix string, path st handler := metrics.InstrumentRouteFunc("PROXY", resource, routeFunction(proxyHandler)) proxyRoute := ws.Method(method).Path(path).To(handler). Doc(doc). - Operation("proxy" + strings.Title(method) + namespaced + kind + strings.Title(subresource)). + Operation("proxy" + strings.Title(method) + namespaced + kind + strings.Title(subresource) + operationSuffix). Produces("*/*"). Consumes("*/*"). Writes("string")