Commit 80946795 by Torkel Ödegaard

Merge branch 'master' into create-annotations

parents 7aa992bd db36639f
...@@ -76,7 +76,7 @@ ...@@ -76,7 +76,7 @@
"systemjs-builder": "^0.15.34", "systemjs-builder": "^0.15.34",
"tether": "^1.4.0", "tether": "^1.4.0",
"tether-drop": "https://github.com/torkelo/drop", "tether-drop": "https://github.com/torkelo/drop",
"tslint": "^4.0.2", "tslint": "^4.5.1",
"typescript": "^2.1.4", "typescript": "^2.1.4",
"virtual-scroll": "^1.1.1" "virtual-scroll": "^1.1.1"
} }
......
...@@ -37,14 +37,8 @@ MAX_OPEN_FILES=10000 ...@@ -37,14 +37,8 @@ MAX_OPEN_FILES=10000
PID_FILE=/var/run/$NAME.pid PID_FILE=/var/run/$NAME.pid
DAEMON=/usr/sbin/$NAME DAEMON=/usr/sbin/$NAME
umask 0027 umask 0027
if [ `id -u` -ne 0 ]; then
echo "You need root privileges to run this script"
exit 4
fi
if [ ! -x $DAEMON ]; then if [ ! -x $DAEMON ]; then
echo "Program not installed or not executable" echo "Program not installed or not executable"
exit 5 exit 5
...@@ -63,9 +57,16 @@ fi ...@@ -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}" 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 case "$1" in
start) start)
checkUser
log_daemon_msg "Starting $DESC" log_daemon_msg "Starting $DESC"
pid=`pidofproc -p $PID_FILE grafana` pid=`pidofproc -p $PID_FILE grafana`
...@@ -112,6 +113,7 @@ case "$1" in ...@@ -112,6 +113,7 @@ case "$1" in
log_end_msg $return log_end_msg $return
;; ;;
stop) stop)
checkUser
log_daemon_msg "Stopping $DESC" log_daemon_msg "Stopping $DESC"
if [ -f "$PID_FILE" ]; then if [ -f "$PID_FILE" ]; then
......
...@@ -36,11 +36,6 @@ MAX_OPEN_FILES=10000 ...@@ -36,11 +36,6 @@ MAX_OPEN_FILES=10000
PID_FILE=/var/run/$NAME.pid PID_FILE=/var/run/$NAME.pid
DAEMON=/usr/sbin/$NAME 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 if [ ! -x $DAEMON ]; then
echo "Program not installed or not executable" echo "Program not installed or not executable"
exit 5 exit 5
...@@ -70,8 +65,16 @@ function isRunning() { ...@@ -70,8 +65,16 @@ function isRunning() {
status -p $PID_FILE $NAME > /dev/null 2>&1 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 case "$1" in
start) start)
checkUser
isRunning isRunning
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "Already running." echo "Already running."
...@@ -115,6 +118,7 @@ case "$1" in ...@@ -115,6 +118,7 @@ case "$1" in
exit $return exit $return
;; ;;
stop) stop)
checkUser
echo -n "Stopping $DESC: ..." echo -n "Stopping $DESC: ..."
if [ -f "$PID_FILE" ]; then if [ -f "$PID_FILE" ]; then
......
<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 ng-repeat="variable in ctrl.variables" ng-hide="variable.hide === 2" class="submenu-item gf-form-inline">
<div class="gf-form"> <div class="gf-form">
......
<query-editor-row query-ctrl="ctrl" has-text-edit-mode="true"> <query-editor-row query-ctrl="ctrl" has-text-edit-mode="true">
<div class="gf-form" ng-show="ctrl.target.textEditor"> <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>
<div ng-hide="ctrl.target.textEditor"> <div ng-hide="ctrl.target.textEditor">
......
...@@ -28,7 +28,6 @@ export class GraphiteQueryCtrl extends QueryCtrl { ...@@ -28,7 +28,6 @@ export class GraphiteQueryCtrl extends QueryCtrl {
} }
toggleEditorMode() { toggleEditorMode() {
this.target.textEditor = !this.target.textEditor;
this.parseTarget(); this.parseTarget();
} }
...@@ -55,7 +54,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { ...@@ -55,7 +54,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
} }
try { try {
this.parseTargeRecursive(astNode, null, 0); this.parseTargetRecursive(astNode, null, 0);
} catch (err) { } catch (err) {
console.log('error parsing target:', err.message); console.log('error parsing target:', err.message);
this.error = err.message; this.error = err.message;
...@@ -72,7 +71,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { ...@@ -72,7 +71,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
func.params[index] = value; func.params[index] = value;
} }
parseTargeRecursive(astNode, func, index) { parseTargetRecursive(astNode, func, index) {
if (astNode === null) { if (astNode === null) {
return null; return null;
} }
...@@ -81,7 +80,7 @@ export class GraphiteQueryCtrl extends QueryCtrl { ...@@ -81,7 +80,7 @@ export class GraphiteQueryCtrl extends QueryCtrl {
case 'function': case 'function':
var innerFunc = gfunc.createFuncInstance(astNode.name, { withDefaultParams: false }); var innerFunc = gfunc.createFuncInstance(astNode.name, { withDefaultParams: false });
_.each(astNode.params, (param, index) => { _.each(astNode.params, (param, index) => {
this.parseTargeRecursive(param, innerFunc, index); this.parseTargetRecursive(param, innerFunc, index);
}); });
innerFunc.updateText(); innerFunc.updateText();
...@@ -209,30 +208,61 @@ export class GraphiteQueryCtrl extends QueryCtrl { ...@@ -209,30 +208,61 @@ export class GraphiteQueryCtrl extends QueryCtrl {
} }
targetTextChanged() { targetTextChanged() {
this.parseTarget(); this.updateModelTarget();
this.panelCtrl.refresh(); this.refresh();
} }
updateModelTarget() { updateModelTarget() {
// render query // render query
if (!this.target.textEditor) {
var metricPath = this.getSegmentPathUpTo(this.segments.length); var metricPath = this.getSegmentPathUpTo(this.segments.length);
this.target.target = _.reduce(this.functions, this.wrapFunction, metricPath); 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 // render nested query
var targetsByRefId = _.keyBy(this.panelCtrl.panel.targets, 'refId'); var targetsByRefId = _.keyBy(this.panelCtrl.panel.targets, 'refId');
// no references to self
delete targetsByRefId[target.refId];
var nestedSeriesRefRegex = /\#([A-Z])/g; var nestedSeriesRefRegex = /\#([A-Z])/g;
var targetWithNestedQueries = this.target.target.replace(nestedSeriesRefRegex, (match, g1) => { var targetWithNestedQueries = target.target;
var target = targetsByRefId[g1];
if (!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; return match;
} }
return target.targetFull || target.target; // no circular references
delete targetsByRefId[g1];
return t.target;
}); });
delete this.target.targetFull; if (updated === targetWithNestedQueries) {
if (this.target.target !== targetWithNestedQueries) { break;
this.target.targetFull = targetWithNestedQueries; }
targetWithNestedQueries = updated;
}
delete target.targetFull;
if (target.target !== targetWithNestedQueries) {
target.targetFull = targetWithNestedQueries;
} }
} }
......
...@@ -186,4 +186,24 @@ describe('GraphiteQueryCtrl', function() { ...@@ -186,4 +186,24 @@ describe('GraphiteQueryCtrl', function() {
expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count)'); 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)');
});
});
}); });
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
<div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 2"> <div class="grafana-info-box span6" ng-if="ctrl.panelCtrl.editorHelpIndex === 2">
<h5>Stacking and fill</h5> <h5>Stacking and fill</h5>
<ul> <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>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>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> <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>
......
.submenu-controls { .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; margin: 0 $panel-margin ($panel-margin*2) $panel-margin;
} }
......
module.exports = function(config) { module.exports = function(config, grunt) {
'use strict' 'use strict'
return { return {
tslint : "node ./node_modules/tslint/lib/tslint-cli.js -c tslint.json --project ./tsconfig.json", 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", 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", tswatch: "node ./node_modules/typescript/lib/tsc.js -p tsconfig.json --diagnostics --watch",
}; };
......
module.exports = function(config, grunt) {
'use strict'
// dummy to avoid template compile error
return {
source: {
files: {
src: ""
}
}
};
};
...@@ -8,6 +8,10 @@ module.exports = function(config, grunt) { ...@@ -8,6 +8,10 @@ module.exports = function(config, grunt) {
var lastTime; var lastTime;
grunt.registerTask('watch', function() { 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(); done = this.async();
lastTime = new Date().getTime(); lastTime = new Date().getTime();
...@@ -58,7 +62,15 @@ module.exports = function(config, grunt) { ...@@ -58,7 +62,15 @@ module.exports = function(config, grunt) {
newPath = filepath.replace(/^public/, 'public_gen'); newPath = filepath.replace(/^public/, 'public_gen');
grunt.log.writeln('Copying to ' + newPath); grunt.log.writeln('Copying to ' + newPath);
grunt.file.copy(filepath, 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(); done();
......
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