mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge branch 'master' into alerting_definitions
This commit is contained in:
commit
ba5978abd3
13
CHANGELOG.md
13
CHANGELOG.md
@ -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)
|
||||
|
||||
### Bug fixes
|
||||
|
@ -54,7 +54,7 @@
|
||||
"phantomjs-prebuilt": "^2.1.3",
|
||||
"reflect-metadata": "0.1.2",
|
||||
"rxjs": "5.0.0-beta.4",
|
||||
"sass-lint": "^1.5.0",
|
||||
"sass-lint": "^1.6.0",
|
||||
"systemjs": "0.19.24"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -56,7 +56,7 @@ func init() {
|
||||
"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/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/Logs": {"IncomingBytes", "IncomingLogEvents", "ForwardedBytes", "ForwardedLogEvents", "DeliveryErrors", "DeliveryThrottling"},
|
||||
"AWS/ML": {"PredictCount", "PredictFailureCount"},
|
||||
@ -88,7 +88,7 @@ func init() {
|
||||
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
|
||||
"AWS/ES": {},
|
||||
"AWS/Events": {"RuleName"},
|
||||
"AWS/Kinesis": {"StreamName"},
|
||||
"AWS/Kinesis": {"StreamName", "ShardID"},
|
||||
"AWS/Lambda": {"FunctionName"},
|
||||
"AWS/Logs": {"LogGroupName", "DestinationType", "FilterName"},
|
||||
"AWS/ML": {"MLModelId", "RequestMode"},
|
||||
|
@ -21,6 +21,10 @@ func GetSharingOptions(c *middleware.Context) {
|
||||
}
|
||||
|
||||
func CreateDashboardSnapshot(c *middleware.Context, cmd m.CreateDashboardSnapshotCommand) {
|
||||
if cmd.Name == "" {
|
||||
cmd.Name = "Unnamed snapshot"
|
||||
}
|
||||
|
||||
if cmd.External {
|
||||
// external snapshot ref requires key and delete key
|
||||
if cmd.Key == "" || cmd.DeleteKey == "" {
|
||||
|
@ -126,8 +126,8 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
retryCount++
|
||||
if retryCount == 1 {
|
||||
log.Debug("\nFailed downloading. Will retry once.\n")
|
||||
if retryCount < 3 {
|
||||
fmt.Printf("\nFailed downloading. Will retry once.\n%v\n", r)
|
||||
downloadFile(pluginName, filePath, url)
|
||||
} else {
|
||||
panic(r)
|
||||
@ -164,14 +164,14 @@ func downloadFile(pluginName, filePath, url string) (err error) {
|
||||
return fmt.Errorf(permissionsDeniedMessage, newFile)
|
||||
}
|
||||
|
||||
defer dst.Close()
|
||||
src, err := zf.Open()
|
||||
if err != nil {
|
||||
log.Errorf("%v", err)
|
||||
log.Errorf("Failed to extract file: %v", err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
io.Copy(dst, src)
|
||||
dst.Close()
|
||||
src.Close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package commands
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||
"fmt"
|
||||
m "github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
|
||||
services "github.com/grafana/grafana/pkg/cmd/grafana-cli/services"
|
||||
)
|
||||
@ -15,22 +15,17 @@ func removeCommand(c CommandLine) error {
|
||||
pluginPath := c.GlobalString("pluginsDir")
|
||||
localPlugins := getPluginss(pluginPath)
|
||||
|
||||
log.Info("remove!\n")
|
||||
|
||||
plugin := c.Args().First()
|
||||
log.Info("plugin: " + plugin + "\n")
|
||||
if plugin == "" {
|
||||
return errors.New("Missing plugin parameter")
|
||||
}
|
||||
|
||||
log.Infof("plugins : \n%v\n", localPlugins)
|
||||
|
||||
for _, p := range localPlugins {
|
||||
if p.Id == c.Args().First() {
|
||||
log.Infof("removing plugin %s", p.Id)
|
||||
removePlugin(pluginPath, p.Id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return fmt.Errorf("Could not find plugin named %s", c.Args().First())
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/commands"
|
||||
"github.com/grafana/grafana/pkg/cmd/grafana-cli/log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var version = "master"
|
||||
@ -18,7 +17,7 @@ func getGrafanaPluginDir() string {
|
||||
defaultNix := "/var/lib/grafana/plugins"
|
||||
|
||||
if currentOS == "windows" {
|
||||
return "C:\\opt\\grafana\\plugins"
|
||||
return "../data/plugins"
|
||||
}
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
@ -29,16 +28,17 @@ func getGrafanaPluginDir() string {
|
||||
}
|
||||
|
||||
if isDevenvironment(pwd) {
|
||||
return "../../../data/plugins"
|
||||
return "../data/plugins"
|
||||
}
|
||||
|
||||
return defaultNix
|
||||
}
|
||||
|
||||
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.
|
||||
return strings.HasSuffix(pwd, "/pkg/cmd/grafana-cli")
|
||||
_, err := os.Stat("../conf/defaults.ini")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
@ -45,7 +45,7 @@ type DashboardSnapshotDTO struct {
|
||||
|
||||
type CreateDashboardSnapshotCommand struct {
|
||||
Dashboard *simplejson.Json `json:"dashboard" binding:"Required"`
|
||||
Name string `json:"name" binding:"Required"`
|
||||
Name string `json:"name"`
|
||||
Expires int64 `json:"expires"`
|
||||
|
||||
// these are passed when storing an external snapshot ref
|
||||
|
@ -36,7 +36,7 @@ function (angular, _, $) {
|
||||
self.update(payload);
|
||||
});
|
||||
|
||||
$scope.onAppEvent('panel-instantiated', function(evt, payload) {
|
||||
$scope.onAppEvent('panel-initialized', function(evt, payload) {
|
||||
self.registerPanel(payload.scope);
|
||||
});
|
||||
|
||||
|
@ -50,8 +50,11 @@ export class PanelCtrl {
|
||||
}
|
||||
|
||||
init() {
|
||||
this.publishAppEvent('panel-instantiated', {scope: this.$scope});
|
||||
this.calculatePanelHeight();
|
||||
|
||||
this.publishAppEvent('panel-initialized', {scope: this.$scope});
|
||||
this.events.emit('panel-initialized');
|
||||
|
||||
this.refresh();
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ function (angular, _) {
|
||||
}
|
||||
|
||||
var escapedValues = _.map(value, regexEscape);
|
||||
return escapedValues.join('|');
|
||||
return '(' + escapedValues.join('|') + ')';
|
||||
}
|
||||
case "lucene": {
|
||||
if (typeof value === 'string') {
|
||||
@ -152,6 +152,10 @@ function (angular, _) {
|
||||
value = variable.current.value;
|
||||
if (self.isAllValue(value)) {
|
||||
value = self.getAllValue(variable);
|
||||
// skip formating of custom all values
|
||||
if (variable.allValue) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
var res = self.formatValue(value, format, variable);
|
||||
|
@ -294,11 +294,6 @@ function (angular, _, kbn) {
|
||||
};
|
||||
|
||||
this.addAllOption = function(variable) {
|
||||
if (variable.allValue) {
|
||||
variable.options.unshift({text: 'All', value: variable.allValue});
|
||||
return;
|
||||
}
|
||||
|
||||
variable.options.unshift({text: 'All', value: "$__all"});
|
||||
};
|
||||
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 38 KiB |
@ -80,6 +80,13 @@ function (_, $) {
|
||||
category: categories.Calculate,
|
||||
});
|
||||
|
||||
addFuncDef({
|
||||
name: 'stddevSeries',
|
||||
params: optionalSeriesRefArgs,
|
||||
defaultParams: [''],
|
||||
category: categories.Calculate,
|
||||
});
|
||||
|
||||
addFuncDef({
|
||||
name: 'divideSeries',
|
||||
params: optionalSeriesRefArgs,
|
||||
|
@ -152,7 +152,9 @@ export default class InfluxQuery {
|
||||
if (interpolate) {
|
||||
value = this.templateSrv.replace(value, this.scopedVars);
|
||||
}
|
||||
value = "'" + value.replace('\\', '\\\\') + "'";
|
||||
if (isNaN(+value)) {
|
||||
value = "'" + value.replace('\\', '\\\\') + "'";
|
||||
}
|
||||
} else if (interpolate){
|
||||
value = this.templateSrv.replace(value, this.scopedVars, 'regex');
|
||||
}
|
||||
|
@ -25,8 +25,8 @@ function (_) {
|
||||
}
|
||||
}
|
||||
|
||||
// quote value unless regex
|
||||
if (operator !== '=~' && operator !== '!~') {
|
||||
// quote value unless regex or number
|
||||
if (operator !== '=~' && operator !== '!~' && isNaN(+value)) {
|
||||
value = "'" + value + "'";
|
||||
}
|
||||
|
||||
|
@ -12,17 +12,29 @@ export default class ResponseParser {
|
||||
return [];
|
||||
}
|
||||
|
||||
var series = influxResults.series[0];
|
||||
return _.map(series.values, (value) => {
|
||||
if (_.isArray(value)) {
|
||||
if (query.toLowerCase().indexOf('show tag values') >= 0) {
|
||||
return { text: (value[1] || value[0]) };
|
||||
var influxdb11format = query.toLowerCase().indexOf('show tag values') >= 0;
|
||||
|
||||
var res = {};
|
||||
_.each(influxResults.series, serie => {
|
||||
_.each(serie.values, value => {
|
||||
if (_.isArray(value)) {
|
||||
if (influxdb11format) {
|
||||
addUnique(res, value[1] || value[0]);
|
||||
} else {
|
||||
addUnique(res, value[0]);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ describe("influxdb response parser", () => {
|
||||
{
|
||||
"name": "hostnameTagValues",
|
||||
"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 = {
|
||||
"results": [
|
||||
{
|
||||
@ -62,8 +62,19 @@ describe("influxdb response parser", () => {
|
||||
{
|
||||
"name": "cpu",
|
||||
"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);
|
||||
|
||||
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[1].text).to.be('api');
|
||||
expect(result[2].text).to.be('webapi');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("SHOW FIELD response", () => {
|
||||
|
@ -5,27 +5,26 @@ import config from 'app/core/config';
|
||||
import {PanelCtrl} from 'app/plugins/sdk';
|
||||
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 {
|
||||
static templateUrl = 'module.html';
|
||||
|
||||
groups: any[];
|
||||
modes: any[];
|
||||
|
||||
panelDefaults = {
|
||||
query: '',
|
||||
limit: 10,
|
||||
tags: [],
|
||||
recent: false,
|
||||
search: false,
|
||||
starred: true,
|
||||
headings: true,
|
||||
};
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope, $injector, private backendSrv) {
|
||||
super($scope, $injector);
|
||||
_.defaults(this.panel, panelDefaults);
|
||||
_.defaults(this.panel, this.panelDefaults);
|
||||
|
||||
if (this.panel.tag) {
|
||||
this.panel.tags = [this.panel.tag];
|
||||
|
@ -73,7 +73,7 @@ function (angular, $, moment, _, kbn, GraphTooltip) {
|
||||
var legendSeries = _.filter(data, function(series) {
|
||||
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));
|
||||
} else {
|
||||
return 26;
|
||||
|
@ -30,7 +30,7 @@ export class TableRenderer {
|
||||
}
|
||||
|
||||
if (_.isArray(v)) {
|
||||
v = v.join(', ');
|
||||
v = v.join(', ');
|
||||
}
|
||||
|
||||
return v;
|
||||
|
@ -67,18 +67,20 @@
|
||||
}
|
||||
|
||||
// Links within the dropdown menu
|
||||
> li > a {
|
||||
display: block;
|
||||
padding: 3px 20px 3px 15px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: $line-height-base;
|
||||
color: $dropdownLinkColor;
|
||||
white-space: nowrap;
|
||||
> li {
|
||||
> a {
|
||||
display: block;
|
||||
padding: 3px 20px 3px 15px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: $line-height-base;
|
||||
color: $dropdownLinkColor;
|
||||
white-space: nowrap;
|
||||
|
||||
i {
|
||||
padding-right: 5px;
|
||||
color: $link-color-disabled;
|
||||
i {
|
||||
padding-right: 5px;
|
||||
color: $link-color-disabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,8 @@
|
||||
}
|
||||
|
||||
.graph-legend-table {
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
.graph-legend-series {
|
||||
display: table-row;
|
||||
|
@ -24,7 +24,7 @@ describe("Emitter", () => {
|
||||
expect(sub2Called).to.be(true);
|
||||
});
|
||||
|
||||
it.only('should handle errors', () => {
|
||||
it('should handle errors', () => {
|
||||
var events = new Emitter();
|
||||
var sub1Called = 0;
|
||||
var sub2Called = 0;
|
||||
|
@ -99,6 +99,11 @@ define([
|
||||
var target = _templateSrv.replace('this.$test.filters', {}, 'glob');
|
||||
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() {
|
||||
@ -127,7 +132,7 @@ define([
|
||||
|
||||
it('multi value and regex format should render regex string', function() {
|
||||
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() {
|
||||
|
@ -280,7 +280,7 @@ define([
|
||||
});
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -25,6 +25,19 @@ module.exports = function(grunt) {
|
||||
'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)
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user