K8s: Add API Enablement for apps (#109019)

This commit is contained in:
Todd Treece
2025-08-01 10:02:01 -04:00
committed by GitHub
parent 772f647210
commit 1831953f7f
7 changed files with 67 additions and 19 deletions
@@ -8,17 +8,19 @@ import (
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
"github.com/grafana/grafana-app-sdk/logging"
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"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstore "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kube-openapi/pkg/common"
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"
)
type LegacyStorageGetterFunc func(schema.GroupVersionResource) grafanarest.Storage
@@ -31,13 +33,6 @@ type AuthorizerProvider interface {
GetAuthorizer() authorizer.Authorizer
}
type APIEnablementProvider interface {
// Do not implement this unless you have special circumstances! This is a list of resources that are allowed to be accessed in v0alpha1,
// 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.
GetAllowedV0Alpha1Resources() []string
}
type AppInstallerConfig struct {
CustomConfig any
AllowedV0Alpha1Resources []string
@@ -132,6 +127,7 @@ func InstallAPIs(
dualWriteService dualwrite.Service,
dualWriterMetrics *grafanarest.DualWriterMetrics,
builderMetrics *builder.BuilderMetrics,
apiResourceConfig *serverstore.ResourceConfig,
) error {
logger := logging.FromContext(ctx)
for _, installer := range appInstallers {
@@ -148,6 +144,7 @@ func InstallAPIs(
dualWriteService: dualWriteService,
dualWriterMetrics: dualWriterMetrics,
builderMetrics: builderMetrics,
apiResourceConfig: apiResourceConfig,
}
if err := installer.InstallAPIs(wrapper, restOpsGetter); err != nil {
return fmt.Errorf("failed to install APIs for app %s: %w", installer.ManifestData().AppName, err)
@@ -0,0 +1,32 @@
package appinstaller
import (
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
"k8s.io/apimachinery/pkg/runtime/schema"
serverstorage "k8s.io/apiserver/pkg/server/storage"
)
func NewAPIResourceConfig(installers []appsdkapiserver.AppInstaller) *serverstorage.ResourceConfig {
ret := serverstorage.NewResourceConfig()
enable := []schema.GroupVersion{}
disable := []schema.GroupVersion{}
for _, installer := range installers {
for _, version := range installer.ManifestData().Versions {
gv := schema.GroupVersion{
Group: installer.ManifestData().Group,
Version: version.Name,
}
if version.Served {
enable = append(enable, gv)
} else {
disable = append(disable, gv)
}
}
}
ret.EnableVersions(enable...)
ret.DisableVersions(disable...)
return ret
}
@@ -10,6 +10,7 @@ import (
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"
@@ -35,6 +36,7 @@ type serverWrapper struct {
dualWriteService dualwrite.Service
dualWriterMetrics *grafanarest.DualWriterMetrics
builderMetrics *builder.BuilderMetrics
apiResourceConfig *serverstorage.ResourceConfig
}
func (s *serverWrapper) InstallAPIGroup(apiGroupInfo *genericapiserver.APIGroupInfo) error {
@@ -50,6 +52,12 @@ func (s *serverWrapper) InstallAPIGroup(apiGroupInfo *genericapiserver.APIGroupI
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)
+7
View File
@@ -39,6 +39,13 @@ func applyGrafanaConfig(cfg *setting.Cfg, features featuremgmt.FeatureToggles, o
apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver")
runtimeConfig := apiserverCfg.Key("runtime_config").String()
if runtimeConfig != "" {
if err := o.APIEnablementOptions.RuntimeConfig.Set(runtimeConfig); err != nil {
return fmt.Errorf("failed to set runtime config: %w", err)
}
}
o.RecommendedOptions.Etcd.StorageConfig.Transport.ServerList = apiserverCfg.Key("etcd_servers").Strings(",")
o.RecommendedOptions.SecureServing.BindAddress = ip
@@ -21,6 +21,7 @@ const defaultEtcdPathPrefix = "/registry/grafana.app"
type Options struct {
RecommendedOptions *genericoptions.RecommendedOptions
APIEnablementOptions *genericoptions.APIEnablementOptions
GrafanaAggregatorOptions *GrafanaAggregatorOptions
StorageOptions *StorageOptions
ExtraOptions *ExtraOptions
@@ -30,6 +31,7 @@ type Options struct {
func NewOptions(codec runtime.Codec) *Options {
return &Options{
RecommendedOptions: NewRecommendedOptions(codec),
APIEnablementOptions: genericoptions.NewAPIEnablementOptions(),
GrafanaAggregatorOptions: NewGrafanaAggregatorOptions(),
StorageOptions: NewStorageOptions(),
ExtraOptions: NewExtraOptions(),
@@ -38,6 +40,7 @@ func NewOptions(codec runtime.Codec) *Options {
func (o *Options) AddFlags(fs *pflag.FlagSet) {
o.RecommendedOptions.AddFlags(fs)
o.APIEnablementOptions.AddFlags(fs)
o.GrafanaAggregatorOptions.AddFlags(fs)
o.StorageOptions.AddFlags(fs)
o.ExtraOptions.AddFlags(fs)
+9
View File
@@ -304,11 +304,19 @@ func (s *service) start(ctx context.Context) error {
return errs[0]
}
if errs := o.APIEnablementOptions.Validate(s.scheme); len(errs) != 0 {
return errs[0]
}
serverConfig := genericapiserver.NewRecommendedConfig(s.codecs)
if err := o.ApplyTo(serverConfig); err != nil {
return err
}
if err := o.APIEnablementOptions.ApplyTo(&serverConfig.Config, appinstaller.NewAPIResourceConfig(s.appInstallers), s.scheme); err != nil {
return err
}
serverConfig.Authorization.Authorizer = s.authorizer
serverConfig.Authentication.Authenticator = authenticator.NewAuthenticator(serverConfig.Authentication.Authenticator)
serverConfig.TracerProvider = s.tracing.GetTracerProvider()
@@ -395,6 +403,7 @@ func (s *service) start(ctx context.Context) error {
s.storageStatus,
s.dualWriterMetrics,
s.builderMetrics,
serverConfig.MergedResourceConfig,
); err != nil {
return err
}