plugins.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package plugins
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "io/ioutil"
  6. "os"
  7. "path"
  8. "path/filepath"
  9. "reflect"
  10. "strings"
  11. "github.com/grafana/grafana/pkg/log"
  12. "github.com/grafana/grafana/pkg/setting"
  13. "github.com/grafana/grafana/pkg/util"
  14. )
  15. var (
  16. DataSources map[string]*DataSourcePlugin
  17. Panels map[string]*PanelPlugin
  18. StaticRoutes []*PluginStaticRoute
  19. Apps map[string]*AppPlugin
  20. Plugins map[string]*PluginBase
  21. PluginTypes map[string]interface{}
  22. )
  23. type PluginScanner struct {
  24. pluginPath string
  25. errors []error
  26. }
  27. func Init() error {
  28. DataSources = make(map[string]*DataSourcePlugin)
  29. StaticRoutes = make([]*PluginStaticRoute, 0)
  30. Panels = make(map[string]*PanelPlugin)
  31. Apps = make(map[string]*AppPlugin)
  32. Plugins = make(map[string]*PluginBase)
  33. PluginTypes = map[string]interface{}{
  34. "panel": PanelPlugin{},
  35. "datasource": DataSourcePlugin{},
  36. "app": AppPlugin{},
  37. }
  38. log.Info("Plugins: Scan starting")
  39. scan(path.Join(setting.StaticRootPath, "app/plugins"))
  40. // check if plugins dir exists
  41. if _, err := os.Stat(setting.PluginsPath); os.IsNotExist(err) {
  42. log.Warn("Plugins: Plugin dir %v does not exist", setting.PluginsPath)
  43. if err = os.MkdirAll(setting.PluginsPath, os.ModePerm); err != nil {
  44. log.Warn("Plugins: Failed to create plugin dir: %v, error: %v", setting.PluginsPath, err)
  45. } else {
  46. log.Info("Plugins: Plugin dir %v created", setting.PluginsPath)
  47. scan(setting.PluginsPath)
  48. }
  49. } else {
  50. scan(setting.PluginsPath)
  51. }
  52. // check plugin paths defined in config
  53. checkPluginPaths()
  54. for _, panel := range Panels {
  55. panel.initFrontendPlugin()
  56. }
  57. for _, panel := range DataSources {
  58. panel.initFrontendPlugin()
  59. }
  60. for _, app := range Apps {
  61. app.initApp()
  62. }
  63. return nil
  64. }
  65. func checkPluginPaths() error {
  66. for _, section := range setting.Cfg.Sections() {
  67. if strings.HasPrefix(section.Name(), "plugin.") {
  68. path := section.Key("path").String()
  69. if path != "" {
  70. scan(path)
  71. }
  72. }
  73. }
  74. return nil
  75. }
  76. func scan(pluginDir string) error {
  77. scanner := &PluginScanner{
  78. pluginPath: pluginDir,
  79. }
  80. if err := util.Walk(pluginDir, true, true, scanner.walker); err != nil {
  81. if pluginDir != "data/plugins" {
  82. log.Warn("Could not scan dir \"%v\" error: %s", pluginDir, err)
  83. }
  84. return err
  85. }
  86. if len(scanner.errors) > 0 {
  87. return errors.New("Some plugins failed to load")
  88. }
  89. return nil
  90. }
  91. func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err error) error {
  92. if err != nil {
  93. return err
  94. }
  95. if f.Name() == "node_modules" {
  96. return util.WalkSkipDir
  97. }
  98. if f.IsDir() {
  99. return nil
  100. }
  101. if f.Name() == "plugin.json" {
  102. err := scanner.loadPluginJson(currentPath)
  103. if err != nil {
  104. log.Error(3, "Plugins: Failed to load plugin json file: %v, err: %v", currentPath, err)
  105. scanner.errors = append(scanner.errors, err)
  106. }
  107. }
  108. return nil
  109. }
  110. func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
  111. currentDir := filepath.Dir(pluginJsonFilePath)
  112. reader, err := os.Open(pluginJsonFilePath)
  113. if err != nil {
  114. return err
  115. }
  116. defer reader.Close()
  117. jsonParser := json.NewDecoder(reader)
  118. pluginCommon := PluginBase{}
  119. if err := jsonParser.Decode(&pluginCommon); err != nil {
  120. return err
  121. }
  122. if pluginCommon.Id == "" || pluginCommon.Type == "" {
  123. return errors.New("Did not find type and id property in plugin.json")
  124. }
  125. var loader PluginLoader
  126. if pluginGoType, exists := PluginTypes[pluginCommon.Type]; !exists {
  127. return errors.New("Unknown plugin type " + pluginCommon.Type)
  128. } else {
  129. loader = reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
  130. }
  131. reader.Seek(0, 0)
  132. return loader.Load(jsonParser, currentDir)
  133. }
  134. func GetPluginReadme(pluginId string) ([]byte, error) {
  135. plug, exists := Plugins[pluginId]
  136. if !exists {
  137. return nil, PluginNotFoundError{pluginId}
  138. }
  139. if plug.Readme != nil {
  140. return plug.Readme, nil
  141. }
  142. readmePath := filepath.Join(plug.PluginDir, "README.md")
  143. if _, err := os.Stat(readmePath); os.IsNotExist(err) {
  144. readmePath = filepath.Join(plug.PluginDir, "readme.md")
  145. }
  146. if _, err := os.Stat(readmePath); os.IsNotExist(err) {
  147. plug.Readme = make([]byte, 0)
  148. return plug.Readme, nil
  149. }
  150. if readmeBytes, err := ioutil.ReadFile(readmePath); err != nil {
  151. return nil, err
  152. } else {
  153. plug.Readme = readmeBytes
  154. return plug.Readme, nil
  155. }
  156. }