// SPDX-License-Identifier: AGPL-3.0-only // Provenance-includes-location: https://github.com/kubernetes/kube-aggregator/blob/master/pkg/apiserver/apiserver.go // Provenance-includes-license: Apache-2.0 // Provenance-includes-copyright: The Kubernetes Authors. package apiserver import ( "context" "net/http" "time" "k8s.io/apimachinery/pkg/util/sets" genericapiserver "k8s.io/apiserver/pkg/server" serverstorage "k8s.io/apiserver/pkg/server/storage" "github.com/grafana/grafana/pkg/aggregator/apis/aggregation/v0alpha1" v0alpha1helper "github.com/grafana/grafana/pkg/aggregator/apis/aggregation/v0alpha1/helper" "github.com/grafana/grafana/pkg/aggregator/apiserver/discovery" "github.com/grafana/grafana/pkg/aggregator/apiserver/plugin" clientset "github.com/grafana/grafana/pkg/aggregator/generated/clientset/versioned" informers "github.com/grafana/grafana/pkg/aggregator/generated/informers/externalversions" dataplaneservicerest "github.com/grafana/grafana/pkg/aggregator/registry/dataplaneservice/rest" ) type ExtraConfig struct { PluginClient plugin.PluginClient PluginContextProvider plugin.PluginContextProvider } type Config struct { GenericConfig *genericapiserver.RecommendedConfig ExtraConfig ExtraConfig } type completedConfig struct { GenericConfig genericapiserver.CompletedConfig ExtraConfig *ExtraConfig } // CompletedConfig same as Config, just to swap private object. type CompletedConfig struct { // Embed a private pointer that cannot be instantiated outside of this package. *completedConfig } type runnable interface { RunWithContext(ctx context.Context) error } // preparedGenericAPIServer is a private wrapper that enforces a call of PrepareRun() before Run can be invoked. type preparedGrafanaAggregator struct { *GrafanaAggregator runnable runnable } // GrafanaAggregator contains state for a Kubernetes cluster master/api server. type GrafanaAggregator struct { GenericAPIServer *genericapiserver.GenericAPIServer RegistrationInformers informers.SharedInformerFactory delegateHandler http.Handler proxyHandlers map[string]*dataPlaneServiceHandler handledGroupVersions map[string]sets.Set[string] PluginClient plugin.PluginClient PluginContextProvider plugin.PluginContextProvider } // Complete fills in any fields not set that are required to have valid data. It's mutating the receiver. func (cfg *Config) Complete() CompletedConfig { c := completedConfig{ cfg.GenericConfig.Complete(), &cfg.ExtraConfig, } c.GenericConfig.EnableDiscovery = true return CompletedConfig{&c} } // NewWithDelegate returns a new instance of GrafanaAggregator from the given config. func (c completedConfig) NewWithDelegate(delegationTarget genericapiserver.DelegationTarget) (*GrafanaAggregator, error) { genericServer, err := c.GenericConfig.New("grafana-aggregator", delegationTarget) if err != nil { return nil, err } discoveryHandler := discovery.NewRootDiscoveryHandler(delegationTarget.UnprotectedHandler()) genericServer.Handler.GoRestfulContainer.Filter(discoveryHandler.Handle) dataplaneServiceRegistrationControllerInitiated := make(chan struct{}) if err := genericServer.RegisterMuxAndDiscoveryCompleteSignal("DataPlaneServiceRegistrationControllerInitiated", dataplaneServiceRegistrationControllerInitiated); err != nil { return nil, err } dataplaneServiceClient, err := clientset.NewForConfig(c.GenericConfig.LoopbackClientConfig) if err != nil { return nil, err } informerFactory := informers.NewSharedInformerFactory( dataplaneServiceClient, 5*time.Minute, ) s := &GrafanaAggregator{ GenericAPIServer: genericServer, RegistrationInformers: informerFactory, delegateHandler: delegationTarget.UnprotectedHandler(), handledGroupVersions: map[string]sets.Set[string]{}, proxyHandlers: map[string]*dataPlaneServiceHandler{}, PluginClient: c.ExtraConfig.PluginClient, PluginContextProvider: c.ExtraConfig.PluginContextProvider, } dataplaneServiceRegistrationController := NewDataPlaneServiceRegistrationController(informerFactory.Aggregation().V0alpha1().DataPlaneServices(), s) apiGroupInfo := dataplaneservicerest.NewRESTStorage(c.GenericConfig.MergedResourceConfig, c.GenericConfig.RESTOptionsGetter, true) if err := s.GenericAPIServer.InstallAPIGroup(&apiGroupInfo); err != nil { return nil, err } s.GenericAPIServer.AddPostStartHookOrDie("start-dataplane-aggregator-informers", func(context genericapiserver.PostStartHookContext) error { informerFactory.Start(context.Done()) return nil }) s.GenericAPIServer.AddPostStartHookOrDie("dataplaneservice-registration-controller", func(context genericapiserver.PostStartHookContext) error { go dataplaneServiceRegistrationController.Run(context, dataplaneServiceRegistrationControllerInitiated) select { case <-context.Done(): case <-dataplaneServiceRegistrationControllerInitiated: } return nil }) return s, nil } // PrepareRun prepares the aggregator to run func (s *GrafanaAggregator) PrepareRun() (preparedGrafanaAggregator, error) { prepared := s.GenericAPIServer.PrepareRun() return preparedGrafanaAggregator{GrafanaAggregator: s, runnable: prepared}, nil } func (s preparedGrafanaAggregator) RunWithContext(ctx context.Context) error { return s.runnable.RunWithContext(ctx) } func (s *GrafanaAggregator) AddDataPlaneService(dataplaneService *v0alpha1.DataPlaneService) error { if proxyHandler, exists := s.proxyHandlers[dataplaneService.Name]; exists { proxyHandler.updateDataPlaneService(dataplaneService) return nil } proxyPath := "/apis/" + dataplaneService.Spec.Group + "/" + dataplaneService.Spec.Version proxyHandler := &dataPlaneServiceHandler{ localDelegate: s.delegateHandler, client: s.PluginClient, pluginContextProvider: s.PluginContextProvider, } proxyHandler.updateDataPlaneService(dataplaneService) s.proxyHandlers[dataplaneService.Name] = proxyHandler s.GenericAPIServer.Handler.NonGoRestfulMux.Handle(proxyPath, proxyHandler) s.GenericAPIServer.Handler.NonGoRestfulMux.UnlistedHandlePrefix(proxyPath+"/", proxyHandler) versions, exist := s.handledGroupVersions[dataplaneService.Spec.Group] if exist { versions.Insert(dataplaneService.Spec.Version) return nil } s.handledGroupVersions[dataplaneService.Spec.Group] = sets.New[string](dataplaneService.Spec.Version) return nil } func (s *GrafanaAggregator) RemoveDataPlaneService(dataplaneServiceName string) { version := v0alpha1helper.DataPlaneServiceNameToGroupVersion(dataplaneServiceName) proxyPath := "/apis/" + version.Group + "/" + version.Version s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath) s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(proxyPath + "/") delete(s.proxyHandlers, dataplaneServiceName) versions, exist := s.handledGroupVersions[version.Group] if !exist { return } versions.Delete(version.Version) if versions.Len() > 0 { return } delete(s.handledGroupVersions, version.Group) groupPath := "/apis/" + version.Group s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(groupPath) s.GenericAPIServer.Handler.NonGoRestfulMux.Unregister(groupPath + "/") } // DefaultAPIResourceConfigSource returns default configuration for an APIResource. func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { ret := serverstorage.NewResourceConfig() ret.EnableVersions( v0alpha1.SchemeGroupVersion, ) return ret }