2023-03-06 13:06:52 -06:00
package modules
import (
"context"
"errors"
2023-07-14 14:22:10 -05:00
"fmt"
2023-03-06 13:06:52 -06:00
"github.com/grafana/dskit/modules"
"github.com/grafana/dskit/services"
"github.com/grafana/grafana/pkg/infra/log"
2023-07-14 14:22:10 -05:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2023-03-06 13:06:52 -06:00
"github.com/grafana/grafana/pkg/setting"
2023-07-06 07:45:47 -05:00
"github.com/grafana/grafana/pkg/systemd"
2023-03-06 13:06:52 -06:00
)
type Engine interface {
Init ( context . Context ) error
Run ( context . Context ) error
Shutdown ( context . Context ) error
}
type Manager interface {
2023-06-29 06:58:45 -05:00
RegisterModule ( name string , initFn func ( ) ( services . Service , error ) )
RegisterInvisibleModule ( name string , initFn func ( ) ( services . Service , error ) )
2023-03-06 13:06:52 -06:00
}
var _ Engine = ( * service ) ( nil )
var _ Manager = ( * service ) ( nil )
// service manages the registration and lifecycle of modules.
type service struct {
2023-06-29 06:58:45 -05:00
cfg * setting . Cfg
log log . Logger
targets [ ] string
moduleManager * modules . Manager
serviceManager * services . Manager
serviceMap map [ string ] services . Service
2023-07-14 14:22:10 -05:00
features * featuremgmt . FeatureManager
2023-03-06 13:06:52 -06:00
}
2023-07-14 14:22:10 -05:00
func ProvideService (
cfg * setting . Cfg ,
features * featuremgmt . FeatureManager ,
) * service {
2023-03-06 13:06:52 -06:00
logger := log . New ( "modules" )
return & service {
2023-06-29 06:58:45 -05:00
cfg : cfg ,
log : logger ,
targets : cfg . Target ,
2023-03-06 13:06:52 -06:00
2023-06-29 06:58:45 -05:00
moduleManager : modules . NewManager ( logger ) ,
serviceMap : map [ string ] services . Service { } ,
2023-07-14 14:22:10 -05:00
features : features ,
2023-03-06 13:06:52 -06:00
}
}
// Init initializes all registered modules.
func ( m * service ) Init ( _ context . Context ) error {
var err error
2023-07-14 14:22:10 -05:00
if err = m . processFeatureFlags ( ) ; err != nil {
return err
}
m . log . Debug ( "Initializing module manager" , "targets" , m . targets )
2023-06-29 06:58:45 -05:00
for mod , targets := range dependencyMap {
if err := m . moduleManager . AddDependency ( mod , targets ... ) ; err != nil {
2023-03-06 13:06:52 -06:00
return err
}
}
2023-06-29 06:58:45 -05:00
m . serviceMap , err = m . moduleManager . InitModuleServices ( m . targets ... )
2023-03-06 13:06:52 -06:00
if err != nil {
return err
}
// if no modules are registered, we don't need to start the service manager
2023-06-29 06:58:45 -05:00
if len ( m . serviceMap ) == 0 {
2023-03-06 13:06:52 -06:00
return nil
}
var svcs [ ] services . Service
2023-06-29 06:58:45 -05:00
for _ , s := range m . serviceMap {
2023-03-06 13:06:52 -06:00
svcs = append ( svcs , s )
}
2023-06-29 06:58:45 -05:00
m . serviceManager , err = services . NewManager ( svcs ... )
2023-03-06 13:06:52 -06:00
return err
}
// Run starts all registered modules.
func ( m * service ) Run ( ctx context . Context ) error {
// we don't need to continue if no modules are registered.
// this behavior may need to change if dskit services replace the
// current background service registry.
2023-06-29 06:58:45 -05:00
if len ( m . serviceMap ) == 0 {
2023-03-06 13:06:52 -06:00
m . log . Warn ( "No modules registered..." )
<- ctx . Done ( )
return nil
}
listener := newServiceListener ( m . log , m )
2023-06-29 06:58:45 -05:00
m . serviceManager . AddListener ( listener )
2023-03-06 13:06:52 -06:00
2023-07-14 14:22:10 -05:00
m . log . Debug ( "Starting module service manager" )
2023-03-06 13:06:52 -06:00
// wait until a service fails or stop signal was received
2023-06-29 06:58:45 -05:00
err := m . serviceManager . StartAsync ( ctx )
2023-03-06 13:06:52 -06:00
if err != nil {
return err
}
2023-07-06 07:45:47 -05:00
err = m . serviceManager . AwaitHealthy ( ctx )
if err != nil {
return err
}
systemd . NotifyReady ( m . log )
2023-06-29 06:58:45 -05:00
err = m . serviceManager . AwaitStopped ( ctx )
2023-03-06 13:06:52 -06:00
if err != nil {
return err
}
2023-06-29 06:58:45 -05:00
failed := m . serviceManager . ServicesByState ( ) [ services . Failed ]
2023-03-06 13:06:52 -06:00
for _ , f := range failed {
// the service listener will log error details for all modules that failed,
// so here we return the first error that is not ErrStopProcess
if ! errors . Is ( f . FailureCase ( ) , modules . ErrStopProcess ) {
return f . FailureCase ( )
}
}
return nil
}
// Shutdown stops all modules and waits for them to stop.
func ( m * service ) Shutdown ( ctx context . Context ) error {
2023-06-29 06:58:45 -05:00
if m . serviceManager == nil {
2023-03-06 13:06:52 -06:00
m . log . Debug ( "No modules registered, nothing to stop..." )
return nil
}
2023-06-29 06:58:45 -05:00
m . serviceManager . StopAsync ( )
2023-03-06 13:06:52 -06:00
m . log . Info ( "Awaiting services to be stopped..." )
2023-06-29 06:58:45 -05:00
return m . serviceManager . AwaitStopped ( ctx )
2023-03-06 13:06:52 -06:00
}
// RegisterModule registers a module with the dskit module manager.
2023-06-29 06:58:45 -05:00
func ( m * service ) RegisterModule ( name string , initFn func ( ) ( services . Service , error ) ) {
m . moduleManager . RegisterModule ( name , initFn )
2023-03-06 13:06:52 -06:00
}
// RegisterInvisibleModule registers an invisible module with the dskit module manager.
2023-06-29 06:58:45 -05:00
// 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 ) ) {
m . moduleManager . RegisterModule ( name , initFn , modules . UserInvisibleModule )
2023-03-06 13:06:52 -06:00
}
// IsModuleEnabled returns true if the module is enabled.
func ( m * service ) IsModuleEnabled ( name string ) bool {
return stringsContain ( m . targets , name )
}
2023-07-14 14:22:10 -05:00
// processFeatureFlags adds or removes targets based on feature flags.
func ( m * service ) processFeatureFlags ( ) error {
// add GrafanaAPIServer to targets if feature is enabled
if m . features . IsEnabled ( featuremgmt . FlagGrafanaAPIServer ) {
m . targets = append ( m . targets , GrafanaAPIServer )
}
if ! m . features . IsEnabled ( featuremgmt . FlagGrafanaAPIServer ) {
// error if GrafanaAPIServer is in targets
for _ , t := range m . targets {
if t == GrafanaAPIServer {
return fmt . Errorf ( "feature flag %s is disabled, but target %s is still enabled" , featuremgmt . FlagGrafanaAPIServer , GrafanaAPIServer )
}
}
// error if GrafanaAPIServer is a dependency of a target
for parent , targets := range dependencyMap {
for _ , t := range targets {
if t == GrafanaAPIServer && m . IsModuleEnabled ( parent ) {
return fmt . Errorf ( "feature flag %s is disabled, but target %s is enabled with dependency on %s" , featuremgmt . FlagGrafanaAPIServer , parent , GrafanaAPIServer )
}
}
}
}
return nil
}