Merge branch 'master' into alerting_definitions

This commit is contained in:
bergquist 2016-04-22 15:52:56 +02:00
commit ba5978abd3
27 changed files with 144 additions and 81 deletions

View File

@ -1,3 +1,16 @@
# 3.0.0-beta6 (unreleased)
### Enhancements
* **Singlestat**: Support for gauges in singlestat panel. closes [#3688](https://github.com/grafana/grafana/pull/3688)
### Bug fixes
* **InfluxDB 0.12**: Fixed issue templating and `show tag values` query only returning tags for first measurement, fixes [#4726](https://github.com/grafana/grafana/issues/4726)
* **Templating**: Fixed issue with regex formating when matching multiple values, fixes [#4755](https://github.com/grafana/grafana/issues/4755)
* **Templating**: Fixed issue with custom all value and escaping, fixes [#4736](https://github.com/grafana/grafana/issues/4736)
* **Dashlist**: Fixed issue dashboard list panel and caching tags, fixes [#4768](https://github.com/grafana/grafana/issues/4768)
* **Graph**: Fixed issue with unneeded scrollbar in legend for Firefox, fixes [#4760](https://github.com/grafana/grafana/issues/4760)
* **Table panel**: Fixed issue table panel formating string array properties, fixes [#4791](https://github.com/grafana/grafana/issues/4791)
# 3.0.0-beta5 (2016-04-15) # 3.0.0-beta5 (2016-04-15)
### Bug fixes ### Bug fixes

View File

@ -54,7 +54,7 @@
"phantomjs-prebuilt": "^2.1.3", "phantomjs-prebuilt": "^2.1.3",
"reflect-metadata": "0.1.2", "reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.4", "rxjs": "5.0.0-beta.4",
"sass-lint": "^1.5.0", "sass-lint": "^1.6.0",
"systemjs": "0.19.24" "systemjs": "0.19.24"
}, },
"engines": { "engines": {

View File

@ -56,7 +56,7 @@ func init() {
"HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"}, "HbaseBackupFailed", "MostRecentBackupDuration", "TimeSinceLastSuccessfulBackup"},
"AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"}, "AWS/ES": {"ClusterStatus.green", "ClusterStatus.yellow", "ClusterStatus.red", "Nodes", "SearchableDocuments", "DeletedDocuments", "CPUUtilization", "FreeStorageSpace", "JVMMemoryPressure", "AutomatedSnapshotFailure", "MasterCPUUtilization", "MasterFreeStorageSpace", "MasterJVMMemoryPressure", "ReadLatency", "WriteLatency", "ReadThroughput", "WriteThroughput", "DiskQueueLength", "ReadIOPS", "WriteIOPS"},
"AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"}, "AWS/Events": {"Invocations", "FailedInvocations", "TriggeredRules", "MatchedEvents", "ThrottledRules"},
"AWS/Kinesis": {"PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "IncomingBytes", "IncomingRecords", "GetRecords.Bytes", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Success"}, "AWS/Kinesis": {"GetRecords.Bytes", "GetRecords.IteratorAge", "GetRecords.IteratorAgeMilliseconds", "GetRecords.Latency", "GetRecords.Records", "GetRecords.Success", "IncomingBytes", "IncomingRecords", "PutRecord.Bytes", "PutRecord.Latency", "PutRecord.Success", "PutRecords.Bytes", "PutRecords.Latency", "PutRecords.Records", "PutRecords.Success", "ReadProvisionedThroughputExceeded", "WriteProvisionedThroughputExceeded", "IteratorAgeMilliseconds", "OutgoingBytes", "OutgoingRecords"},
"AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"}, "AWS/Lambda": {"Invocations", "Errors", "Duration", "Throttles"},
"AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"}, "AWS/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
"AWS/ML": {"PredictCount", "PredictFailureCount"}, "AWS/ML": {"PredictCount", "PredictFailureCount"},
@ -88,7 +88,7 @@ func init() {
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"}, "AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
"AWS/ES": {}, "AWS/ES": {},
"AWS/Events": {"RuleName"}, "AWS/Events": {"RuleName"},
"AWS/Kinesis": {"StreamName"}, "AWS/Kinesis": {"StreamName", "ShardID"},
"AWS/Lambda": {"FunctionName"}, "AWS/Lambda": {"FunctionName"},
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"}, "AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
"AWS/ML": {"MLModelId", "RequestMode"}, "AWS/ML": {"MLModelId", "RequestMode"},

View File

@ -21,6 +21,10 @@ func GetSharingOptions(c *middleware.Context) {
} }
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) { func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
if cmd.Name == "" {
cmd.Name = "Unnamed snapshot"
}
if cmd.External { if cmd.External {
// external snapshot ref requires key and delete key // external snapshot ref requires key and delete key
if cmd.Key == "" || cmd.DeleteKey == "" { if cmd.Key == "" || cmd.DeleteKey == "" {

View File

@ -126,8 +126,8 @@ func downloadFile(pluginName, filePath, url string) (err error) {
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
retryCount++ retryCount++
if retryCount == 1 { if retryCount < 3 {
log.Debug("\nFailed downloading. Will retry once.\n") fmt.Printf("\nFailed downloading. Will retry once.\n%v\n", r)
downloadFile(pluginName, filePath, url) downloadFile(pluginName, filePath, url)
} else { } else {
panic(r) panic(r)
@ -164,14 +164,14 @@ func downloadFile(pluginName, filePath, url string) (err error) {
return fmt.Errorf(permissionsDeniedMessage, newFile) return fmt.Errorf(permissionsDeniedMessage, newFile)
} }
defer dst.Close()
src, err := zf.Open() src, err := zf.Open()
if err != nil { if err != nil {
log.Errorf("%v", err) log.Errorf("Failed to extract file: %v", err)
} }
defer src.Close()
io.Copy(dst, src) io.Copy(dst, src)
dst.Close()
src.Close()
} }
} }

View File

@ -3,7 +3,7 @@ package commands
import ( import (
"errors" "errors"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log" "fmt"
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models" m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services" services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
) )
@ -15,22 +15,17 @@ func removeCommand(c CommandLine) error {
pluginPath := c.GlobalString("pluginsDir") pluginPath := c.GlobalString("pluginsDir")
localPlugins := getPluginss(pluginPath) localPlugins := getPluginss(pluginPath)
log.Info("remove!\n")
plugin := c.Args().First() plugin := c.Args().First()
log.Info("plugin: " + plugin + "\n")
if plugin == "" { if plugin == "" {
return errors.New("Missing plugin parameter") return errors.New("Missing plugin parameter")
} }
log.Infof("plugins : \n%v\n", localPlugins)
for _, p := range localPlugins { for _, p := range localPlugins {
if p.Id == c.Args().First() { if p.Id == c.Args().First() {
log.Infof("removing plugin %s", p.Id)
removePlugin(pluginPath, p.Id) removePlugin(pluginPath, p.Id)
return nil
} }
} }
return nil return fmt.Errorf("Could not find plugin named %s", c.Args().First())
} }

View File

@ -8,7 +8,6 @@ import (
"github.com/codegangsta/cli" "github.com/codegangsta/cli"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands" "github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log" "github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
"strings"
) )
var version = "master" var version = "master"
@ -18,7 +17,7 @@ func getGrafanaPluginDir() string {
defaultNix := "/var/lib/grafana/plugins" defaultNix := "/var/lib/grafana/plugins"
if currentOS == "windows" { if currentOS == "windows" {
return "C:\\opt\\grafana\\plugins" return "../data/plugins"
} }
pwd, err := os.Getwd() pwd, err := os.Getwd()
@ -29,16 +28,17 @@ func getGrafanaPluginDir() string {
} }
if isDevenvironment(pwd) { if isDevenvironment(pwd) {
return "../../../data/plugins" return "../data/plugins"
} }
return defaultNix return defaultNix
} }
func isDevenvironment(pwd string) bool { func isDevenvironment(pwd string) bool {
// if grafana-cli is executed from the cmd folder we can assume // if ../conf/defaults.ini exists, grafana is not installed as package
// that its in development environment. // that its in development environment.
return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli") _, err := os.Stat("../conf/defaults.ini")
return err == nil
} }
func main() { func main() {

View File

@ -45,7 +45,7 @@ type DashboardSnapshotDTO struct {
type CreateDashboardSnapshotCommand struct { type CreateDashboardSnapshotCommand struct {
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"` Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
Name string `json:"name" binding:"Required"` Name string `json:"name"`
Expires int64 `json:"expires"` Expires int64 `json:"expires"`
// these are passed when storing an external snapshot ref // these are passed when storing an external snapshot ref

View File

@ -36,7 +36,7 @@ function (angular, _, $) {
self.update(payload); self.update(payload);
}); });
$scope.onAppEvent('panel-instantiated', function(evt, payload) { $scope.onAppEvent('panel-initialized', function(evt, payload) {
self.registerPanel(payload.scope); self.registerPanel(payload.scope);
}); });

View File

@ -50,8 +50,11 @@ export class PanelCtrl {
} }
init() { init() {
this.publishAppEvent('panel-instantiated', {scope: this.$scope});
this.calculatePanelHeight(); this.calculatePanelHeight();
this.publishAppEvent('panel-initialized', {scope: this.$scope});
this.events.emit('panel-initialized');
this.refresh(); this.refresh();
} }

View File

@ -57,7 +57,7 @@ function (angular, _) {
} }
var escapedValues = _.map(value, regexEscape); var escapedValues = _.map(value, regexEscape);
return escapedValues.join('|'); return '(' + escapedValues.join('|') + ')';
} }
case "lucene": { case "lucene": {
if (typeof value === 'string') { if (typeof value === 'string') {
@ -152,6 +152,10 @@ function (angular, _) {
value = variable.current.value; value = variable.current.value;
if (self.isAllValue(value)) { if (self.isAllValue(value)) {
value = self.getAllValue(variable); value = self.getAllValue(variable);
// skip formating of custom all values
if (variable.allValue) {
return value;
}
} }
var res = self.formatValue(value, format, variable); var res = self.formatValue(value, format, variable);

View File

@ -294,11 +294,6 @@ function (angular, _, kbn) {
}; };
this.addAllOption = function(variable) { this.addAllOption = function(variable) {
if (variable.allValue) {
variable.options.unshift({text: 'All', value: variable.allValue});
return;
}
variable.options.unshift({text: 'All', value: "$__all"}); variable.options.unshift({text: 'All', value: "$__all"});
}; };

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -80,6 +80,13 @@ function (_, $) {
category: categories.Calculate, category: categories.Calculate,
}); });
addFuncDef({
name: 'stddevSeries',
params: optionalSeriesRefArgs,
defaultParams: [''],
category: categories.Calculate,
});
addFuncDef({ addFuncDef({
name: 'divideSeries', name: 'divideSeries',
params: optionalSeriesRefArgs, params: optionalSeriesRefArgs,

View File

@ -152,7 +152,9 @@ export default class InfluxQuery {
if (interpolate) { if (interpolate) {
value = this.templateSrv.replace(value, this.scopedVars); value = this.templateSrv.replace(value, this.scopedVars);
} }
value = "'" + value.replace('\\', '\\\\') + "'"; if (isNaN(+value)) {
value = "'" + value.replace('\\', '\\\\') + "'";
}
} else if (interpolate){ } else if (interpolate){
value = this.templateSrv.replace(value, this.scopedVars, 'regex'); value = this.templateSrv.replace(value, this.scopedVars, 'regex');
} }

View File

@ -25,8 +25,8 @@ function (_) {
} }
} }
// quote value unless regex // quote value unless regex or number
if (operator !== '=~' && operator !== '!~') { if (operator !== '=~' && operator !== '!~' && isNaN(+value)) {
value = "'" + value + "'"; value = "'" + value + "'";
} }

View File

@ -12,17 +12,29 @@ export default class ResponseParser {
return []; return [];
} }
var series = influxResults.series[0]; var influxdb11format = query.toLowerCase().indexOf('show tag values') >= 0;
return _.map(series.values, (value) => {
if (_.isArray(value)) { var res = {};
if (query.toLowerCase().indexOf('show tag values') >= 0) { _.each(influxResults.series, serie => {
return { text: (value[1] || value[0]) }; _.each(serie.values, value => {
if (_.isArray(value)) {
if (influxdb11format) {
addUnique(res, value[1] || value[0]);
} else {
addUnique(res, value[0]);
}
} else { } else {
return { text: value[0] }; addUnique(res, value);
} }
} else { });
return { text: value }; });
}
return _.map(res, value => {
return { text: value};
}); });
} }
} }
function addUnique(arr, value) {
arr[value] = value;
}

