Merge branch 'master' into alerting_definitions

This commit is contained in:
bergquist
2016-05-09 11:34:56 +02:00
23 changed files with 97 additions and 55 deletions

View File

@ -1,11 +1,16 @@
# 3.0.0 stable (unreleased)
* **Templating**: Fixed issue with new data source variable not persisting current selected value, fixes [#4934](https://github.com/grafana/grafana/issues/4934)
# 3.0.0-beta7 (2016-05-02)
### Bug fixes
* **Dashboard title**: Fixed max dashboard title width (media query) for large screens, fixes [#4859](https://github.com/grafana/grafana/issues/4859)
* **Annotations**: Fixed issue with entering annotation edit view, fixes [#4857](https://github.com/grafana/grafana/issues/4857)
* **Remove query**: Fixed issue with removing query for data sources without collapsable query editors, fixes [#4856](https://github.com/grafana/grafana/issues/4856)
* **Graphite PNG*: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864)
* **Graphite PNG**: Fixed issue graphite png rendering option, fixes [#4864](https://github.com/grafana/grafana/issues/4864)
* **InfluxDB**: Fixed issue missing plus group by iconn, fixes [#4862](https://github.com/grafana/grafana/issues/4862)
* **Graph**: Fixes missing line mode for thresholds, fixes [#4902](https://github.com/grafana/grafana/pull/4902)
### Enhancements
* **InfluxDB**: Added new functions moving_average and difference to query editor, closes [#4698](https://github.com/grafana/grafana/issues/4698)

View File

@ -78,7 +78,7 @@ the latest master builds [here](http://grafana.org/download/builds)
### Dependencies
- Go 1.5
- NodeJS
- NodeJS v0.12.0
- [Godep](https://github.com/tools/godep)
### Get Code
@ -87,8 +87,19 @@ the latest master builds [here](http://grafana.org/download/builds)
go get github.com/grafana/grafana
```
Since imports of dependencies use the absolute path github.com/grafana/grafana within the $GOPATH,
you will need to put your version of the code in $GOPATH/src/github.com/grafana/grafana to be able
to develop and build grafana on a cloned repository. To do so, you can clone your forked repository
directly to $GOPATH/src/github.com/grafana or you can create a symbolic link from your version
of the code to $GOPATH/src/github.com/grafana/grafana. The last options makes it possible to change
easily the grafana repository you want to build.
```bash
go get github.com/*your_account*/grafana
mkdir $GOPATH/src/github.com/grafana
ln -s github.com/*your_account*/grafana $GOPATH/src/github.com/grafana/grafana
```
### Building the backend
Replace X.Y.Z by actual version number.
```bash
cd $GOPATH/src/github.com/grafana/grafana
go run build.go setup (only needed once to install godep)
@ -98,7 +109,7 @@ go run build.go build
### Building frontend assets
To build less to css for the frontend you will need a recent version of of node (v0.12.0),
To build less to css for the frontend you will need a recent version of of **node (v0.12.0)**,
npm (v2.5.0) and grunt (v0.4.5). Run the following:
```bash
@ -106,6 +117,13 @@ npm install
npm run build
```
To build the frontend assets only on changes:
```bash
sudo npm install -g grunt-cli # to do only once to install grunt command line interface
grunt watch
```
### Recompile backend on source change
To rebuild on source change (requires that you executed godep restore)
```bash

View File

@ -136,7 +136,7 @@ func readVersionFromPackageJson() {
// add timestamp to iteration
linuxPackageIteration = fmt.Sprintf("%s%v", linuxPackageIteration, time.Now().Unix())
}
log.Println(fmt.Sprintf("teration %v", linuxPackageIteration))
log.Println(fmt.Sprintf("Iteration %v", linuxPackageIteration))
}
}

View File

@ -241,14 +241,14 @@ templates_pattern = emails/*.html
#################################### Logging ##########################
[log]
# Either "console", "file", default is "console"
# Either "console", "file", "syslog". Default is console and file
# Use comma to separate multiple modes, e.g. "console, file"
mode = console, file
# Buffer length of channel, keep it as it is if you don't know what it is.
buffer_len = 10000
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info"
level = Info
# For "console" mode only

View File

@ -223,14 +223,14 @@ check_for_updates = true
#################################### Logging ##########################
[log]
# Either "console", "file", default is "console"
# Either "console", "file", "syslog". Default is console and file
# Use comma to separate multiple modes, e.g. "console, file"
;mode = console, file
# Buffer length of channel, keep it as it is if you don't know what it is.
;buffer_len = 10000
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace"
# Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Info"
;level = Info
# For "console" mode only

View File

@ -30,7 +30,7 @@ Access | Proxy = access via Grafana backend, Direct = access directory from brow
## Query editor
Open a graph in edit mode by click the title.
![](/img/v2/kairos_query_editor.png)
![](/img/v2/kairos_query_editor.jpg)
For details on KairosDB metric queries checkout the official.
- [Query Metrics - KairosDB 0.9.4 documentation](http://kairosdb.github.io/kairosdocs/restapi/QueryMetrics.html).

View File

@ -86,7 +86,7 @@ func init() {
"AWS/EC2": {"AutoScalingGroupName", "ImageId", "InstanceId", "InstanceType"},
"AWS/ELB": {"LoadBalancerName", "AvailabilityZone"},
"AWS/ElasticMapReduce": {"ClusterId", "JobFlowId", "JobId"},
"AWS/ES": {},
"AWS/ES": {"ClientId", "DomainName"},
"AWS/Events": {"RuleName"},
"AWS/Kinesis": {"StreamName", "ShardID"},
"AWS/Lambda": {"FunctionName"},

View File

@ -29,6 +29,7 @@ type PluginListItem struct {
Info *plugins.PluginInfo `json:"info"`
LatestVersion string `json:"latestVersion"`
HasUpdate bool `json:"hasUpdate"`
DefaultNavUrl string `json:"defaultNavUrl"`
}
type PluginList []PluginListItem

View File

@ -8,6 +8,7 @@ import (
"github.com/grafana/grafana/pkg/middleware"
m "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/setting"
)
func GetPluginList(c *middleware.Context) Response {
@ -46,6 +47,7 @@ func GetPluginList(c *middleware.Context) Response {
Info: &pluginDef.Info,
LatestVersion: pluginDef.GrafanaNetVersion,
HasUpdate: pluginDef.GrafanaNetHasUpdate,
DefaultNavUrl: pluginDef.DefaultNavUrl,
}
if pluginSetting, exists := pluginSettingsMap[pluginDef.Id]; exists {
@ -53,6 +55,10 @@ func GetPluginList(c *middleware.Context) Response {
listItem.Pinned = pluginSetting.Pinned
}
if listItem.DefaultNavUrl == "" || !listItem.Enabled {
listItem.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + listItem.Id + "/edit"
}
// filter out disabled
if enabledFilter == "1" && !listItem.Enabled {
continue

View File

@ -31,7 +31,7 @@ func RenderToPng(c *middleware.Context) {
Width: queryReader.Get("width", "800"),
Height: queryReader.Get("height", "400"),
SessionId: c.Session.ID(),
Timeout: queryReader.Get("timeout", "15"),
Timeout: queryReader.Get("timeout", "30"),
}
renderOpts.Url = setting.ToAbsUrl(renderOpts.Url)

View File

@ -1,6 +1,7 @@
package renderer
import (
"fmt"
"io"
"os"
"os/exec"
@ -72,6 +73,7 @@ func RenderToPng(params *RenderOpts) (string, error) {
if err := cmd.Process.Kill(); err != nil {
log.Error(4, "failed to kill: %v", err)
}
return "", fmt.Errorf("PhantomRenderer::renderToPng timeout (>%vs)", timeout)
case <-done:
}

View File

@ -76,8 +76,6 @@ func (app *AppPlugin) initApp() {
}
}
app.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + app.Id + "/edit"
// slugify pages
for _, include := range app.Includes {
if include.Slug == "" {

View File

@ -42,6 +42,8 @@ export class GrafanaApp {
app.constant('grafanaVersion', "@grafanaVersion@");
app.config(($locationProvider, $controllerProvider, $compileProvider, $filterProvider, $provide) => {
//$compileProvider.debugInfoEnabled(false);
this.registerFunctions.controller = $controllerProvider.register;
this.registerFunctions.directive = $compileProvider.directive;
this.registerFunctions.factory = $provide.factory;

View File

@ -86,7 +86,7 @@
<button class="btn btn-inverse gf-from-btn" ng-click="_.move(dashboard.rows,$index,$index+1)">
<i ng-class="{'invisible': $last}" class="fa fa-arrow-down"></i>
</button>
<button class="btn btn-inverse gf-form-btn" click="dashboard.rows = _.without(dashboard.rows,row)">
<button class="btn btn-inverse gf-form-btn" ng-click="dashboard.rows = _.without(dashboard.rows,row)">
<i class="fa fa-trash"></i>
</button>
</div>

View File

@ -8,14 +8,16 @@
<p class="playlist-description">Each organization contains their own dashboards, data sources and configuration, and cannot be shared between orgs. While users may belong to more than one, mutiple organization are most frequently used in multi-tenant deployments. </p>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Org. name</span>
<input type="text" ng-model="newOrg.name" required class="gf-form-input max-width-21" placeholder="organization name">
<form>
<div class="gf-form-group">
<div class="gf-form">
<span class="gf-form-label width-10">Org. name</span>
<input type="text" ng-model="newOrg.name" required class="gf-form-input max-width-21" placeholder="organization name">
</div>
<br>
<div class="gf-form-buttons-row">
<button type="submit" class="btn btn-success" ng-click="createOrg()">Create</button>
</div>
</div>
<br>
<div class="gf-form-buttons-row">
<button class="btn btn-success" ng-click="createOrg()">Create</button>
</div>
</div>
</form>
</div>

View File

@ -213,8 +213,7 @@ function (angular, _, kbn) {
this.updateOptions = function(variable) {
if (variable.type !== 'query') {
self._updateNonQueryVariable(variable);
self.setVariableValue(variable, variable.options[0]);
return $q.when([]);
return self.validateVariableSelectionState(variable);
}
return datasourceSrv.get(variable.datasource)
@ -251,7 +250,7 @@ function (angular, _, kbn) {
if (_.isArray(variable.current.value)) {
self.selectOptionsForCurrentValue(variable);
} else {
var currentOption = _.findWhere(variable.options, { text: variable.current.text });
var currentOption = _.findWhere(variable.options, {text: variable.current.text});
if (currentOption) {
return self.setVariableValue(variable, currentOption, true);
} else {

View File

@ -376,11 +376,16 @@ function (angular, _, dateMath) {
if (target.filters && target.filters.length > 0) {
query.filters = angular.copy(target.filters);
if(query.filters){
for(var filter_key in query.filters){
query.filters[filter_key].filter = templateSrv.replace(query.filters[filter_key].filter, options.scopedVars, 'pipe');
}
}
} else {
query.tags = angular.copy(target.tags);
if(query.tags){
for(var key in query.tags){
query.tags[key] = templateSrv.replace(query.tags[key], options.scopedVars, 'pipe');
for(var tag_key in query.tags){
query.tags[tag_key] = templateSrv.replace(query.tags[tag_key], options.scopedVars, 'pipe');
}
}
}

View File

@ -46,7 +46,7 @@
<h5 class="section-heading">Thresholds</h5>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-5">Level 1</label>
<label class="gf-form-label width-6">Level 1</label>
<input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold1" ng-change="ctrl.render()" ng-model-onblur>
</div>
<div class="gf-form">
@ -58,7 +58,7 @@
</div>
<div class="gf-form-inline">
<div class="gf-form">
<label class="gf-form-label width-5">Level 2</label>
<label class="gf-form-label width-6">Level 2</label>
<input type="number" class="gf-form-input max-width-5" ng-model="ctrl.panel.grid.threshold2" ng-change="ctrl.render()" ng-model-onblur>
</div>
<div class="gf-form">
@ -68,5 +68,7 @@
</div>
</div>
</div>
<gf-form-switch class="gf-form" label="Line Mode" label-class="width-6" checked="ctrl.panel.grid.thresholdLine" on-change="ctrl.render()"></gf-form-switch>
</div>
</div>

View File

@ -4,7 +4,7 @@
{{category.header}}
</h6>
<div class="pluginlist-item" ng-repeat="plugin in category.list">
<a class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" href="plugins/{{plugin.id}}/edit">
<a class="pluginlist-link pluginlist-link-{{plugin.state}} pointer" href="{{plugin.defaultNavUrl}}">
<span>
<img ng-src="{{plugin.info.logos.small}}" class="pluginlist-image">
<span class="pluginlist-title">{{plugin.name}}</span>
@ -23,7 +23,7 @@
</div>
<div class="pluginlist-item" ng-show="category.list.length === 0">
<a class="pluginlist-link pluginlist-link-{{plugin.state}}" href="https://grafana.net/plugins">
<span class="pluginlist-none-installed">No additional panels installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
<span class="pluginlist-none-installed">None installed. <span class="pluginlist-emphasis">Browse Grafana.net</span></span>
</a>
</div>
</div>

View File

@ -82,7 +82,7 @@ class SingleStatCtrl extends MetricsPanelCtrl {
}
onDataError(err) {
this.onDataReceived({data: []});
this.onDataReceived([]);
}
onDataReceived(dataList) {
@ -91,11 +91,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
var data: any = {};
this.setValues(data);
data.thresholds = this.panel.thresholds.split(',').map(function(strVale) {
return Number(strVale.trim());
});
data.colorMap = this.panel.colors;
this.data = data;
this.render();
}
@ -324,9 +319,9 @@ class SingleStatCtrl extends MetricsPanelCtrl {
? 'rgb(230,230,230)'
: 'rgb(38,38,38)';
var fontScale = parseInt(panel.valueFontSize) / 100;
var dimension = Math.min(width, height);
var fontSize = Math.min(dimension/4, 100);
var fontSize = Math.min(dimension/5, 100) * fontScale;
var gaugeWidth = Math.min(dimension/6, 60);
var thresholdMarkersWidth = gaugeWidth/5;
@ -374,15 +369,6 @@ class SingleStatCtrl extends MetricsPanelCtrl {
$.plot(plotCanvas, [plotSeries], options);
}
function getGaugeFontSize() {
if (panel.valueFontSize) {
var num = parseInt(panel.valueFontSize.substring(0, panel.valueFontSize.length - 1));
return (30 * (num / 100)) + 15;
} else {
return 30;
}
}
function addSparkline() {
var width = elem.width() + 20;
if (width < 30) {
@ -444,8 +430,14 @@ class SingleStatCtrl extends MetricsPanelCtrl {
function render() {
if (!ctrl.data) { return; }
ctrl.setValues(ctrl.data);
data = ctrl.data;
// get thresholds
data.thresholds = panel.thresholds.split(',').map(function(strVale) {
return Number(strVale.trim());
});
data.colorMap = panel.colors;
setElementHeight();
var body = panel.gauge.show ? '' : getBigValueHtml();

View File

@ -47,6 +47,7 @@ dashboard.templating = {
refresh: true,
options: [],
current: null,
type: 'custom'
},
{
name: 'test2',
@ -54,6 +55,7 @@ dashboard.templating = {
refresh: true,
options: [],
current: null,
type: 'custom'
}
]
};

View File

@ -166,7 +166,7 @@ define([
describeUpdateVariable('update custom variable', function(scenario) {
scenario.setup(function() {
scenario.variable = { type: 'custom', query: 'hej, hop, asd', name: 'test'};
scenario.variable = {type: 'custom', query: 'hej, hop, asd', name: 'test'};
});
it('should update options array', function() {
@ -286,7 +286,13 @@ define([
describeUpdateVariable('datasource variable with regex filter', function(scenario) {
scenario.setup(function() {
scenario.variable = {type: 'datasource', query: 'graphite', name: 'test', current: {}, regex: '/pee$/' };
scenario.variable = {
type: 'datasource',
query: 'graphite',
name: 'test',
current: {value: 'backend4_pee', text: 'backend4_pee'},
regex: '/pee$/'
};
scenario.metricSources = [
{name: 'backend1', meta: {id: 'influx'}},
{name: 'backend2_pee', meta: {id: 'graphite'}},
@ -300,6 +306,10 @@ define([
expect(scenario.variable.options[0].value).to.be('backend2_pee');
expect(scenario.variable.options[1].value).to.be('backend4_pee');
});
it('should keep current value if available', function() {
expect(scenario.variable.current.value).to.be('backend4_pee');
});
});
});

View File

@ -15,8 +15,6 @@ module.exports = function(config,grunt) {
src = confDir+src;
}
var exec = require('child_process').execFileSync;
try {
grunt.config('copy.phantom_bin', {
src: src,