Commit 462a9394 by Nick Christus

Merge branch 'master' of github.com:grafana/grafana

parents 3d178c8e 1ebb18ae
# 2.6.0 (unreleased) # 2.6.0 (2015-12-04)
### Bug Fixes
* **metric editors**: Fix for clicking typeahead auto dropdown option, fixes [#3428](https://github.com/grafana/grafana/issues/3428)
# 2.6.0-Beta1 (2015-12-04)
### New Table Panel ### New Table Panel
* **table**: New powerful and flexible table panel, closes [#215](https://github.com/grafana/grafana/issues/215) * **table**: New powerful and flexible table panel, closes [#215](https://github.com/grafana/grafana/issues/215)
...@@ -6,6 +11,7 @@ ...@@ -6,6 +11,7 @@
### Enhancements ### Enhancements
* **CloudWatch**: Support for multiple AWS Credentials, closes [#3053](https://github.com/grafana/grafana/issues/3053), [#3080](https://github.com/grafana/grafana/issues/3080) * **CloudWatch**: Support for multiple AWS Credentials, closes [#3053](https://github.com/grafana/grafana/issues/3053), [#3080](https://github.com/grafana/grafana/issues/3080)
* **Elasticsearch**: Support for dynamic daily indices for annotations, closes [#3061](https://github.com/grafana/grafana/issues/3061) * **Elasticsearch**: Support for dynamic daily indices for annotations, closes [#3061](https://github.com/grafana/grafana/issues/3061)
* **Elasticsearch**: Support for setting min_doc_count for date histogram, closes [#3416](https://github.com/grafana/grafana/issues/3416)
* **Graph Panel**: Option to hide series with all zeroes from legend and tooltip, closes [#1381](https://github.com/grafana/grafana/issues/1381), [#3336](https://github.com/grafana/grafana/issues/3336) * **Graph Panel**: Option to hide series with all zeroes from legend and tooltip, closes [#1381](https://github.com/grafana/grafana/issues/1381), [#3336](https://github.com/grafana/grafana/issues/3336)
### Bug Fixes ### Bug Fixes
......
...@@ -5,7 +5,7 @@ os: Windows Server 2012 R2 ...@@ -5,7 +5,7 @@ os: Windows Server 2012 R2
clone_folder: c:\gopath\src\github.com\grafana\grafana clone_folder: c:\gopath\src\github.com\grafana\grafana
environment: environment:
nodejs_version: "0.12.2" nodejs_version: "4"
GOPATH: c:\gopath GOPATH: c:\gopath
install: install:
......
...@@ -76,6 +76,14 @@ func main() { ...@@ -76,6 +76,14 @@ func main() {
grunt("release") grunt("release")
createLinuxPackages() createLinuxPackages()
case "pkg-rpm":
grunt("release")
createRpmPackages()
case "pkg-deb":
grunt("release")
createDebPackages()
case "latest": case "latest":
makeLatestDistCopies() makeLatestDistCopies()
...@@ -147,7 +155,7 @@ type linuxPackageOptions struct { ...@@ -147,7 +155,7 @@ type linuxPackageOptions struct {
depends []string depends []string
} }
func createLinuxPackages() { func createDebPackages() {
createPackage(linuxPackageOptions{ createPackage(linuxPackageOptions{
packageType: "deb", packageType: "deb",
homeDir: "/usr/share/grafana", homeDir: "/usr/share/grafana",
...@@ -167,7 +175,9 @@ func createLinuxPackages() { ...@@ -167,7 +175,9 @@ func createLinuxPackages() {
depends: []string{"adduser", "libfontconfig"}, depends: []string{"adduser", "libfontconfig"},
}) })
}
func createRpmPackages() {
createPackage(linuxPackageOptions{ createPackage(linuxPackageOptions{
packageType: "rpm", packageType: "rpm",
homeDir: "/usr/share/grafana", homeDir: "/usr/share/grafana",
...@@ -189,6 +199,11 @@ func createLinuxPackages() { ...@@ -189,6 +199,11 @@ func createLinuxPackages() {
}) })
} }
func createLinuxPackages() {
createDebPackages()
createRpmPackages()
}
func createPackage(options linuxPackageOptions) { func createPackage(options linuxPackageOptions) {
packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack") packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack")
...@@ -315,6 +330,8 @@ func build(pkg string, tags []string) { ...@@ -315,6 +330,8 @@ func build(pkg string, tags []string) {
args = append(args, "-o", binary) args = append(args, "-o", binary)
args = append(args, pkg) args = append(args, pkg)
setBuildEnv() setBuildEnv()
runPrint("go", "version")
runPrint("go", args...) runPrint("go", args...)
// Create an md5 checksum of the binary, to be included in the archive for // Create an md5 checksum of the binary, to be included in the archive for
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
"company": "Coding Instinct AB" "company": "Coding Instinct AB"
}, },
"name": "grafana", "name": "grafana",
"version": "2.6.0-pre1", "version": "2.6.0-beta1",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "http://github.com/torkelo/grafana.git" "url": "http://github.com/torkelo/grafana.git"
......
...@@ -174,6 +174,9 @@ func applyEnvVariableOverrides() { ...@@ -174,6 +174,9 @@ func applyEnvVariableOverrides() {
if len(envValue) > 0 { if len(envValue) > 0 {
key.SetValue(envValue) key.SetValue(envValue)
if strings.Contains(envKey, "PASSWORD") {
envValue = "*********"
}
appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue)) appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue))
} }
} }
...@@ -188,6 +191,9 @@ func applyCommandLineDefaultProperties(props map[string]string) { ...@@ -188,6 +191,9 @@ func applyCommandLineDefaultProperties(props map[string]string) {
value, exists := props[keyString] value, exists := props[keyString]
if exists { if exists {
key.SetValue(value) key.SetValue(value)
if strings.Contains(keyString, "password") {
value = "*********"
}
appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value)) appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value))
} }
} }
......
...@@ -55,8 +55,8 @@ function (_, $, coreModule) { ...@@ -55,8 +55,8 @@ function (_, $, coreModule) {
}); });
}; };
$scope.switchToLink = function() { $scope.switchToLink = function(fromClick) {
if (linkMode) { return; } if (linkMode && !fromClick) { return; }
clearTimeout(cancelBlur); clearTimeout(cancelBlur);
cancelBlur = null; cancelBlur = null;
...@@ -69,7 +69,7 @@ function (_, $, coreModule) { ...@@ -69,7 +69,7 @@ function (_, $, coreModule) {
$scope.inputBlur = function() { $scope.inputBlur = function() {
// happens long before the click event on the typeahead options // happens long before the click event on the typeahead options
// need to have long delay because the blur // need to have long delay because the blur
cancelBlur = setTimeout($scope.switchToLink, 100); cancelBlur = setTimeout($scope.switchToLink, 200);
}; };
$scope.source = function(query, callback) { $scope.source = function(query, callback) {
...@@ -100,7 +100,7 @@ function (_, $, coreModule) { ...@@ -100,7 +100,7 @@ function (_, $, coreModule) {
} }
$input.val(value); $input.val(value);
$scope.switchToLink(); $scope.switchToLink(true);
return value; return value;
}; };
......
...@@ -156,7 +156,7 @@ function (angular, _, coreModule) { ...@@ -156,7 +156,7 @@ function (angular, _, coreModule) {
vm.selectionsChanged = function(commitChange) { vm.selectionsChanged = function(commitChange) {
vm.selectedValues = _.filter(vm.options, {selected: true}); vm.selectedValues = _.filter(vm.options, {selected: true});
if (vm.selectedValues.length > 1 && vm.selectedValues.length !== vm.options.length) { if (vm.selectedValues.length > 1) {
if (vm.selectedValues[0].text === 'All') { if (vm.selectedValues[0].text === 'All') {
vm.selectedValues[0].selected = false; vm.selectedValues[0].selected = false;
vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length); vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length);
......
...@@ -47,8 +47,9 @@ define([ ...@@ -47,8 +47,9 @@ define([
if (value.length === 15) { if (value.length === 15) {
return moment.utc(value, 'YYYYMMDDTHHmmss'); return moment.utc(value, 'YYYYMMDDTHHmmss');
} }
var epoch = parseInt(value);
if (!_.isNaN(epoch)) { if (!isNaN(value)) {
var epoch = parseInt(value);
return moment(epoch); return moment(epoch);
} }
......
...@@ -26,7 +26,7 @@ export class TablePanelCtrl { ...@@ -26,7 +26,7 @@ export class TablePanelCtrl {
var panelDefaults = { var panelDefaults = {
targets: [{}], targets: [{}],
transform: 'timeseries_to_rows', transform: 'timeseries_to_columns',
pageSize: null, pageSize: null,
showHeader: true, showHeader: true,
styles: [ styles: [
......
...@@ -45,11 +45,17 @@ export class TablePanelEditorCtrl { ...@@ -45,11 +45,17 @@ export class TablePanelEditorCtrl {
}; };
$scope.addColumn = function() { $scope.addColumn = function() {
$scope.panel.columns.push({text: $scope.addColumnSegment.value, value: $scope.addColumnSegment.value}); var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw);
$scope.render(); var column = _.findWhere(columns, {text: $scope.addColumnSegment.value});
if (column) {
$scope.panel.columns.push(column);
$scope.render();
}
var plusButton = uiSegmentSrv.newPlusButton(); var plusButton = uiSegmentSrv.newPlusButton();
$scope.addColumnSegment.html = plusButton.html; $scope.addColumnSegment.html = plusButton.html;
$scope.addColumnSegment.value = plusButton.value;
}; };
$scope.transformChanged = function() { $scope.transformChanged = function() {
......
...@@ -112,7 +112,7 @@ export class TableRenderer { ...@@ -112,7 +112,7 @@ export class TableRenderer {
// this hack adds header content to cell (not visible) // this hack adds header content to cell (not visible)
var widthHack = ''; var widthHack = '';
if (addWidthHack) { if (addWidthHack) {
widthHack = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].text + '<div>'; widthHack = '<div class="table-panel-width-hack">' + this.table.columns[columnIndex].text + '</div>';
} }
return '<td' + style + '>' + value + widthHack + '</td>'; return '<td' + style + '>' + value + widthHack + '</td>';
......
...@@ -25,7 +25,7 @@ function (angular, _) { ...@@ -25,7 +25,7 @@ function (angular, _) {
var end = convertToCloudWatchTime(options.range.to); var end = convertToCloudWatchTime(options.range.to);
var queries = []; var queries = [];
options = _.clone(options); options = angular.copy(options);
_.each(options.targets, _.bind(function(target) { _.each(options.targets, _.bind(function(target) {
if (target.hide || !target.namespace || !target.metricName || _.isEmpty(target.statistics)) { if (target.hide || !target.namespace || !target.metricName || _.isEmpty(target.statistics)) {
return; return;
...@@ -129,7 +129,7 @@ function (angular, _) { ...@@ -129,7 +129,7 @@ function (angular, _) {
.pluck('Dimensions') .pluck('Dimensions')
.flatten() .flatten()
.filter(function(dimension) { .filter(function(dimension) {
return dimension.Name === dimensionKey; return dimension !== null && dimension.Name === dimensionKey;
}) })
.pluck('Value') .pluck('Value')
.uniq() .uniq()
......
...@@ -92,8 +92,10 @@ function (angular, _, queryDef) { ...@@ -92,8 +92,10 @@ function (angular, _, queryDef) {
} }
case 'date_histogram': { case 'date_histogram': {
settings.interval = settings.interval || 'auto'; settings.interval = settings.interval || 'auto';
settings.min_doc_count = settings.min_doc_count || 0;
$scope.agg.field = $scope.target.timeField; $scope.agg.field = $scope.target.timeField;
settingsLinkText = 'Interval: ' + settings.interval; settingsLinkText = 'Interval: ' + settings.interval;
settingsLinkText += ', Min Doc Count: ' + settings.min_doc_count;
} }
} }
......
...@@ -35,9 +35,9 @@ ...@@ -35,9 +35,9 @@
<div class="tight-form" ng-if="showOptions"> <div class="tight-form" ng-if="showOptions">
<div class="tight-form-inner-box" ng-if="agg.type === 'date_histogram'"> <div class="tight-form-inner-box" ng-if="agg.type === 'date_histogram'">
<div class="tight-form last"> <div class="tight-form">
<ul class="tight-form-list"> <ul class="tight-form-list">
<li class="tight-form-item" style="width: 60px"> <li class="tight-form-item" style="width: 94px">
Interval Interval
</li> </li>
<li> <li>
...@@ -46,6 +46,17 @@ ...@@ -46,6 +46,17 @@
</ul> </ul>
<div class="clearfix"></div> <div class="clearfix"></div>
</div> </div>
<div class="tight-form last">
<ul class="tight-form-list">
<li class="tight-form-item" style="width: 94px">
Min Doc Count
</li>
<li>
<input type="number" class="tight-form-input" ng-model="agg.settings.min_doc_count" ng-blur="onChangeInternal()"></input>
</li>
</ul>
<div class="clearfix"></div>
</div>
</div> </div>
<div class="tight-form-inner-box" ng-if="agg.type === 'terms'"> <div class="tight-form-inner-box" ng-if="agg.type === 'terms'">
<div class="tight-form"> <div class="tight-form">
......
...@@ -50,12 +50,23 @@ function () { ...@@ -50,12 +50,23 @@ function () {
return queryNode; return queryNode;
}; };
ElasticQueryBuilder.prototype.getInterval = function(agg) { ElasticQueryBuilder.prototype.getDateHistogramAgg = function(aggDef) {
if (agg.settings && agg.settings.interval !== 'auto') { var esAgg = {};
return agg.settings.interval; var settings = aggDef.settings || {};
} else { esAgg.interval = settings.interval;
return '$interval'; esAgg.field = this.timeField;
esAgg.min_doc_count = settings.min_doc_count || 0;
esAgg.extended_bounds = {min: "$timeFrom", max: "$timeTo"};
if (esAgg.interval === 'auto') {
esAgg.interval = "$interval";
} }
if (this.esVersion >= 2) {
esAgg.format = "epoch_millis";
}
return esAgg;
}; };
ElasticQueryBuilder.prototype.getFiltersAgg = function(aggDef) { ElasticQueryBuilder.prototype.getFiltersAgg = function(aggDef) {
...@@ -130,15 +141,7 @@ function () { ...@@ -130,15 +141,7 @@ function () {
switch(aggDef.type) { switch(aggDef.type) {
case 'date_histogram': { case 'date_histogram': {
esAgg["date_histogram"] = { esAgg["date_histogram"] = this.getDateHistogramAgg(aggDef);
"interval": this.getInterval(aggDef),
"field": this.timeField,
"min_doc_count": 0,
"extended_bounds": { "min": "$timeFrom", "max": "$timeTo" }
};
if (this.esVersion >= 2) {
esAgg["date_histogram"]["format"] = "epoch_millis";
}
break; break;
} }
case 'filters': { case 'filters': {
......
...@@ -55,12 +55,12 @@ ...@@ -55,12 +55,12 @@
<metric-segment segment="segment" get-options="getTagsOrValues(segment, $index)" on-change="tagSegmentUpdated(segment, $index)"></metric-segment> <metric-segment segment="segment" get-options="getTagsOrValues(segment, $index)" on-change="tagSegmentUpdated(segment, $index)"></metric-segment>
</li> </li>
</ul> </ul>
<div class="clearfix"></div>
<div style="padding: 10px" ng-if="target.rawQuery"> <div class="tight-form-flex-wrapper" ng-show="target.rawQuery">
<textarea ng-model="target.query" rows="8" spellcheck="false" style="width: 100%; box-sizing: border-box;" ng-blur="get_data()"></textarea> <input type="text" class="tight-form-clear-input" ng-model="target.query" spellcheck="false" style="width: 100%;" ng-blur="get_data()"></input>
</div> </div>
<div class="clearfix"></div>
</div> </div>
<div ng-hide="target.rawQuery"> <div ng-hide="target.rawQuery">
......
...@@ -23,7 +23,6 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) { ...@@ -23,7 +23,6 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) {
$scope.resultFormats = [ $scope.resultFormats = [
{text: 'Time series', value: 'time_series'}, {text: 'Time series', value: 'time_series'},
{text: 'Table', value: 'table'}, {text: 'Table', value: 'table'},
{text: 'JSON field', value: 'json_field'},
]; ];
if (!$scope.target.measurement) { if (!$scope.target.measurement) {
......
...@@ -205,7 +205,7 @@ describe('when generating timeseries from influxdb response', function() { ...@@ -205,7 +205,7 @@ describe('when generating timeseries from influxdb response', function() {
expect(table.type).to.be('table'); expect(table.type).to.be('table');
expect(table.columns.length).to.be(3); expect(table.columns.length).to.be(3);
expect(table.rows[0]).to.eql([1431946625000, 'America', 10]);; expect(table.rows[0]).to.eql([1431946625000, 'America', 10]);
}); });
}); });
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
} }
code, pre { code, pre {
background-color: @grayLighter; background-color: @codeTagBackground;
} }
div.editor-row { div.editor-row {
...@@ -587,8 +587,10 @@ div.flot-text { ...@@ -587,8 +587,10 @@ div.flot-text {
// pre // pre
code, pre { code, pre {
background-color: @grafanaPanelBackground; background-color: @codeTagBackground;
color: @textColor; color: @textColor;
border: 1px solid darken(@codeTagBackground, 15%);
padding: 2px;
} }
.dropdown-menu { .dropdown-menu {
......
...@@ -59,6 +59,11 @@ ...@@ -59,6 +59,11 @@
} }
} }
.tight-form-flex-wrapper {
display: flex;
flex-direction: row;
}
.grafana-metric-options { .grafana-metric-options {
margin-top: 25px; margin-top: 25px;
} }
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
@grafanaTargetFuncHightlight: #444; @grafanaTargetFuncHightlight: #444;
@modalBackground: @black; @modalBackground: @black;
@codeTagBackground: #444;
// Scaffolding // Scaffolding
// ------------------------- // -------------------------
......
...@@ -61,6 +61,7 @@ ...@@ -61,6 +61,7 @@
@grafanaTargetFuncHightlight: darken(@grafanaTargetBackground, 10%); @grafanaTargetFuncHightlight: darken(@grafanaTargetBackground, 10%);
@modalBackground: @bodyBackground; @modalBackground: @bodyBackground;
@codeTagBackground: #ddd;
// Scaffolding // Scaffolding
// ------------------------- // -------------------------
......
...@@ -15,7 +15,7 @@ define([], ...@@ -15,7 +15,7 @@ define([],
rows: [], rows: [],
pulldowns: [ { type: 'templating' }, { type: 'annotations' } ], pulldowns: [ { type: 'templating' }, { type: 'annotations' } ],
nav: [ { type: 'timepicker' } ], nav: [ { type: 'timepicker' } ],
time: {from: '1h', to: 'now'}, time: {from: 'now-6h', to: 'now'},
templating: { templating: {
list: [] list: []
}, },
......
...@@ -75,6 +75,14 @@ define([ ...@@ -75,6 +75,14 @@ define([
expect(time.to.valueOf()).to.equal(1410337665699); expect(time.to.valueOf()).to.equal(1410337665699);
}); });
it('should handle bad dates', function() {
ctx.$routeParams.from = '20151126T00010%3C%2Fp%3E%3Cspan%20class';
ctx.$routeParams.to = 'now';
_dashboard.time.from = 'now-6h';
ctx.service.init(_dashboard);
expect(ctx.service.time.from).to.equal('now-6h');
expect(ctx.service.time.to).to.equal('now');
});
}); });
describe('setTime', function() { describe('setTime', function() {
......
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