provisioning.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. package provisioning
  2. import (
  3. "context"
  4. "github.com/grafana/grafana/pkg/log"
  5. "github.com/pkg/errors"
  6. "path"
  7. "sync"
  8. "github.com/grafana/grafana/pkg/registry"
  9. "github.com/grafana/grafana/pkg/services/provisioning/dashboards"
  10. "github.com/grafana/grafana/pkg/services/provisioning/datasources"
  11. "github.com/grafana/grafana/pkg/services/provisioning/notifiers"
  12. "github.com/grafana/grafana/pkg/setting"
  13. )
  14. func init() {
  15. registry.RegisterService(NewProvisioningServiceImpl(
  16. func(path string) (dashboards.DashboardProvisioner, error) {
  17. return dashboards.NewDashboardProvisionerImpl(path)
  18. },
  19. notifiers.Provision,
  20. datasources.Provision,
  21. ))
  22. }
  23. type ProvisioningService interface {
  24. ProvisionDatasources() error
  25. ProvisionNotifications() error
  26. ProvisionDashboards() error
  27. }
  28. func NewProvisioningServiceImpl(
  29. newDashboardProvisioner dashboards.DashboardProvisionerFactory,
  30. provisionNotifiers func(string) error,
  31. provisionDatasources func(string) error,
  32. ) *provisioningServiceImpl {
  33. return &provisioningServiceImpl{
  34. log: log.New("provisioning"),
  35. newDashboardProvisioner: newDashboardProvisioner,
  36. provisionNotifiers: provisionNotifiers,
  37. provisionDatasources: provisionDatasources,
  38. }
  39. }
  40. type provisioningServiceImpl struct {
  41. Cfg *setting.Cfg `inject:""`
  42. log log.Logger
  43. pollingCtxCancel context.CancelFunc
  44. newDashboardProvisioner dashboards.DashboardProvisionerFactory
  45. dashboardProvisioner dashboards.DashboardProvisioner
  46. provisionNotifiers func(string) error
  47. provisionDatasources func(string) error
  48. mutex sync.Mutex
  49. }
  50. func (ps *provisioningServiceImpl) Init() error {
  51. err := ps.ProvisionDatasources()
  52. if err != nil {
  53. return err
  54. }
  55. err = ps.ProvisionNotifications()
  56. if err != nil {
  57. return err
  58. }
  59. err = ps.ProvisionDashboards()
  60. if err != nil {
  61. return err
  62. }
  63. return nil
  64. }
  65. func (ps *provisioningServiceImpl) Run(ctx context.Context) error {
  66. for {
  67. // Wait for unlock. This is tied to new dashboardProvisioner to be instantiated before we start polling.
  68. ps.mutex.Lock()
  69. pollingContext, cancelFun := context.WithCancel(ctx)
  70. ps.pollingCtxCancel = cancelFun
  71. ps.dashboardProvisioner.PollChanges(pollingContext)
  72. ps.mutex.Unlock()
  73. select {
  74. case <-pollingContext.Done():
  75. // Polling was canceled.
  76. continue
  77. case <-ctx.Done():
  78. // Root server context was cancelled so just leave.
  79. return ctx.Err()
  80. }
  81. }
  82. }
  83. func (ps *provisioningServiceImpl) ProvisionDatasources() error {
  84. datasourcePath := path.Join(ps.Cfg.ProvisioningPath, "datasources")
  85. err := ps.provisionDatasources(datasourcePath)
  86. return errors.Wrap(err, "Datasource provisioning error")
  87. }
  88. func (ps *provisioningServiceImpl) ProvisionNotifications() error {
  89. alertNotificationsPath := path.Join(ps.Cfg.ProvisioningPath, "notifiers")
  90. err := ps.provisionNotifiers(alertNotificationsPath)
  91. return errors.Wrap(err, "Alert notification provisioning error")
  92. }
  93. func (ps *provisioningServiceImpl) ProvisionDashboards() error {
  94. dashboardPath := path.Join(ps.Cfg.ProvisioningPath, "dashboards")
  95. dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
  96. if err != nil {
  97. return errors.Wrap(err, "Failed to create provisioner")
  98. }
  99. ps.mutex.Lock()
  100. defer ps.mutex.Unlock()
  101. ps.cancelPolling()
  102. if err := dashProvisioner.Provision(); err != nil {
  103. // If we fail to provision with the new provisioner, mutex will unlock and the polling we restart with the
  104. // old provisioner as we did not switch them yet.
  105. return errors.Wrap(err, "Failed to provision dashboards")
  106. }
  107. ps.dashboardProvisioner = dashProvisioner
  108. return nil
  109. }
  110. func (ps *provisioningServiceImpl) cancelPolling() {
  111. if ps.pollingCtxCancel != nil {
  112. ps.log.Debug("Stop polling for dashboard changes")
  113. ps.pollingCtxCancel()
  114. }
  115. ps.pollingCtxCancel = nil
  116. }