Kaynağa Gözat

Merge branch 'master' into dashboard_loading_refactoring

Torkel Ödegaard 11 yıl önce
ebeveyn
işleme
d198095d7a

+ 4 - 0
CHANGELOG.md

@@ -14,6 +14,10 @@
 - Improvement to InfluxDB query editor and function/value column selection (Issue #473)
 - Initial support for filtering (templated queries) for InfluxDB (PR #375) - thx @mavimo
 - Row editing and adding new panel is now a lot quicker and easier with the new row menu (Issue #475)
+- New datasource! Initial support for OpenTSDB (PR #211) - thx @mpage
+- Improvement and polish to the OpenTSDB query editor (Issue #492)
+- Influxdb group by support (Issue #441) thx @piis3
+
 
 #### Changes
 - Graphite panel is now renamed graph (Existing dashboards will still work)

+ 1 - 1
src/app/components/require.config.js

@@ -3,7 +3,7 @@
  */
 require.config({
   baseUrl: 'app',
-  // urlArgs: 'r=@REV@',
+
   paths: {
     config:                   ['../config', '../config.sample'],
     settings:                 'components/settings',

+ 1 - 0
src/app/controllers/all.js

@@ -12,4 +12,5 @@ define([
   './influxTargetCtrl',
   './playlistCtrl',
   './inspectCtrl',
+  './opentsdbTargetCtrl',
 ], function () {});

+ 118 - 0
src/app/controllers/opentsdbTargetCtrl.js

@@ -0,0 +1,118 @@
+define([
+  'angular',
+  'underscore',
+  'kbn'
+],
+function (angular, _, kbn) {
+  'use strict';
+
+  var module = angular.module('kibana.controllers');
+
+  module.controller('OpenTSDBTargetCtrl', function($scope, $timeout) {
+
+    $scope.init = function() {
+      $scope.target.errors = validateTarget($scope.target);
+      $scope.aggregators = ['avg', 'sum', 'min', 'max', 'dev', 'zimsum', 'mimmin', 'mimmax'];
+
+      if (!$scope.target.aggregator) {
+        $scope.target.aggregator = 'sum';
+      }
+
+      if (!$scope.target.downsampleAggregator) {
+        $scope.target.downsampleAggregator = 'sum';
+      }
+
+      $scope.$on('typeahead-updated', function() {
+        $timeout($scope.targetBlur);
+      });
+    };
+
+    $scope.targetBlur = function() {
+      $scope.target.errors = validateTarget($scope.target);
+
+      // this does not work so good
+      if (!_.isEqual($scope.oldTarget, $scope.target) && _.isEmpty($scope.target.errors)) {
+        $scope.oldTarget = angular.copy($scope.target);
+        $scope.get_data();
+      }
+    };
+
+    $scope.duplicate = function() {
+      var clone = angular.copy($scope.target);
+      $scope.panel.targets.push(clone);
+    };
+
+    $scope.suggestMetrics = function(query, callback) {
+      $scope.datasource
+        .performSuggestQuery(query, 'metrics')
+        .then(callback);
+    };
+
+    $scope.suggestTagKeys = function(query, callback) {
+      $scope.datasource
+        .performSuggestQuery(query, 'tagk')
+        .then(callback);
+    };
+
+    $scope.suggestTagValues = function(query, callback) {
+      $scope.datasource
+        .performSuggestQuery(query, 'tagv')
+        .then(callback);
+    };
+
+    $scope.addTag = function() {
+      if (!$scope.addTagMode) {
+        $scope.addTagMode = true;
+        return;
+      }
+
+      if (!$scope.target.tags) {
+        $scope.target.tags = {};
+      }
+
+      $scope.target.errors = validateTarget($scope.target);
+
+      if (!$scope.target.errors.tags) {
+        $scope.target.tags[$scope.target.currentTagKey] = $scope.target.currentTagValue;
+        $scope.target.currentTagKey = '';
+        $scope.target.currentTagValue = '';
+        $scope.targetBlur();
+      }
+
+      $scope.addTagMode = false;
+    };
+
+    $scope.removeTag = function(key) {
+      delete $scope.target.tags[key];
+      $scope.targetBlur();
+    };
+
+    function validateTarget(target) {
+      var errs = {};
+
+      if (!target.metric) {
+        errs.metric = "You must supply a metric name.";
+      }
+
+      if (target.shouldDownsample) {
+        try {
+          if (target.downsampleInterval) {
+            kbn.describe_interval(target.downsampleInterval);
+          } else {
+            errs.downsampleInterval = "You must supply a downsample interval (e.g. '1m' or '1h').";
+          }
+        } catch(err) {
+          errs.downsampleInterval = err.message;
+        }
+      }
+
+      if (target.tags && _.has(target.tags, target.currentTagKey)) {
+        errs.tags = "Duplicate tag key '" + target.currentTagKey + "'.";
+      }
+
+      return errs;
+    }
+
+  });
+
+});

+ 3 - 3
src/app/directives/influxdbFuncEditor.js

@@ -114,10 +114,10 @@ function (angular, _, $) {
             $paramLink.appendTo(elem);
             $input.appendTo(elem);
 
-            $input.blur(_.partial(inputBlur));
+            $input.blur(inputBlur);
             $input.keyup(inputKeyDown);
-            $input.keypress(_.partial(inputKeyPress));
-            $paramLink.click(_.partial(clickFuncParam));
+            $input.keypress(inputKeyPress);
+            $paramLink.click(clickFuncParam);
 
             addTypeahead($input);
 

+ 22 - 6
src/app/partials/influxdb/editor.html

@@ -75,16 +75,13 @@
 
           <li>
             <a class="grafana-target-segment"
-               ng-click="target.condiction_filter = !target.condiction_filter; get_data();"
+               ng-click="target.condition_filter = !target.condition_filter; get_data();"
+               bs-tooltip="'Add a where clause'"
                role="menuitem">
               <i class="icon-filter"></i>
             </a>
           </li>
-          <li ng-show="target.condiction_filter">
-            <select class="input-small grafana-target-segment-input"
-                    ng-change="get_data()"
-                    ng-model="target.condition_add"
-                    ng-options="f for f in ['and', 'or']" ></select>
+          <li ng-show="target.condition_filter">
             <input type="text"
                    class="input-small grafana-target-segment-input"
                    ng-model="target.condition_key"
@@ -118,6 +115,25 @@
                    spellcheck='false'
                    ng-model-onblur ng-change="get_data()" >
           </li>
+          <li>
+            <a class="grafana-target-segment"
+               ng-click="target.groupby_field_add = !target.groupby_field_add; get_data();"
+               bs-tooltip="'Add a group by column'"
+               role="menuitem">
+              <i class="icon-plus"></i>
+            </a>
+          </li>
+
+          <li ng-show="target.groupby_field_add">
+            <input type="text"
+                   class="input-small grafana-target-segment-input"
+                   ng-model="target.groupby_field"
+                   placeholder="column"
+                   spellcheck="false"
+                   bs-typeahead="listColumns"
+                   data-min-length=0
+                   ng-blur="get_data()">
+          </li>
 
           <li class="grafana-target-segment">
             as

+ 176 - 0
src/app/partials/opentsdb/editor.html

@@ -0,0 +1,176 @@
+<div class="editor-row" style="margin-top: 10px;">
+  <div  ng-repeat="target in panel.targets"
+        class="grafana-target"
+        ng-class="{'grafana-target-hidden': target.hide}"
+        ng-controller="OpenTSDBTargetCtrl"
+        ng-init="init()">
+
+    <div class="grafana-target-inner-wrapper">
+      <div class="grafana-target-inner">
+        <ul class="grafana-target-controls">
+          <li class="dropdown">
+            <a  class="pointer dropdown-toggle"
+                data-toggle="dropdown"
+                tabindex="1">
+              <i class="icon-cog"></i>
+            </a>
+            <ul class="dropdown-menu pull-right" role="menu">
+              <li role="menuitem">
+                <a  tabindex="1"
+                    ng-click="duplicate()">
+                  Duplicate
+                </a>
+              </li>
+            </ul>
+          </li>
+          <li>
+            <a class="pointer" tabindex="1" ng-click="removeTarget(target)">
+              <i class="icon-remove"></i>
+            </a>
+          </li>
+        </ul>
+
+        <ul class="grafana-target-controls-left">
+          <li>
+            <a  class="grafana-target-segment"
+                ng-click="target.hide = !target.hide; get_data();"
+                role="menuitem">
+              <i class="icon-eye-open"></i>
+            </a>
+          </li>
+        </ul>
+
+        <ul class="grafana-segment-list" role="menu">
+          <li>
+            <input type="text"
+                   class="input-xxlarge grafana-target-segment-input"
+                   ng-model="target.metric"
+                   spellcheck='false'
+                   bs-typeahead="suggestMetrics"
+                   placeholder="metric name"
+                   data-min-length=0 data-items=100
+                   ng-blur="targetBlur()"
+                   >
+            <a bs-tooltip="target.errors.metric"
+               style="color: rgb(229, 189, 28)"
+               ng-show="target.errors.metric">
+              <i class="icon-warning-sign"></i>
+            </a>
+          </li>
+          <li class="grafana-target-segment">
+            Aggregator
+          <li>
+            <select ng-model="target.aggregator"
+                    class="grafana-target-segment-input input-small"
+                    ng-options="agg for agg in aggregators"
+                    ng-change="targetBlur()">
+            </select>
+            <a bs-tooltip="target.errors.aggregator"
+               style="color: rgb(229, 189, 28)"
+               ng-show="target.errors.aggregator">
+              <i class="icon-warning-sign"></i>
+            </a>
+          </li>
+
+          <li class="grafana-target-segment">
+              Rate:
+              <input type="checkbox"
+                     class="grafana-target-option-checkbox"
+                     ng-model="target.shouldComputeRate"
+                     ng-change="targetBlur()"
+                     >
+          </li>
+          <li class="grafana-target-segment" ng-hide="!target.shouldComputeRate">
+            Counter:
+            <input type="checkbox"
+                   class="grafana-target-option-checkbox"
+                   ng-disabled="!target.shouldComputeRate"
+                   ng-model="target.isCounter"
+                   ng-change="targetBlur()">
+          </li>
+        </ul>
+
+        <div class="clearfix"></div>
+      </div>
+
+      <div class="grafana-target-inner">
+        <ul class="grafana-segment-list" role="menu">
+
+          <li class="grafana-target-segment">
+            Downsample:
+            <input type="checkbox"
+                   class="grafana-target-option-checkbox"
+                   ng-model="target.shouldDownsample"
+                   ng-change="targetBlur(target)"
+                   >
+          </li>
+
+          <li ng-hide="!target.shouldDownsample">
+            <input type="text"
+                   class="input-small grafana-target-segment-input"
+                   ng-disabled="!target.shouldDownsample"
+                   ng-model="target.downsampleInterval"
+                   ng-change="targetBlur()"
+                   placeholder="interval"
+                   >
+          </li>
+
+          <li class="grafana-target-segment" ng-hide="!target.shouldDownsample">
+            Aggregator
+          </li>
+
+          <li ng-hide="!target.shouldDownsample">
+            <select ng-model="target.downsampleAggregator"
+                    class="grafana-target-segment-input input-small"
+                    ng-options="agg for agg in aggregators"
+                    ng-change="targetBlur()">
+            </select>
+          </li>
+
+          <li class="grafana-target-segment">
+              Tags:
+          </li>
+          <li ng-repeat="(key, value) in target.tags track by $index" class="grafana-target-segment">
+            {{key}}&nbsp;=&nbsp;{{value}}
+            <a ng-click="removeTag(key)">
+              <i class="icon-remove"></i>
+            </a>
+          </li>
+
+          <li class="grafana-target-segment" ng-hide="addTagMode">
+            <a ng-click="addTag()">
+              <i class="icon-plus-sign"></i>
+            </a>
+          </li>
+
+          <li ng-show="addTagMode">
+              <input type="text"
+                     class="input-small grafana-target-segment-input"
+                     spellcheck='false'
+                     bs-typeahead="suggestTagKeys"
+                     data-min-length=0 data-items=100
+                     ng-model="target.currentTagKey"
+                     placeholder="key">
+              <input type="text"
+                     class="input-small grafana-target-segment-input"
+                     spellcheck='false'
+                     bs-typeahead="suggestTagValues"
+                     data-min-length=0 data-items=100
+                     ng-model="target.currentTagValue"
+                     placeholder="value">
+              <a ng-click="addTag()">
+                <i class="icon-plus-sign"></i>
+              </a>
+              <a bs-tooltip="target.errors.tags"
+                 style="color: rgb(229, 189, 28)"
+                 ng-show="target.errors.tags">
+                <i class="icon-warning-sign"></i>
+              </a>
+          </li>
+        </ul>
+
+        <div class="clearfix"></div>
+      </div>
+    </div>
+  </div>
+</div>

+ 4 - 1
src/app/services/datasourceSrv.js

@@ -4,13 +4,14 @@ define([
   'config',
   './graphite/graphiteDatasource',
   './influxdb/influxdbDatasource',
+  './opentsdb/opentsdbDatasource',
 ],
 function (angular, _, config) {
   'use strict';
 
   var module = angular.module('kibana.services');
 
-  module.service('datasourceSrv', function($q, $http, GraphiteDatasource, InfluxDatasource) {
+  module.service('datasourceSrv', function($q, filterSrv, $http, GraphiteDatasource, InfluxDatasource, OpenTSDBDatasource) {
 
     this.init = function() {
       var defaultDatasource = _.findWhere(_.values(config.datasources), { default: true });
@@ -23,6 +24,8 @@ function (angular, _, config) {
         return new GraphiteDatasource(ds);
       case 'influxdb':
         return new InfluxDatasource(ds);
+      case 'opentsdb':
+        return new OpenTSDBDatasource(ds);
       }
     };
 

+ 63 - 24
src/app/services/influxdb/influxdbDatasource.js

@@ -17,7 +17,6 @@ function (angular, _, kbn) {
       this.username = datasource.username;
       this.password = datasource.password;
       this.name = datasource.name;
-
       this.templateSettings = {
         interpolate : /\[\[([\s\S]+?)\]\]/g,
       };
@@ -33,6 +32,7 @@ function (angular, _, kbn) {
         }
 
         var timeFilter = getTimeFilter(options);
+        var groupByField;
 
         if (target.rawQuery) {
           query = target.query;
@@ -43,6 +43,10 @@ function (angular, _, kbn) {
           var groupByIndex = lowerCaseQueryElements.indexOf("group");
           var orderIndex = lowerCaseQueryElements.indexOf("order");
 
+          if (lowerCaseQueryElements[1].indexOf(',')) {
+            groupByField = lowerCaseQueryElements[1].replace(',', '');
+          }
+
           if (whereIndex !== -1) {
             queryElements.splice(whereIndex + 1, 0, timeFilter, "and");
           }
@@ -63,9 +67,10 @@ function (angular, _, kbn) {
           query = filterSrv.applyTemplateToTarget(query);
         }
         else {
-          var template = "select [[func]](\"[[column]]\") as \"[[column]]_[[func]]\" from \"[[series]]\" " +
+
+          var template = "select [[group]][[group_comma]] [[func]](\"[[column]]\") as \"[[column]]_[[func]]\" from \"[[series]]\" " +
                          "where  [[timeFilter]] [[condition_add]] [[condition_key]] [[condition_op]] [[condition_value]] " +
-                         "group by time([[interval]]) order asc";
+                         "group by time([[interval]])[[group_comma]] [[group]] order asc";
 
           var templateData = {
             series: target.series,
@@ -73,10 +78,12 @@ function (angular, _, kbn) {
             func: target.function,
             timeFilter: timeFilter,
             interval: target.interval || options.interval,
-            condition_add: target.condiction_filter ? target.condition_add : '',
-            condition_key: target.condiction_filter ? target.condition_key : '',
-            condition_op: target.condiction_filter ? target.condition_op : '',
-            condition_value: target.condiction_filter ? target.condition_value: ''
+            condition_add: target.condition_filter ? 'and' : '',
+            condition_key: target.condition_filter ? target.condition_key : '',
+            condition_op: target.condition_filter ? target.condition_op : '',
+            condition_value: target.condition_filter ? target.condition_value : '',
+            group_comma: target.groupby_field_add && target.groupby_field ? ',' : '',
+            group: target.groupby_field_add ? target.groupby_field : '',
           };
 
           query = _.template(template, templateData, this.templateSettings);
@@ -86,10 +93,15 @@ function (angular, _, kbn) {
             alias = filterSrv.applyTemplateToTarget(target.alias);
           }
 
+          if (target.groupby_field_add) {
+            groupByField = target.groupby_field;
+          }
+
           target.query = query;
         }
 
-        return this.doInfluxRequest(query, alias).then(handleInfluxQueryResponse);
+        var handleResponse = _.partial(handleInfluxQueryResponse, alias, groupByField);
+        return this.doInfluxRequest(query, alias).then(handleResponse);
 
       }, this);
 
@@ -126,7 +138,7 @@ function (angular, _, kbn) {
         return $q.reject(err);
       }
 
-      return this.doInfluxRequest(query, 'filters')
+      return this.doInfluxRequest(query)
         .then(function (results) {
           return _.map(results[0].points, function (metric) {
             return {
@@ -139,16 +151,18 @@ function (angular, _, kbn) {
 
     function retry(deferred, callback, delay) {
       return callback().then(undefined, function(reason) {
-        if (reason.status !== 0) {
+        if (reason.status !== 0 || reason.status >= 300) {
           deferred.reject(reason);
         }
-        setTimeout(function() {
-          return retry(deferred, callback, Math.min(delay * 2, 30000));
-        }, delay);
+        else {
+          setTimeout(function() {
+            return retry(deferred, callback, Math.min(delay * 2, 30000));
+          }, delay);
+        }
       });
     }
 
-    InfluxDatasource.prototype.doInfluxRequest = function(query, alias) {
+    InfluxDatasource.prototype.doInfluxRequest = function(query) {
       var _this = this;
       var deferred = $q.defer();
 
@@ -170,7 +184,6 @@ function (angular, _, kbn) {
         };
 
         return $http(options).success(function (data) {
-          data.alias = alias;
           deferred.resolve(data);
         });
       }, 10);
@@ -178,28 +191,54 @@ function (angular, _, kbn) {
       return deferred.promise;
     };
 
-    function handleInfluxQueryResponse(data) {
+    function handleInfluxQueryResponse(alias, groupByField, data) {
       var output = [];
 
       _.each(data, function(series) {
+        var seriesName;
         var timeCol = series.columns.indexOf('time');
+        var valueCol = 1;
+        var groupByCol = -1;
+
+        if (groupByField) {
+          groupByCol = series.columns.indexOf(groupByField);
+        }
 
+        // find value column
         _.each(series.columns, function(column, index) {
-          if (column === "time" || column === "sequence_number") {
-            return;
+          if (column !== 'time' && column !== 'sequence_number' && column !== groupByField) {
+            valueCol = index;
           }
+        });
+
+        var groups = {};
+
+        if (groupByField) {
+          groups = _.groupBy(series.points, function (point) {
+            return point[groupByCol];
+          });
+        }
+        else {
+          groups[series.columns[valueCol]] = series.points;
+        }
 
-          var target = data.alias || series.name + "." + column;
+        _.each(groups, function(groupPoints, key) {
           var datapoints = [];
-          var value;
+          for (var i = 0; i < groupPoints.length; i++) {
+            var metricValue = isNaN(groupPoints[i][valueCol]) ? null : groupPoints[i][valueCol];
+            datapoints[i] = [metricValue, groupPoints[i][timeCol]];
+          }
 
-          for (var i = 0; i < series.points.length; i++) {
-            value = isNaN(series.points[i][index]) ? null : series.points[i][index];
-            datapoints[i] = [value, series.points[i][timeCol]];
+          seriesName = alias ? alias : key;
+
+          // if mulitple groups append key to alias
+          if (alias && groupByField) {
+            seriesName += key;
           }
 
-          output.push({ target: target, datapoints: datapoints });
+          output.push({ target: seriesName, datapoints: datapoints });
         });
+
       });
 
       return output;

+ 155 - 0
src/app/services/opentsdb/opentsdbDatasource.js

@@ -0,0 +1,155 @@
+define([
+  'angular',
+  'underscore',
+  'kbn'
+],
+function (angular, _, kbn) {
+  'use strict';
+
+  var module = angular.module('kibana.services');
+
+  module.factory('OpenTSDBDatasource', function($q, $http) {
+
+    function OpenTSDBDatasource(datasource) {
+      this.type = 'opentsdb';
+      this.editorSrc = 'app/partials/opentsdb/editor.html';
+      this.url = datasource.url;
+      this.name = datasource.name;
+    }
+
+    // Called once per panel (graph)
+    OpenTSDBDatasource.prototype.query = function(filterSrv, options) {
+      var start = convertToTSDBTime(options.range.from);
+      var end = convertToTSDBTime(options.range.to);
+      var queries = _.compact(_.map(options.targets, convertTargetToQuery));
+
+      // No valid targets, return the empty result to save a round trip.
+      if (_.isEmpty(queries)) {
+        var d = $q.defer();
+        d.resolve({ data: [] });
+        return d.promise;
+      }
+
+      var groupByTags = {};
+      _.each(queries, function(query) {
+        _.each(query.tags, function(val, key) {
+          if (val === "*") {
+            groupByTags[key] = true;
+          }
+        });
+      });
+
+      return this.performTimeSeriesQuery(queries, start, end)
+        .then(function(response) {
+          var result = _.map(response.data, function(metricData) {
+            return transformMetricData(metricData, groupByTags);
+          });
+          return { data: result };
+        });
+    };
+
+    OpenTSDBDatasource.prototype.performTimeSeriesQuery = function(queries, start, end) {
+      var reqBody = {
+        start: start,
+        queries: queries
+      };
+
+      // Relative queries (e.g. last hour) don't include an end time
+      if (end) {
+        reqBody.end = end;
+      }
+
+      var options = {
+        method: 'POST',
+        url: this.url + '/api/query',
+        data: reqBody
+      };
+
+      return $http(options);
+    };
+
+    OpenTSDBDatasource.prototype.performSuggestQuery = function(query, type) {
+      var options = {
+        method: 'GET',
+        url: this.url + '/api/suggest',
+        params: {
+          type: type,
+          q: query
+        }
+      };
+      return $http(options).then(function(result) {
+        return result.data;
+      });
+    };
+
+    function transformMetricData(md, groupByTags) {
+      var dps = [];
+
+      // TSDB returns datapoints has a hash of ts => value.
+      // Can't use _.pairs(invert()) because it stringifies keys/values
+      _.each(md.dps, function (v, k) {
+        dps.push([v, k]);
+      });
+
+      var target = md.metric;
+      if (!_.isEmpty(md.tags)) {
+        var tagData = [];
+
+        _.each(_.pairs(md.tags), function(tag) {
+          if (_.has(groupByTags, tag[0])) {
+            tagData.push(tag[0] + "=" + tag[1]);
+          }
+        });
+
+        if (!_.isEmpty(tagData)) {
+          target = target + "{" + tagData.join(", ") + "}";
+        }
+      }
+
+      return { target: target, datapoints: dps };
+    }
+
+    function convertTargetToQuery(target) {
+      if (!target.metric) {
+        return null;
+      }
+
+      var query = {
+        metric: target.metric,
+        aggregator: "avg"
+      };
+
+      if (target.aggregator) {
+        query.aggregator = target.aggregator;
+      }
+
+      if (target.shouldComputeRate) {
+        query.rate = true;
+        query.rateOptions = {
+          counter: !!target.isCounter
+        };
+      }
+
+      if (target.shouldDownsample) {
+        query.downsample = target.downsampleInterval + "-" + target.downsampleAggregator;
+      }
+
+      query.tags = angular.copy(target.tags);
+
+      return query;
+    }
+
+    function convertToTSDBTime(date) {
+      if (date === 'now') {
+        return null;
+      }
+
+      date = kbn.parseDate(date);
+
+      return date.getTime();
+    }
+
+    return OpenTSDBDatasource;
+  });
+
+});

+ 2 - 2
src/config.sample.js

@@ -18,8 +18,8 @@ function (Settings) {
       },
       influxdb: {
         type: 'influxdb',
-        url: "http://my_influxdb_server:8080/db/database_name",
-        user: 'admin',
+        url: "http://my_influxdb_server:8086/db/database_name",
+        username: 'admin',
         password: 'admin'
       },
     },

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/css/bootstrap.dark.min.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/css/bootstrap.light.min.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
src/css/default.min.css


+ 4 - 0
src/css/less/grafana.less

@@ -371,6 +371,10 @@ input[type=text].grafana-target-segment-input {
   padding: 8px 4px;
 }
 
+input[type=checkbox].grafana-target-option-checkbox {
+  margin: 0;
+}
+
 select.grafana-target-segment-input {
   border: none;
   border-right: 1px solid @grafanaTargetSegmentBorder;

+ 11 - 24
tasks/build_task.js

@@ -11,40 +11,27 @@ module.exports = function(grunt) {
     'htmlmin:build',
     'ngtemplates',
     'cssmin:build',
+    'build:grafanaVersion',
     'ngmin:build',
     'requirejs:build',
     'concat:js',
     'filerev',
     'usemin',
     'clean:temp',
-    'build:write_revision',
     'uglify:dest'
   ]);
 
-  // run a string replacement on the require config, using the latest revision number as the cache buster
-  grunt.registerTask('build:write_revision', function() {
-    grunt.event.once('git-describe', function (desc) {
 
-      grunt.config('string-replace.config', {
-        files: {
-          '<%= destDir %>/app/components/require.config.js': '<%= destDir %>/app/components/require.config.js',
-          '<%= destDir %>/app/app.js': '<%= destDir %>/app/app.js'
-        },
-        options: {
-          replacements: [
-            {
-              pattern: /@REV@/g,
-              replacement: desc.object
-            },
-            {
-              pattern: /@grafanaVersion@/g,
-              replacement: '<%= pkg.version %>'
-            }
-          ]
-        }
-      });
-      grunt.task.run('string-replace:config');
+  grunt.registerTask('build:grafanaVersion', function() {
+    grunt.config('string-replace.config', {
+      files: {
+        '<%= tempDir %>/app/app.js': '<%= tempDir %>/app/app.js'
+      },
+      options: {
+        replacements: [{ pattern: /@grafanaVersion@/g,  replacement: '<%= pkg.version %>' }]
+      }
     });
-    grunt.task.run('git-describe');
+    grunt.task.run('string-replace:config');
   });
+
 };

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor