mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 01:53:33 -06:00
199 lines
4.8 KiB
Go
199 lines
4.8 KiB
Go
package grafanaapiserver
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"github.com/gorilla/mux"
|
|
restclient "k8s.io/client-go/rest"
|
|
"k8s.io/kube-openapi/pkg/spec3"
|
|
)
|
|
|
|
type requestHandler struct {
|
|
router *mux.Router
|
|
}
|
|
|
|
func getAPIHandler(delegateHandler http.Handler, restConfig *restclient.Config, builders []APIGroupBuilder) (http.Handler, error) {
|
|
useful := false // only true if any routes exist anywhere
|
|
router := mux.NewRouter()
|
|
var err error
|
|
|
|
for _, builder := range builders {
|
|
routes := builder.GetAPIRoutes()
|
|
if routes == nil {
|
|
continue
|
|
}
|
|
|
|
gv := builder.GetGroupVersion()
|
|
prefix := "/apis/" + gv.String()
|
|
|
|
// Root handlers
|
|
var sub *mux.Router
|
|
for _, route := range routes.Root {
|
|
err = validPath(route.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if sub == nil {
|
|
sub = router.PathPrefix(prefix).Subrouter()
|
|
sub.MethodNotAllowedHandler = &methodNotAllowedHandler{}
|
|
}
|
|
|
|
useful = true
|
|
methods, err := methodsFromSpec(route.Path, route.Spec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sub.HandleFunc(route.Path, route.Handler).
|
|
Methods(methods...)
|
|
}
|
|
|
|
// Namespace handlers
|
|
sub = nil
|
|
prefix += "/namespaces/{namespace}"
|
|
for _, route := range routes.Namespace {
|
|
err = validPath(route.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if sub == nil {
|
|
sub = router.PathPrefix(prefix).Subrouter()
|
|
sub.MethodNotAllowedHandler = &methodNotAllowedHandler{}
|
|
}
|
|
|
|
useful = true
|
|
methods, err := methodsFromSpec(route.Path, route.Spec)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sub.HandleFunc(route.Path, route.Handler).
|
|
Methods(methods...)
|
|
}
|
|
|
|
// getter := makeGetter(restConfig)
|
|
for resource, route := range routes.Resource {
|
|
err = validPath(route.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
fmt.Printf("TODO: %s/%v\n", resource, route)
|
|
|
|
// get a client for that resource kind
|
|
//getter := makeGetter(restConfig)
|
|
|
|
useful = true
|
|
// sub.HandleFunc(v.Slug, SubresourceHandlerWrapper(v.Handler, getter)).
|
|
// Methods(methods...)
|
|
}
|
|
}
|
|
|
|
if !useful {
|
|
return delegateHandler, nil
|
|
}
|
|
|
|
// Per Gorilla Mux issue here: https://github.com/gorilla/mux/issues/616#issuecomment-798807509
|
|
// default handler must come last
|
|
router.PathPrefix("/").Handler(delegateHandler)
|
|
|
|
return &requestHandler{
|
|
router: router,
|
|
}, nil
|
|
}
|
|
|
|
// The registered path must start with a slash, and (for now) not have any more
|
|
func validPath(p string) error {
|
|
if !strings.HasPrefix(p, "/") {
|
|
return fmt.Errorf("path must start with slash")
|
|
}
|
|
if strings.Count(p, "/") > 1 {
|
|
return fmt.Errorf("path can only have one slash (for now)")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (h *requestHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
h.router.ServeHTTP(w, req)
|
|
}
|
|
|
|
func methodsFromSpec(slug string, props *spec3.PathProps) ([]string, error) {
|
|
if props == nil {
|
|
return []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, nil
|
|
}
|
|
|
|
methods := make([]string, 0)
|
|
if props.Get != nil {
|
|
methods = append(methods, "GET")
|
|
}
|
|
if props.Post != nil {
|
|
methods = append(methods, "POST")
|
|
}
|
|
if props.Put != nil {
|
|
methods = append(methods, "PUT")
|
|
}
|
|
if props.Patch != nil {
|
|
methods = append(methods, "PATCH")
|
|
}
|
|
if props.Delete != nil {
|
|
methods = append(methods, "DELETE")
|
|
}
|
|
|
|
if len(methods) == 0 {
|
|
return nil, fmt.Errorf("invalid OpenAPI Spec for slug=%s without any methods in PathProps", slug)
|
|
}
|
|
|
|
return methods, nil
|
|
}
|
|
|
|
type methodNotAllowedHandler struct{}
|
|
|
|
func (h *methodNotAllowedHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
w.WriteHeader(405) // method not allowed
|
|
}
|
|
|
|
// Modify the the OpenAPI spec to include the additional routes.
|
|
// Currently this requires: https://github.com/kubernetes/kube-openapi/pull/420
|
|
// In future k8s release, the hook will use Config3 rather than the same hook for both v2 and v3
|
|
func getOpenAPIPostProcessor(builders []APIGroupBuilder) func(*spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
|
return func(s *spec3.OpenAPI) (*spec3.OpenAPI, error) {
|
|
if s.Paths == nil {
|
|
return s, nil
|
|
}
|
|
for _, builder := range builders {
|
|
routes := builder.GetAPIRoutes()
|
|
if routes == nil {
|
|
continue
|
|
}
|
|
|
|
gv := builder.GetGroupVersion()
|
|
prefix := "/apis/" + gv.String()
|
|
if s.Paths.Paths[prefix] != nil {
|
|
copy := *s // will copy the rest of the properties
|
|
copy.Info.Title = "Grafana API server: " + gv.Group
|
|
|
|
for _, route := range routes.Root {
|
|
copy.Paths.Paths[prefix+route.Path] = &spec3.Path{
|
|
PathProps: *route.Spec,
|
|
}
|
|
}
|
|
|
|
for _, route := range routes.Namespace {
|
|
copy.Paths.Paths[prefix+"/namespaces/{namespace}"+route.Path] = &spec3.Path{
|
|
PathProps: *route.Spec,
|
|
}
|
|
}
|
|
|
|
for resource, route := range routes.Resource {
|
|
copy.Paths.Paths[prefix+"/namespaces/{namespace}/"+resource+"{name}"+route.Path] = &spec3.Path{
|
|
PathProps: *route.Spec,
|
|
}
|
|
}
|
|
|
|
return ©, nil
|
|
}
|
|
}
|
|
return s, nil
|
|
}
|
|
}
|