Browse Source

Merge branch 'master' into create-annotations

Torkel Ödegaard 8 years ago
parent
commit
809467955a

+ 1 - 1
package.json

@@ -76,7 +76,7 @@
     "systemjs-builder": "^0.15.34",
     "tether": "^1.4.0",
     "tether-drop": "https://github.com/torkelo/drop",
-    "tslint": "^4.0.2",
+    "tslint": "^4.5.1",
     "typescript": "^2.1.4",
     "virtual-scroll": "^1.1.1"
   }

+ 9 - 7
packaging/deb/init.d/grafana-server

@@ -37,14 +37,8 @@ MAX_OPEN_FILES=10000
 PID_FILE=/var/run/$NAME.pid
 DAEMON=/usr/sbin/$NAME
 
-
 umask 0027
 
-if [ `id -u` -ne 0 ]; then
-	echo "You need root privileges to run this script"
-	exit 4
-fi
-
 if [ ! -x $DAEMON ]; then
   echo "Program not installed or not executable"
   exit 5
@@ -63,9 +57,16 @@ fi
 
 DAEMON_OPTS="--pidfile=${PID_FILE} --config=${CONF_FILE} cfg:default.paths.data=${DATA_DIR} cfg:default.paths.logs=${LOG_DIR} cfg:default.paths.plugins=${PLUGINS_DIR}"
 
+function checkUser() {
+  if [ `id -u` -ne 0 ]; then
+  	echo "You need root privileges to run this script"
+  	exit 4
+  fi
+}
+
 case "$1" in
   start)
-
+  checkUser
 	log_daemon_msg "Starting $DESC"
 
 	pid=`pidofproc -p $PID_FILE grafana`
@@ -112,6 +113,7 @@ case "$1" in
   log_end_msg $return
 	;;
   stop)
+  checkUser
 	log_daemon_msg "Stopping $DESC"
 
 	if [ -f "$PID_FILE" ]; then

+ 9 - 5
packaging/rpm/init.d/grafana-server

@@ -36,11 +36,6 @@ MAX_OPEN_FILES=10000
 PID_FILE=/var/run/$NAME.pid
 DAEMON=/usr/sbin/$NAME
 
-if [ `id -u` -ne 0 ]; then
-  echo "You need root privileges to run this script"
-  exit 4
-fi
-
 if [ ! -x $DAEMON ]; then
   echo "Program not installed or not executable"
   exit 5
@@ -70,8 +65,16 @@ function isRunning() {
   status -p $PID_FILE $NAME > /dev/null 2>&1
 }
 
+function checkUser() {
+  if [ `id -u` -ne 0 ]; then
+    echo "You need root privileges to run this script"
+    exit 4
+  fi
+}
+
 case "$1" in
   start)
+    checkUser
     isRunning
     if [ $? -eq 0 ]; then
       echo "Already running."
@@ -115,6 +118,7 @@ case "$1" in
     exit $return
     ;;
   stop)
+    checkUser
     echo -n "Stopping $DESC: ..."
 
     if [ -f "$PID_FILE" ]; then

+ 1 - 1
public/app/features/dashboard/submenu/submenu.html

@@ -1,4 +1,4 @@
-<div class="submenu-controls gf-form-query">
+<div class="submenu-controls">
 
   <div ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
     <div class="gf-form">

+ 1 - 1
public/app/plugins/datasource/graphite/partials/query.editor.html

@@ -1,7 +1,7 @@
 <query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
 
 	<div class="gf-form" ng-show="ctrl.target.textEditor">
-		<input type="text" class="gf-form-input" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.refresh()"></input>
+		<input type="text" class="gf-form-input" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.targetTextChanged()"></input>
 	</div>
 
   <div ng-hide="ctrl.target.textEditor">

+ 47 - 17
public/app/plugins/datasource/graphite/query_ctrl.ts

@@ -28,7 +28,6 @@ export class GraphiteQueryCtrl extends QueryCtrl {
   }
 
   toggleEditorMode() {
-    this.target.textEditor = !this.target.textEditor;
     this.parseTarget();
   }
 
@@ -55,7 +54,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
     }
 
     try {
-      this.parseTargeRecursive(astNode, null, 0);
+      this.parseTargetRecursive(astNode, null, 0);
     } catch (err) {
       console.log('error parsing target:', err.message);
       this.error = err.message;
@@ -72,7 +71,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
     func.params[index] = value;
   }
 
