mirror of
https://github.com/grafana/grafana.git
synced 2025-01-07 14:43:42 -06:00
Began work on plugin system
This commit is contained in:
parent
011fdf7ab6
commit
5bd5713a52
@ -24,6 +24,7 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||
}
|
||||
|
||||
datasources := make(map[string]interface{})
|
||||
var defaultDatasource string
|
||||
|
||||
for _, ds := range orgDataSources {
|
||||
url := ds.Url
|
||||
@ -33,9 +34,12 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||
}
|
||||
|
||||
var dsMap = map[string]interface{}{
|
||||
"type": ds.Type,
|
||||
"url": url,
|
||||
"default": ds.IsDefault,
|
||||
"type": ds.Type,
|
||||
"url": url,
|
||||
}
|
||||
|
||||
if ds.IsDefault {
|
||||
defaultDatasource = ds.Name
|
||||
}
|
||||
|
||||
if ds.Type == m.DS_INFLUXDB_08 {
|
||||
@ -69,8 +73,9 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro
|
||||
}
|
||||
|
||||
jsonObj := map[string]interface{}{
|
||||
"datasources": datasources,
|
||||
"appSubUrl": setting.AppSubUrl,
|
||||
"defaultDatasource": defaultDatasource,
|
||||
"datasources": datasources,
|
||||
"appSubUrl": setting.AppSubUrl,
|
||||
"buildInfo": map[string]interface{}{
|
||||
"version": setting.BuildVersion,
|
||||
"commit": setting.BuildCommit,
|
||||
|
@ -83,6 +83,8 @@ func importDashboard(path string, orgId int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
dash := m.NewDashboard("temp")
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
|
||||
|
81
pkg/plugins/plugins.go
Normal file
81
pkg/plugins/plugins.go
Normal file
@ -0,0 +1,81 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/grafana/grafana/pkg/log"
|
||||
)
|
||||
|
||||
type PluginMeta struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
var (
|
||||
List []*PluginMeta
|
||||
)
|
||||
|
||||
type PluginScanner struct {
|
||||
pluginPath string
|
||||
errors []error
|
||||
}
|
||||
|
||||
func Scan(pluginDir string) error {
|
||||
List = make([]*PluginMeta, 0)
|
||||
|
||||
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(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.Name() == "plugin.json" {
|
||||
pluginMeta, err := loadPluginMeta(path)
|
||||
if err != nil {
|
||||
log.Error(3, "Failed to load plugin json file: %v, err: %v", path, err)
|
||||
scanner.errors = append(scanner.errors, err)
|
||||
} else {
|
||||
List = append(List, pluginMeta)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func loadPluginMeta(path string) (*PluginMeta, error) {
|
||||
reader, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer reader.Close()
|
||||
|
||||
jsonParser := json.NewDecoder(reader)
|
||||
|
||||
pluginMeta := &PluginMeta{}
|
||||
if err := jsonParser.Decode(pluginMeta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return pluginMeta, nil
|
||||
}
|
19
pkg/plugins/plugins_test.go
Normal file
19
pkg/plugins/plugins_test.go
Normal file
@ -0,0 +1,19 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestPluginScans(t *testing.T) {
|
||||
|
||||
Convey("When scaning for plugins", t, func() {
|
||||
path, _ := filepath.Abs("../../src/app/plugins")
|
||||
err := Scan(path)
|
||||
|
||||
So(err, ShouldBeNil)
|
||||
So(len(List), ShouldEqual, 1)
|
||||
})
|
||||
}
|
@ -8,14 +8,13 @@ function (angular, _, config) {
|
||||
|
||||
var module = angular.module('grafana.controllers');
|
||||
|
||||
module.controller('SearchCtrl', function($scope, $rootScope, $element, $location, datasourceSrv, $timeout) {
|
||||
module.controller('SearchCtrl', function($scope, $location, $timeout, backendSrv) {
|
||||
|
||||
$scope.init = function() {
|
||||
$scope.giveSearchFocus = 0;
|
||||
$scope.selectedIndex = -1;
|
||||
$scope.results = {dashboards: [], tags: [], metrics: []};
|
||||
$scope.query = { query: '', tag: '', starred: false };
|
||||
$scope.db = datasourceSrv.getGrafanaDB();
|
||||
$scope.currentSearchId = 0;
|
||||
|
||||
$timeout(function() {
|
||||
@ -61,21 +60,20 @@ function (angular, _, config) {
|
||||
$scope.currentSearchId = $scope.currentSearchId + 1;
|
||||
var localSearchId = $scope.currentSearchId;
|
||||
|
||||
return $scope.db.searchDashboards($scope.query)
|
||||
.then(function(results) {
|
||||
if (localSearchId < $scope.currentSearchId) { return; }
|
||||
return backendSrv.get('/api/search', $scope.query).then(function(results) {
|
||||
if (localSearchId < $scope.currentSearchId) { return; }
|
||||
|
||||
$scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length;
|
||||
$scope.results.tags = results.tags;
|
||||
$scope.results.dashboards = _.map(results.dashboards, function(dash) {
|
||||
dash.url = 'dashboard/db/' + dash.slug;
|
||||
return dash;
|
||||
});
|
||||
|
||||
if ($scope.queryHasNoFilters()) {
|
||||
$scope.results.dashboards.unshift({ title: 'Home', url: config.appSubUrl + '/', isHome: true });
|
||||
}
|
||||
$scope.resultCount = results.tagsOnly ? results.tags.length : results.dashboards.length;
|
||||
$scope.results.tags = results.tags;
|
||||
$scope.results.dashboards = _.map(results.dashboards, function(dash) {
|
||||
dash.url = 'dashboard/db/' + dash.slug;
|
||||
return dash;
|
||||
});
|
||||
|
||||
if ($scope.queryHasNoFilters()) {
|
||||
$scope.results.dashboards.unshift({ title: 'Home', url: config.appSubUrl + '/', isHome: true });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$scope.queryHasNoFilters = function() {
|
||||
@ -118,13 +116,13 @@ function (angular, _, config) {
|
||||
height: '250px',
|
||||
editable: true,
|
||||
panels: [
|
||||
{
|
||||
type: 'graphite',
|
||||
title: 'test',
|
||||
span: 12,
|
||||
targets: [{ target: metricId }]
|
||||
}
|
||||
]
|
||||
{
|
||||
type: 'graphite',
|
||||
title: 'test',
|
||||
span: 12,
|
||||
targets: [{ target: metricId }]
|
||||
}
|
||||
]
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -2,11 +2,11 @@ define([
|
||||
'./panellinkeditor/module',
|
||||
'./annotations/annotationsSrv',
|
||||
'./templating/templateSrv',
|
||||
'./graphite/datasource',
|
||||
'./influxdb/datasource',
|
||||
'./influxdb_08/datasource',
|
||||
'./opentsdb/datasource',
|
||||
'./elasticsearch/datasource',
|
||||
// './graphite/datasource',
|
||||
// './influxdb/datasource',
|
||||
// './influxdb_08/datasource',
|
||||
// './opentsdb/datasource',
|
||||
// './elasticsearch/datasource',
|
||||
'./dashboard/all',
|
||||
'./panel/all',
|
||||
'./profile/profileCtrl',
|
||||
|
@ -94,8 +94,8 @@ function (angular, _, config) {
|
||||
$scope.fullscreen = false;
|
||||
$scope.editor = { index: 1 };
|
||||
|
||||
$scope.datasources = datasourceSrv.getMetricSources();
|
||||
$scope.setDatasource($scope.panel.datasource);
|
||||
// $scope.datasources = datasourceSrv.getMetricSources();
|
||||
// $scope.setDatasource($scope.panel.datasource);
|
||||
$scope.dashboardViewState.registerPanel($scope);
|
||||
|
||||
if ($scope.get_data) {
|
||||
|
@ -23,7 +23,7 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
||||
};
|
||||
});
|
||||
|
||||
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv) {
|
||||
module.controller('GraphCtrl', function($scope, $rootScope, panelSrv, annotationsSrv, timeSrv, datasourceSrv) {
|
||||
|
||||
$scope.panelMeta = new PanelMeta({
|
||||
panelName: 'Graph',
|
||||
@ -183,15 +183,17 @@ function (angular, app, $, _, kbn, moment, TimeSeries, PanelMeta) {
|
||||
|
||||
$scope.annotationsPromise = annotationsSrv.getAnnotations($scope.rangeUnparsed, $scope.dashboard);
|
||||
|
||||
return $scope.datasource.query(metricsQuery)
|
||||
.then($scope.dataHandler)
|
||||
.then(null, function(err) {
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.panelMeta.error = err.message || "Timeseries data request error";
|
||||
$scope.inspector.error = err;
|
||||
$scope.seriesList = [];
|
||||
$scope.render([]);
|
||||
});
|
||||
return datasourceSrv.get($scope.panel.datasource).then(function(ds) {
|
||||
return ds.query(metricsQuery)
|
||||
.then($scope.dataHandler)
|
||||
.then(null, function(err) {
|
||||
$scope.panelMeta.loading = false;
|
||||
$scope.panelMeta.error = err.message || "Timeseries data request error";
|
||||
$scope.inspector.error = err;
|
||||
$scope.seriesList = [];
|
||||
$scope.render([]);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.dataHandler = function(results) {
|
||||
|
20
src/app/plugins/datasources/graphite/module.js
Normal file
20
src/app/plugins/datasources/graphite/module.js
Normal file
@ -0,0 +1,20 @@
|
||||
define([
|
||||
'angular',
|
||||
],
|
||||
function (angular) {
|
||||
'use strict';
|
||||
|
||||
var module = angular.module('grafana.services');
|
||||
|
||||
module.factory('MyDataSource', function() {
|
||||
|
||||
function MyDataSource(datasource) {
|
||||
this.type = 'my_ds';
|
||||
this.datasource = datasource;
|
||||
}
|
||||
|
||||
return MyDataSource;
|
||||
|
||||
});
|
||||
|
||||
});
|
7
src/app/plugins/datasources/graphite/plugin.json
Normal file
7
src/app/plugins/datasources/graphite/plugin.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "datasource",
|
||||
"name": "My Data source",
|
||||
"keyName": "graphite"
|
||||
"serviceName": "MyDataSource",
|
||||
"editPartial": "./partials/edit.html",
|
||||
}
|
@ -7,9 +7,7 @@ function (angular) {
|
||||
|
||||
var module = angular.module('grafana.routes');
|
||||
|
||||
module.controller('DashFromDBProvider', function($scope, datasourceSrv, $routeParams, backendSrv) {
|
||||
|
||||
var db = datasourceSrv.getGrafanaDB();
|
||||
module.controller('DashFromDBProvider', function($scope, $routeParams, backendSrv) {
|
||||
|
||||
if (!$routeParams.id) {
|
||||
backendSrv.get('/api/dashboards/home').then(function(result) {
|
||||
@ -22,9 +20,9 @@ function (angular) {
|
||||
return;
|
||||
}
|
||||
|
||||
db.getDashboard($routeParams.id, false).then(function(result) {
|
||||
return backendSrv.get('/api/dashboards/db/' + $routeParams.id).then(function(result) {
|
||||
$scope.initDashboard(result, $scope);
|
||||
}).then(null, function() {
|
||||
}, function() {
|
||||
$scope.initDashboard({
|
||||
meta: {},
|
||||
model: { title: 'Not found' }
|
||||
|
@ -16,46 +16,22 @@ function (angular, _, config) {
|
||||
'grafana': 'GrafanaDatasource',
|
||||
};
|
||||
|
||||
module.service('datasourceSrv', function($q, $http, $injector) {
|
||||
var plugins = {
|
||||
datasources: {
|
||||
'graphite': {
|
||||
'serviceName': 'GraphiteDatasource',
|
||||
'module': 'features/graphite/datasource'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.service('datasourceSrv', function($q, $injector, $rootScope) {
|
||||
var self = this;
|
||||
|
||||
this.datasources = {};
|
||||
|
||||
this.init = function(dsSettingList) {
|
||||
config.datasources = dsSettingList;
|
||||
|
||||
this.datasources = {};
|
||||
this.metricSources = [];
|
||||
this.annotationSources = [];
|
||||
|
||||
_.each(dsSettingList, function(value, key) {
|
||||
var ds = this.datasourceFactory(value);
|
||||
ds.name = key;
|
||||
if (value.default) {
|
||||
this.default = ds;
|
||||
ds.default = true;
|
||||
}
|
||||
this.datasources[key] = ds;
|
||||
}, this);
|
||||
|
||||
if (!this.default) {
|
||||
this.default = this.datasources[_.keys(this.datasources)[0]];
|
||||
this.default.default = true;
|
||||
}
|
||||
|
||||
// create list of different source types
|
||||
_.each(this.datasources, function(value, key) {
|
||||
if (value.supportMetrics) {
|
||||
this.metricSources.push({
|
||||
name: value.name,
|
||||
value: value.default ? null : key,
|
||||
default: value.default,
|
||||
});
|
||||
}
|
||||
if (value.supportAnnotations) {
|
||||
this.annotationSources.push({ name: key, editorSrc: value.annotationEditorSrc });
|
||||
}
|
||||
if (value.grafanaDB) {
|
||||
this.grafanaDB = value;
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
this.datasourceFactory = function(ds) {
|
||||
@ -65,10 +41,35 @@ function (angular, _, config) {
|
||||
};
|
||||
|
||||
this.get = function(name) {
|
||||
if (!name) { return this.default; }
|
||||
if (this.datasources[name]) { return this.datasources[name]; }
|
||||
if (!name) {
|
||||
return this.get(config.defaultDatasource);
|
||||
}
|
||||
|
||||
return this.default;
|
||||
if (this.datasources[name]) {
|
||||
return $q.when(this.datasources[name]);
|
||||
}
|
||||
|
||||
return this.loadDatasource(name);
|
||||
};
|
||||
|
||||
this.loadDatasource = function(name) {
|
||||
var datasourceConfig = config.datasources[name];
|
||||
var pluginDef = plugins.datasources[datasourceConfig.type];
|
||||
|
||||
if (!pluginDef) {
|
||||
throw { message: "No plugin definition for data source: " + name };
|
||||
}
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
$rootScope.require([pluginDef.module], function() {
|
||||
var AngularService = $injector.get(pluginDef.serviceName);
|
||||
var instance = new AngularService(datasourceConfig);
|
||||
self.datasources[name] = instance;
|
||||
deferred.resolve(instance);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
this.getAll = function() {
|
||||
|
Loading…
Reference in New Issue
Block a user