Files
grafana/pkg/services/apiserver/appinstaller/server.go
2025-08-01 10:02:01 -04:00

120 lines
4.1 KiB
Go

package appinstaller
import (
"context"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
genericrest "k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
"github.com/grafana/grafana-app-sdk/logging"
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"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
grafanaapiserveroptions "github.com/grafana/grafana/pkg/services/apiserver/options"
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
)
var _ appsdkapiserver.GenericAPIServer = (*serverWrapper)(nil)
type serverWrapper struct {
ctx context.Context
appsdkapiserver.GenericAPIServer
installer appsdkapiserver.AppInstaller
restOptionsGetter generic.RESTOptionsGetter
storageOpts *grafanaapiserveroptions.StorageOptions
kvStore grafanarest.NamespacedKVStore
lock serverLock
namespaceMapper request.NamespaceMapper
dualWriteService dualwrite.Service
dualWriterMetrics *grafanarest.DualWriterMetrics
builderMetrics *builder.BuilderMetrics
apiResourceConfig *serverstorage.ResourceConfig
}
func (s *serverWrapper) InstallAPIGroup(apiGroupInfo *genericapiserver.APIGroupInfo) error {
log := logging.FromContext(s.ctx)
for v, storageMap := range apiGroupInfo.VersionedResourcesStorageMap {
for storagePath, restStorage := range storageMap {
legacyProvider, dualWriteSupported := s.installer.(LegacyStorageProvider)
resource, err := getResourceFromStoragePath(storagePath)
if err != nil {
return err
}
gr := schema.GroupResource{
Group: s.installer.ManifestData().Group,
Resource: resource,
}
gvr := gr.WithVersion(v)
if s.apiResourceConfig != nil && !s.apiResourceConfig.ResourceEnabled(gvr) {
log.Debug("Skipping storage for disabled resource", "gvr", gvr.String(), "storagePath", storagePath)
delete(apiGroupInfo.VersionedResourcesStorageMap[v], storagePath)
continue
}
storage := s.configureStorage(gr, dualWriteSupported, restStorage)
if unifiedStorage, ok := storage.(grafanarest.Storage); ok && dualWriteSupported {
log.Debug("Configuring dual writer for storage", "resource", gr.String(), "version", v, "storagePath", storagePath)
dw, err := NewDualWriter(
s.ctx,
gr,
s.storageOpts,
legacyProvider.GetLegacyStorage(gr.WithVersion(v)),
unifiedStorage,
s.kvStore,
s.lock,
s.namespaceMapper,
s.dualWriteService,
s.dualWriterMetrics,
s.builderMetrics,
)
if err != nil {
return err
}
storage = dw
}
apiGroupInfo.VersionedResourcesStorageMap[v][storagePath] = storage
}
}
return s.GenericAPIServer.InstallAPIGroup(apiGroupInfo)
}
func getResourceFromStoragePath(storagePath string) (string, error) {
parts := strings.Split(storagePath, "/")
if len(parts) < 1 {
return "", fmt.Errorf("invalid storage path: %s", storagePath)
}
return parts[0], nil
}
func (s *serverWrapper) configureStorage(gr schema.GroupResource, dualWriteSupported bool, storage genericrest.Storage) genericrest.Storage {
if gs, ok := storage.(*genericregistry.Store); ok {
// if dual write is supported, we need to modify the update strategy
// this is not needed for the status store
if dualWriteSupported {
gs.UpdateStrategy = &updateStrategyWrapper{
RESTUpdateStrategy: gs.UpdateStrategy,
}
}
gs.KeyFunc = grafanaregistry.NamespaceKeyFunc(gr)
gs.KeyRootFunc = grafanaregistry.KeyRootFunc(gr)
return gs
}
// if the storage is a status store, we need to extract the underlying generic registry store
if statusStore, ok := storage.(*appsdkapiserver.StatusREST); ok {
statusStore.Store.KeyFunc = grafanaregistry.NamespaceKeyFunc(gr)
statusStore.Store.KeyRootFunc = grafanaregistry.KeyRootFunc(gr)
return statusStore
}
return storage
}