Merge remote-tracking branch 'upstream/master' into postgres-query-builder

This commit is contained in:
Sven Klemm 2018-07-07 20:54:33 +02:00
commit 3f4c808cc5
27 changed files with 181 additions and 116 deletions

1
.gitignore vendored
View File

@ -43,6 +43,7 @@ fig.yml
docker-compose.yml docker-compose.yml
docker-compose.yaml docker-compose.yaml
/conf/provisioning/**/custom.yaml /conf/provisioning/**/custom.yaml
/conf/provisioning/**/dev.yaml
/conf/ldap_dev.toml /conf/ldap_dev.toml
profile.cov profile.cov
/grafana /grafana

View File

@ -10,10 +10,19 @@
* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps) * **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps)
* **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2) * **Table**: Make table sorting stable when null values exist [#12362](https://github.com/grafana/grafana/pull/12362), thx [@bz2](https://github.com/bz2)
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) * **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
* **Prometheus**: Heatmap - fix unhandled error when some points are missing [#12484](https://github.com/grafana/grafana/issues/12484)
* **Variables**: Skip unneeded extra query request when de-selecting variable values used for repeated panels [#8186](https://github.com/grafana/grafana/issues/8186), thx [@mtanda](https://github.com/mtanda) * **Variables**: Skip unneeded extra query request when de-selecting variable values used for repeated panels [#8186](https://github.com/grafana/grafana/issues/8186), thx [@mtanda](https://github.com/mtanda)
* **Postgres/MySQL/MSSQL**: Use floor rounding in $__timeGroup macro function [#12460](https://github.com/grafana/grafana/issues/12460), thx [@svenklemm](https://github.com/svenklemm) * **Postgres/MySQL/MSSQL**: Use floor rounding in $__timeGroup macro function [#12460](https://github.com/grafana/grafana/issues/12460), thx [@svenklemm](https://github.com/svenklemm)
* **MySQL/MSSQL**: Use datetime format instead of epoch for $__timeFilter, $__timeFrom and $__timeTo macros [#11618](https://github.com/grafana/grafana/issues/11618) [#11619](https://github.com/grafana/grafana/issues/11619), thx [@AustinWinstanley](https://github.com/AustinWinstanley)
* **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber) * **Github OAuth**: Allow changes of user info at Github to be synched to Grafana when signing in [#11818](https://github.com/grafana/grafana/issues/11818), thx [@rwaweber](https://github.com/rwaweber)
# 5.2.2 (unreleased)
### Minor
* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379)
* **Dashboard**: Dashboard links not updated when changing variables [#12506](https://github.com/grafana/grafana/issues/12506)
# 5.2.1 (2018-06-29) # 5.2.1 (2018-06-29)
### Minor ### Minor

9
devenv/dashboards.yaml Normal file
View File

@ -0,0 +1,9 @@
apiVersion: 1
providers:
- name: 'gdev dashboards'
folder: 'gdev dashboards'
type: file
options:
path: devenv/dev-dashboards

View File

@ -1,9 +0,0 @@
apiVersion: 1
providers:
- name: 'dev dashboards'
folder: 'dev dashboards'
type: file
options:
path: devenv/dashboards/dev-dashboards

View File

@ -1,20 +1,20 @@
apiVersion: 1 apiVersion: 1
datasources: datasources:
- name: Graphite - name: gdev-graphite
type: graphite type: graphite
access: proxy access: proxy
url: http://localhost:8080 url: http://localhost:8080
jsonData: jsonData:
graphiteVersion: "1.1" graphiteVersion: "1.1"
- name: Prometheus - name: gdev-prometheus
type: prometheus type: prometheus
access: proxy access: proxy
isDefault: true isDefault: true
url: http://localhost:9090 url: http://localhost:9090
- name: InfluxDB - name: gdev-influxdb
type: influxdb type: influxdb
access: proxy access: proxy
database: site database: site
@ -24,7 +24,7 @@ datasources:
jsonData: jsonData:
timeInterval: "15s" timeInterval: "15s"
- name: OpenTsdb - name: gdev-opentsdb
type: opentsdb type: opentsdb
access: proxy access: proxy
url: http://localhost:4242 url: http://localhost:4242
@ -32,7 +32,7 @@ datasources:
tsdbResolution: 1 tsdbResolution: 1
tsdbVersion: 1 tsdbVersion: 1
- name: Elastic - name: gdev-elasticsearch-metrics
type: elasticsearch type: elasticsearch
access: proxy access: proxy
database: "[metrics-]YYYY.MM.DD" database: "[metrics-]YYYY.MM.DD"
@ -41,21 +41,21 @@ datasources:
interval: Daily interval: Daily
timeField: "@timestamp" timeField: "@timestamp"
- name: MySQL - name: gdev-mysql
type: mysql type: mysql
url: localhost:3306 url: localhost:3306
database: grafana database: grafana
user: grafana user: grafana
password: password password: password
- name: MSSQL - name: gdev-mssql
type: mssql type: mssql
url: localhost:1433 url: localhost:1433
database: grafana database: grafana
user: grafana user: grafana
password: "Password!" password: "Password!"
- name: Postgres - name: gdev-postgres
type: postgres type: postgres
url: localhost:5432 url: localhost:5432
database: grafana database: grafana
@ -64,7 +64,7 @@ datasources:
jsonData: jsonData:
sslmode: "disable" sslmode: "disable"
- name: Cloudwatch - name: gdev-cloudwatch
type: cloudwatch type: cloudwatch
editable: true editable: true
jsonData: jsonData:

View File

@ -23,41 +23,36 @@ requiresJsonnet() {
} }
defaultDashboards() { defaultDashboards() {
requiresJsonnet ln -s -f ../../../devenv/dashboards.yaml ../conf/provisioning/dashboards/dev.yaml
ln -s -f -r ./dashboards/dev-dashboards/dev-dashboards.yaml ../conf/provisioning/dashboards/custom.yaml
} }
defaultDatasources() { defaultDatasources() {
echo "setting up all default datasources using provisioning" echo "setting up all default datasources using provisioning"
ln -s -f -r ./datasources/default/default.yaml ../conf/provisioning/datasources/custom.yaml ln -s -f ../../../devenv/datasources.yaml ../conf/provisioning/datasources/dev.yaml
} }
usage() { usage() {
echo -e "install.sh\n\tThis script installs my basic setup for a debian laptop\n" echo -e "install.sh\n\tThis script setups dev provision for datasources and dashboards"
echo "Usage:" echo "Usage:"
echo " bulk-dashboards - create and provisioning 400 dashboards" echo " bulk-dashboards - create and provisioning 400 dashboards"
echo " default-datasources - provisiong all core datasources" echo " no args - provisiong core datasources and dev dashboards"
} }
main() { main() {
local cmd=$1 local cmd=$1
if [[ -z "$cmd" ]]; then
usage
exit 1
fi
if [[ $cmd == "bulk-dashboards" ]]; then if [[ $cmd == "bulk-dashboards" ]]; then
bulkDashboard bulkDashboard
elif [[ $cmd == "default-datasources" ]]; then
defaultDatasources
elif [[ $cmd == "default-dashboards" ]]; then
defaultDashboards
else else
defaultDashboards
defaultDatasources
fi
if [[ -z "$cmd" ]]; then
usage usage
fi fi
} }
main "$@" main "$@"

View File

@ -77,9 +77,9 @@ Macro example | Description
------------ | ------------- ------------ | -------------
*$__time(dateColumn)* | Will be replaced by an expression to rename the column to *time*. For example, *dateColumn as time* *$__time(dateColumn)* | Will be replaced by an expression to rename the column to *time*. For example, *dateColumn as time*
*$__timeEpoch(dateColumn)* | Will be replaced by an expression to convert a DATETIME column type to unix timestamp and rename it to *time*. <br/>For example, *DATEDIFF(second, '1970-01-01', dateColumn) AS time* *$__timeEpoch(dateColumn)* | Will be replaced by an expression to convert a DATETIME column type to unix timestamp and rename it to *time*. <br/>For example, *DATEDIFF(second, '1970-01-01', dateColumn) AS time*
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. <br/>For example, *dateColumn >= DATEADD(s, 1494410783, '1970-01-01') AND dateColumn <= DATEADD(s, 1494410783, '1970-01-01')* *$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. <br/>For example, *dateColumn BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:06:17Z'*
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *DATEADD(second, 1494410783, '1970-01-01')* *$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *'2017-04-21T05:01:17Z'*
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *DATEADD(second, 1494410783, '1970-01-01')* *$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *'2017-04-21T05:06:17Z'*
*$__timeGroup(dateColumn,'5m'[, fillvalue])* | Will be replaced by an expression usable in GROUP BY clause. Providing a *fillValue* of *NULL* or *floating value* will automatically fill empty series in timerange with that value. <br/>For example, *CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)\*300*. *$__timeGroup(dateColumn,'5m'[, fillvalue])* | Will be replaced by an expression usable in GROUP BY clause. Providing a *fillValue* of *NULL* or *floating value* will automatically fill empty series in timerange with that value. <br/>For example, *CAST(ROUND(DATEDIFF(second, '1970-01-01', time_column)/300.0, 0) as bigint)\*300*.
*$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example). *$__timeGroup(dateColumn,'5m', 0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example).
*$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183*

View File

@ -60,9 +60,9 @@ Macro example | Description
------------ | ------------- ------------ | -------------
*$__time(dateColumn)* | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, *UNIX_TIMESTAMP(dateColumn) as time_sec* *$__time(dateColumn)* | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, *UNIX_TIMESTAMP(dateColumn) as time_sec*
*$__timeEpoch(dateColumn)* | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, *UNIX_TIMESTAMP(dateColumn) as time_sec* *$__timeEpoch(dateColumn)* | Will be replaced by an expression to convert to a UNIX timestamp and rename the column to `time_sec`. For example, *UNIX_TIMESTAMP(dateColumn) as time_sec*
*$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn > FROM_UNIXTIME(1494410783) AND dateColumn < FROM_UNIXTIME(1494497183)* *$__timeFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name. For example, *dateColumn BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:06:17Z'*
*$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *FROM_UNIXTIME(1494410783)* *$__timeFrom()* | Will be replaced by the start of the currently active time selection. For example, *'2017-04-21T05:01:17Z'*
*$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *FROM_UNIXTIME(1494497183)* *$__timeTo()* | Will be replaced by the end of the currently active time selection. For example, *'2017-04-21T05:06:17Z'*
*$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),* *$__timeGroup(dateColumn,'5m')* | Will be replaced by an expression usable in GROUP BY clause. For example, *cast(cast(UNIX_TIMESTAMP(dateColumn)/(300) as signed)*300 as signed),*
*$__timeGroup(dateColumn,'5m',0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example). *$__timeGroup(dateColumn,'5m',0)* | Same as above but with a fill parameter so all null values will be converted to the fill value (all null values would be set to zero using this example).
*$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183* *$__unixEpochFilter(dateColumn)* | Will be replaced by a time range filter using the specified column name with times represented as unix timestamp. For example, *dateColumn > 1494410783 AND dateColumn < 1494497183*

