mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Modules: Add registry (#70859)
This commit is contained in:
parent
703bf4afcc
commit
8f975cfdb8
11
pkg/modules/dependencies.go
Normal file
11
pkg/modules/dependencies.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package modules
|
||||||
|
|
||||||
|
const (
|
||||||
|
// All includes all modules necessary for Grafana to run as a standalone application.
|
||||||
|
All string = "all"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dependencyMap defines Module Targets => Dependencies
|
||||||
|
var dependencyMap = map[string][]string{
|
||||||
|
All: {},
|
||||||
|
}
|
@ -21,11 +21,19 @@ func newServiceListener(logger log.Logger, s *service) *serviceListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *serviceListener) Healthy() {
|
func (l *serviceListener) Healthy() {
|
||||||
l.log.Info("All modules healthy")
|
l.log.Info("All modules healthy", "modules", l.moduleNames())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *serviceListener) Stopped() {
|
func (l *serviceListener) Stopped() {
|
||||||
l.log.Info("All modules stopped")
|
l.log.Info("All modules stopped", "modules", l.moduleNames())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *serviceListener) moduleNames() []string {
|
||||||
|
var ms []string
|
||||||
|
for m := range l.service.serviceMap {
|
||||||
|
ms = append(ms, m)
|
||||||
|
}
|
||||||
|
return ms
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *serviceListener) Failure(service services.Service) {
|
func (l *serviceListener) Failure(service services.Service) {
|
||||||
@ -35,7 +43,7 @@ func (l *serviceListener) Failure(service services.Service) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// log which module failed
|
// log which module failed
|
||||||
for module, s := range l.service.ServiceMap {
|
for module, s := range l.service.serviceMap {
|
||||||
if s == service {
|
if s == service {
|
||||||
if errors.Is(service.FailureCase(), modules.ErrStopProcess) {
|
if errors.Is(service.FailureCase(), modules.ErrStopProcess) {
|
||||||
l.log.Info("Received stop signal via return error", "module", module, "err", service.FailureCase())
|
l.log.Info("Received stop signal via return error", "module", module, "err", service.FailureCase())
|
||||||
|
@ -11,11 +11,6 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
// List of available targets.
|
|
||||||
const (
|
|
||||||
All string = "all"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Engine interface {
|
type Engine interface {
|
||||||
Init(context.Context) error
|
Init(context.Context) error
|
||||||
Run(context.Context) error
|
Run(context.Context) error
|
||||||
@ -23,8 +18,8 @@ type Engine interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Manager interface {
|
type Manager interface {
|
||||||
RegisterModule(name string, initFn func() (services.Service, error), deps ...string)
|
RegisterModule(name string, initFn func() (services.Service, error))
|
||||||
RegisterInvisibleModule(name string, initFn func() (services.Service, error), deps ...string)
|
RegisterInvisibleModule(name string, initFn func() (services.Service, error))
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Engine = (*service)(nil)
|
var _ Engine = (*service)(nil)
|
||||||
@ -35,11 +30,10 @@ type service struct {
|
|||||||
cfg *setting.Cfg
|
cfg *setting.Cfg
|
||||||
log log.Logger
|
log log.Logger
|
||||||
targets []string
|
targets []string
|
||||||
dependencyMap map[string][]string
|
|
||||||
|
|
||||||
ModuleManager *modules.Manager
|
moduleManager *modules.Manager
|
||||||
ServiceManager *services.Manager
|
serviceManager *services.Manager
|
||||||
ServiceMap map[string]services.Service
|
serviceMap map[string]services.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func ProvideService(cfg *setting.Cfg) *service {
|
func ProvideService(cfg *setting.Cfg) *service {
|
||||||
@ -49,10 +43,9 @@ func ProvideService(cfg *setting.Cfg) *service {
|
|||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
log: logger,
|
log: logger,
|
||||||
targets: cfg.Target,
|
targets: cfg.Target,
|
||||||
dependencyMap: map[string][]string{},
|
|
||||||
|
|
||||||
ModuleManager: modules.NewManager(logger),
|
moduleManager: modules.NewManager(logger),
|
||||||
ServiceMap: map[string]services.Service{},
|
serviceMap: map[string]services.Service{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,30 +53,27 @@ func ProvideService(cfg *setting.Cfg) *service {
|
|||||||
func (m *service) Init(_ context.Context) error {
|
func (m *service) Init(_ context.Context) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// module registration
|
for mod, targets := range dependencyMap {
|
||||||
m.RegisterModule(All, nil)
|
if err := m.moduleManager.AddDependency(mod, targets...); err != nil {
|
||||||
|
|
||||||
for mod, targets := range m.dependencyMap {
|
|
||||||
if err := m.ModuleManager.AddDependency(mod, targets...); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.ServiceMap, err = m.ModuleManager.InitModuleServices(m.targets...)
|
m.serviceMap, err = m.moduleManager.InitModuleServices(m.targets...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no modules are registered, we don't need to start the service manager
|
// if no modules are registered, we don't need to start the service manager
|
||||||
if len(m.ServiceMap) == 0 {
|
if len(m.serviceMap) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var svcs []services.Service
|
var svcs []services.Service
|
||||||
for _, s := range m.ServiceMap {
|
for _, s := range m.serviceMap {
|
||||||
svcs = append(svcs, s)
|
svcs = append(svcs, s)
|
||||||
}
|
}
|
||||||
m.ServiceManager, err = services.NewManager(svcs...)
|
m.serviceManager, err = services.NewManager(svcs...)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -93,27 +83,27 @@ func (m *service) Run(ctx context.Context) error {
|
|||||||
// we don't need to continue if no modules are registered.
|
// we don't need to continue if no modules are registered.
|
||||||
// this behavior may need to change if dskit services replace the
|
// this behavior may need to change if dskit services replace the
|
||||||
// current background service registry.
|
// current background service registry.
|
||||||
if len(m.ServiceMap) == 0 {
|
if len(m.serviceMap) == 0 {
|
||||||
m.log.Warn("No modules registered...")
|
m.log.Warn("No modules registered...")
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
listener := newServiceListener(m.log, m)
|
listener := newServiceListener(m.log, m)
|
||||||
m.ServiceManager.AddListener(listener)
|
m.serviceManager.AddListener(listener)
|
||||||
|
|
||||||
// wait until a service fails or stop signal was received
|
// wait until a service fails or stop signal was received
|
||||||
err := m.ServiceManager.StartAsync(ctx)
|
err := m.serviceManager.StartAsync(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.ServiceManager.AwaitStopped(ctx)
|
err = m.serviceManager.AwaitStopped(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
failed := m.ServiceManager.ServicesByState()[services.Failed]
|
failed := m.serviceManager.ServicesByState()[services.Failed]
|
||||||
for _, f := range failed {
|
for _, f := range failed {
|
||||||
// the service listener will log error details for all modules that failed,
|
// the service listener will log error details for all modules that failed,
|
||||||
// so here we return the first error that is not ErrStopProcess
|
// so here we return the first error that is not ErrStopProcess
|
||||||
@ -127,26 +117,24 @@ func (m *service) Run(ctx context.Context) error {
|
|||||||
|
|
||||||
// Shutdown stops all modules and waits for them to stop.
|
// Shutdown stops all modules and waits for them to stop.
|
||||||
func (m *service) Shutdown(ctx context.Context) error {
|
func (m *service) Shutdown(ctx context.Context) error {
|
||||||
if m.ServiceManager == nil {
|
if m.serviceManager == nil {
|
||||||
m.log.Debug("No modules registered, nothing to stop...")
|
m.log.Debug("No modules registered, nothing to stop...")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
m.ServiceManager.StopAsync()
|
m.serviceManager.StopAsync()
|
||||||
m.log.Info("Awaiting services to be stopped...")
|
m.log.Info("Awaiting services to be stopped...")
|
||||||
return m.ServiceManager.AwaitStopped(ctx)
|
return m.serviceManager.AwaitStopped(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterModule registers a module with the dskit module manager.
|
// RegisterModule registers a module with the dskit module manager.
|
||||||
func (m *service) RegisterModule(name string, initFn func() (services.Service, error), deps ...string) {
|
func (m *service) RegisterModule(name string, initFn func() (services.Service, error)) {
|
||||||
m.ModuleManager.RegisterModule(name, initFn)
|
m.moduleManager.RegisterModule(name, initFn)
|
||||||
m.dependencyMap[name] = deps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterInvisibleModule registers an invisible module with the dskit module manager.
|
// RegisterInvisibleModule registers an invisible module with the dskit module manager.
|
||||||
// Invisible modules are not visible to the user, and are intendent to be used as dependencies.
|
// Invisible modules are not visible to the user, and are intended to be used as dependencies.
|
||||||
func (m *service) RegisterInvisibleModule(name string, initFn func() (services.Service, error), deps ...string) {
|
func (m *service) RegisterInvisibleModule(name string, initFn func() (services.Service, error)) {
|
||||||
m.ModuleManager.RegisterModule(name, initFn, modules.UserInvisibleModule)
|
m.moduleManager.RegisterModule(name, initFn, modules.UserInvisibleModule)
|
||||||
m.dependencyMap[name] = deps
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsModuleEnabled returns true if the module is enabled.
|
// IsModuleEnabled returns true if the module is enabled.
|
||||||
|
46
pkg/modules/registry/registry.go
Normal file
46
pkg/modules/registry/registry.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/grafana/dskit/services"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/modules"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Registry interface{}
|
||||||
|
|
||||||
|
type registry struct {
|
||||||
|
moduleManager modules.Manager
|
||||||
|
log log.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProvideRegistry(
|
||||||
|
moduleManager modules.Manager,
|
||||||
|
) *registry {
|
||||||
|
return newRegistry(
|
||||||
|
log.New("modules.registry"),
|
||||||
|
moduleManager,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRegistry(logger log.Logger, moduleManager modules.Manager, svcs ...services.NamedService) *registry {
|
||||||
|
r := ®istry{
|
||||||
|
log: logger,
|
||||||
|
moduleManager: moduleManager,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register (invisible) modules which act solely as dependencies to module targets
|
||||||
|
for _, svc := range svcs {
|
||||||
|
s := svc
|
||||||
|
logger.Debug("Registering invisible module", "name", s.ServiceName())
|
||||||
|
r.moduleManager.RegisterInvisibleModule(s.ServiceName(), func() (services.Service, error) {
|
||||||
|
return s, nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register module targets
|
||||||
|
logger.Debug("Registering module", "name", modules.All)
|
||||||
|
r.moduleManager.RegisterModule(modules.All, nil)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
34
pkg/modules/registry/registry_test.go
Normal file
34
pkg/modules/registry/registry_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/grafana/dskit/services"
|
||||||
|
|
||||||
|
"github.com/grafana/grafana/pkg/infra/log"
|
||||||
|
"github.com/grafana/grafana/pkg/modules"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRegistry(t *testing.T) {
|
||||||
|
var registeredInvisibleModules []string
|
||||||
|
var registeredModules []string
|
||||||
|
|
||||||
|
moduleManager := &modules.MockModuleManager{
|
||||||
|
RegisterModuleFunc: func(name string, initFn func() (services.Service, error)) {
|
||||||
|
registeredModules = append(registeredModules, name)
|
||||||
|
},
|
||||||
|
RegisterInvisibleModuleFunc: func(name string, initFn func() (services.Service, error)) {
|
||||||
|
registeredInvisibleModules = append(registeredInvisibleModules, name)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
mockSvcName := "test-registry"
|
||||||
|
mockSvc := modules.NewMockNamedService(mockSvcName)
|
||||||
|
|
||||||
|
r := newRegistry(log.New("modules.registry"), moduleManager, mockSvc)
|
||||||
|
require.NotNil(t, r)
|
||||||
|
require.Equal(t, []string{mockSvcName}, registeredInvisibleModules)
|
||||||
|
require.Equal(t, []string{modules.All}, registeredModules)
|
||||||
|
}
|
8
pkg/modules/registry/wire.go
Normal file
8
pkg/modules/registry/wire.go
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package registry
|
||||||
|
|
||||||
|
import "github.com/google/wire"
|
||||||
|
|
||||||
|
var WireSet = wire.NewSet(
|
||||||
|
ProvideRegistry,
|
||||||
|
wire.Bind(new(Registry), new(*registry)),
|
||||||
|
)
|
@ -1,5 +1,58 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/grafana/dskit/services"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Manager = (*MockModuleManager)(nil)
|
||||||
|
var _ Engine = (*MockModuleEngine)(nil)
|
||||||
|
|
||||||
|
type MockModuleManager struct {
|
||||||
|
RegisterModuleFunc func(name string, initFn func() (services.Service, error))
|
||||||
|
RegisterInvisibleModuleFunc func(name string, initFn func() (services.Service, error))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockModuleManager) RegisterModule(name string, initFn func() (services.Service, error)) {
|
||||||
|
if m.RegisterModuleFunc != nil {
|
||||||
|
m.RegisterModuleFunc(name, initFn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockModuleManager) RegisterInvisibleModule(name string, initFn func() (services.Service, error)) {
|
||||||
|
if m.RegisterInvisibleModuleFunc != nil {
|
||||||
|
m.RegisterInvisibleModuleFunc(name, initFn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockModuleEngine struct {
|
||||||
|
InitFunc func(context.Context) error
|
||||||
|
RunFunc func(context.Context) error
|
||||||
|
ShutdownFunc func(context.Context) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockModuleEngine) Init(ctx context.Context) error {
|
||||||
|
if m.InitFunc != nil {
|
||||||
|
return m.InitFunc(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockModuleEngine) Run(ctx context.Context) error {
|
||||||
|
if m.RunFunc != nil {
|
||||||
|
return m.RunFunc(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MockModuleEngine) Shutdown(ctx context.Context) error {
|
||||||
|
if m.ShutdownFunc != nil {
|
||||||
|
return m.ShutdownFunc(ctx)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func stringsContain(values []string, search string) bool {
|
func stringsContain(values []string, search string) bool {
|
||||||
for _, v := range values {
|
for _, v := range values {
|
||||||
if search == v {
|
if search == v {
|
||||||
@ -9,3 +62,14 @@ func stringsContain(values []string, search string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MockNamedService struct {
|
||||||
|
*services.BasicService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMockNamedService(name string) *MockNamedService {
|
||||||
|
startFn := func(_ context.Context) error { return nil }
|
||||||
|
return &MockNamedService{
|
||||||
|
BasicService: services.NewIdleService(startFn, nil).WithName(name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package modules
|
package modules
|
||||||
|
|
||||||
import "github.com/google/wire"
|
import (
|
||||||
|
"github.com/google/wire"
|
||||||
|
)
|
||||||
|
|
||||||
var WireSet = wire.NewSet(
|
var WireSet = wire.NewSet(
|
||||||
ProvideService,
|
ProvideService,
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/infra/metrics"
|
"github.com/grafana/grafana/pkg/infra/metrics"
|
||||||
"github.com/grafana/grafana/pkg/infra/usagestats/statscollector"
|
"github.com/grafana/grafana/pkg/infra/usagestats/statscollector"
|
||||||
"github.com/grafana/grafana/pkg/modules"
|
"github.com/grafana/grafana/pkg/modules"
|
||||||
|
moduleRegistry "github.com/grafana/grafana/pkg/modules/registry"
|
||||||
"github.com/grafana/grafana/pkg/registry"
|
"github.com/grafana/grafana/pkg/registry"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
"github.com/grafana/grafana/pkg/services/provisioning"
|
"github.com/grafana/grafana/pkg/services/provisioning"
|
||||||
@ -40,6 +41,7 @@ func New(opts Options, cfg *setting.Cfg, httpServer *api.HTTPServer, roleRegistr
|
|||||||
provisioningService provisioning.ProvisioningService, backgroundServiceProvider registry.BackgroundServiceRegistry,
|
provisioningService provisioning.ProvisioningService, backgroundServiceProvider registry.BackgroundServiceRegistry,
|
||||||
usageStatsProvidersRegistry registry.UsageStatsProvidersRegistry, statsCollectorService *statscollector.Service,
|
usageStatsProvidersRegistry registry.UsageStatsProvidersRegistry, statsCollectorService *statscollector.Service,
|
||||||
moduleService modules.Engine,
|
moduleService modules.Engine,
|
||||||
|
_ moduleRegistry.Registry, // imported to invoke initialization via Wire
|
||||||
) (*Server, error) {
|
) (*Server, error) {
|
||||||
statsCollectorService.RegisterProviders(usageStatsProvidersRegistry.GetServices())
|
statsCollectorService.RegisterProviders(usageStatsProvidersRegistry.GetServices())
|
||||||
s, err := newServer(opts, cfg, httpServer, roleRegistry, provisioningService, backgroundServiceProvider, moduleService)
|
s, err := newServer(opts, cfg, httpServer, roleRegistry, provisioningService, backgroundServiceProvider, moduleService)
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/middleware/csrf"
|
"github.com/grafana/grafana/pkg/middleware/csrf"
|
||||||
"github.com/grafana/grafana/pkg/middleware/loggermw"
|
"github.com/grafana/grafana/pkg/middleware/loggermw"
|
||||||
"github.com/grafana/grafana/pkg/modules"
|
"github.com/grafana/grafana/pkg/modules"
|
||||||
|
moduleRegistry "github.com/grafana/grafana/pkg/modules/registry"
|
||||||
pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards"
|
pluginDashboards "github.com/grafana/grafana/pkg/plugins/manager/dashboards"
|
||||||
"github.com/grafana/grafana/pkg/registry/corekind"
|
"github.com/grafana/grafana/pkg/registry/corekind"
|
||||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||||
@ -357,6 +358,7 @@ var wireBasicSet = wire.NewSet(
|
|||||||
wire.Bind(new(oauthserver.OAuth2Server), new(*oasimpl.OAuth2ServiceImpl)),
|
wire.Bind(new(oauthserver.OAuth2Server), new(*oasimpl.OAuth2ServiceImpl)),
|
||||||
loggermw.Provide,
|
loggermw.Provide,
|
||||||
modules.WireSet,
|
modules.WireSet,
|
||||||
|
moduleRegistry.WireSet,
|
||||||
signingkeysimpl.ProvideEmbeddedSigningKeysService,
|
signingkeysimpl.ProvideEmbeddedSigningKeysService,
|
||||||
wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)),
|
wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)),
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user