Restructure API Extensions to Enterprise-only + MT-only
**OSS Changes (Removal):** - Deleted `pkg/registry/apis/apiextensions/` directory (8,122 LOC) - Removed apiextensions from OSS wireset and apis.go ServiceSink - Removed apiExtensionsEnabled block and createAPIExtensionsServer from service.go - Cleaned up wireexts_oss.go imports **Enterprise Changes (Added separately):** - Created `pkg/extensions/apiserver/registry/apiextensions/` with: - `register.go`: RegisterAPIService (prod) + RegisterAPIServiceForTesting (test) - `storage.go`: EnterpriseCRDStorageProvider with Unified Storage integration - `README.md`: Comprehensive testing and development documentation - Updated `pkg/extensions/apiserver/registry/wireset.go` with new registrations - Updated `pkg/extensions/apiserver/factory.go` to use new builder **Configuration:** - Updated feature flag description to indicate "Enterprise + MT-only" Following the Secrets API pattern, API Extensions are now: 1. **Enterprise-only**: No code in OSS builds 2. **MT-only in production**: Double gating (StackID + feature flag) 3. **Testable without StackID**: Via RegisterAPIServiceForTesting 4. **Wire-based registration**: Standard pattern, no manual factory registration - ✅ OSS build succeeds without apiextensions code - ✅ Enterprise build succeeds with apiextensions code - ✅ Wire generation works for both OSS and Enterprise - ✅ `make gen-go` and `make build-go` pass - Epic: https://github.com/grafana/grafana-org/issues/545 - OSS PR: https://github.com/grafana/grafana/pull/114466 - Enterprise PR: https://github.com/grafana/grafana-enterprise/pull/10324 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Igor Suleymanov <igor.suleymanov@grafana.com>
This commit is contained in:
co-authored by
Claude
parent
09d98402c4
commit
719d779171
@@ -1,228 +0,0 @@
|
||||
package apiextensions
|
||||
|
||||
import (
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
|
||||
apiextensionsopenapi "k8s.io/apiextensions-apiserver/pkg/generated/openapi"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
genericapiserver "k8s.io/apiserver/pkg/server"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/apistore"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
)
|
||||
|
||||
// CRDStorageProvider is an interface for creating CRD REST options getters.
|
||||
// Enterprise provides the real implementation, OSS returns nil.
|
||||
type CRDStorageProvider interface {
|
||||
NewCRDRESTOptionsGetter(
|
||||
delegate *apistore.RESTOptionsGetter,
|
||||
unifiedClient resource.ResourceClient,
|
||||
) genericregistry.RESTOptionsGetter
|
||||
}
|
||||
|
||||
// OSSCRDStorageProvider is the OSS implementation that returns nil (feature disabled)
|
||||
type OSSCRDStorageProvider struct{}
|
||||
|
||||
func ProvideOSSCRDStorageProvider() CRDStorageProvider {
|
||||
return &OSSCRDStorageProvider{}
|
||||
}
|
||||
|
||||
func (p *OSSCRDStorageProvider) NewCRDRESTOptionsGetter(
|
||||
delegate *apistore.RESTOptionsGetter,
|
||||
unifiedClient resource.ResourceClient,
|
||||
) genericregistry.RESTOptionsGetter {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ builder.APIGroupBuilder = (*Builder)(nil)
|
||||
|
||||
// Builder implements builder.APIGroupBuilder for CustomResourceDefinitions.
|
||||
// This implementation uses the Kubernetes apiextensions-apiserver for CRD handling,
|
||||
// adapted to work with Grafana's unified storage backend.
|
||||
//
|
||||
// IMPORTANT: This builder only registers the CRD types with the scheme.
|
||||
// The actual CRD storage and custom resource handling is done by the
|
||||
// Kubernetes apiextensions-apiserver, which is created separately and
|
||||
// chained as a delegate server.
|
||||
type Builder struct {
|
||||
features featuremgmt.FeatureToggles
|
||||
accessClient authlib.AccessClient
|
||||
unifiedClient resource.ResourceClient
|
||||
apiExtensionsServer *apiextensionsapiserver.CustomResourceDefinitions
|
||||
storageProvider CRDStorageProvider
|
||||
}
|
||||
|
||||
// RegisterAPIService registers the apiextensions API group in single-tenant mode
|
||||
func RegisterAPIService(
|
||||
cfg *setting.Cfg,
|
||||
features featuremgmt.FeatureToggles,
|
||||
apiregistration builder.APIRegistrar,
|
||||
accessClient authlib.AccessClient,
|
||||
registerer prometheus.Registerer,
|
||||
unified resource.ResourceClient,
|
||||
storageProvider CRDStorageProvider,
|
||||
) (*Builder, error) {
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagApiExtensions) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
b := &Builder{
|
||||
features: features,
|
||||
accessClient: accessClient,
|
||||
unifiedClient: unified,
|
||||
storageProvider: storageProvider,
|
||||
}
|
||||
|
||||
// Register the builder to install the schema
|
||||
apiregistration.RegisterAPI(b)
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// GetAuthorizer returns the authorizer for CRD resources
|
||||
// Breaks locally now for ST, will need to test in MT
|
||||
// For ST just comment this out to test
|
||||
// func (b *Builder) GetAuthorizer() authorizer.Authorizer {
|
||||
// return grafanaauthorizer.NewServiceAuthorizer()
|
||||
// }
|
||||
|
||||
// NewAPIService creates an Builder for multi-tenant mode
|
||||
func NewAPIService(
|
||||
accessClient authlib.AccessClient,
|
||||
unified resource.ResourceClient,
|
||||
registerer prometheus.Registerer,
|
||||
features featuremgmt.FeatureToggles,
|
||||
storageProvider CRDStorageProvider,
|
||||
) (*Builder, error) {
|
||||
return &Builder{
|
||||
features: features,
|
||||
accessClient: accessClient,
|
||||
unifiedClient: unified,
|
||||
storageProvider: storageProvider,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetGroupVersion returns the API group version for apiextensions.k8s.io/v1
|
||||
func (b *Builder) GetGroupVersion() schema.GroupVersion {
|
||||
return apiextensionsv1.SchemeGroupVersion
|
||||
}
|
||||
|
||||
// InstallSchema installs the CRD types into the scheme
|
||||
func (b *Builder) InstallSchema(scheme *runtime.Scheme) error {
|
||||
gv := b.GetGroupVersion()
|
||||
|
||||
// Register the apiextensions types from the K8s apiextensions-apiserver
|
||||
// This uses the types and scheme from the K8s package
|
||||
metav1.AddToGroupVersion(scheme, gv)
|
||||
|
||||
// Add the CRD types to the scheme
|
||||
scheme.AddKnownTypes(gv,
|
||||
&apiextensionsv1.CustomResourceDefinition{},
|
||||
&apiextensionsv1.CustomResourceDefinitionList{},
|
||||
)
|
||||
|
||||
return scheme.SetVersionPriority(gv)
|
||||
}
|
||||
|
||||
func (b *Builder) AllowedV0Alpha1Resources() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateAPIGroupInfo is a no-op for the apiextensions builder.
|
||||
// The actual CRD storage is created by the apiextensions server, not the builder.
|
||||
// This is called by the builder framework but we don't need to do anything here
|
||||
// since we're using the K8s apiextensions-apiserver which creates its own storage.
|
||||
func (b *Builder) UpdateAPIGroupInfo(
|
||||
_ *genericapiserver.APIGroupInfo,
|
||||
_ builder.APIGroupOptions,
|
||||
) error {
|
||||
// Don't install any storage here - the apiextensions server handles this
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetOpenAPIDefinitions returns the OpenAPI definitions for CRD types
|
||||
func (b *Builder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
|
||||
return func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
|
||||
return apiextensionsopenapi.GetOpenAPIDefinitions(ref)
|
||||
}
|
||||
}
|
||||
|
||||
// CreateAPIExtensionsServer creates the Kubernetes apiextensions-apiserver
|
||||
// This server handles CRD storage and custom resource (CR) handling.
|
||||
// It should be used as a delegate for the main Grafana API server.
|
||||
func (b *Builder) CreateAPIExtensionsServer(
|
||||
serverConfig genericapiserver.RecommendedConfig,
|
||||
delegationTarget genericapiserver.DelegationTarget,
|
||||
restOptsGetter *apistore.RESTOptionsGetter,
|
||||
) (*apiextensionsapiserver.CustomResourceDefinitions, error) {
|
||||
if restOptsGetter == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create the CRD REST options getter that uses unified storage
|
||||
crdRestOptsGetter := b.storageProvider.NewCRDRESTOptionsGetter(restOptsGetter, b.unifiedClient)
|
||||
if crdRestOptsGetter == nil {
|
||||
// Enterprise feature not available
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create a fresh copy of the config for the apiextensions server
|
||||
// We need to clear PostStartHooks to avoid conflicts with hooks
|
||||
// already registered by the main server (e.g., "playlist")
|
||||
apiExtensionsGenericConfig := serverConfig
|
||||
apiExtensionsGenericConfig.PostStartHooks = map[string]genericapiserver.PostStartHookConfigEntry{}
|
||||
|
||||
// Set the RESTOptionsGetter on the GenericConfig
|
||||
// The K8s apiextensions-apiserver uses GenericConfig.RESTOptionsGetter for CRD storage
|
||||
// and ExtraConfig.CRDRESTOptionsGetter for Custom Resource storage
|
||||
apiExtensionsGenericConfig.RESTOptionsGetter = crdRestOptsGetter
|
||||
|
||||
// Enable the CRD resources in the API resource config
|
||||
apiResourceConfig := serverstorage.NewResourceConfig()
|
||||
apiResourceConfig.EnableVersions(apiextensionsv1.SchemeGroupVersion)
|
||||
apiExtensionsGenericConfig.MergedResourceConfig = apiResourceConfig
|
||||
|
||||
// Configure the apiextensions server
|
||||
apiextensionsConfig := &apiextensionsapiserver.Config{
|
||||
GenericConfig: &apiExtensionsGenericConfig,
|
||||
ExtraConfig: apiextensionsapiserver.ExtraConfig{
|
||||
// CRDRESTOptionsGetter is used for Custom Resource (CR) storage, not CRD storage
|
||||
CRDRESTOptionsGetter: crdRestOptsGetter,
|
||||
MasterCount: 1,
|
||||
// Webhook conversion is not supported yet
|
||||
ServiceResolver: nil,
|
||||
AuthResolverWrapper: nil,
|
||||
},
|
||||
}
|
||||
|
||||
server, err := apiextensionsConfig.Complete().New(delegationTarget)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b.apiExtensionsServer = server
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// GetAPIExtensionsServer returns the apiextensions server (if created)
|
||||
func (b *Builder) GetAPIExtensionsServer() *apiextensionsapiserver.CustomResourceDefinitions {
|
||||
return b.apiExtensionsServer
|
||||
}
|
||||
|
||||
// SetAPIServer is a no-op for compatibility with the builder interface
|
||||
func (b *Builder) SetAPIServer(server *genericapiserver.GenericAPIServer) {
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package apiregistry
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/apiextensions"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
@@ -20,7 +19,6 @@ type Service struct{}
|
||||
// ProvideRegistryServiceSink is an entry point for each service that will force initialization
|
||||
// and give each builder the chance to register itself with the main server
|
||||
func ProvideRegistryServiceSink(
|
||||
_ *apiextensions.Builder,
|
||||
_ *dashboardinternal.DashboardsAPIBuilder,
|
||||
_ *datasource.DataSourceAPIBuilder,
|
||||
_ *folders.FolderAPIBuilder,
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"github.com/google/wire"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apiserver/auditing"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/apiextensions"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
dashboardinternal "github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
@@ -64,7 +63,6 @@ var WireSet = wire.NewSet(
|
||||
provisioningExtras,
|
||||
|
||||
// Each must be added here *and* in the ServiceSink above
|
||||
apiextensions.RegisterAPIService,
|
||||
dashboardinternal.RegisterAPIService,
|
||||
datasource.RegisterAPIService,
|
||||
folders.RegisterAPIService,
|
||||
|
||||
Generated
+2
-13
@@ -49,7 +49,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/pluginscdn"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
"github.com/grafana/grafana/pkg/registry/apis"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/apiextensions"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/collections"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/dashboard/legacy"
|
||||
@@ -874,11 +873,6 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService)
|
||||
ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService)
|
||||
apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService)
|
||||
crdStorageProvider := apiextensions.ProvideOSSCRDStorageProvider()
|
||||
apiextensionsBuilder, err := apiextensions.RegisterAPIService(cfg, featureToggles, apiserverService, accessClient, registerer, resourceClient, crdStorageProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dashboardActivityChannel := live.ProvideDashboardActivityChannel(grafanaLive)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl, dashboardActivityChannel)
|
||||
dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService)
|
||||
@@ -932,7 +926,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(apiextensionsBuilder, dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1539,11 +1533,6 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
identitySynchronizer := authnimpl.ProvideIdentitySynchronizer(authnimplService)
|
||||
ldapImpl := service12.ProvideService(cfg, featureToggles, ssosettingsimplService)
|
||||
apiService := api4.ProvideService(cfg, routeRegisterImpl, accessControl, userService, authinfoimplService, ossGroups, identitySynchronizer, orgService, ldapImpl, userAuthTokenService, bundleregistryService)
|
||||
crdStorageProvider := apiextensions.ProvideOSSCRDStorageProvider()
|
||||
apiextensionsBuilder, err := apiextensions.RegisterAPIService(cfg, featureToggles, apiserverService, accessClient, registerer, resourceClient, crdStorageProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dashboardActivityChannel := live.ProvideDashboardActivityChannel(grafanaLive)
|
||||
dashboardsAPIBuilder := dashboard.RegisterAPIService(cfg, featureToggles, apiserverService, dashboardService, dashboardProvisioningService, service15, dashboardServiceImpl, dashboardPermissionsService, accessControl, accessClient, provisioningServiceImpl, dashboardsStore, registerer, sqlStore, tracingService, resourceClient, dualwriteService, sortService, quotaService, libraryPanelService, eventualRestConfigProvider, userService, libraryElementService, publicDashboardServiceImpl, serviceImpl, dashboardActivityChannel)
|
||||
dataSourceAPIBuilder, err := datasource.RegisterAPIService(featureToggles, apiserverService, middlewareHandler, scopedPluginDatasourceProvider, plugincontextProvider, accessControl, registerer, sourcesService)
|
||||
@@ -1597,7 +1586,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(apiextensionsBuilder, dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
apiregistryService := apiregistry.ProvideRegistryServiceSink(dashboardsAPIBuilder, dataSourceAPIBuilder, folderAPIBuilder, identityAccessManagementAPIBuilder, queryAPIBuilder, userStorageAPIBuilder, apiBuilder, collectionsAPIBuilder, provisioningAPIBuilder, ofrepAPIBuilder, dependencyRegisterer, provisioningDependencyRegisterer)
|
||||
teamPermissionsService, err := ossaccesscontrol.ProvideTeamPermissions(cfg, featureToggles, routeRegisterImpl, sqlStore, accessControl, ossLicensingService, acimplService, teamService, userService, actionSetService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins/manager"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
apisregistry "github.com/grafana/grafana/pkg/registry/apis"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/apiextensions"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/extras"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/secret/contracts"
|
||||
@@ -150,7 +149,6 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
sql.ProvideStorageBackend,
|
||||
builder.ProvideDefaultBuildHandlerChainFuncFromBuilders,
|
||||
aggregatorrunner.ProvideNoopAggregatorConfigurator,
|
||||
apiextensions.ProvideOSSCRDStorageProvider,
|
||||
apisregistry.WireSetExts,
|
||||
gsmKMSProviders.ProvideOSSKMSProviders,
|
||||
secret.ProvideSecureValueClient,
|
||||
|
||||
@@ -21,8 +21,6 @@ import (
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/dskit/services"
|
||||
appsdkapiserver "github.com/grafana/grafana-app-sdk/k8s/apiserver"
|
||||
@@ -402,30 +400,6 @@ func (s *service) start(ctx context.Context) error {
|
||||
|
||||
// Determine the delegate for the main server
|
||||
var delegationTarget = genericapiserver.NewEmptyDelegateWithCustomHandler(notFoundHandler)
|
||||
var apiExtensionsServer *apiextensionsapiserver.CustomResourceDefinitions
|
||||
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
apiExtensionsEnabled := s.features.IsEnabledGlobally(featuremgmt.FlagApiExtensions)
|
||||
|
||||
if apiExtensionsEnabled && restOptsGetter != nil {
|
||||
// Create the K8s apiextensions-apiserver for CRD/CR handling
|
||||
// This server handles:
|
||||
// - CRD storage (create, get, list, update, delete CRDs)
|
||||
// - Custom resource handling (dynamically serves CRs based on registered CRDs)
|
||||
s.log.Info("Creating apiextensions server")
|
||||
apiExtensionsServer, err = s.createAPIExtensionsServer(builders, serverConfig, delegationTarget, restOptsGetter)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create apiextensions server: %w", err)
|
||||
}
|
||||
if apiExtensionsServer != nil {
|
||||
s.log.Info("apiextensions server created successfully, chaining as delegate")
|
||||
// Chain the apiextensions server as the delegate
|
||||
// Requests go: grafana-apiserver -> apiextensions-apiserver -> notFoundHandler
|
||||
delegationTarget = apiExtensionsServer.GenericAPIServer
|
||||
} else {
|
||||
s.log.Warn("apiextensions server was not created (returned nil)")
|
||||
}
|
||||
}
|
||||
|
||||
// Create the main Grafana API server
|
||||
server, err := serverConfig.Complete().New("grafana-apiserver", delegationTarget)
|
||||
@@ -484,11 +458,6 @@ func (s *service) start(ctx context.Context) error {
|
||||
isDataplaneAggregatorEnabled := s.features.IsEnabledGlobally(featuremgmt.FlagDataplaneAggregator)
|
||||
|
||||
if isKubernetesAggregatorEnabled {
|
||||
// Pass CRD informer to aggregator if apiextensions is enabled (for auto-registering APIServices for CRDs)
|
||||
if apiExtensionsServer != nil && apiExtensionsServer.Informers != nil {
|
||||
s.log.Info("Starting CRD informer for apiextensions service")
|
||||
s.aggregatorRunner.SetCRDInformer(apiExtensionsServer.Informers.Apiextensions().V1().CustomResourceDefinitions())
|
||||
}
|
||||
aggregatorServer, err := s.aggregatorRunner.Configure(s.options, serverConfig, delegate, s.scheme, builders)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -692,31 +661,4 @@ func useNamespaceFromPath(path string, user *user.SignedInUser) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// createAPIExtensionsServer creates the Kubernetes apiextensions-apiserver for CRD/CR handling.
|
||||
// This server is chained as a delegate, handling CRD storage and dynamic custom resource serving.
|
||||
func (s *service) createAPIExtensionsServer(
|
||||
builders []builder.APIGroupBuilder,
|
||||
serverConfig *genericapiserver.RecommendedConfig,
|
||||
delegationTarget genericapiserver.DelegationTarget,
|
||||
restOptsGetter *apistore.RESTOptionsGetter,
|
||||
) (*apiextensionsapiserver.CustomResourceDefinitions, error) {
|
||||
// Find the apiextensions Builder
|
||||
type apiExtensionsCreator interface {
|
||||
CreateAPIExtensionsServer(
|
||||
serverConfig genericapiserver.RecommendedConfig,
|
||||
delegationTarget genericapiserver.DelegationTarget,
|
||||
restOptsGetter *apistore.RESTOptionsGetter,
|
||||
) (*apiextensionsapiserver.CustomResourceDefinitions, error)
|
||||
}
|
||||
|
||||
for _, b := range builders {
|
||||
if creator, ok := b.(apiExtensionsCreator); ok {
|
||||
return creator.CreateAPIExtensionsServer(*serverConfig, delegationTarget, restOptsGetter)
|
||||
}
|
||||
}
|
||||
|
||||
// No apiextensions builder found
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
@@ -785,7 +785,7 @@ var (
|
||||
},
|
||||
{
|
||||
Name: "apiExtensions",
|
||||
Description: "Enable Kubernetes CustomResourceDefinition (CRD) support with dynamic API registration",
|
||||
Description: "Enable Kubernetes CustomResourceDefinition (CRD) support with dynamic API registration (Enterprise + MT-only)",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaAppPlatformSquad,
|
||||
RequiresRestart: true,
|
||||
|
||||
Reference in New Issue
Block a user