-  parseTargeRecursive(astNode, func, index) {
+  parseTargetRecursive(astNode, func, index) {
     if (astNode === null) {
       return null;
     }
@@ -81,7 +80,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
       case 'function':
         var innerFunc = gfunc.createFuncInstance(astNode.name, { withDefaultParams: false });
         _.each(astNode.params, (param, index) => {
-          this.parseTargeRecursive(param, innerFunc, index);
+          this.parseTargetRecursive(param, innerFunc, index);
         });
 
         innerFunc.updateText();
@@ -209,30 +208,61 @@ export class GraphiteQueryCtrl extends QueryCtrl {
   }
 
   targetTextChanged() {
-    this.parseTarget();
-    this.panelCtrl.refresh();
+    this.updateModelTarget();
+    this.refresh();
   }
 
   updateModelTarget() {
     // render query
-    var metricPath = this.getSegmentPathUpTo(this.segments.length);
-    this.target.target = _.reduce(this.functions, this.wrapFunction, metricPath);
+    if (!this.target.textEditor) {
+      var metricPath = this.getSegmentPathUpTo(this.segments.length);
+      this.target.target = _.reduce(this.functions, this.wrapFunction, metricPath);
+    }
 
+    this.updateRenderedTarget(this.target);
+
+    // loop through other queries and update targetFull as needed
+    for (const target of this.panelCtrl.panel.targets || []) {
+      if (target.refId !== this.target.refId) {
+        this.updateRenderedTarget(target);
+      }
+    }
+  }
+
+  updateRenderedTarget(target) {
     // render nested query
     var targetsByRefId = _.keyBy(this.panelCtrl.panel.targets, 'refId');
+
+    // no references to self
+    delete targetsByRefId[target.refId];
+
     var nestedSeriesRefRegex = /\#([A-Z])/g;
-    var targetWithNestedQueries = this.target.target.replace(nestedSeriesRefRegex, (match, g1) => {
-      var target  = targetsByRefId[g1];
-      if (!target) {
-        return match;
+    var targetWithNestedQueries = target.target;
+
+    // Keep interpolating until there are no query references
+    // The reason for the loop is that the referenced query might contain another reference to another query
+    while (targetWithNestedQueries.match(nestedSeriesRefRegex)) {
+      var updated = targetWithNestedQueries.replace(nestedSeriesRefRegex, (match, g1) => {
+        var t = targetsByRefId[g1];
+        if (!t) {
+          return match;
+        }
+
+        // no circular references
+        delete targetsByRefId[g1];
+        return t.target;
+      });
+
+      if (updated === targetWithNestedQueries) {
+        break;
       }
 
-      return target.targetFull || target.target;
-    });
+      targetWithNestedQueries = updated;
+    }
 
-    delete this.target.targetFull;
-    if (this.target.target !== targetWithNestedQueries) {
-      this.target.targetFull = targetWithNestedQueries;
+    delete target.targetFull;
+    if (target.target !== targetWithNestedQueries) {
+      target.targetFull = targetWithNestedQueries;
     }
   }
 

+ 20 - 0
public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts

@@ -186,4 +186,24 @@ describe('GraphiteQueryCtrl', function() {
       expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count)');
     });
   });
+
+  describe('when updating target used in other query', function() {
+    beforeEach(function() {
+      ctx.ctrl.target.target = 'metrics.a.count';
+      ctx.ctrl.target.refId = 'A';
+      ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{expandable: false}]));
+      ctx.ctrl.parseTarget();
+
+      ctx.ctrl.panelCtrl.panel.targets = [
+        ctx.ctrl.target, {target: 'sumSeries(#A)', refId: 'B'}
+      ];
+
+      ctx.ctrl.updateModelTarget();
+    });
+
+    it('targetFull of other query should update', function() {
+      expect(ctx.ctrl.panel.targets[1].targetFull).to.be('sumSeries(metrics.a.count)');
+    });
+  });
+
 });

+ 1 - 1
public/app/plugins/datasource/influxdb/partials/query.options.html

@@ -54,7 +54,7 @@
 		<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 2">
 			<h5>Stacking and fill</h5>
 			<ul>
-				<li>When stacking is enabled it important that points align</li>
+				<li>When stacking is enabled it is important that points align</li>
 				<li>If there are missing points for one series it can cause gaps or missing bars</li>
 				<li>You must use fill(0), and select a group by time low limit</li>
 				<li>Use the group by time option below your queries and specify for example &gt;10s if your metrics are written every 10 seconds</li>

+ 6 - 0
public/sass/components/_submenu.scss

@@ -1,4 +1,10 @@
 .submenu-controls {
+  display: flex;
+  flex-direction: row;
+  flex-wrap: wrap;
+  align-content: flex-start;
+  align-items: flex-start;
+
   margin: 0 $panel-margin ($panel-margin*2) $panel-margin;
 }
 

+ 2 - 1
tasks/options/exec.js

@@ -1,7 +1,8 @@
-module.exports = function(config) {
+module.exports = function(config, grunt) {
   'use strict'
   return {
     tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json",
+    tslintfile : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json <%= tslint.source.files.src %>",
     tscompile: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics",
     tswatch: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics --watch",
   };

+ 11 - 0
tasks/options/tslint.js

@@ -0,0 +1,11 @@
+module.exports = function(config, grunt) {
+  'use strict'
+  // dummy to avoid template compile error
+  return {
+    source: {
+      files: {
+        src: ""
+      }
+    }
+  };
+};

+ 13 - 1
tasks/options/watch.js

@@ -8,6 +8,10 @@ module.exports = function(config, grunt) {
   var lastTime;
 
   grunt.registerTask('watch', function() {
+    if (!grunt.option('skip-ts-compile')) {
+      grunt.log.writeln('We recommoned starting with: grunt watch --force --skip-ts-compile')
+      grunt.log.writeln('Then do incremental typescript builds with: grunt exec:tswatch')
+    }
 
     done = this.async();
     lastTime = new Date().getTime();
@@ -58,7 +62,15 @@ module.exports = function(config, grunt) {
           newPath = filepath.replace(/^public/, 'public_gen');
           grunt.log.writeln('Copying to ' + newPath);
           grunt.file.copy(filepath, newPath);
-          grunt.task.run('exec:tslint');
+
+          if (grunt.option('skip-ts-compile')) {
+            grunt.log.writeln('Skipping ts compile, run grunt exec:tswatch to start typescript watcher')
+          } else {
+            grunt.task.run('exec:tscompile');
+          }
+
+          grunt.config('tslint.source.files.src', filepath);
+          grunt.task.run('exec:tslintfile');
         }
 
         done();

+ 0 - 0
tasks/tslint.js