dashboard.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package dashboards
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "github.com/grafana/grafana/pkg/infra/log"
  7. "github.com/grafana/grafana/pkg/util/errutil"
  8. )
  9. type DashboardProvisionerImpl struct {
  10. log log.Logger
  11. fileReaders []*fileReader
  12. }
  13. func NewDashboardProvisionerImpl(configDirectory string) (*DashboardProvisionerImpl, error) {
  14. logger := log.New("provisioning.dashboard")
  15. cfgReader := &configReader{path: configDirectory, log: logger}
  16. configs, err := cfgReader.readConfig()
  17. if err != nil {
  18. return nil, errutil.Wrap("Failed to read dashboards config", err)
  19. }
  20. fileReaders, err := getFileReaders(configs, logger)
  21. if err != nil {
  22. return nil, errutil.Wrap("Failed to initialize file readers", err)
  23. }
  24. d := &DashboardProvisionerImpl{
  25. log: logger,
  26. fileReaders: fileReaders,
  27. }
  28. return d, nil
  29. }
  30. func (provider *DashboardProvisionerImpl) Provision() error {
  31. for _, reader := range provider.fileReaders {
  32. if err := reader.startWalkingDisk(); err != nil {
  33. if os.IsNotExist(err) {
  34. // don't stop the provisioning service in case the folder is missing. The folder can appear after the startup
  35. provider.log.Warn("Failed to provision config", "name", reader.Cfg.Name, "error", err)
  36. return nil
  37. }
  38. return errutil.Wrapf(err, "Failed to provision config %v", reader.Cfg.Name)
  39. }
  40. }
  41. return nil
  42. }
  43. // PollChanges starts polling for changes in dashboard definition files. It creates goroutine for each provider
  44. // defined in the config.
  45. func (provider *DashboardProvisionerImpl) PollChanges(ctx context.Context) {
  46. for _, reader := range provider.fileReaders {
  47. go reader.pollChanges(ctx)
  48. }
  49. }
  50. // GetProvisionerResolvedPath returns resolved path for the specified provisioner name. Can be used to generate
  51. // relative path to provisioning file from it's external_id.
  52. func (provider *DashboardProvisionerImpl) GetProvisionerResolvedPath(name string) string {
  53. for _, reader := range provider.fileReaders {
  54. if reader.Cfg.Name == name {
  55. return reader.resolvedPath()
  56. }
  57. }
  58. return ""
  59. }
  60. func getFileReaders(configs []*DashboardsAsConfig, logger log.Logger) ([]*fileReader, error) {
  61. var readers []*fileReader
  62. for _, config := range configs {
  63. switch config.Type {
  64. case "file":
  65. fileReader, err := NewDashboardFileReader(config, logger.New("type", config.Type, "name", config.Name))
  66. if err != nil {
  67. return nil, errutil.Wrapf(err, "Failed to create file reader for config %v", config.Name)
  68. }
  69. readers = append(readers, fileReader)
  70. default:
  71. return nil, fmt.Errorf("type %s is not supported", config.Type)
  72. }
  73. }
  74. return readers, nil
  75. }