Plugins: Move store init to dskit service (#111823)
This commit is contained in:
@@ -7,6 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/dskit/services"
|
||||
"github.com/grafana/grafana/pkg/infra/db"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
@@ -39,6 +40,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql/dualwrite"
|
||||
)
|
||||
|
||||
const ServiceName = "provisioning"
|
||||
|
||||
func ProvideService(
|
||||
ac accesscontrol.AccessControl,
|
||||
cfg *setting.Cfg,
|
||||
@@ -91,6 +94,8 @@ func ProvideService(
|
||||
dual: dual,
|
||||
}
|
||||
|
||||
s.NamedService = services.NewBasicService(s.starting, s.running, nil).WithName(ServiceName)
|
||||
|
||||
if err := s.setDashboardProvisioner(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -98,6 +103,67 @@ func ProvideService(
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) starting(ctx context.Context) error {
|
||||
if err := ps.ProvisionDatasources(ctx); err != nil {
|
||||
ps.log.Error("Failed to provision data sources", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ps.ProvisionPlugins(ctx); err != nil {
|
||||
ps.log.Error("Failed to provision plugins", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ps.ProvisionAlerting(ctx); err != nil {
|
||||
ps.log.Error("Failed to provision alerting", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Migrating prom types relies on data source provisioning to already be completed
|
||||
// If we can make services depend on other services completing first,
|
||||
// then we should remove this from provisioning
|
||||
if err := ps.migratePrometheusType(ctx); err != nil {
|
||||
ps.log.Error("Failed to migrate Prometheus type", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ps.ProvisionDashboards(ctx); err != nil {
|
||||
ps.log.Error("Failed to provision dashboard", "error", err)
|
||||
// Consider the allow list of errors for which running the provisioning service should not
|
||||
// fail. For now this includes only dashboards.ErrGetOrCreateFolder.
|
||||
if !errors.Is(err, dashboards.ErrGetOrCreateFolder) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ps.dashboardProvisioner.HasDashboardSources() {
|
||||
ps.searchService.TriggerReIndex()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) running(ctx context.Context) error {
|
||||
for {
|
||||
// Wait for unlock. This is tied to new dashboardProvisioner to be instantiated before we start polling.
|
||||
ps.mutex.Lock()
|
||||
// Using background here because otherwise if root context was canceled the select later on would
|
||||
// non-deterministically take one of the route possibly going into one polling loop before exiting.
|
||||
pollingContext, cancelFun := context.WithCancel(context.Background())
|
||||
ps.pollingCtxCancel = cancelFun
|
||||
ps.dashboardProvisioner.PollChanges(pollingContext)
|
||||
ps.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case <-pollingContext.Done():
|
||||
// Polling was canceled.
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
// Root server context was cancelled so cancel polling and leave.
|
||||
ps.cancelPolling()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) setDashboardProvisioner() error {
|
||||
dashboardPath := filepath.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||
dashProvisioner, err := ps.newDashboardProvisioner(context.Background(), dashboardPath, ps.dashboardProvisioningService, ps.orgService, ps.dashboardService, ps.folderService, ps.dual)
|
||||
@@ -137,6 +203,8 @@ func newProvisioningServiceImpl(
|
||||
migratePrometheusType: migratePrometheusType,
|
||||
}
|
||||
|
||||
s.NamedService = services.NewBasicService(s.starting, s.running, nil).WithName(ServiceName)
|
||||
|
||||
if err := s.setDashboardProvisioner(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -145,6 +213,7 @@ func newProvisioningServiceImpl(
|
||||
}
|
||||
|
||||
type ProvisioningServiceImpl struct {
|
||||
services.NamedService
|
||||
Cfg *setting.Cfg
|
||||
SQLStore db.DB
|
||||
orgService org.Service
|
||||
@@ -173,7 +242,6 @@ type ProvisioningServiceImpl struct {
|
||||
resourcePermissions accesscontrol.ReceiverPermissionsService
|
||||
tracer tracing.Tracer
|
||||
dual dualwrite.Service
|
||||
onceInitProvisioners sync.Once
|
||||
migratePrometheusType func(context.Context) error
|
||||
}
|
||||
|
||||
@@ -185,78 +253,11 @@ func (ps *ProvisioningServiceImpl) RunInitProvisioners(ctx context.Context) erro
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) Run(ctx context.Context) error {
|
||||
var err error
|
||||
|
||||
// Run Datasources, Plugins and Alerting Provisioning only once.
|
||||
// It can't be initialized at RunInitProvisioners because it
|
||||
// depends on the /apis endpoints to be already running and listeningq
|
||||
ps.onceInitProvisioners.Do(func() {
|
||||
err = ps.ProvisionDatasources(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to provision data sources", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ps.ProvisionPlugins(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to provision plugins", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = ps.ProvisionAlerting(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to provision alerting", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Migrating prom types relies on data source provisioning to already be completed
|
||||
// If we can make services depend on other services completing first,
|
||||
// then we should remove this from provisioning
|
||||
err = ps.migratePrometheusType(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to migrate Prometheus type", "error", err)
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// error already logged
|
||||
if err := ps.StartAsync(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ps.ProvisionDashboards(ctx)
|
||||
if err != nil {
|
||||
ps.log.Error("Failed to provision dashboard", "error", err)
|
||||
// Consider the allow list of errors for which running the provisioning service should not
|
||||
// fail. For now this includes only dashboards.ErrGetOrCreateFolder.
|
||||
if !errors.Is(err, dashboards.ErrGetOrCreateFolder) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if ps.dashboardProvisioner.HasDashboardSources() {
|
||||
ps.searchService.TriggerReIndex()
|
||||
}
|
||||
|
||||
for {
|
||||
// Wait for unlock. This is tied to new dashboardProvisioner to be instantiated before we start polling.
|
||||
ps.mutex.Lock()
|
||||
// Using background here because otherwise if root context was canceled the select later on would
|
||||
// non-deterministically take one of the route possibly going into one polling loop before exiting.
|
||||
pollingContext, cancelFun := context.WithCancel(context.Background())
|
||||
ps.pollingCtxCancel = cancelFun
|
||||
ps.dashboardProvisioner.PollChanges(pollingContext)
|
||||
ps.mutex.Unlock()
|
||||
|
||||
select {
|
||||
case <-pollingContext.Done():
|
||||
// Polling was canceled.
|
||||
continue
|
||||
case <-ctx.Done():
|
||||
// Root server context was cancelled so cancel polling and leave.
|
||||
ps.cancelPolling()
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
stopCtx := context.Background()
|
||||
return ps.AwaitTerminated(stopCtx)
|
||||
}
|
||||
|
||||
func (ps *ProvisioningServiceImpl) ProvisionDatasources(ctx context.Context) error {
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestProvisioningServiceImpl(t *testing.T) {
|
||||
serviceTest.waitForStop()
|
||||
|
||||
assert.False(t, serviceTest.serviceRunning, "Service should not be running")
|
||||
assert.Equal(t, context.Canceled, serviceTest.serviceError, "Service should have returned canceled error")
|
||||
assert.NoError(t, serviceTest.serviceError, "Service should not have returned an error")
|
||||
})
|
||||
|
||||
t.Run("Failed reloading does not stop polling with old provisioned", func(t *testing.T) {
|
||||
@@ -91,7 +91,7 @@ func TestProvisioningServiceImpl(t *testing.T) {
|
||||
serviceTest.cancel()
|
||||
serviceTest.waitForStop()
|
||||
|
||||
assert.Equal(t, context.Canceled, serviceTest.serviceError)
|
||||
assert.NoError(t, serviceTest.serviceError, "Service should not have returned an error")
|
||||
})
|
||||
|
||||
t.Run("Should return run error when dashboard provisioning fails for non-allow-listed error", func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user