package builder import ( "maps" "strings" common "k8s.io/kube-openapi/pkg/common" "k8s.io/kube-openapi/pkg/spec3" spec "k8s.io/kube-openapi/pkg/validation/spec" "github.com/grafana/grafana/pkg/apis/common/v0alpha1" "github.com/grafana/grafana/pkg/setting" ) // This should eventually live in grafana-app-sdk func GetOpenAPIDefinitions(builders []APIGroupBuilder) common.GetOpenAPIDefinitions { return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { defs := v0alpha1.GetOpenAPIDefinitions(ref) // common grafana apis for _, b := range builders { g := b.GetOpenAPIDefinitions() if g != nil { out := g(ref) maps.Copy(defs, out) } } return defs } } // 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 _, b := range builders { routes := b.GetAPIRoutes() gv := b.GetGroupVersion() prefix := "/apis/" + gv.String() + "/" if s.Paths.Paths[prefix] != nil { copy := spec3.OpenAPI{ Version: s.Version, Info: &spec.Info{ InfoProps: spec.InfoProps{ Title: gv.String(), Version: setting.BuildVersion, }, }, Components: s.Components, ExternalDocs: s.ExternalDocs, Servers: s.Servers, Paths: s.Paths, } if routes == nil { routes = &APIRoutes{} } 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, } } // Make the sub-resources (connect) share the same tags as the main resource for path, spec := range copy.Paths.Paths { idx := strings.LastIndex(path, "{name}/") if idx > 0 { parent := copy.Paths.Paths[path[:idx+6]] if parent != nil && parent.Get != nil { for _, op := range GetPathOperations(spec) { if op != nil && op.Extensions != nil { action, ok := op.Extensions.GetString("x-kubernetes-action") if ok && action == "connect" { op.Tags = parent.Get.Tags } } } } } } // Support direct manipulation of API results processor, ok := b.(OpenAPIPostProcessor) if ok { return processor.PostProcessOpenAPI(©) } return ©, nil } } return s, nil } } func GetPathOperations(path *spec3.Path) []*spec3.Operation { return []*spec3.Operation{ path.Get, path.Head, path.Delete, path.Patch, path.Post, path.Put, path.Trace, path.Options, } }