View File

@ -82,11 +82,12 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er
if len(args) == 0 { if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name) return "", fmt.Errorf("missing time column argument for macro %v", name)
} }
return fmt.Sprintf("%s >= DATEADD(s, %d, '1970-01-01') AND %s <= DATEADD(s, %d, '1970-01-01')", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil
return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339), m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
case "__timeFrom": case "__timeFrom":
return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", m.TimeRange.GetFromAsSecondsEpoch()), nil return fmt.Sprintf("'%s'", m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil
case "__timeTo": case "__timeTo":
return fmt.Sprintf("DATEADD(second, %d, '1970-01-01')", m.TimeRange.GetToAsSecondsEpoch()), nil return fmt.Sprintf("'%s'", m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
case "__timeGroup": case "__timeGroup":
if len(args) < 2 { if len(args) < 2 {
return "", fmt.Errorf("macro %v needs time column and interval", name) return "", fmt.Errorf("macro %v needs time column and interval", name)

View File

@ -49,7 +49,7 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeGroup function", func() { Convey("interpolate __timeGroup function", func() {
@ -96,14 +96,14 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {
@ -137,21 +137,21 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeFrom function", func() { Convey("interpolate __timeFrom function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {
@ -185,21 +185,21 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= DATEADD(s, %d, '1970-01-01') AND time_column <= DATEADD(s, %d, '1970-01-01')", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeFrom function", func() { Convey("interpolate __timeFrom function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select DATEADD(second, %d, '1970-01-01')", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {

View File

@ -77,11 +77,12 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er
if len(args) == 0 { if len(args) == 0 {
return "", fmt.Errorf("missing time column argument for macro %v", name) return "", fmt.Errorf("missing time column argument for macro %v", name)
} }
return fmt.Sprintf("%s >= FROM_UNIXTIME(%d) AND %s <= FROM_UNIXTIME(%d)", args[0], m.TimeRange.GetFromAsSecondsEpoch(), args[0], m.TimeRange.GetToAsSecondsEpoch()), nil
return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339), m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
case "__timeFrom": case "__timeFrom":
return fmt.Sprintf("FROM_UNIXTIME(%d)", m.TimeRange.GetFromAsSecondsEpoch()), nil return fmt.Sprintf("'%s'", m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil
case "__timeTo": case "__timeTo":
return fmt.Sprintf("FROM_UNIXTIME(%d)", m.TimeRange.GetToAsSecondsEpoch()), nil return fmt.Sprintf("'%s'", m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil
case "__timeGroup": case "__timeGroup":
if len(args) < 2 { if len(args) < 2 {
return "", fmt.Errorf("macro %v needs time column and interval", name) return "", fmt.Errorf("macro %v needs time column and interval", name)

View File

@ -54,21 +54,21 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeFrom function", func() { Convey("interpolate __timeFrom function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {
@ -102,21 +102,21 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeFrom function", func() { Convey("interpolate __timeFrom function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {
@ -150,21 +150,21 @@ func TestMacroEngine(t *testing.T) {
sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column >= FROM_UNIXTIME(%d) AND time_column <= FROM_UNIXTIME(%d)", from.Unix(), to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", from.Format(time.RFC3339), to.Format(time.RFC3339)))
}) })
Convey("interpolate __timeFrom function", func() { Convey("interpolate __timeFrom function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", from.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339)))
}) })
Convey("interpolate __timeTo function", func() { Convey("interpolate __timeTo function", func() {
sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)")
So(err, ShouldBeNil) So(err, ShouldBeNil)
So(sql, ShouldEqual, fmt.Sprintf("select FROM_UNIXTIME(%d)", to.Unix())) So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339)))
}) })
Convey("interpolate __unixEpochFilter function", func() { Convey("interpolate __unixEpochFilter function", func() {

View File

@ -41,20 +41,20 @@ function dashLink($compile, $sanitize, linkSrv) {
elem.html(template); elem.html(template);
$compile(elem.contents())(scope); $compile(elem.contents())(scope);
var anchor = elem.find('a');
var icon = elem.find('i');
var span = elem.find('span');
function update() { function update() {
var linkInfo = linkSrv.getAnchorInfo(link); var linkInfo = linkSrv.getAnchorInfo(link);
const anchor = elem.find('a');
const span = elem.find('span');
span.text(linkInfo.title); span.text(linkInfo.title);
if (!link.asDropdown) { if (!link.asDropdown) {
anchor.attr('href', linkInfo.href); anchor.attr('href', linkInfo.href);
sanitizeAnchor(); sanitizeAnchor();
} }
elem.find('a').attr('data-placement', 'bottom'); anchor.attr('data-placement', 'bottom');
// tooltip // tooltip
elem.find('a').tooltip({ anchor.tooltip({
title: $sanitize(scope.link.tooltip), title: $sanitize(scope.link.tooltip),
html: true, html: true,
container: 'body', container: 'body',
@ -62,12 +62,13 @@ function dashLink($compile, $sanitize, linkSrv) {
} }
function sanitizeAnchor() { function sanitizeAnchor() {
const anchor = elem.find('a');
const anchorSanitized = $sanitize(anchor.parent().html()); const anchorSanitized = $sanitize(anchor.parent().html());
anchor.parent().html(anchorSanitized); anchor.parent().html(anchorSanitized);
} }
icon.attr('class', 'fa fa-fw ' + scope.link.icon); elem.find('i').attr('class', 'fa fa-fw ' + scope.link.icon);
anchor.attr('target', scope.link.target); elem.find('a').attr('target', scope.link.target);
// fix for menus on the far right // fix for menus on the far right
if (link.asDropdown && scope.$last) { if (link.asDropdown && scope.$last) {

View File

@ -142,10 +142,12 @@ export class DatasourceSrv {
var ds = config.datasources[first]; var ds = config.datasources[first];
if (ds) { if (ds) {
const key = `$${variable.name}`;
list.push({ list.push({
name: '$' + variable.name, name: key,
value: '$' + variable.name, value: key,
meta: ds.meta, meta: ds.meta,
sort: key,
}); });
} }
} }

View File

@ -32,8 +32,8 @@
<div class="gf-form"> <div class="gf-form">
<label class="gf-form-label query-keyword pointer" ng-click="toggleAccessHelp()"> <label class="gf-form-label query-keyword pointer" ng-click="toggleAccessHelp()">
Help&nbsp; Help&nbsp;
<i class="fa fa-caret-down" ng-show="ctrl.showAccessHelp"></i> <i class="fa fa-caret-down" ng-show="showAccessHelp"></i>
<i class="fa fa-caret-right" ng-hide="ctrl.showAccessHelp">&nbsp;</i> <i class="fa fa-caret-right" ng-hide="showAccessHelp">&nbsp;</i>
</label> </label>
</div> </div>
</div> </div>

View File

@ -2,8 +2,21 @@ import config from 'app/core/config';
import 'app/features/plugins/datasource_srv'; import 'app/features/plugins/datasource_srv';
import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; import { DatasourceSrv } from 'app/features/plugins/datasource_srv';
// Datasource variable $datasource with current value 'BBB'
const templateSrv = {
variables: [
{
type: 'datasource',
name: 'datasource',
current: {
value: 'BBB',
},
},
],
};
describe('datasource_srv', function() { describe('datasource_srv', function() {
let _datasourceSrv = new DatasourceSrv({}, {}, {}, {}); let _datasourceSrv = new DatasourceSrv({}, {}, {}, templateSrv);
let metricSources; let metricSources;
describe('when loading metric sources', () => { describe('when loading metric sources', () => {
@ -35,25 +48,27 @@ describe('datasource_srv', function() {
}; };
beforeEach(() => { beforeEach(() => {
config.datasources = unsortedDatasources; config.datasources = unsortedDatasources;
metricSources = _datasourceSrv.getMetricSources({ skipVariables: true }); metricSources = _datasourceSrv.getMetricSources({});
});
it('should return a list of sources sorted case insensitively with builtin sources last', () => {
expect(metricSources[0].name).toBe('aaa');
expect(metricSources[1].name).toBe('BBB');
expect(metricSources[2].name).toBe('mmm');
expect(metricSources[3].name).toBe('ZZZ');
expect(metricSources[4].name).toBe('--Grafana--');
expect(metricSources[5].name).toBe('--Mixed--');
});
beforeEach(() => {
config.defaultDatasource = 'BBB'; config.defaultDatasource = 'BBB';
}); });
it('should return a list of sources sorted case insensitively with builtin sources last', () => {
expect(metricSources[1].name).toBe('aaa');
expect(metricSources[2].name).toBe('BBB');
expect(metricSources[3].name).toBe('mmm');
expect(metricSources[4].name).toBe('ZZZ');
expect(metricSources[5].name).toBe('--Grafana--');
expect(metricSources[6].name).toBe('--Mixed--');
});
it('should set default data source', () => { it('should set default data source', () => {
expect(metricSources[2].name).toBe('default'); expect(metricSources[3].name).toBe('default');
expect(metricSources[2].sort).toBe('BBB'); expect(metricSources[3].sort).toBe('BBB');
});
it('should set default inject the variable datasources', () => {
expect(metricSources[0].name).toBe('$datasource');
expect(metricSources[0].sort).toBe('$datasource');
}); });
}); });
}); });

View File

@ -28,12 +28,12 @@ An annotation is an event that is overlaid on top of graphs. The query can have
Macros: Macros:
- $__time(column) -&gt; column AS time - $__time(column) -&gt; column AS time
- $__timeEpoch(column) -&gt; DATEDIFF(second, '1970-01-01', column) AS time - $__timeEpoch(column) -&gt; DATEDIFF(second, '1970-01-01', column) AS time
- $__timeFilter(column) -&gt; column &gt;= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &lt;= DATEADD(s, 18446744066914187038, '1970-01-01') - $__timeFilter(column) -&gt; column BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:01:17Z'
- $__unixEpochFilter(column) -&gt; column &gt;= 1492750877 AND column &lt;= 1492750877 - $__unixEpochFilter(column) -&gt; column &gt;= 1492750877 AND column &lt;= 1492750877
Or build your own conditionals using these macros which just return the values: Or build your own conditionals using these macros which just return the values:
- $__timeFrom() -&gt; DATEADD(second, 1492750877, '1970-01-01') - $__timeFrom() -&gt; '2017-04-21T05:01:17Z'
- $__timeTo() -&gt; DATEADD(second, 1492750877, '1970-01-01') - $__timeTo() -&gt; '2017-04-21T05:01:17Z'
- $__unixEpochFrom() -&gt; 1492750877 - $__unixEpochFrom() -&gt; 1492750877
- $__unixEpochTo() -&gt; 1492750877 - $__unixEpochTo() -&gt; 1492750877
</pre> </pre>

View File

@ -49,7 +49,7 @@ Table:
Macros: Macros:
- $__time(column) -&gt; column AS time - $__time(column) -&gt; column AS time
- $__timeEpoch(column) -&gt; DATEDIFF(second, '1970-01-01', column) AS time - $__timeEpoch(column) -&gt; DATEDIFF(second, '1970-01-01', column) AS time
- $__timeFilter(column) -&gt; column &gt;= DATEADD(s, 18446744066914186738, '1970-01-01') AND column &lt;= DATEADD(s, 18446744066914187038, '1970-01-01') - $__timeFilter(column) -&gt; column BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:01:17Z'
- $__unixEpochFilter(column) -&gt; column &gt;= 1492750877 AND column &lt;= 1492750877 - $__unixEpochFilter(column) -&gt; column &gt;= 1492750877 AND column &lt;= 1492750877
- $__timeGroup(column, '5m'[, fillvalue]) -&gt; CAST(ROUND(DATEDIFF(second, '1970-01-01', column)/300.0, 0) as bigint)*300. Providing a <i>fillValue</i> of <i>NULL</i> or floating value will automatically fill empty series in timerange with that value. - $__timeGroup(column, '5m'[, fillvalue]) -&gt; CAST(ROUND(DATEDIFF(second, '1970-01-01', column)/300.0, 0) as bigint)*300. Providing a <i>fillValue</i> of <i>NULL</i> or floating value will automatically fill empty series in timerange with that value.
@ -62,8 +62,8 @@ GROUP BY $__timeGroup(date_time_col, '1h')
ORDER BY 1 ORDER BY 1
Or build your own conditionals using these macros which just return the values: Or build your own conditionals using these macros which just return the values:
- $__timeFrom() -&gt; DATEADD(second, 1492750877, '1970-01-01') - $__timeFrom() -&gt; '2017-04-21T05:01:17Z'
- $__timeTo() -&gt; DATEADD(second, 1492750877, '1970-01-01') - $__timeTo() -&gt; '2017-04-21T05:01:17Z'
- $__unixEpochFrom() -&gt; 1492750877 - $__unixEpochFrom() -&gt; 1492750877
- $__unixEpochTo() -&gt; 1492750877 - $__unixEpochTo() -&gt; 1492750877
</pre> </pre>

View File

@ -28,12 +28,12 @@ An annotation is an event that is overlaid on top of graphs. The query can have
Macros: Macros:
- $__time(column) -&gt; UNIX_TIMESTAMP(column) as time (or as time_sec) - $__time(column) -&gt; UNIX_TIMESTAMP(column) as time (or as time_sec)
- $__timeEpoch(column) -&gt; UNIX_TIMESTAMP(column) as time (or as time_sec) - $__timeEpoch(column) -&gt; UNIX_TIMESTAMP(column) as time (or as time_sec)
- $__timeFilter(column) -&gt; UNIX_TIMESTAMP(time_date_time) &gt; 1492750877 AND UNIX_TIMESTAMP(time_date_time) &lt; 1492750877 - $__timeFilter(column) -&gt; column BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:01:17Z'
- $__unixEpochFilter(column) -&gt; time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877 - $__unixEpochFilter(column) -&gt; time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877
Or build your own conditionals using these macros which just return the values: Or build your own conditionals using these macros which just return the values:
- $__timeFrom() -&gt; FROM_UNIXTIME(1492750877) - $__timeFrom() -&gt; '2017-04-21T05:01:17Z'
- $__timeTo() -&gt; FROM_UNIXTIME(1492750877) - $__timeTo() -&gt; '2017-04-21T05:01:17Z'
- $__unixEpochFrom() -&gt; 1492750877 - $__unixEpochFrom() -&gt; 1492750877
- $__unixEpochTo() -&gt; 1492750877 - $__unixEpochTo() -&gt; 1492750877
</pre> </pre>

View File

@ -48,7 +48,7 @@ Table:
Macros: Macros:
- $__time(column) -&gt; UNIX_TIMESTAMP(column) as time_sec - $__time(column) -&gt; UNIX_TIMESTAMP(column) as time_sec
- $__timeEpoch(column) -&gt; UNIX_TIMESTAMP(column) as time_sec - $__timeEpoch(column) -&gt; UNIX_TIMESTAMP(column) as time_sec
- $__timeFilter(column) -&gt; UNIX_TIMESTAMP(time_date_time) &ge; 1492750877 AND UNIX_TIMESTAMP(time_date_time) &le; 1492750877 - $__timeFilter(column) -&gt; column BETWEEN '2017-04-21T05:01:17Z' AND '2017-04-21T05:01:17Z'
- $__unixEpochFilter(column) -&gt; time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877 - $__unixEpochFilter(column) -&gt; time_unix_epoch &gt; 1492750877 AND time_unix_epoch &lt; 1492750877
- $__timeGroup(column,'5m') -&gt; cast(cast(UNIX_TIMESTAMP(column)/(300) as signed)*300 as signed) - $__timeGroup(column,'5m') -&gt; cast(cast(UNIX_TIMESTAMP(column)/(300) as signed)*300 as signed)
@ -61,8 +61,8 @@ GROUP BY 1
ORDER BY 1 ORDER BY 1
Or build your own conditionals using these macros which just return the values: Or build your own conditionals using these macros which just return the values:
- $__timeFrom() -&gt; FROM_UNIXTIME(1492750877) - $__timeFrom() -&gt; '2017-04-21T05:01:17Z'
- $__timeTo() -&gt; FROM_UNIXTIME(1492750877) - $__timeTo() -&gt; '2017-04-21T05:01:17Z'
- $__unixEpochFrom() -&gt; 1492750877 - $__unixEpochFrom() -&gt; 1492750877
- $__unixEpochTo() -&gt; 1492750877 - $__unixEpochTo() -&gt; 1492750877
</pre> </pre>

View File

@ -28,15 +28,20 @@ export class ResultTransformer {
} }
} }
transformMetricData(md, options, start, end) { transformMetricData(metricData, options, start, end) {
let dps = [], let dps = [],
metricLabel = null; metricLabel = null;
metricLabel = this.createMetricLabel(md.metric, options); metricLabel = this.createMetricLabel(metricData.metric, options);
const stepMs = parseInt(options.step) * 1000; const stepMs = parseInt(options.step) * 1000;
let baseTimestamp = start * 1000; let baseTimestamp = start * 1000;
for (let value of md.values) {
if (metricData.values === undefined) {
throw new Error('Prometheus heatmap error: data should be a time series');
}
for (let value of metricData.values) {
let dp_value = parseFloat(value[1]); let dp_value = parseFloat(value[1]);
if (_.isNaN(dp_value)) { if (_.isNaN(dp_value)) {
dp_value = null; dp_value = null;
@ -164,8 +169,13 @@ export class ResultTransformer {
for (let i = seriesList.length - 1; i > 0; i--) { for (let i = seriesList.length - 1; i > 0; i--) {
let topSeries = seriesList[i].datapoints; let topSeries = seriesList[i].datapoints;
let bottomSeries = seriesList[i - 1].datapoints; let bottomSeries = seriesList[i - 1].datapoints;
if (!topSeries || !bottomSeries) {
throw new Error('Prometheus heatmap transform error: data should be a time series');
}
for (let j = 0; j < topSeries.length; j++) { for (let j = 0; j < topSeries.length; j++) {
topSeries[j][0] -= bottomSeries[j][0]; const bottomPoint = bottomSeries[j] || [0];
topSeries[j][0] -= bottomPoint[0];
} }
} }

View File

@ -126,6 +126,36 @@ describe('Prometheus Result Transformer', () => {
{ target: '3', datapoints: [[10, 1445000010000], [0, 1445000020000], [10, 1445000030000]] }, { target: '3', datapoints: [[10, 1445000010000], [0, 1445000020000], [10, 1445000030000]] },
]); ]);
}); });
it('should handle missing datapoints', () => {
const seriesList = [
{ datapoints: [[1, 1000], [2, 2000]] },
{ datapoints: [[2, 1000], [5, 2000], [1, 3000]] },
{ datapoints: [[3, 1000], [7, 2000]] },
];
const expected = [
{ datapoints: [[1, 1000], [2, 2000]] },
{ datapoints: [[1, 1000], [3, 2000], [1, 3000]] },
{ datapoints: [[1, 1000], [2, 2000]] },
];
const result = ctx.resultTransformer.transformToHistogramOverTime(seriesList);
expect(result).toEqual(expected);
});
it('should throw error when data in wrong format', () => {
const seriesList = [{ rows: [] }, { datapoints: [] }];
expect(() => {
ctx.resultTransformer.transformToHistogramOverTime(seriesList);
}).toThrow();
});
it('should throw error when prometheus returned non-timeseries', () => {
// should be { metric: {}, values: [] } for timeseries
const metricData = { metric: {}, value: [] };
expect(() => {
ctx.resultTransformer.transformMetricData(metricData, { step: 1 }, 1000, 2000);
}).toThrow();
});
}); });
describe('When resultFormat is time series', () => { describe('When resultFormat is time series', () => {

View File

@ -302,6 +302,10 @@ export class HeatmapCtrl extends MetricsPanelCtrl {
} }
seriesHandler(seriesData) { seriesHandler(seriesData) {
if (seriesData.datapoints === undefined) {
throw new Error('Heatmap error: data should be a time series');
}
let series = new TimeSeries({ let series = new TimeSeries({
datapoints: seriesData.datapoints, datapoints: seriesData.datapoints,
alias: seriesData.target, alias: seriesData.target,

View File

@ -36,10 +36,6 @@
} }
} }
.sidemenu {
display: none;
}
.gf-timepicker-nav-btn { .gf-timepicker-nav-btn {
transform: translate3d(40px, 0, 0); transform: translate3d(40px, 0, 0);
} }