mirror of
https://github.com/grafana/grafana.git
synced 2025-08-06 20:59:35 +08:00
feat(plugins): major improvement in plugins golang code
This commit is contained in:
38
pkg/plugins/app_plugin.go
Normal file
38
pkg/plugins/app_plugin.go
Normal file
@ -0,0 +1,38 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type AppPluginPage struct {
|
||||
Text string `json:"text"`
|
||||
Icon string `json:"icon"`
|
||||
Url string `json:"url"`
|
||||
ReqRole models.RoleType `json:"reqRole"`
|
||||
}
|
||||
|
||||
type AppPluginCss struct {
|
||||
Light string `json:"light"`
|
||||
Dark string `json:"dark"`
|
||||
}
|
||||
|
||||
type AppPlugin struct {
|
||||
FrontendPluginBase
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Css *AppPluginCss `json:"css"`
|
||||
Page *AppPluginPage `json:"page"`
|
||||
}
|
||||
|
||||
func (p *AppPlugin) Load(decoder *json.Decoder, pluginDir string) error {
|
||||
if err := decoder.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PluginDir = pluginDir
|
||||
p.initFrontendPlugin()
|
||||
Apps[p.Id] = p
|
||||
return nil
|
||||
}
|
25
pkg/plugins/datasource_plugin.go
Normal file
25
pkg/plugins/datasource_plugin.go
Normal file
@ -0,0 +1,25 @@
|
||||
package plugins
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type DataSourcePlugin struct {
|
||||
FrontendPluginBase
|
||||
DefaultMatchFormat string `json:"defaultMatchFormat"`
|
||||
Annotations bool `json:"annotations"`
|
||||
Metrics bool `json:"metrics"`
|
||||
BuiltIn bool `json:"builtIn"`
|
||||
Mixed bool `json:"mixed"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
func (p *DataSourcePlugin) Load(decoder *json.Decoder, pluginDir string) error {
|
||||
if err := decoder.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PluginDir = pluginDir
|
||||
p.initFrontendPlugin()
|
||||
DataSources[p.Id] = p
|
||||
|
||||
return nil
|
||||
}
|
47
pkg/plugins/frontend_plugin.go
Normal file
47
pkg/plugins/frontend_plugin.go
Normal file
@ -0,0 +1,47 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"path"
|
||||
)
|
||||
|
||||
type FrontendPluginBase struct {
|
||||
PluginBase
|
||||
Module string `json:"module"`
|
||||
StaticRoot string `json:"staticRoot"`
|
||||
}
|
||||
|
||||
func (fp *FrontendPluginBase) initFrontendPlugin() {
|
||||
if fp.StaticRoot != "" {
|
||||
StaticRoutes = append(StaticRoutes, &PluginStaticRoute{
|
||||
Directory: fp.StaticRoot,
|
||||
PluginId: fp.Id,
|
||||
})
|
||||
}
|
||||
|
||||
fp.Info.Logos.Small = evalRelativePluginUrlPath(fp.Info.Logos.Small, fp.Id)
|
||||
fp.Info.Logos.Large = evalRelativePluginUrlPath(fp.Info.Logos.Large, fp.Id)
|
||||
|
||||
fp.handleModuleDefaults()
|
||||
}
|
||||
|
||||
func (fp *FrontendPluginBase) handleModuleDefaults() {
|
||||
if fp.Module != "" {
|
||||
return
|
||||
}
|
||||
|
||||
if fp.StaticRoot != "" {
|
||||
fp.Module = path.Join("plugins", fp.Type, fp.Id, "module")
|
||||
return
|
||||
}
|
||||
|
||||
fp.Module = path.Join("app/plugins", fp.Type, fp.Id, "module")
|
||||
}
|
||||
|
||||
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
|
||||
u, _ := url.Parse(pathStr)
|
||||
if u.IsAbs() {
|
||||
return pathStr
|
||||
}
|
||||
return path.Join("public/plugins", pluginId, pathStr)
|
||||
}
|
@ -1,15 +1,22 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
)
|
||||
|
||||
type PluginCommon struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
StaticRoot string `json:"staticRoot"`
|
||||
Info PluginInfo `json:"info"`
|
||||
type PluginLoader interface {
|
||||
Load(decoder *json.Decoder, pluginDir string) error
|
||||
}
|
||||
|
||||
type PluginBase struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
Id string `json:"id"`
|
||||
App string `json:"app"`
|
||||
Info PluginInfo `json:"info"`
|
||||
PluginDir string `json:"-"`
|
||||
}
|
||||
|
||||
type PluginInfo struct {
|
||||
@ -29,30 +36,11 @@ type PluginLogos struct {
|
||||
Large string `json:"large"`
|
||||
}
|
||||
|
||||
type DataSourcePlugin struct {
|
||||
PluginCommon
|
||||
Module string `json:"module"`
|
||||
ServiceName string `json:"serviceName"`
|
||||
Partials map[string]interface{} `json:"partials"`
|
||||
DefaultMatchFormat string `json:"defaultMatchFormat"`
|
||||
Annotations bool `json:"annotations"`
|
||||
Metrics bool `json:"metrics"`
|
||||
BuiltIn bool `json:"builtIn"`
|
||||
Mixed bool `json:"mixed"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type PluginStaticRoute struct {
|
||||
Directory string
|
||||
PluginId string
|
||||
}
|
||||
|
||||
type PanelPlugin struct {
|
||||
PluginCommon
|
||||
Module string `json:"module"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type ApiPluginRoute struct {
|
||||
Path string `json:"path"`
|
||||
Method string `json:"method"`
|
||||
@ -60,34 +48,11 @@ type ApiPluginRoute struct {
|
||||
ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"`
|
||||
ReqRole models.RoleType `json:"reqRole"`
|
||||
Url string `json:"url"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type AppPluginPage struct {
|
||||
Text string `json:"text"`
|
||||
Icon string `json:"icon"`
|
||||
Url string `json:"url"`
|
||||
ReqRole models.RoleType `json:"reqRole"`
|
||||
}
|
||||
|
||||
type AppPluginCss struct {
|
||||
Light string `json:"light"`
|
||||
Dark string `json:"dark"`
|
||||
}
|
||||
|
||||
type ApiPlugin struct {
|
||||
PluginCommon
|
||||
PluginBase
|
||||
Routes []*ApiPluginRoute `json:"routes"`
|
||||
App string `json:"app"`
|
||||
}
|
||||
|
||||
type AppPlugin struct {
|
||||
PluginCommon
|
||||
Enabled bool `json:"enabled"`
|
||||
Pinned bool `json:"pinned"`
|
||||
Module string `json:"module"`
|
||||
Css *AppPluginCss `json:"css"`
|
||||
Page *AppPluginPage `json:"page"`
|
||||
}
|
||||
|
||||
type EnabledPlugins struct {
|
||||
|
19
pkg/plugins/panel_plugin.go
Normal file
19
pkg/plugins/panel_plugin.go
Normal file
@ -0,0 +1,19 @@
|
||||
package plugins
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type PanelPlugin struct {
|
||||
FrontendPluginBase
|
||||
}
|
||||
|
||||
func (p *PanelPlugin) Load(decoder *json.Decoder, pluginDir string) error {
|
||||
if err := decoder.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.PluginDir = pluginDir
|
||||
p.initFrontendPlugin()
|
||||
Panels[p.Id] = p
|
||||
|
||||
return nil
|
||||
}
|
@ -5,10 +5,10 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
@ -24,6 +24,7 @@ var (
|
||||
ApiPlugins map[string]*ApiPlugin
|
||||
StaticRoutes []*PluginStaticRoute
|
||||
Apps map[string]*AppPlugin
|
||||
PluginTypes map[string]interface{}
|
||||
)
|
||||
|
||||
type PluginScanner struct {
|
||||
@ -37,6 +38,12 @@ func Init() error {
|
||||
StaticRoutes = make([]*PluginStaticRoute, 0)
|
||||
Panels = make(map[string]*PanelPlugin)
|
||||
Apps = make(map[string]*AppPlugin)
|
||||
PluginTypes = map[string]interface{}{
|
||||
"panel": PanelPlugin{},
|
||||
"datasource": DataSourcePlugin{},
|
||||
"api": ApiPlugin{},
|
||||
"app": AppPlugin{},
|
||||
}
|
||||
|
||||
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
||||
checkPluginPaths()
|
||||
@ -115,27 +122,7 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
|
||||
u, _ := url.Parse(pathStr)
|
||||
if u.IsAbs() {
|
||||
return pathStr
|
||||
}
|
||||
return path.Join("public/plugins", pluginId, pathStr)
|
||||
}
|
||||
|
||||
func addPublicContent(plugin *PluginCommon, currentDir string) {
|
||||
if plugin.StaticRoot != "" {
|
||||
StaticRoutes = append(StaticRoutes, &PluginStaticRoute{
|
||||
Directory: path.Join(currentDir, plugin.StaticRoot),
|
||||
PluginId: plugin.Id,
|
||||
})
|
||||
}
|
||||
|
||||
plugin.Info.Logos.Small = evalRelativePluginUrlPath(plugin.Info.Logos.Small, plugin.Id)
|
||||
plugin.Info.Logos.Large = evalRelativePluginUrlPath(plugin.Info.Logos.Large, plugin.Id)
|
||||
}
|
||||
|
||||
func interpolatePluginJson(reader io.Reader, pluginCommon *PluginCommon) (io.Reader, error) {
|
||||
func interpolatePluginJson(reader io.Reader, pluginCommon *PluginBase) (io.Reader, error) {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(reader)
|
||||
jsonStr := buf.String() //
|
||||
@ -167,7 +154,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
defer reader.Close()
|
||||
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
pluginCommon := PluginCommon{}
|
||||
pluginCommon := PluginBase{}
|
||||
if err := jsonParser.Decode(&pluginCommon); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -177,52 +164,21 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
||||
}
|
||||
|
||||
reader.Seek(0, 0)
|
||||
|
||||
if newReader, err := interpolatePluginJson(reader, &pluginCommon); err != nil {
|
||||
return err
|
||||
} else {
|
||||
jsonParser = json.NewDecoder(newReader)
|
||||
}
|
||||
|
||||
switch pluginCommon.Type {
|
||||
case "datasource":
|
||||
p := DataSourcePlugin{}
|
||||
if err := jsonParser.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
var loader PluginLoader
|
||||
|
||||
DataSources[p.Id] = &p
|
||||
addPublicContent(&p.PluginCommon, currentDir)
|
||||
|
||||
case "panel":
|
||||
p := PanelPlugin{}
|
||||
reader.Seek(0, 0)
|
||||
if err := jsonParser.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
Panels[p.Id] = &p
|
||||
addPublicContent(&p.PluginCommon, currentDir)
|
||||
case "api":
|
||||
p := ApiPlugin{}
|
||||
reader.Seek(0, 0)
|
||||
if err := jsonParser.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
ApiPlugins[p.Id] = &p
|
||||
case "app":
|
||||
p := AppPlugin{}
|
||||
reader.Seek(0, 0)
|
||||
if err := jsonParser.Decode(&p); err != nil {
|
||||
return err
|
||||
}
|
||||
Apps[p.Id] = &p
|
||||
addPublicContent(&p.PluginCommon, currentDir)
|
||||
default:
|
||||
if pluginGoType, exists := PluginTypes[pluginCommon.Type]; !exists {
|
||||
return errors.New("Unkown plugin type " + pluginCommon.Type)
|
||||
} else {
|
||||
loader = reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
|
||||
}
|
||||
|
||||
return nil
|
||||
return loader.Load(jsonParser, currentDir)
|
||||
}
|
||||
|
||||
func GetEnabledPlugins(orgApps []*models.AppPlugin) EnabledPlugins {
|
||||
|
@ -19,6 +19,10 @@ func TestPluginScans(t *testing.T) {
|
||||
So(err, ShouldBeNil)
|
||||
So(len(DataSources), ShouldBeGreaterThan, 1)
|
||||
So(len(Panels), ShouldBeGreaterThan, 1)
|
||||
|
||||
Convey("Should set module automatically", func() {
|
||||
So(DataSources["graphite"].Module, ShouldEqual, "app/plugins/datasource/graphite/module")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When reading app plugin definition", t, func() {
|
||||
|
Reference in New Issue
Block a user