Files
grafana/pkg/plugins/plugins.go
woodsaj c35b51a268 refactor.
Rename externalPlugin to apiPlugin
Rename bundle to app
Move js, css, menuItem and staticRoot to be properties os App
Add "app" field to panel, datasource and api plugin models. If populated
then the plugin is only enabled if the specific app is enabled for the Org.
If app is "", then the plugin is enabled for all orgs and can't be disabled.
2015-12-17 23:53:58 +08:00

267 lines
6.2 KiB
Go

package plugins
import (
"encoding/json"
"errors"
"os"
"path"
"path/filepath"
"strings"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
)
var (
DataSources map[string]*DataSourcePlugin
Panels map[string]*PanelPlugin
ApiPlugins map[string]*ApiPlugin
StaticRoutes []*StaticRootConfig
Apps map[string]*AppPlugin
)
type PluginScanner struct {
pluginPath string
errors []error
}
func Init() error {
DataSources = make(map[string]*DataSourcePlugin)
ApiPlugins = make(map[string]*ApiPlugin)
StaticRoutes = make([]*StaticRootConfig, 0)
Panels = make(map[string]*PanelPlugin)
Apps = make(map[string]*AppPlugin)
scan(path.Join(setting.StaticRootPath, "app/plugins"))
checkPluginPaths()
checkDependencies()
return nil
}
func checkDependencies() {
for appType, app := range Apps {
for _, reqPanel := range app.PanelPlugins {
if _, ok := Panels[reqPanel]; !ok {
log.Fatal(4, "App %s requires Panel type %s, but it is not present.", appType, reqPanel)
}
}
for _, reqDataSource := range app.DatasourcePlugins {
if _, ok := DataSources[reqDataSource]; !ok {
log.Fatal(4, "App %s requires DataSource type %s, but it is not present.", appType, reqDataSource)
}
}
for _, reqApiPlugin := range app.ApiPlugins {
if _, ok := ApiPlugins[reqApiPlugin]; !ok {
log.Fatal(4, "App %s requires ApiPlugin type %s, but it is not present.", appType, reqApiPlugin)
}
}
}
}
func checkPluginPaths() error {
for _, section := range setting.Cfg.Sections() {
if strings.HasPrefix(section.Name(), "plugin.") {
path := section.Key("path").String()
if path != "" {
log.Info("Plugin: Scaning dir %s", path)
scan(path)
}
}
}
return nil
}
func scan(pluginDir string) error {
scanner := &PluginScanner{
pluginPath: pluginDir,
}
if err := filepath.Walk(pluginDir, scanner.walker); err != nil {
return err
}
if len(scanner.errors) > 0 {
return errors.New("Some plugins failed to load")
}
return nil
}
func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err error) error {
if err != nil {
return err
}
if f.IsDir() {
return nil
}
if f.Name() == "plugin.json" {
err := scanner.loadPluginJson(currentPath)
if err != nil {
log.Error(3, "Failed to load plugin json file: %v, err: %v", currentPath, err)
scanner.errors = append(scanner.errors, err)
}
}
return nil
}
func addStaticRoot(staticRootConfig *StaticRootConfig, currentDir string) {
if staticRootConfig != nil {
staticRootConfig.Path = path.Join(currentDir, staticRootConfig.Path)
StaticRoutes = append(StaticRoutes, staticRootConfig)
}
}
func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
currentDir := filepath.Dir(pluginJsonFilePath)
reader, err := os.Open(pluginJsonFilePath)
if err != nil {
return err
}
defer reader.Close()
jsonParser := json.NewDecoder(reader)
pluginJson := make(map[string]interface{})
if err := jsonParser.Decode(&pluginJson); err != nil {
return err
}
pluginType, exists := pluginJson["pluginType"]
if !exists {
return errors.New("Did not find pluginType property in plugin.json")
}
if pluginType == "datasource" {
p := DataSourcePlugin{}
reader.Seek(0, 0)
if err := jsonParser.Decode(&p); err != nil {
return err
}
if p.Type == "" {
return errors.New("Did not find type property in plugin.json")
}
DataSources[p.Type] = &p
addStaticRoot(p.StaticRootConfig, currentDir)
}
if pluginType == "panel" {
p := PanelPlugin{}
reader.Seek(0, 0)
if err := jsonParser.Decode(&p); err != nil {
return err
}
if p.Type == "" {
return errors.New("Did not find type property in plugin.json")
}
Panels[p.Type] = &p
addStaticRoot(p.StaticRootConfig, currentDir)
}
if pluginType == "api" {
p := ApiPlugin{}
reader.Seek(0, 0)
if err := jsonParser.Decode(&p); err != nil {
return err
}
if p.Type == "" {
return errors.New("Did not find type property in plugin.json")
}
ApiPlugins[p.Type] = &p
}
if pluginType == "app" {
p := AppPlugin{}
reader.Seek(0, 0)
if err := jsonParser.Decode(&p); err != nil {
return err
}
if p.Type == "" {
return errors.New("Did not find type property in plugin.json")
}
Apps[p.Type] = &p
addStaticRoot(p.StaticRootConfig, currentDir)
}
return nil
}
func GetEnabledPlugins(orgApps []*models.AppPlugin) EnabledPlugins {
enabledPlugins := NewEnabledPlugins()
orgAppsMap := make(map[string]*models.AppPlugin)
for _, orgApp := range orgApps {
orgAppsMap[orgApp.Type] = orgApp
}
seenPanels := make(map[string]bool)
seenApi := make(map[string]bool)
for appType, app := range Apps {
// start with enabled set to the default state listed in the json config.
enabled := app.Enabled
// check if the app is stored in the DB for this org and if so, use the
// enabled state stored there.
if b, ok := orgAppsMap[appType]; ok {
enabled = b.Enabled
}
if enabled {
for _, d := range app.DatasourcePlugins {
if ds, ok := DataSources[d]; ok {
enabledPlugins.DataSourcePlugins[d] = ds
}
}
for _, p := range app.PanelPlugins {
if panel, ok := Panels[p]; ok {
if _, ok := seenPanels[p]; !ok {
seenPanels[p] = true
enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel)
}
}
}
for _, a := range app.ApiPlugins {
if api, ok := ApiPlugins[a]; ok {
if _, ok := seenApi[a]; !ok {
seenApi[a] = true
enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api)
}
}
}
enabledPlugins.AppPlugins = append(enabledPlugins.AppPlugins, app)
}
}
// add all plugins that are not part of an App.
for d, installedDs := range DataSources {
if installedDs.App == "" {
enabledPlugins.DataSourcePlugins[d] = installedDs
}
}
for p, panel := range Panels {
if panel.App == "" {
if _, ok := seenPanels[p]; !ok {
seenPanels[p] = true
enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, panel)
}
}
}
for a, api := range ApiPlugins {
if api.App == "" {
if _, ok := seenApi[a]; !ok {
seenApi[a] = true
enabledPlugins.ApiPlugins = append(enabledPlugins.ApiPlugins, api)
}
}
}
return enabledPlugins
}