2023-03-06 13:06:52 -06:00
|
|
|
package modules
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"errors"
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 14:21:21 -06:00
|
|
|
"strings"
|
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"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Engine interface {
|
|
|
|
Run(context.Context) error
|
2023-08-31 08:12:01 -05:00
|
|
|
Shutdown(context.Context, string) error
|
2023-03-06 13:06:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type Manager interface {
|
2023-08-31 08:12:01 -05:00
|
|
|
RegisterModule(name string, fn initFn)
|
|
|
|
RegisterInvisibleModule(name string, fn initFn)
|
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-08-31 08:12:01 -05:00
|
|
|
log log.Logger
|
|
|
|
targets []string
|
|
|
|
|
|
|
|
moduleManager *modules.Manager
|
|
|
|
serviceManager *services.Manager
|
|
|
|
serviceMap map[string]services.Service
|
2023-03-06 13:06:52 -06:00
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
func New(
|
|
|
|
targets []string,
|
2023-07-14 14:22:10 -05:00
|
|
|
) *service {
|
2023-03-06 13:06:52 -06:00
|
|
|
logger := log.New("modules")
|
|
|
|
|
|
|
|
return &service{
|
2023-08-31 08:12:01 -05:00
|
|
|
log: logger,
|
|
|
|
targets: targets,
|
2023-03-06 13:06:52 -06:00
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
moduleManager: modules.NewManager(logger),
|
|
|
|
serviceMap: map[string]services.Service{},
|
2023-07-24 13:01:07 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
// Run starts all registered modules.
|
|
|
|
func (m *service) Run(ctx context.Context) error {
|
2023-03-06 13:06:52 -06:00
|
|
|
var err error
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
for mod, targets := range dependencyMap {
|
|
|
|
if !m.moduleManager.IsModuleRegistered(mod) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if err := m.moduleManager.AddDependency(mod, targets...); err != nil {
|
2023-03-06 13:06:52 -06:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -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-08-31 08:12:01 -05:00
|
|
|
if len(m.serviceMap) == 0 {
|
2023-03-06 13:06:52 -06:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var svcs []services.Service
|
2023-08-31 08:12:01 -05:00
|
|
|
for _, s := range m.serviceMap {
|
2023-03-06 13:06:52 -06:00
|
|
|
svcs = append(svcs, s)
|
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
m.serviceManager, err = services.NewManager(svcs...)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2023-03-06 13:06:52 -06:00
|
|
|
|
|
|
|
// 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-08-31 08:12:01 -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-08-31 08:12:01 -05:00
|
|
|
m.serviceManager.AddListener(listener)
|
2023-03-06 13:06:52 -06:00
|
|
|
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 14:21:21 -06:00
|
|
|
m.log.Debug("Starting module service manager", "targets", strings.Join(m.targets, ","))
|
2023-03-06 13:06:52 -06:00
|
|
|
// wait until a service fails or stop signal was received
|
2023-08-31 08:12:01 -05:00
|
|
|
err = m.serviceManager.StartAsync(ctx)
|
2023-07-06 07:45:47 -05:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
err = m.serviceManager.AwaitStopped(ctx)
|
2023-03-06 13:06:52 -06:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -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.
|
2023-08-31 08:12:01 -05:00
|
|
|
func (m *service) Shutdown(ctx context.Context, reason string) error {
|
|
|
|
if m.serviceManager == nil {
|
2023-03-06 13:06:52 -06:00
|
|
|
m.log.Debug("No modules registered, nothing to stop...")
|
|
|
|
return nil
|
|
|
|
}
|
2023-08-31 08:12:01 -05:00
|
|
|
m.serviceManager.StopAsync()
|
|
|
|
m.log.Info("Awaiting services to be stopped...", "reason", reason)
|
|
|
|
return m.serviceManager.AwaitStopped(ctx)
|
2023-03-06 13:06:52 -06:00
|
|
|
}
|
|
|
|
|
2023-08-31 08:12:01 -05:00
|
|
|
type initFn func() (services.Service, error)
|
|
|
|
|
2023-03-06 13:06:52 -06:00
|
|
|
// RegisterModule registers a module with the dskit module manager.
|
2023-08-31 08:12:01 -05:00
|
|
|
func (m *service) RegisterModule(name string, fn initFn) {
|
|
|
|
m.moduleManager.RegisterModule(name, fn)
|
2023-03-06 13:06:52 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// RegisterInvisibleModule registers an invisible module with the dskit module manager.
|
Storage: Unified Storage based on Entity API (#71977)
* first round of entityapi updates
- quote column names and clean up insert/update queries
- replace grn with guid
- streamline table structure
fixes
streamline entity history
move EntitySummary into proto
remove EntitySummary
add guid to json
fix tests
change DB_Uuid to DB_NVarchar
fix folder test
convert interface to any
more cleanup
start entity store under grafana-apiserver dskit target
CRUD working, kind of
rough cut of wiring entity api to kube-apiserver
fake grafana user in context
add key to entity
list working
revert unnecessary changes
move entity storage files to their own package, clean up
use accessor to read/write grafana annotations
implement separate Create and Update functions
* go mod tidy
* switch from Kind to resource
* basic grpc storage server
* basic support for grpc entity store
* don't connect to database unless it's needed, pass user identity over grpc
* support getting user from k8s context, fix some mysql issues
* assign owner to snowflake dependency
* switch from ulid to uuid for guids
* cleanup, rename Search to List
* remove entityListResult
* EntityAPI: remove extra user abstraction (#79033)
* remove extra user abstraction
* add test stub (but
* move grpc context setup into client wrapper, fix lint issue
* remove unused constants
* remove custom json stuff
* basic list filtering, add todo
* change target to storage-server, allow entityStore flag in prod mode
* fix issue with Update
* EntityAPI: make test work, need to resolve expected differences (#79123)
* make test work, need to resolve expected differences
* remove the fields not supported by legacy
* sanitize out the bits legacy does not support
* sanitize out the bits legacy does not support
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
* update feature toggle generated files
* remove unused http headers
* update feature flag strategy
* devmode
* update readme
* spelling
* readme
---------
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2023-12-06 14:21:21 -06:00
|
|
|
// Invisible modules are not visible to the user, and are intended to be used as dependencies.
|
2023-08-31 08:12:01 -05:00
|
|
|
func (m *service) RegisterInvisibleModule(name string, fn initFn) {
|
|
|
|
m.moduleManager.RegisterModule(name, fn, 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)
|
|
|
|
}
|