grafana/pkg/modules/modules.go
Dan Cech c4c9bfaf2e
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 15:21:21 -05:00

148 lines
3.6 KiB
Go

package modules
import (
"context"
"errors"
"strings"
"github.com/grafana/dskit/modules"
"github.com/grafana/dskit/services"
"github.com/grafana/grafana/pkg/infra/log"
)
type Engine interface {
Run(context.Context) error
Shutdown(context.Context, string) error
}
type Manager interface {
RegisterModule(name string, fn initFn)
RegisterInvisibleModule(name string, fn initFn)
}
var _ Engine = (*service)(nil)
var _ Manager = (*service)(nil)
// service manages the registration and lifecycle of modules.
type service struct {
log log.Logger
targets []string
moduleManager *modules.Manager
serviceManager *services.Manager
serviceMap map[string]services.Service
}
func New(
targets []string,
) *service {
logger := log.New("modules")
return &service{
log: logger,
targets: targets,
moduleManager: modules.NewManager(logger),
serviceMap: map[string]services.Service{},
}
}
// Run starts all registered modules.
func (m *service) Run(ctx context.Context) error {
var err error
for mod, targets := range dependencyMap {
if !m.moduleManager.IsModuleRegistered(mod) {
continue
}
if err := m.moduleManager.AddDependency(mod, targets...); err != nil {
return err
}
}
m.serviceMap, err = m.moduleManager.InitModuleServices(m.targets...)
if err != nil {
return err
}
// if no modules are registered, we don't need to start the service manager
if len(m.serviceMap) == 0 {
return nil
}
var svcs []services.Service
for _, s := range m.serviceMap {
svcs = append(svcs, s)
}
m.serviceManager, err = services.NewManager(svcs...)
if err != nil {
return err
}
// 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.
if len(m.serviceMap) == 0 {
m.log.Warn("No modules registered...")
<-ctx.Done()
return nil
}
listener := newServiceListener(m.log, m)
m.serviceManager.AddListener(listener)
m.log.Debug("Starting module service manager", "targets", strings.Join(m.targets, ","))
// wait until a service fails or stop signal was received
err = m.serviceManager.StartAsync(ctx)
if err != nil {
return err
}
err = m.serviceManager.AwaitStopped(ctx)
if err != nil {
return err
}
failed := m.serviceManager.ServicesByState()[services.Failed]
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, reason string) error {
if m.serviceManager == nil {
m.log.Debug("No modules registered, nothing to stop...")
return nil
}
m.serviceManager.StopAsync()
m.log.Info("Awaiting services to be stopped...", "reason", reason)
return m.serviceManager.AwaitStopped(ctx)
}
type initFn func() (services.Service, error)
// RegisterModule registers a module with the dskit module manager.
func (m *service) RegisterModule(name string, fn initFn) {
m.moduleManager.RegisterModule(name, fn)
}
// RegisterInvisibleModule registers an invisible module with the dskit module manager.
// Invisible modules are not visible to the user, and are intended to be used as dependencies.
func (m *service) RegisterInvisibleModule(name string, fn initFn) {
m.moduleManager.RegisterModule(name, fn, modules.UserInvisibleModule)
}
// IsModuleEnabled returns true if the module is enabled.
func (m *service) IsModuleEnabled(name string) bool {
return stringsContain(m.targets, name)
}