Commit 5eab5dc4 by Torkel Ödegaard

feat(plugins): removed external plugins and bundle code, not ready for master…

feat(plugins): removed external plugins and bundle code, not ready for master yet, will revert this commit in seperate branch
parent 2ec5bc77
...@@ -156,12 +156,6 @@ func Register(r *macaron.Macaron) { ...@@ -156,12 +156,6 @@ func Register(r *macaron.Macaron) {
r.Get("/plugins", GetDataSourcePlugins) r.Get("/plugins", GetDataSourcePlugins)
}, reqOrgAdmin) }, reqOrgAdmin)
// PluginBundles
r.Group("/plugins", func() {
r.Get("/", wrap(GetPluginBundles))
r.Post("/", bind(m.UpdatePluginBundleCmd{}), wrap(UpdatePluginBundle))
}, reqOrgAdmin)
r.Get("/frontend/settings/", GetFrontendSettings) r.Get("/frontend/settings/", GetFrontendSettings)
r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest) r.Any("/datasources/proxy/:id/*", reqSignedIn, ProxyDataSourceRequest)
r.Any("/datasources/proxy/:id", reqSignedIn, ProxyDataSourceRequest) r.Any("/datasources/proxy/:id", reqSignedIn, ProxyDataSourceRequest)
...@@ -197,7 +191,5 @@ func Register(r *macaron.Macaron) { ...@@ -197,7 +191,5 @@ func Register(r *macaron.Macaron) {
// rendering // rendering
r.Get("/render/*", reqSignedIn, RenderToPng) r.Get("/render/*", reqSignedIn, RenderToPng)
InitExternalPluginRoutes(r)
r.NotFound(NotFoundHandler) r.NotFound(NotFoundHandler)
} }
...@@ -117,14 +117,7 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) { ...@@ -117,14 +117,7 @@ func UpdateDataSource(c *middleware.Context, cmd m.UpdateDataSourceCommand) {
func GetDataSourcePlugins(c *middleware.Context) { func GetDataSourcePlugins(c *middleware.Context) {
dsList := make(map[string]interface{}) dsList := make(map[string]interface{})
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId} for key, value := range plugins.DataSources {
err := bus.Dispatch(&orgBundles)
if err != nil {
c.JsonApiErr(500, "Failed to get org plugin Bundles", err)
}
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
for key, value := range enabledPlugins.DataSourcePlugins {
if !value.BuiltIn { if !value.BuiltIn {
dsList[key] = value dsList[key] = value
} }
......
package api
import (
"encoding/json"
"net/http"
"net/http/httputil"
"net/url"
"github.com/Unknwon/macaron"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/util"
)
func InitExternalPluginRoutes(r *macaron.Macaron) {
for _, plugin := range plugins.ExternalPlugins {
log.Info("Plugin: Adding proxy routes for backend plugin")
for _, route := range plugin.Routes {
url := util.JoinUrlFragments("/api/plugin-proxy/", route.Path)
handlers := make([]macaron.Handler, 0)
if route.ReqSignedIn {
handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true}))
}
if route.ReqGrafanaAdmin {
handlers = append(handlers, middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true}))
}
if route.ReqSignedIn && route.ReqRole != "" {
if route.ReqRole == m.ROLE_ADMIN {
handlers = append(handlers, middleware.RoleAuth(m.ROLE_ADMIN))
} else if route.ReqRole == m.ROLE_EDITOR {
handlers = append(handlers, middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN))
}
}
handlers = append(handlers, ExternalPlugin(route.Url))
r.Route(url, route.Method, handlers...)
log.Info("Plugin: Adding route %s", url)
}
}
}
func ExternalPlugin(routeUrl string) macaron.Handler {
return func(c *middleware.Context) {
path := c.Params("*")
//Create a HTTP header with the context in it.
ctx, err := json.Marshal(c.SignedInUser)
if err != nil {
c.JsonApiErr(500, "failed to marshal context to json.", err)
return
}
targetUrl, _ := url.Parse(routeUrl)
proxy := NewExternalPluginProxy(string(ctx), path, targetUrl)
proxy.Transport = dataProxyTransport
proxy.ServeHTTP(c.RW(), c.Req.Request)
}
}
func NewExternalPluginProxy(ctx string, proxyPath string, targetUrl *url.URL) *httputil.ReverseProxy {
director := func(req *http.Request) {
req.URL.Scheme = targetUrl.Scheme
req.URL.Host = targetUrl.Host
req.Host = targetUrl.Host
req.URL.Path = util.JoinUrlFragments(targetUrl.Path, proxyPath)
// clear cookie headers
req.Header.Del("Cookie")
req.Header.Del("Set-Cookie")
req.Header.Add("Grafana-Context", ctx)
}
return &httputil.ReverseProxy{Director: director}
}
...@@ -29,13 +29,6 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro ...@@ -29,13 +29,6 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
datasources := make(map[string]interface{}) datasources := make(map[string]interface{})
var defaultDatasource string var defaultDatasource string
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId}
err := bus.Dispatch(&orgBundles)
if err != nil {
return nil, err
}
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
for _, ds := range orgDataSources { for _, ds := range orgDataSources {
url := ds.Url url := ds.Url
...@@ -49,7 +42,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro ...@@ -49,7 +42,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
"url": url, "url": url,
} }
meta, exists := enabledPlugins.DataSourcePlugins[ds.Type] meta, exists := plugins.DataSources[ds.Type]
if !exists { if !exists {
log.Error(3, "Could not find plugin definition for data source: %v", ds.Type) log.Error(3, "Could not find plugin definition for data source: %v", ds.Type)
continue continue
...@@ -117,7 +110,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro ...@@ -117,7 +110,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
} }
panels := map[string]interface{}{} panels := map[string]interface{}{}
for _, panel := range enabledPlugins.PanelPlugins { for _, panel := range plugins.Panels {
panels[panel.Type] = map[string]interface{}{ panels[panel.Type] = map[string]interface{}{
"module": panel.Module, "module": panel.Module,
"name": panel.Name, "name": panel.Name,
......
...@@ -2,10 +2,8 @@ package api ...@@ -2,10 +2,8 @@ package api
import ( import (
"github.com/grafana/grafana/pkg/api/dtos" "github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware" "github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models" m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
...@@ -60,53 +58,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) { ...@@ -60,53 +58,9 @@ func setIndexViewData(c *middleware.Context) (*dtos.IndexViewData, error) {
Text: "Data Sources", Text: "Data Sources",
Icon: "fa fa-fw fa-database", Icon: "fa fa-fw fa-database",
Href: "/datasources", Href: "/datasources",
}, &dtos.NavLink{
Text: "Plugins",
Icon: "fa fa-fw fa-cubes",
Href: "/plugins",
}) })
} }
orgBundles := m.GetPluginBundlesQuery{OrgId: c.OrgId}
err = bus.Dispatch(&orgBundles)
if err != nil {
return nil, err
}
enabledPlugins := plugins.GetEnabledPlugins(orgBundles.Result)
for _, plugin := range enabledPlugins.ExternalPlugins {
for _, js := range plugin.Js {
data.PluginJs = append(data.PluginJs, js.Module)
}
for _, css := range plugin.Css {
data.PluginCss = append(data.PluginCss, &dtos.PluginCss{Light: css.Light, Dark: css.Dark})
}
for _, item := range plugin.MainNavLinks {
// only show menu items for the specified roles.
var validRoles []m.RoleType
if string(item.ReqRole) == "" || item.ReqRole == m.ROLE_VIEWER {
validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR, m.ROLE_VIEWER}
} else if item.ReqRole == m.ROLE_EDITOR {
validRoles = []m.RoleType{m.ROLE_ADMIN, m.ROLE_EDITOR}
} else if item.ReqRole == m.ROLE_ADMIN {
validRoles = []m.RoleType{m.ROLE_ADMIN}
}
ok := true
if len(validRoles) > 0 {
ok = false
for _, role := range validRoles {
if role == c.OrgRole {
ok = true
break
}
}
}
if ok {
data.MainNavLinks = append(data.MainNavLinks, &dtos.NavLink{Text: item.Text, Href: item.Href, Icon: item.Icon})
}
}
}
return &data, nil return &data, nil
} }
......
package api
import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
)
func GetPluginBundles(c *middleware.Context) Response {
query := m.GetPluginBundlesQuery{OrgId: c.OrgId}
if err := bus.Dispatch(&query); err != nil {
return ApiError(500, "Failed to list Plugin Bundles", err)
}
installedBundlesMap := make(map[string]*dtos.PluginBundle)
for t, b := range plugins.Bundles {
installedBundlesMap[t] = &dtos.PluginBundle{
Type: b.Type,
Enabled: b.Enabled,
Module: b.Module,
JsonData: make(map[string]interface{}),
}
}
seenBundles := make(map[string]bool)
result := make([]*dtos.PluginBundle, 0)
for _, b := range query.Result {
if def, ok := installedBundlesMap[b.Type]; ok {
result = append(result, &dtos.PluginBundle{
Type: b.Type,
Enabled: b.Enabled,
Module: def.Module,
JsonData: b.JsonData,
})
seenBundles[b.Type] = true
}
}
for t, b := range installedBundlesMap {
if _, ok := seenBundles[t]; !ok {
result = append(result, b)
}
}
return Json(200, result)
}
func UpdatePluginBundle(c *middleware.Context, cmd m.UpdatePluginBundleCmd) Response {
cmd.OrgId = c.OrgId
if _, ok := plugins.Bundles[cmd.Type]; !ok {
return ApiError(404, "Bundle type not installed.", nil)
}
err := bus.Dispatch(&cmd)
if err != nil {
return ApiError(500, "Failed to update plugin bundle", err)
}
return ApiSuccess("Plugin updated")
}
package plugins package plugins
import "github.com/grafana/grafana/pkg/models"
type DataSourcePlugin struct { type DataSourcePlugin struct {
Type string `json:"type"` Type string `json:"type"`
Name string `json:"name"` Name string `json:"name"`
...@@ -26,60 +24,3 @@ type StaticRootConfig struct { ...@@ -26,60 +24,3 @@ type StaticRootConfig struct {
Url string `json:"url"` Url string `json:"url"`
Path string `json:"path"` Path string `json:"path"`
} }
type ExternalPluginRoute struct {
Path string `json:"path"`
Method string `json:"method"`
ReqSignedIn bool `json:"reqSignedIn"`
ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"`
ReqRole models.RoleType `json:"reqRole"`
Url string `json:"url"`
}
type ExternalPluginJs struct {
Module string `json:"module"`
}
type ExternalPluginNavLink struct {
Text string `json:"text"`
Icon string `json:"icon"`
Href string `json:"href"`
ReqRole models.RoleType `json:"reqRole"`
}
type ExternalPluginCss struct {
Light string `json:"light"`
Dark string `json:"dark"`
}
type ExternalPlugin struct {
Type string `json:"type"`
Routes []*ExternalPluginRoute `json:"routes"`
Js []*ExternalPluginJs `json:"js"`
Css []*ExternalPluginCss `json:"css"`
MainNavLinks []*ExternalPluginNavLink `json:"mainNavLinks"`
StaticRootConfig *StaticRootConfig `json:"staticRoot"`
}
type PluginBundle struct {
Type string `json:"type"`
Enabled bool `json:"enabled"`
PanelPlugins []string `json:"panelPlugins"`
DatasourcePlugins []string `json:"datasourcePlugins"`
ExternalPlugins []string `json:"externalPlugins"`
Module string `json:"module"`
}
type EnabledPlugins struct {
PanelPlugins []*PanelPlugin
DataSourcePlugins map[string]*DataSourcePlugin
ExternalPlugins []*ExternalPlugin
}
func NewEnabledPlugins() EnabledPlugins {
return EnabledPlugins{
PanelPlugins: make([]*PanelPlugin, 0),
DataSourcePlugins: make(map[string]*DataSourcePlugin),
ExternalPlugins: make([]*ExternalPlugin, 0),
}
}
...@@ -9,16 +9,13 @@ import ( ...@@ -9,16 +9,13 @@ import (
"strings" "strings"
"github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
var ( var (
DataSources map[string]DataSourcePlugin DataSources map[string]DataSourcePlugin
Panels map[string]PanelPlugin Panels map[string]PanelPlugin
ExternalPlugins map[string]ExternalPlugin
StaticRoutes []*StaticRootConfig StaticRoutes []*StaticRootConfig
Bundles map[string]PluginBundle
) )
type PluginScanner struct { type PluginScanner struct {
...@@ -28,37 +25,14 @@ type PluginScanner struct { ...@@ -28,37 +25,14 @@ type PluginScanner struct {
func Init() error { func Init() error {
DataSources = make(map[string]DataSourcePlugin) DataSources = make(map[string]DataSourcePlugin)
ExternalPlugins = make(map[string]ExternalPlugin)
StaticRoutes = make([]*StaticRootConfig, 0) StaticRoutes = make([]*StaticRootConfig, 0)
Panels = make(map[string]PanelPlugin) Panels = make(map[string]PanelPlugin)
Bundles = make(map[string]PluginBundle)
scan(path.Join(setting.StaticRootPath, "app/plugins")) scan(path.Join(setting.StaticRootPath, "app/plugins"))
checkExternalPluginPaths() checkExternalPluginPaths()
checkDependencies()
return nil return nil
} }
func checkDependencies() {
for bundleType, bundle := range Bundles {
for _, reqPanel := range bundle.PanelPlugins {
if _, ok := Panels[reqPanel]; !ok {
log.Fatal(4, "Bundle %s requires Panel type %s, but it is not present.", bundleType, reqPanel)
}
}
for _, reqDataSource := range bundle.DatasourcePlugins {
if _, ok := DataSources[reqDataSource]; !ok {
log.Fatal(4, "Bundle %s requires DataSource type %s, but it is not present.", bundleType, reqDataSource)
}
}
for _, reqExtPlugin := range bundle.ExternalPlugins {
if _, ok := ExternalPlugins[reqExtPlugin]; !ok {
log.Fatal(4, "Bundle %s requires DataSource type %s, but it is not present.", bundleType, reqExtPlugin)
}
}
}
}
func checkExternalPluginPaths() error { func checkExternalPluginPaths() error {
for _, section := range setting.Cfg.Sections() { for _, section := range setting.Cfg.Sections() {
if strings.HasPrefix(section.Name(), "plugin.") { if strings.HasPrefix(section.Name(), "plugin.") {
...@@ -165,66 +139,5 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error { ...@@ -165,66 +139,5 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
addStaticRoot(p.StaticRootConfig, currentDir) addStaticRoot(p.StaticRootConfig, currentDir)
} }
if pluginType == "external" {
p := ExternalPlugin{}
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")
}
ExternalPlugins[p.Type] = p
addStaticRoot(p.StaticRootConfig, currentDir)
}
if pluginType == "bundle" {
p := PluginBundle{}
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")
}
Bundles[p.Type] = p
}
return nil return nil
} }
func GetEnabledPlugins(orgBundles []*models.PluginBundle) EnabledPlugins {
enabledPlugins := NewEnabledPlugins()
orgBundlesMap := make(map[string]*models.PluginBundle)
for _, orgBundle := range orgBundles {
orgBundlesMap[orgBundle.Type] = orgBundle
}
for bundleType, bundle := range Bundles {
enabled := bundle.Enabled
// check if the bundle is stored in the DB.
if b, ok := orgBundlesMap[bundleType]; ok {
enabled = b.Enabled
}
if enabled {
for _, d := range bundle.DatasourcePlugins {
if ds, ok := DataSources[d]; ok {
enabledPlugins.DataSourcePlugins[d] = &ds
}
}
for _, p := range bundle.PanelPlugins {
if panel, ok := Panels[p]; ok {
enabledPlugins.PanelPlugins = append(enabledPlugins.PanelPlugins, &panel)
}
}
for _, e := range bundle.ExternalPlugins {
if external, ok := ExternalPlugins[e]; ok {
enabledPlugins.ExternalPlugins = append(enabledPlugins.ExternalPlugins, &external)
}
}
}
}
return enabledPlugins
}
Example app is available at https://github.com/raintank/grafana-plugin-example
* Clone plugin repo git@github.com:raintank/grafana-plugin-example.git
* Modify grafana.ini (or custom.ini if your developing Grafana locally)
```ini
[plugin.external-test]
path = /<the_path_were_you_cloned_it>/grafana-plugin-example
```
{
"pluginType": "bundle",
"type": "core",
"module": "",
"enabled": true,
"panelPlugins": ["graph", "singlestat", "text", "dashlist", "table"],
"datasourcePlugins": ["mixed", "grafana", "graphite", "cloudwatch", "elasticsearch", "influxdb", "influxdb_08", "kairosdb", "opentsdb", "prometheus"],
"externalPlugins": []
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment