K8s: APIGroupBuilder App SDK Support (#95638)

This commit is contained in:
Todd Treece
2024-11-01 08:28:56 -04:00
committed by GitHub
parent 2ba1740698
commit 30b1e3b289
24 changed files with 510 additions and 71 deletions
@@ -0,0 +1,107 @@
package runner
import (
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/resource"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/kube-openapi/pkg/common"
"github.com/grafana/grafana/pkg/apimachinery/utils"
grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
)
var _ builder.APIGroupBuilder = (*AppBuilder)(nil)
type LegacyStorageGetter func(schema.GroupVersionResource) grafanarest.LegacyStorage
type AppBuilderConfig struct {
Authorizer authorizer.Authorizer
LegacyStorageGetter LegacyStorageGetter
OpenAPIDefGetter common.GetOpenAPIDefinitions
ManagedKinds map[schema.GroupVersion]resource.Kind
CustomConfig any
groupVersion schema.GroupVersion
}
type AppBuilder struct {
app app.App
config AppBuilderConfig
}
func NewAppBuilder(appBuilderConfig AppBuilderConfig) (AppBuilder, error) {
return AppBuilder{
config: appBuilderConfig,
}, nil
}
func (b *AppBuilder) setApp(app app.App) {
b.app = app
}
// GetGroupVersion implements APIGroupBuilder.GetGroupVersion
func (b *AppBuilder) GetGroupVersion() schema.GroupVersion {
return b.config.groupVersion
}
// InstallSchema implements APIGroupBuilder.InstallSchema
func (b *AppBuilder) InstallSchema(scheme *runtime.Scheme) error {
gv := b.GetGroupVersion()
for _, kind := range b.config.ManagedKinds {
scheme.AddKnownTypeWithName(gv.WithKind(kind.Kind()), kind.ZeroValue())
scheme.AddKnownTypeWithName(gv.WithKind(kind.Kind()+"List"), kind.ZeroListValue())
}
return scheme.SetVersionPriority(gv)
}
// UpdateAPIGroupInfo implements APIGroupBuilder.UpdateAPIGroupInfo
func (b *AppBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
for _, kind := range b.config.ManagedKinds {
version := kind.GroupVersionKind().Version
if _, ok := apiGroupInfo.VersionedResourcesStorageMap[version]; !ok {
apiGroupInfo.VersionedResourcesStorageMap[version] = make(map[string]rest.Storage)
}
resourceInfo := KindToResourceInfo(kind)
store, err := b.getStorage(resourceInfo, opts)
if err != nil {
return err
}
apiGroupInfo.VersionedResourcesStorageMap[version][resourceInfo.StoragePath()] = store
}
return nil
}
func (b *AppBuilder) getStorage(resourceInfo utils.ResourceInfo, opts builder.APIGroupOptions) (grafanarest.Storage, error) {
store, err := grafanaregistry.NewRegistryStore(opts.Scheme, resourceInfo, opts.OptsGetter)
if err != nil {
return nil, err
}
if b.config.LegacyStorageGetter != nil && opts.DualWriteBuilder != nil {
if legacyStorage := b.config.LegacyStorageGetter(resourceInfo.GroupVersionResource()); legacyStorage != nil {
return opts.DualWriteBuilder(resourceInfo.GroupResource(), legacyStorage, store)
}
}
return store, nil
}
// GetOpenAPIDefinitions implements APIGroupBuilder.GetOpenAPIDefinitions
func (b *AppBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return b.config.OpenAPIDefGetter
}
// GetAPIRoutes implements APIGroupBuilder.GetAPIRoutes
func (b *AppBuilder) GetAPIRoutes() *builder.APIRoutes {
// TODO: The API routes are not yet exposed by the app.App interface.
return nil
}
// GetAuthorizer implements APIGroupBuilder.GetAuthorizer
func (b AppBuilder) GetAuthorizer() authorizer.Authorizer {
return b.config.Authorizer
}
@@ -0,0 +1,22 @@
package runner
import (
"strings"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana/pkg/apimachinery/utils"
"k8s.io/apimachinery/pkg/runtime"
)
func KindToResourceInfo(kind resource.Kind) utils.ResourceInfo {
return utils.NewResourceInfo(
kind.Group(),
kind.Version(),
kind.GroupVersionResource().Resource,
strings.ToLower(kind.Kind()),
kind.Kind(),
func() runtime.Object { return kind.ZeroValue() },
func() runtime.Object { return kind.ZeroListValue() },
utils.TableColumns{}, // TODO: this only supports the default columns
)
}
@@ -0,0 +1,123 @@
package runner
import (
"context"
"fmt"
"github.com/grafana/grafana-app-sdk/app"
"github.com/grafana/grafana-app-sdk/resource"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/rest"
)
type RunnerConfig struct {
RestConfigGetter func(context.Context) *rest.Config
APIRegistrar builder.APIRegistrar
}
func NewAPIGroupRunner(cfg RunnerConfig, providers ...app.Provider) (*APIGroupRunner, error) {
groups := []appBuilderGroup{}
for _, provider := range providers {
created, err := newAppBuilderGroup(cfg, provider)
if err != nil {
return nil, err
}
groups = append(groups, created)
}
return &APIGroupRunner{
config: cfg,
groups: groups,
initialized: make(chan struct{}),
}, nil
}
type APIGroupRunner struct {
config RunnerConfig
groups []appBuilderGroup
initialized chan struct{}
}
func (r *APIGroupRunner) Run(ctx context.Context) error {
<-r.initialized
runner := app.NewMultiRunner()
for _, g := range r.groups {
runner.AddRunnable(g.app.Runner())
}
return runner.Run(ctx)
}
func (r *APIGroupRunner) Init(ctx context.Context) error {
defer close(r.initialized)
restConfig := r.config.RestConfigGetter(ctx)
if restConfig == nil {
return fmt.Errorf("rest config is nil")
}
for i, g := range r.groups {
customCfg := g.builders[0].config.CustomConfig
appConfig := app.Config{
KubeConfig: *restConfig,
ManifestData: *g.provider.Manifest().ManifestData,
SpecificConfig: customCfg,
}
app, err := g.provider.NewApp(appConfig)
if err != nil {
return err
}
r.groups[i].setApp(app)
}
return nil
}
func (r *APIGroupRunner) GetBuilders() []AppBuilder {
builders := []AppBuilder{}
for _, g := range r.groups {
builders = append(builders, g.builders...)
}
return builders
}
type appBuilderGroup struct {
builders []AppBuilder
provider app.Provider
app app.App
}
func newAppBuilderGroup(cfg RunnerConfig, provider app.Provider) (appBuilderGroup, error) {
manifest := provider.Manifest()
if manifest.Location.Type != app.ManifestLocationEmbedded {
return appBuilderGroup{}, fmt.Errorf("app: %s has unsupported manifest location type: %s", manifest.ManifestData.AppName, manifest.Location.Type)
}
group := appBuilderGroup{
provider: provider,
builders: []AppBuilder{},
}
appBuilderConfig, ok := provider.SpecificConfig().(*AppBuilderConfig)
if !ok {
return group, fmt.Errorf("provider's SpecificConfig is not of type *AppBuilderConfig, got %T", provider.SpecificConfig())
}
for gv, kinds := range appBuilderConfig.ManagedKinds {
confCopy := *appBuilderConfig
confCopy.ManagedKinds = map[schema.GroupVersion]resource.Kind{
gv: kinds,
}
confCopy.groupVersion = gv
b, err := NewAppBuilder(confCopy)
if err != nil {
return group, err
}
group.builders = append(group.builders, b)
cfg.APIRegistrar.RegisterAPI(&b)
}
return group, nil
}
func (g *appBuilderGroup) setApp(app app.App) {
g.app = app
for i, _ := range g.builders {
g.builders[i].setApp(app)
}
}
+11 -4
View File
@@ -81,7 +81,7 @@ type Service interface {
}
type RestConfigProvider interface {
GetRestConfig() *clientrest.Config
GetRestConfig(context.Context) *clientrest.Config
}
type DirectRestConfigProvider interface {
@@ -202,7 +202,10 @@ func ProvideService(
return s, nil
}
func (s *service) GetRestConfig() *clientrest.Config {
func (s *service) GetRestConfig(ctx context.Context) *clientrest.Config {
if err := s.BasicService.AwaitRunning(ctx); err != nil {
return nil
}
return s.restConfig
}
@@ -212,10 +215,14 @@ func (s *service) IsDisabled() bool {
// Run is an adapter for the BackgroundService interface.
func (s *service) Run(ctx context.Context) error {
if err := s.start(ctx); err != nil {
if err := s.BasicService.StartAsync(ctx); err != nil {
return err
}
return s.running(ctx)
if err := s.BasicService.AwaitRunning(ctx); err != nil {
return err
}
return s.AwaitTerminated(ctx)
}
func (s *service) RegisterAPI(b builder.APIGroupBuilder) {