mirror of
https://github.com/grafana/grafana.git
synced 2025-07-31 09:02:13 +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
|
package plugins
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/grafana/grafana/pkg/models"
|
"github.com/grafana/grafana/pkg/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PluginCommon struct {
|
type PluginLoader interface {
|
||||||
Type string `json:"type"`
|
Load(decoder *json.Decoder, pluginDir string) error
|
||||||
Name string `json:"name"`
|
}
|
||||||
Id string `json:"id"`
|
|
||||||
StaticRoot string `json:"staticRoot"`
|
type PluginBase struct {
|
||||||
Info PluginInfo `json:"info"`
|
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 {
|
type PluginInfo struct {
|
||||||
@ -29,30 +36,11 @@ type PluginLogos struct {
|
|||||||
Large string `json:"large"`
|
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 {
|
type PluginStaticRoute struct {
|
||||||
Directory string
|
Directory string
|
||||||
PluginId string
|
PluginId string
|
||||||
}
|
}
|
||||||
|
|
||||||
type PanelPlugin struct {
|
|
||||||
PluginCommon
|
|
||||||
Module string `json:"module"`
|
|
||||||
App string `json:"app"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApiPluginRoute struct {
|
type ApiPluginRoute struct {
|
||||||
Path string `json:"path"`
|
Path string `json:"path"`
|
||||||
Method string `json:"method"`
|
Method string `json:"method"`
|
||||||
@ -60,34 +48,11 @@ type ApiPluginRoute struct {
|
|||||||
ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"`
|
ReqGrafanaAdmin bool `json:"reqGrafanaAdmin"`
|
||||||
ReqRole models.RoleType `json:"reqRole"`
|
ReqRole models.RoleType `json:"reqRole"`
|
||||||
Url string `json:"url"`
|
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 {
|
type ApiPlugin struct {
|
||||||
PluginCommon
|
PluginBase
|
||||||
Routes []*ApiPluginRoute `json:"routes"`
|
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 {
|
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"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ var (
|
|||||||
ApiPlugins map[string]*ApiPlugin
|
ApiPlugins map[string]*ApiPlugin
|
||||||
StaticRoutes []*PluginStaticRoute
|
StaticRoutes []*PluginStaticRoute
|
||||||
Apps map[string]*AppPlugin
|
Apps map[string]*AppPlugin
|
||||||
|
PluginTypes map[string]interface{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type PluginScanner struct {
|
type PluginScanner struct {
|
||||||
@ -37,6 +38,12 @@ func Init() error {
|
|||||||
StaticRoutes = make([]*PluginStaticRoute, 0)
|
StaticRoutes = make([]*PluginStaticRoute, 0)
|
||||||
Panels = make(map[string]*PanelPlugin)
|
Panels = make(map[string]*PanelPlugin)
|
||||||
Apps = make(map[string]*AppPlugin)
|
Apps = make(map[string]*AppPlugin)
|
||||||
|
PluginTypes = map[string]interface{}{
|
||||||
|
"panel": PanelPlugin{},
|
||||||
|
"datasource": DataSourcePlugin{},
|
||||||
|
"api": ApiPlugin{},
|
||||||
|
"app": AppPlugin{},
|
||||||
|
}
|
||||||
|
|
||||||
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
scan(path.Join(setting.StaticRootPath, "app/plugins"))
|
||||||
checkPluginPaths()
|
checkPluginPaths()
|
||||||
@ -115,27 +122,7 @@ func (scanner *PluginScanner) walker(currentPath string, f os.FileInfo, err erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalRelativePluginUrlPath(pathStr string, pluginId string) string {
|
func interpolatePluginJson(reader io.Reader, pluginCommon *PluginBase) (io.Reader, error) {
|
||||||
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) {
|
|
||||||
buf := new(bytes.Buffer)
|
buf := new(bytes.Buffer)
|
||||||
buf.ReadFrom(reader)
|
buf.ReadFrom(reader)
|
||||||
jsonStr := buf.String() //
|
jsonStr := buf.String() //
|
||||||
@ -167,7 +154,7 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
|||||||
defer reader.Close()
|
defer reader.Close()
|
||||||
|
|
||||||
jsonParser := json.NewDecoder(reader)
|
jsonParser := json.NewDecoder(reader)
|
||||||
pluginCommon := PluginCommon{}
|
pluginCommon := PluginBase{}
|
||||||
if err := jsonParser.Decode(&pluginCommon); err != nil {
|
if err := jsonParser.Decode(&pluginCommon); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -177,52 +164,21 @@ func (scanner *PluginScanner) loadPluginJson(pluginJsonFilePath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reader.Seek(0, 0)
|
reader.Seek(0, 0)
|
||||||
|
|
||||||
if newReader, err := interpolatePluginJson(reader, &pluginCommon); err != nil {
|
if newReader, err := interpolatePluginJson(reader, &pluginCommon); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
jsonParser = json.NewDecoder(newReader)
|
jsonParser = json.NewDecoder(newReader)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch pluginCommon.Type {
|
var loader PluginLoader
|
||||||
case "datasource":
|
|
||||||
p := DataSourcePlugin{}
|
|
||||||
if err := jsonParser.Decode(&p); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
DataSources[p.Id] = &p
|
if pluginGoType, exists := PluginTypes[pluginCommon.Type]; !exists {
|
||||||
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:
|
|
||||||
return errors.New("Unkown plugin type " + pluginCommon.Type)
|
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 {
|
func GetEnabledPlugins(orgApps []*models.AppPlugin) EnabledPlugins {
|
||||||
|
@ -19,6 +19,10 @@ func TestPluginScans(t *testing.T) {
|
|||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
So(len(DataSources), ShouldBeGreaterThan, 1)
|
So(len(DataSources), ShouldBeGreaterThan, 1)
|
||||||
So(len(Panels), 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() {
|
Convey("When reading app plugin definition", t, func() {
|
||||||
|
@ -3,13 +3,6 @@
|
|||||||
"name": "CloudWatch",
|
"name": "CloudWatch",
|
||||||
"id": "cloudwatch",
|
"id": "cloudwatch",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/cloudwatch/module",
|
|
||||||
|
|
||||||
"partials": {
|
|
||||||
"config": "app/plugins/datasource/cloudwatch/partials/config.html",
|
|
||||||
"query": "app/plugins/datasource/cloudwatch/partials/query.editor.html"
|
|
||||||
},
|
|
||||||
|
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
"annotations": true
|
"annotations": true
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
"name": "Elasticsearch",
|
"name": "Elasticsearch",
|
||||||
"id": "elasticsearch",
|
"id": "elasticsearch",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/elasticsearch/module",
|
|
||||||
|
|
||||||
"defaultMatchFormat": "lucene",
|
"defaultMatchFormat": "lucene",
|
||||||
"annotations": true,
|
"annotations": true,
|
||||||
"metrics": true
|
"metrics": true
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
"name": "Grafana",
|
"name": "Grafana",
|
||||||
"id": "grafana",
|
"id": "grafana",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/grafana/module",
|
|
||||||
|
|
||||||
"builtIn": true,
|
"builtIn": true,
|
||||||
"metrics": true
|
"metrics": true
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,10 @@ function (angular, GraphiteDatasource) {
|
|||||||
return {templateUrl: 'app/plugins/datasource/graphite/partials/annotations.editor.html'};
|
return {templateUrl: 'app/plugins/datasource/graphite/partials/annotations.editor.html'};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.directive('datasourceCustomSettingsViewGraphite', function() {
|
||||||
|
return {templateUrl: 'app/plugins/datasource/graphite/partials/config.html'};
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Datasource: GraphiteDatasource,
|
Datasource: GraphiteDatasource,
|
||||||
};
|
};
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
"type": "datasource",
|
"type": "datasource",
|
||||||
"id": "graphite",
|
"id": "graphite",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/graphite/module",
|
|
||||||
|
|
||||||
"defaultMatchFormat": "glob",
|
"defaultMatchFormat": "glob",
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
"annotations": true
|
"annotations": true
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
"name": "InfluxDB 0.9.x",
|
"name": "InfluxDB 0.9.x",
|
||||||
"id": "influxdb",
|
"id": "influxdb",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/influxdb/module",
|
|
||||||
|
|
||||||
"defaultMatchFormat": "regex values",
|
"defaultMatchFormat": "regex values",
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
"annotations": true
|
"annotations": true
|
||||||
|
@ -5,9 +5,5 @@
|
|||||||
|
|
||||||
"builtIn": true,
|
"builtIn": true,
|
||||||
"mixed": true,
|
"mixed": true,
|
||||||
|
|
||||||
"serviceName": "MixedDatasource",
|
|
||||||
|
|
||||||
"module": "app/plugins/datasource/mixed/datasource",
|
|
||||||
"metrics": true
|
"metrics": true
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
"name": "OpenTSDB",
|
"name": "OpenTSDB",
|
||||||
"id": "opentsdb",
|
"id": "opentsdb",
|
||||||
|
|
||||||
"module": "app/plugins/datasource/opentsdb/module",
|
|
||||||
|
|
||||||
"metrics": true,
|
"metrics": true,
|
||||||
"defaultMatchFormat": "pipe"
|
"defaultMatchFormat": "pipe"
|
||||||
}
|
}
|
||||||
|
3
public/app/plugins/datasource/prometheus/datasource.d.ts
vendored
Normal file
3
public/app/plugins/datasource/prometheus/datasource.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
declare var Datasource: any;
|
||||||
|
export default Datasource;
|
||||||
|
|
@ -3,31 +3,25 @@ define([
|
|||||||
'lodash',
|
'lodash',
|
||||||
'moment',
|
'moment',
|
||||||
'app/core/utils/datemath',
|
'app/core/utils/datemath',
|
||||||
'./directives',
|
|
||||||
'./query_ctrl',
|
'./query_ctrl',
|
||||||
],
|
],
|
||||||
function (angular, _, moment, dateMath) {
|
function (angular, _, moment, dateMath) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var module = angular.module('grafana.services');
|
|
||||||
|
|
||||||
var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
|
var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
|
||||||
|
|
||||||
module.factory('PrometheusDatasource', function($q, backendSrv, templateSrv) {
|
function PrometheusDatasource(instanceSettings, $q, backendSrv, templateSrv) {
|
||||||
|
this.type = 'prometheus';
|
||||||
|
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
||||||
|
this.name = instanceSettings.name;
|
||||||
|
this.supportMetrics = true;
|
||||||
|
this.url = instanceSettings.url;
|
||||||
|
this.directUrl = instanceSettings.directUrl;
|
||||||
|
this.basicAuth = instanceSettings.basicAuth;
|
||||||
|
this.withCredentials = instanceSettings.withCredentials;
|
||||||
|
this.lastErrors = {};
|
||||||
|
|
||||||
function PrometheusDatasource(datasource) {
|
this._request = function(method, url) {
|
||||||
this.type = 'prometheus';
|
|
||||||
this.editorSrc = 'app/features/prometheus/partials/query.editor.html';
|
|
||||||
this.name = datasource.name;
|
|
||||||
this.supportMetrics = true;
|
|
||||||
this.url = datasource.url;
|
|
||||||
this.directUrl = datasource.directUrl;
|
|
||||||
this.basicAuth = datasource.basicAuth;
|
|
||||||
this.withCredentials = datasource.withCredentials;
|
|
||||||
this.lastErrors = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
PrometheusDatasource.prototype._request = function(method, url) {
|
|
||||||
var options = {
|
var options = {
|
||||||
url: this.url + url,
|
url: this.url + url,
|
||||||
method: method
|
method: method
|
||||||
@ -46,7 +40,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Called once per panel (graph)
|
// Called once per panel (graph)
|
||||||
PrometheusDatasource.prototype.query = function(options) {
|
this.query = function(options) {
|
||||||
var start = getPrometheusTime(options.range.from, false);
|
var start = getPrometheusTime(options.range.from, false);
|
||||||
var end = getPrometheusTime(options.range.to, true);
|
var end = getPrometheusTime(options.range.to, true);
|
||||||
|
|
||||||
@ -86,31 +80,31 @@ function (angular, _, moment, dateMath) {
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
return $q.all(allQueryPromise)
|
return $q.all(allQueryPromise)
|
||||||
.then(function(allResponse) {
|
.then(function(allResponse) {
|
||||||
var result = [];
|
var result = [];
|
||||||
|
|
||||||
_.each(allResponse, function(response, index) {
|
_.each(allResponse, function(response, index) {
|
||||||
if (response.status === 'error') {
|
if (response.status === 'error') {
|
||||||
self.lastErrors.query = response.error;
|
self.lastErrors.query = response.error;
|
||||||
throw response.error;
|
throw response.error;
|
||||||
}
|
}
|
||||||
delete self.lastErrors.query;
|
delete self.lastErrors.query;
|
||||||
|
|
||||||
_.each(response.data.data.result, function(metricData) {
|
_.each(response.data.data.result, function(metricData) {
|
||||||
result.push(transformMetricData(metricData, options.targets[index]));
|
result.push(transformMetricData(metricData, options.targets[index]));
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { data: result };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return { data: result };
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
PrometheusDatasource.prototype.performTimeSeriesQuery = function(query, start, end) {
|
this.performTimeSeriesQuery = function(query, start, end) {
|
||||||
var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
|
var url = '/api/v1/query_range?query=' + encodeURIComponent(query.expr) + '&start=' + start + '&end=' + end + '&step=' + query.step;
|
||||||
return this._request('GET', url);
|
return this._request('GET', url);
|
||||||
};
|
};
|
||||||
|
|
||||||
PrometheusDatasource.prototype.performSuggestQuery = function(query) {
|
this.performSuggestQuery = function(query) {
|
||||||
var url = '/api/v1/label/__name__/values';
|
var url = '/api/v1/label/__name__/values';
|
||||||
|
|
||||||
return this._request('GET', url).then(function(result) {
|
return this._request('GET', url).then(function(result) {
|
||||||
@ -120,7 +114,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
PrometheusDatasource.prototype.metricFindQuery = function(query) {
|
this.metricFindQuery = function(query) {
|
||||||
if (!query) { return $q.when([]); }
|
if (!query) { return $q.when([]); }
|
||||||
|
|
||||||
var interpolated;
|
var interpolated;
|
||||||
@ -196,7 +190,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
PrometheusDatasource.prototype.testDatasource = function() {
|
this.testDatasource = function() {
|
||||||
return this.metricFindQuery('metrics(.*)').then(function() {
|
return this.metricFindQuery('metrics(.*)').then(function() {
|
||||||
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
return { status: 'success', message: 'Data source is working', title: 'Success' };
|
||||||
});
|
});
|
||||||
@ -276,8 +270,7 @@ function (angular, _, moment, dateMath) {
|
|||||||
}
|
}
|
||||||
return (date.valueOf() / 1000).toFixed(0);
|
return (date.valueOf() / 1000).toFixed(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PrometheusDatasource;
|
return PrometheusDatasource;
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
define([
|
define([
|
||||||
'angular',
|
'angular',
|
||||||
|
'./datasource',
|
||||||
],
|
],
|
||||||
function (angular) {
|
function (angular, PromDatasource) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var module = angular.module('grafana.directives');
|
var module = angular.module('grafana.directives');
|
||||||
@ -10,4 +11,11 @@ function (angular) {
|
|||||||
return {controller: 'PrometheusQueryCtrl', templateUrl: 'app/plugins/datasource/prometheus/partials/query.editor.html'};
|
return {controller: 'PrometheusQueryCtrl', templateUrl: 'app/plugins/datasource/prometheus/partials/query.editor.html'};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module.directive('datasourceCustomSettingsViewPrometheus', function() {
|
||||||
|
return {templateUrl: 'app/plugins/datasource/prometheus/partials/config.html'};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
Datasource: PromDatasource
|
||||||
|
};
|
||||||
});
|
});
|
@ -3,13 +3,5 @@
|
|||||||
"name": "Prometheus",
|
"name": "Prometheus",
|
||||||
"id": "prometheus",
|
"id": "prometheus",
|
||||||
|
|
||||||
"serviceName": "PrometheusDatasource",
|
|
||||||
|
|
||||||
"module": "app/plugins/datasource/prometheus/datasource",
|
|
||||||
|
|
||||||
"partials": {
|
|
||||||
"config": "app/plugins/datasource/prometheus/partials/config.html"
|
|
||||||
},
|
|
||||||
|
|
||||||
"metrics": true
|
"metrics": true
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
import '../datasource';
|
|
||||||
import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
|
import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import helpers from 'test/specs/helpers';
|
import helpers from 'test/specs/helpers';
|
||||||
|
import Datasource from '../datasource';
|
||||||
|
|
||||||
describe('PrometheusDatasource', function() {
|
describe('PrometheusDatasource', function() {
|
||||||
|
|
||||||
var ctx = new helpers.ServiceTestContext();
|
var ctx = new helpers.ServiceTestContext();
|
||||||
|
var instanceSettings = {url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp' };
|
||||||
|
|
||||||
beforeEach(angularMocks.module('grafana.core'));
|
beforeEach(angularMocks.module('grafana.core'));
|
||||||
beforeEach(angularMocks.module('grafana.services'));
|
beforeEach(angularMocks.module('grafana.services'));
|
||||||
beforeEach(ctx.createService('PrometheusDatasource'));
|
beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
|
||||||
beforeEach(function() {
|
ctx.$q = $q;
|
||||||
ctx.ds = new ctx.service({ url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp' });
|
ctx.$httpBackend = $httpBackend;
|
||||||
});
|
ctx.$rootScope = $rootScope;
|
||||||
|
ctx.ds = $injector.instantiate(Datasource, {instanceSettings: instanceSettings});
|
||||||
|
}));
|
||||||
|
|
||||||
describe('When querying prometheus with one target using query editor target spec', function() {
|
describe('When querying prometheus with one target using query editor target spec', function() {
|
||||||
var results;
|
var results;
|
||||||
|
Reference in New Issue
Block a user