diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d6fa247b59..445fb52289e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ -# 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 * **table**: New powerful and flexible table panel, closes [#215](https://github.com/grafana/grafana/issues/215) @@ -6,6 +11,7 @@ ### Enhancements * **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 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) ### Bug Fixes diff --git a/appveyor.yml b/appveyor.yml index 993f8f2882c..83506312912 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,7 +5,7 @@ os: Windows Server 2012 R2 clone_folder: c:\gopath\src\github.com\grafana\grafana environment: - nodejs_version: "0.12.2" + nodejs_version: "4" GOPATH: c:\gopath install: diff --git a/build.go b/build.go index 4a345c5fc73..fcd7289cd6a 100644 --- a/build.go +++ b/build.go @@ -76,6 +76,14 @@ func main() { grunt("release") createLinuxPackages() + case "pkg-rpm": + grunt("release") + createRpmPackages() + + case "pkg-deb": + grunt("release") + createDebPackages() + case "latest": makeLatestDistCopies() @@ -147,7 +155,7 @@ type linuxPackageOptions struct { depends []string } -func createLinuxPackages() { +func createDebPackages() { createPackage(linuxPackageOptions{ packageType: "deb", homeDir: "/usr/share/grafana", @@ -167,7 +175,9 @@ func createLinuxPackages() { depends: []string{"adduser", "libfontconfig"}, }) +} +func createRpmPackages() { createPackage(linuxPackageOptions{ packageType: "rpm", homeDir: "/usr/share/grafana", @@ -189,6 +199,11 @@ func createLinuxPackages() { }) } +func createLinuxPackages() { + createDebPackages() + createRpmPackages() +} + func createPackage(options linuxPackageOptions) { packageRoot, _ := ioutil.TempDir("", "grafana-linux-pack") @@ -315,6 +330,8 @@ func build(pkg string, tags []string) { args = append(args, "-o", binary) args = append(args, pkg) setBuildEnv() + + runPrint("go", "version") runPrint("go", args...) // Create an md5 checksum of the binary, to be included in the archive for diff --git a/package.json b/package.json index 75136e48f1a..c82da556b1e 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Coding Instinct AB" }, "name": "grafana", - "version": "2.6.0-pre1", + "version": "2.6.0-beta1", "repository": { "type": "git", "url": "http://github.com/torkelo/grafana.git" diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index d6a853a9cdc..34ed216a4a6 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -174,6 +174,9 @@ func applyEnvVariableOverrides() { if len(envValue) > 0 { key.SetValue(envValue) + if strings.Contains(envKey, "PASSWORD") { + envValue = "*********" + } appliedEnvOverrides = append(appliedEnvOverrides, fmt.Sprintf("%s=%s", envKey, envValue)) } } @@ -188,6 +191,9 @@ func applyCommandLineDefaultProperties(props map[string]string) { value, exists := props[keyString] if exists { key.SetValue(value) + if strings.Contains(keyString, "password") { + value = "*********" + } appliedCommandLineProperties = append(appliedCommandLineProperties, fmt.Sprintf("%s=%s", keyString, value)) } } diff --git a/public/app/core/directives/metric_segment.js b/public/app/core/directives/metric_segment.js index 1d07544bb2d..bf7f7309b2b 100644 --- a/public/app/core/directives/metric_segment.js +++ b/public/app/core/directives/metric_segment.js @@ -55,8 +55,8 @@ function (_, $, coreModule) { }); }; - $scope.switchToLink = function() { - if (linkMode) { return; } + $scope.switchToLink = function(fromClick) { + if (linkMode && !fromClick) { return; } clearTimeout(cancelBlur); cancelBlur = null; @@ -69,7 +69,7 @@ function (_, $, coreModule) { $scope.inputBlur = function() { // happens long before the click event on the typeahead options // need to have long delay because the blur - cancelBlur = setTimeout($scope.switchToLink, 100); + cancelBlur = setTimeout($scope.switchToLink, 200); }; $scope.source = function(query, callback) { @@ -100,7 +100,7 @@ function (_, $, coreModule) { } $input.val(value); - $scope.switchToLink(); + $scope.switchToLink(true); return value; }; diff --git a/public/app/core/directives/value_select_dropdown.js b/public/app/core/directives/value_select_dropdown.js index 4f5076abd2f..873174c6fc6 100644 --- a/public/app/core/directives/value_select_dropdown.js +++ b/public/app/core/directives/value_select_dropdown.js @@ -156,7 +156,7 @@ function (angular, _, coreModule) { vm.selectionsChanged = function(commitChange) { 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') { vm.selectedValues[0].selected = false; vm.selectedValues = vm.selectedValues.slice(1, vm.selectedValues.length); diff --git a/public/app/features/dashboard/timeSrv.js b/public/app/features/dashboard/timeSrv.js index 691bfd07904..62d7b8c591e 100644 --- a/public/app/features/dashboard/timeSrv.js +++ b/public/app/features/dashboard/timeSrv.js @@ -47,8 +47,9 @@ define([ if (value.length === 15) { return moment.utc(value, 'YYYYMMDDTHHmmss'); } - var epoch = parseInt(value); - if (!_.isNaN(epoch)) { + + if (!isNaN(value)) { + var epoch = parseInt(value); return moment(epoch); } diff --git a/public/app/panels/table/controller.ts b/public/app/panels/table/controller.ts index 270e2f65a3d..54deb28b11e 100644 --- a/public/app/panels/table/controller.ts +++ b/public/app/panels/table/controller.ts @@ -26,7 +26,7 @@ export class TablePanelCtrl { var panelDefaults = { targets: [{}], - transform: 'timeseries_to_rows', + transform: 'timeseries_to_columns', pageSize: null, showHeader: true, styles: [ diff --git a/public/app/panels/table/editor.ts b/public/app/panels/table/editor.ts index e41ff422800..b9fc0c3dd63 100644 --- a/public/app/panels/table/editor.ts +++ b/public/app/panels/table/editor.ts @@ -45,11 +45,17 @@ export class TablePanelEditorCtrl { }; $scope.addColumn = function() { - $scope.panel.columns.push({text: $scope.addColumnSegment.value, value: $scope.addColumnSegment.value}); - $scope.render(); + var columns = transformers[$scope.panel.transform].getColumns($scope.dataRaw); + var column = _.findWhere(columns, {text: $scope.addColumnSegment.value}); + + if (column) { + $scope.panel.columns.push(column); + $scope.render(); + } var plusButton = uiSegmentSrv.newPlusButton(); $scope.addColumnSegment.html = plusButton.html; + $scope.addColumnSegment.value = plusButton.value; }; $scope.transformChanged = function() { diff --git a/public/app/panels/table/renderer.ts b/public/app/panels/table/renderer.ts index a55a8bf2fac..c782cce3579 100644 --- a/public/app/panels/table/renderer.ts +++ b/public/app/panels/table/renderer.ts @@ -112,7 +112,7 @@ export class TableRenderer { // this hack adds header content to cell (not visible) var widthHack = ''; if (addWidthHack) { - widthHack = '
' + this.table.columns[columnIndex].text + '
'; + widthHack = '
' + this.table.columns[columnIndex].text + '
'; } return '' + value + widthHack + ''; diff --git a/public/app/panels/table/specs/table_model_specs.ts b/public/app/panels/table/specs/table_model_specs.ts new file mode 100644 index 00000000000..8b137891791 --- /dev/null +++ b/public/app/panels/table/specs/table_model_specs.ts @@ -0,0 +1 @@ + diff --git a/public/app/plugins/datasource/cloudwatch/datasource.js b/public/app/plugins/datasource/cloudwatch/datasource.js index da95578782e..e01bdce6096 100644 --- a/public/app/plugins/datasource/cloudwatch/datasource.js +++ b/public/app/plugins/datasource/cloudwatch/datasource.js @@ -25,7 +25,7 @@ function (angular, _) { var end = convertToCloudWatchTime(options.range.to); var queries = []; - options = _.clone(options); + options = angular.copy(options); _.each(options.targets, _.bind(function(target) { if (target.hide || !target.namespace || !target.metricName || _.isEmpty(target.statistics)) { return; @@ -129,7 +129,7 @@ function (angular, _) { .pluck('Dimensions') .flatten() .filter(function(dimension) { - return dimension.Name === dimensionKey; + return dimension !== null && dimension.Name === dimensionKey; }) .pluck('Value') .uniq() diff --git a/public/app/plugins/datasource/elasticsearch/bucket_agg.js b/public/app/plugins/datasource/elasticsearch/bucket_agg.js index 014761da7f2..2a21bc17960 100644 --- a/public/app/plugins/datasource/elasticsearch/bucket_agg.js +++ b/public/app/plugins/datasource/elasticsearch/bucket_agg.js @@ -92,8 +92,10 @@ function (angular, _, queryDef) { } case 'date_histogram': { settings.interval = settings.interval || 'auto'; + settings.min_doc_count = settings.min_doc_count || 0; $scope.agg.field = $scope.target.timeField; settingsLinkText = 'Interval: ' + settings.interval; + settingsLinkText += ', Min Doc Count: ' + settings.min_doc_count; } } diff --git a/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html b/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html index f6ff3f6cd93..5a84ec8f6cd 100644 --- a/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html +++ b/public/app/plugins/datasource/elasticsearch/partials/bucketAgg.html @@ -35,9 +35,9 @@
-
+
    -
  • +
  • Interval
  • @@ -46,6 +46,17 @@
