Files
2025-06-20 16:37:17 -05:00

146 lines
5.4 KiB
Go

package builder
import (
"context"
"fmt"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/kube-openapi/pkg/common"
"k8s.io/kube-openapi/pkg/spec3"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/options"
"github.com/grafana/grafana/pkg/storage/unified/apistore"
)
// TODO: this (or something like it) belongs in grafana-app-sdk,
// but lets keep it here while we iterate on a few simple examples
type APIGroupBuilder interface {
// Add the kinds to the server scheme
InstallSchema(scheme *runtime.Scheme) error
// UpdateAPIGroupInfo used to be a getter until we ran into the issue
// where separate API Group Info for the same group (different versions) aren't handled well by
// the InstallAPIGroup facility of genericapiserver. Also, we can only ever call InstallAPIGroup
// once on the genericapiserver per group, or we run into double registration startup errors.
//
// The caller should share the apiGroupInfo passed into this function across builder versions of the same group.
// UpdateAPIGroupInfo builds the group+version behavior updating the passed in apiGroupInfo in place
UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts APIGroupOptions) error
// Get OpenAPI definitions
GetOpenAPIDefinitions() common.GetOpenAPIDefinitions
// Do not return anything unless you have special circumstances! This is a list of resources that are allowed to be accessed in v0alpha1, with AllResourcesAllowed allowing all in the group.
// This is to prevent accidental exposure of experimental APIs. While developing, use the feature flag `grafanaAPIServerWithExperimentalAPIs`.
// And then, when you're ready to expose this to the end user, go to v1beta1 instead.
AllowedV0Alpha1Resources() []string
}
const AllResourcesAllowed = "*"
type APIGroupVersionProvider interface {
GetGroupVersion() schema.GroupVersion
}
type APIGroupVersionsProvider interface {
GetGroupVersions() []schema.GroupVersion
}
type APIGroupAuthorizer interface {
GetAuthorizer() authorizer.Authorizer
}
type APIGroupMutation interface {
// Mutate allows the builder to make changes to the object before it is persisted.
// Context is used only for timeout/deadline/cancellation and tracing information.
Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error)
}
type APIGroupValidation interface {
// Validate makes an admission decision based on the request attributes. It is NOT allowed to mutate
// Context is used only for timeout/deadline/cancellation and tracing information.
Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error)
}
type APIGroupRouteProvider interface {
// Support direct HTTP routes from an APIGroup
GetAPIRoutes(gv schema.GroupVersion) *APIRoutes
}
type APIGroupPostStartHookProvider interface {
// GetPostStartHooks returns a list of functions that will be called after the server has started
GetPostStartHooks() (map[string]genericapiserver.PostStartHookFunc, error)
}
type APIGroupOptions struct {
Scheme *runtime.Scheme
OptsGetter generic.RESTOptionsGetter
DualWriteBuilder grafanarest.DualWriteBuilder
MetricsRegister prometheus.Registerer
StorageOptsRegister apistore.StorageOptionsRegister
StorageOpts *options.StorageOptions
}
// Builders that implement OpenAPIPostProcessor are given a chance to modify the schema directly
type OpenAPIPostProcessor interface {
PostProcessOpenAPI(*spec3.OpenAPI) (*spec3.OpenAPI, error)
}
// This is used to implement dynamic sub-resources like pods/x/logs
type APIRouteHandler struct {
Path string // added to the appropriate level
Spec *spec3.PathProps // Exposed in the open api service discovery
Handler http.HandlerFunc // when Level = resource, the resource will be available in context
}
// APIRoutes define explicit HTTP handlers in an apiserver
// TBD: is this actually necessary -- there may be more k8s native options for this
type APIRoutes struct {
// Root handlers are registered directly after the apiVersion identifier
Root []APIRouteHandler
// Namespace handlers are mounted under the namespace
Namespace []APIRouteHandler
}
type APIRegistrar interface {
RegisterAPI(builder APIGroupBuilder)
}
func getGroup(builder APIGroupBuilder) (string, error) {
if v, ok := builder.(APIGroupVersionProvider); ok {
return v.GetGroupVersion().Group, nil
}
if v, ok := builder.(APIGroupVersionsProvider); ok {
if len(v.GetGroupVersions()) == 0 {
return "", fmt.Errorf("unable to get group: builder returned no versions")
}
return v.GetGroupVersions()[0].Group, nil
}
return "", fmt.Errorf("unable to get group: builder does not implement APIGroupVersionProvider or APIGroupVersionsProvider")
}
func GetGroupVersions(builder APIGroupBuilder) []schema.GroupVersion {
if v, ok := builder.(APIGroupVersionProvider); ok {
return []schema.GroupVersion{v.GetGroupVersion()}
}
if v, ok := builder.(APIGroupVersionsProvider); ok {
return v.GetGroupVersions()
}
// this should never happen
panic("builder does not implement APIGroupVersionProvider or APIGroupVersionsProvider")
}