provisioning.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package provisioning
  2. import (
  3. "context"
  4. "path"
  5. "sync"
  6. "github.com/grafana/grafana/pkg/log"
  7. "github.com/pkg/errors"
  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. // Using background here because otherwise if root context was canceled the select later on would
  70. // non-deterministically take one of the route possibly going into one polling loop before exiting.
  71. pollingContext, cancelFun := context.WithCancel(context.Background())
  72. ps.pollingCtxCancel = cancelFun
  73. ps.dashboardProvisioner.PollChanges(pollingContext)
  74. ps.mutex.Unlock()
  75. select {
  76. case <-pollingContext.Done():
  77. // Polling was canceled.
  78. continue
  79. case <-ctx.Done():
  80. // Root server context was cancelled so cancel polling and leave.
  81. ps.cancelPolling()
  82. return ctx.Err()
  83. }
  84. }
  85. }
  86. func (ps *provisioningServiceImpl) ProvisionDatasources() error {
  87. datasourcePath := path.Join(ps.Cfg.ProvisioningPath, "datasources")
  88. err := ps.provisionDatasources(datasourcePath)
  89. return errors.Wrap(err, "Datasource provisioning error")
  90. }
  91. func (ps *provisioningServiceImpl) ProvisionNotifications() error {
  92. alertNotificationsPath := path.Join(ps.Cfg.ProvisioningPath, "notifiers")
  93. err := ps.provisionNotifiers(alertNotificationsPath)
  94. return errors.Wrap(err, "Alert notification provisioning error")
  95. }
  96. func (ps *provisioningServiceImpl) ProvisionDashboards() error {
  97. dashboardPath := path.Join(ps.Cfg.ProvisioningPath, "dashboards")
  98. dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
  99. if err != nil {
  100. return errors.Wrap(err, "Failed to create provisioner")
  101. }
  102. ps.mutex.Lock()
  103. defer ps.mutex.Unlock()
  104. ps.cancelPolling()
  105. if err := dashProvisioner.Provision(); err != nil {
  106. // If we fail to provision with the new provisioner, mutex will unlock and the polling we restart with the
  107. // old provisioner as we did not switch them yet.
  108. return errors.Wrap(err, "Failed to provision dashboards")
  109. }
  110. ps.dashboardProvisioner = dashProvisioner
  111. return nil
  112. }
  113. func (ps *provisioningServiceImpl) cancelPolling() {
  114. if ps.pollingCtxCancel != nil {
  115. ps.log.Debug("Stop polling for dashboard changes")
  116. ps.pollingCtxCancel()
  117. }
  118. ps.pollingCtxCancel = nil
  119. }