View File

@ -38,7 +38,7 @@ describe("influxdb response parser", () => {
{ {
"name": "hostnameTagValues", "name": "hostnameTagValues",
"columns": ["hostname"], "columns": ["hostname"],
"values": [ ["server1"], ["server2"] ] "values": [ ["server1"], ["server2"], ["server2"] ]
} }
] ]
} }
@ -54,7 +54,7 @@ describe("influxdb response parser", () => {
}); });
}); });
describe("response from 0.11.0", () => { describe("response from 0.12.0", () => {
var response = { var response = {
"results": [ "results": [
{ {
@ -62,8 +62,19 @@ describe("influxdb response parser", () => {
{ {
"name": "cpu", "name": "cpu",
"columns": [ "key", "value"], "columns": [ "key", "value"],
"values": [ [ "source", "site" ], [ "source", "api" ] ] "values": [
} [ "source", "site" ],
[ "source", "api" ]
]
},
{
"name": "logins",
"columns": [ "key", "value"],
"values": [
[ "source", "site" ],
[ "source", "webapi"]
]
},
] ]
} }
] ]
@ -72,15 +83,12 @@ describe("influxdb response parser", () => {
var result = this.parser.parse(query, response); var result = this.parser.parse(query, response);
it("should get two responses", () => { it("should get two responses", () => {
expect(_.size(result)).to.be(2); expect(_.size(result)).to.be(3);
expect(result[0].text).to.be('site'); expect(result[0].text).to.be('site');
expect(result[1].text).to.be('api'); expect(result[1].text).to.be('api');
expect(result[2].text).to.be('webapi');
}); });
}); });
}); });
describe("SHOW FIELD response", () => { describe("SHOW FIELD response", () => {

View File

@ -5,27 +5,26 @@ import config from 'app/core/config';
import {PanelCtrl} from 'app/plugins/sdk'; import {PanelCtrl} from 'app/plugins/sdk';
import {impressions} from 'app/features/dashboard/impression_store'; import {impressions} from 'app/features/dashboard/impression_store';
// Set and populate defaults
var panelDefaults = {
query: '',
limit: 10,
tags: [],
recent: false,
search: false,
starred: true,
headings: true,
};
class DashListCtrl extends PanelCtrl { class DashListCtrl extends PanelCtrl {
static templateUrl = 'module.html'; static templateUrl = 'module.html';
groups: any[]; groups: any[];
modes: any[]; modes: any[];
panelDefaults = {
query: '',
limit: 10,
tags: [],
recent: false,
search: false,
starred: true,
headings: true,
};
/** @ngInject */ /** @ngInject */
constructor($scope, $injector, private backendSrv) { constructor($scope, $injector, private backendSrv) {
super($scope, $injector); super($scope, $injector);
_.defaults(this.panel, panelDefaults); _.defaults(this.panel, this.panelDefaults);
if (this.panel.tag) { if (this.panel.tag) {
this.panel.tags = [this.panel.tag]; this.panel.tags = [this.panel.tag];

View File

@ -73,7 +73,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
var legendSeries = _.filter(data, function(series) { var legendSeries = _.filter(data, function(series) {
return series.hideFromLegend(panel.legend) === false; return series.hideFromLegend(panel.legend) === false;
}); });
var total = 23 + (22 * legendSeries.length); var total = 23 + (21 * legendSeries.length);
return Math.min(total, Math.floor(panelHeight/2)); return Math.min(total, Math.floor(panelHeight/2));
} else { } else {
return 26; return 26;

View File

@ -30,7 +30,7 @@ export class TableRenderer {
} }
if (_.isArray(v)) { if (_.isArray(v)) {
v = v.join(',&nbsp;'); v = v.join(', ');
} }
return v; return v;

View File

@ -67,18 +67,20 @@
} }
// Links within the dropdown menu // Links within the dropdown menu
> li > a { > li {
display: block; > a {
padding: 3px 20px 3px 15px; display: block;
clear: both; padding: 3px 20px 3px 15px;
font-weight: normal; clear: both;
line-height: $line-height-base; font-weight: normal;
color: $dropdownLinkColor; line-height: $line-height-base;
white-space: nowrap; color: $dropdownLinkColor;
white-space: nowrap;
i { i {
padding-right: 5px; padding-right: 5px;
color: $link-color-disabled; color: $link-color-disabled;
}
} }
} }
} }

View File

@ -85,7 +85,8 @@
} }
.graph-legend-table { .graph-legend-table {
overflow-y: scroll; overflow-y: auto;
overflow-x: hidden;
.graph-legend-series { .graph-legend-series {
display: table-row; display: table-row;

View File

@ -24,7 +24,7 @@ describe("Emitter", () => {
expect(sub2Called).to.be(true); expect(sub2Called).to.be(true);
}); });
it.only('should handle errors', () => { it('should handle errors', () => {
var events = new Emitter(); var events = new Emitter();
var sub1Called = 0; var sub1Called = 0;
var sub2Called = 0; var sub2Called = 0;

View File

@ -99,6 +99,11 @@ define([
var target = _templateSrv.replace('this.$test.filters', {}, 'glob'); var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
expect(target).to.be('this.*.filters'); expect(target).to.be('this.*.filters');
}); });
it('should not escape custom all value', function() {
var target = _templateSrv.replace('this.$test', {}, 'regex');
expect(target).to.be('this.*');
});
}); });
describe('lucene format', function() { describe('lucene format', function() {
@ -127,7 +132,7 @@ define([
it('multi value and regex format should render regex string', function() { it('multi value and regex format should render regex string', function() {
var result = _templateSrv.formatValue(['test.','test2'], 'regex'); var result = _templateSrv.formatValue(['test.','test2'], 'regex');
expect(result).to.be('test\\.|test2'); expect(result).to.be('(test\\.|test2)');
}); });
it('multi value and pipe should render pipe string', function() { it('multi value and pipe should render pipe string', function() {

View File

@ -280,7 +280,7 @@ define([
}); });
it('should add All option with custom value', function() { it('should add All option with custom value', function() {
expect(scenario.variable.options[0].value).to.be('*'); expect(scenario.variable.options[0].value).to.be('$__all');
}); });
}); });

View File

@ -25,6 +25,19 @@ module.exports = function(grunt) {
'typescript:build' 'typescript:build'
]); ]);
grunt.registerTask('test', ['default', 'karma:test']); grunt.registerTask('test', ['default', 'karma:test', 'no-only-tests']);
grunt.registerTask('no-only-tests', function() {
var files = grunt.file.expand('public/**/*_specs\.ts', 'public/**/*_specs\.js');
files.forEach(function(spec) {
var rows = grunt.file.read(spec).split('\n');
rows.forEach(function(row) {
if (row.indexOf('.only(') > 0) {
grunt.log.errorlns(row);
grunt.fail.warn('found only statement in test: ' + spec)
}
});
});
});
}; };