+
+
    +
  • + Min Doc Count +
  • +
  • + +
  • +
+
+
diff --git a/public/app/plugins/datasource/elasticsearch/query_builder.js b/public/app/plugins/datasource/elasticsearch/query_builder.js index de4e52a8e81..d736966285c 100644 --- a/public/app/plugins/datasource/elasticsearch/query_builder.js +++ b/public/app/plugins/datasource/elasticsearch/query_builder.js @@ -50,12 +50,23 @@ function () { return queryNode; }; - ElasticQueryBuilder.prototype.getInterval = function(agg) { - if (agg.settings && agg.settings.interval !== 'auto') { - return agg.settings.interval; - } else { - return '$interval'; + ElasticQueryBuilder.prototype.getDateHistogramAgg = function(aggDef) { + var esAgg = {}; + var settings = aggDef.settings || {}; + esAgg.interval = settings.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) { @@ -130,15 +141,7 @@ function () { switch(aggDef.type) { case 'date_histogram': { - esAgg["date_histogram"] = { - "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"; - } + esAgg["date_histogram"] = this.getDateHistogramAgg(aggDef); break; } case 'filters': { diff --git a/public/app/plugins/datasource/influxdb/partials/query.editor.html b/public/app/plugins/datasource/influxdb/partials/query.editor.html index 2b9f7e1a620..bec7c92b20a 100644 --- a/public/app/plugins/datasource/influxdb/partials/query.editor.html +++ b/public/app/plugins/datasource/influxdb/partials/query.editor.html @@ -55,12 +55,12 @@ -
-
- +
+
+
diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.js b/public/app/plugins/datasource/influxdb/query_ctrl.js index 52b7e7f1b7a..c3fd9f2db11 100644 --- a/public/app/plugins/datasource/influxdb/query_ctrl.js +++ b/public/app/plugins/datasource/influxdb/query_ctrl.js @@ -23,7 +23,6 @@ function (angular, _, InfluxQueryBuilder, InfluxQuery, queryPart) { $scope.resultFormats = [ {text: 'Time series', value: 'time_series'}, {text: 'Table', value: 'table'}, - {text: 'JSON field', value: 'json_field'}, ]; if (!$scope.target.measurement) { diff --git a/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts index 8352e41d99a..0e8e16a6114 100644 --- a/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts +++ b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts @@ -205,7 +205,7 @@ describe('when generating timeseries from influxdb response', function() { expect(table.type).to.be('table'); 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]); }); }); diff --git a/public/less/overrides.less b/public/less/overrides.less index 229627c0896..291a9a765f4 100644 --- a/public/less/overrides.less +++ b/public/less/overrides.less @@ -61,7 +61,7 @@ } code, pre { - background-color: @grayLighter; + background-color: @codeTagBackground; } div.editor-row { @@ -587,8 +587,10 @@ div.flot-text { // pre code, pre { - background-color: @grafanaPanelBackground; + background-color: @codeTagBackground; color: @textColor; + border: 1px solid darken(@codeTagBackground, 15%); + padding: 2px; } .dropdown-menu { diff --git a/public/less/tightform.less b/public/less/tightform.less index f65a991613a..b46456e74c2 100644 --- a/public/less/tightform.less +++ b/public/less/tightform.less @@ -59,6 +59,11 @@ } } +.tight-form-flex-wrapper { + display: flex; + flex-direction: row; +} + .grafana-metric-options { margin-top: 25px; } diff --git a/public/less/variables.dark.less b/public/less/variables.dark.less index af8f3cdec75..f2b4cba1706 100644 --- a/public/less/variables.dark.less +++ b/public/less/variables.dark.less @@ -48,6 +48,7 @@ @grafanaTargetFuncHightlight: #444; @modalBackground: @black; +@codeTagBackground: #444; // Scaffolding // ------------------------- diff --git a/public/less/variables.light.less b/public/less/variables.light.less index c64d5578e14..d4c0cd18998 100644 --- a/public/less/variables.light.less +++ b/public/less/variables.light.less @@ -61,6 +61,7 @@ @grafanaTargetFuncHightlight: darken(@grafanaTargetBackground, 10%); @modalBackground: @bodyBackground; +@codeTagBackground: #ddd; // Scaffolding // ------------------------- diff --git a/public/test/mocks/dashboard-mock.js b/public/test/mocks/dashboard-mock.js index 6367093bd36..9b61108728e 100644 --- a/public/test/mocks/dashboard-mock.js +++ b/public/test/mocks/dashboard-mock.js @@ -15,7 +15,7 @@ define([], rows: [], pulldowns: [ { type: 'templating' }, { type: 'annotations' } ], nav: [ { type: 'timepicker' } ], - time: {from: '1h', to: 'now'}, + time: {from: 'now-6h', to: 'now'}, templating: { list: [] }, diff --git a/public/test/specs/time_srv_specs.js b/public/test/specs/time_srv_specs.js index 9943aae6cc3..60bdd1a4c5b 100644 --- a/public/test/specs/time_srv_specs.js +++ b/public/test/specs/time_srv_specs.js @@ -75,6 +75,14 @@ define([ 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() {