From 1dd65f7a39f0f25985551bc4ebff671f70493a5a Mon Sep 17 00:00:00 2001 From: Aman Date: Wed, 9 May 2018 13:32:44 +0530 Subject: [PATCH 01/80] Add options to colorize prefix and postfix in singlestat --- .../app/plugins/panel/singlestat/editor.html | 4 ++++ public/app/plugins/panel/singlestat/module.ts | 19 ++++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/public/app/plugins/panel/singlestat/editor.html b/public/app/plugins/panel/singlestat/editor.html index 15f4e6a9efa..3b75cf87eae 100644 --- a/public/app/plugins/panel/singlestat/editor.html +++ b/public/app/plugins/panel/singlestat/editor.html @@ -58,6 +58,10 @@ +
+ + +
@@ -39,7 +39,7 @@
- +
@@ -59,8 +59,8 @@
- - + +
diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index 0cf3a3a95a7..ebd2628b086 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -198,8 +198,8 @@ class SingleStatCtrl extends MetricsPanelCtrl { this.setValueMapping(data); } - canChangeFontSize() { - return this.panel.gauge.show; + canModifyText() { + return !this.panel.gauge.show; } setColoring(options) { From bd805544c5d0040b9233cede4a138c6c350fdb4d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Jun 2018 15:18:26 +0200 Subject: [PATCH 04/80] build: yarn should be included out of the box on circle ci --- .circleci/config.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6e3cfc1e840..3aedd49c935 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -91,9 +91,6 @@ jobs: - image: circleci/node:8 steps: - checkout - - run: - name: install yarn - command: 'sudo npm install -g yarn --quiet' - restore_cache: key: dependency-cache-{{ checksum "yarn.lock" }} - run: From 9f029277615629b6bd0dcbe6361489231456961f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Jun 2018 14:34:06 +0200 Subject: [PATCH 05/80] login: fix layout issues --- public/sass/components/_footer.scss | 13 ++++++++++--- public/sass/pages/_login.scss | 29 ++++++++++++++++------------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/public/sass/components/_footer.scss b/public/sass/components/_footer.scss index 8b7d64e47fe..4a77ec37605 100644 --- a/public/sass/components/_footer.scss +++ b/public/sass/components/_footer.scss @@ -25,7 +25,7 @@ display: inline-block; padding-right: 2px; &::after { - content: " | "; + content: ' | '; padding-left: 2px; } } @@ -33,14 +33,21 @@ li:last-child { &::after { padding-left: 0; - content: ""; + content: ''; } } } .login-page { .footer { + padding: 1rem 0 1rem 0; + } +} + +@include media-breakpoint-up(md) { + .footer { + bottom: $spacer; position: absolute; - bottom: $spacer; + padding: 5rem 0 1rem 0; } } diff --git a/public/sass/pages/_login.scss b/public/sass/pages/_login.scss index b81099ff22b..9a4260576b1 100644 --- a/public/sass/pages/_login.scss +++ b/public/sass/pages/_login.scss @@ -1,9 +1,8 @@ $login-border: #8daac5; .login { - background-position: center; min-height: 85vh; - height: 80vh; + background-position: center; background-repeat: no-repeat; min-width: 100%; margin-left: 0; @@ -95,7 +94,7 @@ select:-webkit-autofill:focus { position: relative; justify-content: center; z-index: 1; - height: 320px; + min-height: 320px; } .login-branding { @@ -106,6 +105,7 @@ select:-webkit-autofill:focus { align-items: center; justify-content: center; flex-grow: 0; + padding-top: 2rem; .logo-icon { width: 70px; @@ -127,7 +127,7 @@ select:-webkit-autofill:focus { .login-inner-box { text-align: center; - padding: 2rem 4rem; + padding: 2rem; display: flex; flex-direction: column; align-items: center; @@ -243,7 +243,7 @@ select:-webkit-autofill:focus { justify-content: space-between; .login-divider-line { - width: 110px; + width: 100px; height: 10px; border-bottom: 1px solid $login-border; @@ -323,7 +323,10 @@ select:-webkit-autofill:focus { width: 35%; padding: 4rem 2rem; border-right: 1px solid $login-border; - justify-content: flex-start; + + .logo-icon { + width: 80px; + } } .login-inner-box { @@ -331,14 +334,18 @@ select:-webkit-autofill:focus { padding: 1rem 2rem; } - .login-branding { - .logo-icon { - width: 80px; + .login-divider { + .login-divider-line { + width: 110px; } } } @include media-breakpoint-up(md) { + .login { + min-height: 100vh; + } + .login-content { flex: 1 0 100%; } @@ -373,10 +380,6 @@ select:-webkit-autofill:focus { } @include media-breakpoint-up(lg) { - .login { - min-height: 100vh; - } - .login-form-input { min-width: 300px; } From e256c61b54a4378c8f022f7606d6a339042ba743 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Jun 2018 15:37:22 +0200 Subject: [PATCH 06/80] docs: upd what's new in v5.2 --- docs/sources/guides/whats-new-in-v5-2.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v5-2.md b/docs/sources/guides/whats-new-in-v5-2.md index 554f8f073d8..43c0c528e96 100644 --- a/docs/sources/guides/whats-new-in-v5-2.md +++ b/docs/sources/guides/whats-new-in-v5-2.md @@ -15,7 +15,7 @@ weight = -8 Grafana v5.2 brings new features, many enhancements and bug fixes. This article will detail the major new features and enhancements. * [Elasticsearch alerting]({{< relref "#elasticsearch-alerting" >}}) it's finally here! -* [Cross platform build support]({{< relref "#cross-platform-build-support" >}}) enables native builds of Grafana for many more platforms! +* [Native builds for ARM]({{< relref "#native-builds-for-armv" >}}) native builds of Grafana for many more platforms! * [Improved Docker image]({{< relref "#improved-docker-image" >}}) with support for docker secrets * [Security]({{< relref "#security" >}}) make your Grafana instance more secure * [Prometheus]({{< relref "#prometheus" >}}) with alignment enhancements @@ -32,12 +32,14 @@ the most requested features by our community and now it's finally here. Please t
-## Cross platform build support - -Grafana v5.2 brings an improved build pipeline with cross platform support. This enables native builds of Grafana for ARMv7 (x32), ARM64 (x64), -MacOS/Darwin (x64) and Windows (x64) in both stable and nightly builds. +## Native builds for ARM +Grafana v5.2 brings an improved build pipeline with cross platform support. This enables native builds of Grafana for ARMv7 (x32) and ARM64 (x64). We've been longing for native ARM build support for a long time. With the help from our amazing community this is now finally available. +Please try it out and let us know what you think. + +Another great addition with the improved build pipeline is that binaries for MacOS/Darwin (x64) and Windows (x64) now automatically are built and +published for both stable and nightly builds. ## Improved Docker image From 8d535dd90d3490c6714e2b307718a40a75368a55 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Jun 2018 15:42:34 +0200 Subject: [PATCH 07/80] docs: upd what's new in v5.2 [skip ci] --- docs/sources/guides/whats-new-in-v5-2.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/guides/whats-new-in-v5-2.md b/docs/sources/guides/whats-new-in-v5-2.md index 43c0c528e96..4391c24ce6c 100644 --- a/docs/sources/guides/whats-new-in-v5-2.md +++ b/docs/sources/guides/whats-new-in-v5-2.md @@ -15,7 +15,7 @@ weight = -8 Grafana v5.2 brings new features, many enhancements and bug fixes. This article will detail the major new features and enhancements. * [Elasticsearch alerting]({{< relref "#elasticsearch-alerting" >}}) it's finally here! -* [Native builds for ARM]({{< relref "#native-builds-for-armv" >}}) native builds of Grafana for many more platforms! +* [Native builds for ARM]({{< relref "#native-builds-for-arm" >}}) native builds of Grafana for many more platforms! * [Improved Docker image]({{< relref "#improved-docker-image" >}}) with support for docker secrets * [Security]({{< relref "#security" >}}) make your Grafana instance more secure * [Prometheus]({{< relref "#prometheus" >}}) with alignment enhancements From ec3a4e105f0fc0d3e4a8da3d35c857240a9d02e5 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 26 Jun 2018 18:11:56 +0200 Subject: [PATCH 08/80] changelog: add notes about closing #12385 [skip ci] --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7df1533e86a..a09d3b89f0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ * **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley) * **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248) -# 5.2.0 (unreleased) +# 5.2.0-stable (2018-06-27) ### Minor @@ -16,6 +16,10 @@ * **Render**: Enhance error message if phantomjs executable is not found [#11868](https://github.com/grafana/grafana/issues/11868) * **Dashboard**: Set correct text in drop down when variable is present in url [#11968](https://github.com/grafana/grafana/issues/11968) +### 5.2.0-beta3 fixes + +* **LDAP**: Handle "dn" ldap attribute more gracefully [#12385](https://github.com/grafana/grafana/pull/12385), reverts [#10970](https://github.com/grafana/grafana/pull/10970) + # 5.2.0-beta3 (2018-06-21) ### Minor From e378f6db88574d8b0ffe1e8eda0930b9c89a1c70 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 11:35:53 +0200 Subject: [PATCH 09/80] docs: update index with link to what's new in 5.2 instead of 5.0 --- docs/sources/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/sources/index.md b/docs/sources/index.md index 3c59b9baba0..da977b73e0c 100644 --- a/docs/sources/index.md +++ b/docs/sources/index.md @@ -60,9 +60,9 @@ aliases = ["v1.1", "guides/reference/admin"]

Provisioning

A guide to help you automate your Grafana setup & configuration.

- }}" class="nav-cards__item nav-cards__item--guide"> -

What's new in v5.0

-

Article on all the new cool features and enhancements in v5.0

+
}}" class="nav-cards__item nav-cards__item--guide"> +

What's new in v5.2

+

Article on all the new cool features and enhancements in v5.2

}}" class="nav-cards__item nav-cards__item--guide">

Screencasts

From b0114442461933ca8d37efece2bb0ed2aaf64d43 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 11:37:05 +0200 Subject: [PATCH 10/80] release 5.2.0 --- docs/versions.json | 3 ++- latest.json | 4 ++-- package.json | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/versions.json b/docs/versions.json index 61e471938f2..caefbe198d6 100644 --- a/docs/versions.json +++ b/docs/versions.json @@ -1,5 +1,6 @@ [ - { "version": "v5.1", "path": "/", "archived": false, "current": true }, + { "version": "v5.2", "path": "/", "archived": false, "current": true }, + { "version": "v5.1", "path": "/v5.1", "archived": true }, { "version": "v5.0", "path": "/v5.0", "archived": true }, { "version": "v4.6", "path": "/v4.6", "archived": true }, { "version": "v4.5", "path": "/v4.5", "archived": true }, diff --git a/latest.json b/latest.json index d8804f98441..8e26289c856 100644 --- a/latest.json +++ b/latest.json @@ -1,4 +1,4 @@ { - "stable": "5.1.3", - "testing": "5.1.3" + "stable": "5.2.0", + "testing": "5.2.0" } diff --git a/package.json b/package.json index 0433dc3e986..4f2220abbae 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Grafana Labs" }, "name": "grafana", - "version": "5.2.0-pre1", + "version": "5.3.0-pre1", "repository": { "type": "git", "url": "http://github.com/grafana/grafana.git" From 6fa87e62786ff78d8c795d5294df45d3f32bcefc Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 14:20:36 +0200 Subject: [PATCH 11/80] docs: upd what's new in v5.2 --- docs/sources/guides/whats-new-in-v5-2.md | 46 ++++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/sources/guides/whats-new-in-v5-2.md b/docs/sources/guides/whats-new-in-v5-2.md index 4391c24ce6c..e084f8618e4 100644 --- a/docs/sources/guides/whats-new-in-v5-2.md +++ b/docs/sources/guides/whats-new-in-v5-2.md @@ -14,14 +14,14 @@ weight = -8 Grafana v5.2 brings new features, many enhancements and bug fixes. This article will detail the major new features and enhancements. -* [Elasticsearch alerting]({{< relref "#elasticsearch-alerting" >}}) it's finally here! -* [Native builds for ARM]({{< relref "#native-builds-for-arm" >}}) native builds of Grafana for many more platforms! -* [Improved Docker image]({{< relref "#improved-docker-image" >}}) with support for docker secrets -* [Security]({{< relref "#security" >}}) make your Grafana instance more secure -* [Prometheus]({{< relref "#prometheus" >}}) with alignment enhancements -* [InfluxDB]({{< relref "#influxdb" >}}) with support for a new function -* [Alerting]({{< relref "#alerting" >}}) with alert notification channel type for Discord -* [Dashboards & Panels]({{< relref "#dashboards-panels" >}}) with save & import enhancements +- [Elasticsearch alerting]({{< relref "#elasticsearch-alerting" >}}) it's finally here! +- [Native builds for ARM]({{< relref "#native-builds-for-arm" >}}) native builds of Grafana for many more platforms! +- [Improved Docker image]({{< relref "#improved-docker-image" >}}) with support for docker secrets +- [Security]({{< relref "#security" >}}) make your Grafana instance more secure +- [Prometheus]({{< relref "#prometheus" >}}) with alignment enhancements +- [InfluxDB]({{< relref "#influxdb" >}}) now supports the `mode` function +- [Alerting]({{< relref "#alerting" >}}) with alert notification channel type for Discord +- [Dashboards & Panels]({{< relref "#dashboards-panels" >}}) with save & import enhancements ## Elasticsearch alerting @@ -34,16 +34,16 @@ the most requested features by our community and now it's finally here. Please t ## Native builds for ARM -Grafana v5.2 brings an improved build pipeline with cross platform support. This enables native builds of Grafana for ARMv7 (x32) and ARM64 (x64). -We've been longing for native ARM build support for a long time. With the help from our amazing community this is now finally available. +Grafana v5.2 brings an improved build pipeline with cross-platform support. This enables native builds of Grafana for ARMv7 (x32) and ARM64 (x64). +We've been longing for native ARM build support for ages. With the help from our amazing community this is now finally available. Please try it out and let us know what you think. -Another great addition with the improved build pipeline is that binaries for MacOS/Darwin (x64) and Windows (x64) now automatically are built and +Another great addition with the improved build pipeline is that binaries for MacOS/Darwin (x64) and Windows (x64) are now automatically built and published for both stable and nightly builds. ## Improved Docker image -The Grafana docker image now includes support for Docker secrets which enables you to supply Grafana with configuration through files. More +The Grafana docker image adds support for Docker secrets which enables you to supply Grafana with configuration through files. More information in the [Installing using Docker documentation](/installation/docker/#reading-secrets-from-files-support-for-docker-secrets). ## Security @@ -51,18 +51,18 @@ information in the [Installing using Docker documentation](/installation/docker/ {{< docs-imagebox img="/img/docs/v52/login_change_password.png" max-width="800px" class="docs-image--right" >}} Starting from Grafana v5.2, when you login with the administrator account using the default password you'll be presented with a form to change the password. -By this we hope to encourage users to follow Grafana's best practices and change the default administrator password. +We hope this encourages users to follow Grafana's best practices and change the default administrator password.
## Prometheus The Prometheus datasource now aligns the start/end of the query sent to Prometheus with the step, which ensures PromQL expressions with *rate* -functions get consistent results, and thus avoid graphs jumping around on reload. +functions get consistent results, and thus avoids graphs jumping around on reload. ## InfluxDB -The InfluxDB datasource now includes support for the *mode* function which allows to return the most frequent value in a list of field values. +The InfluxDB datasource now includes support for the *mode* function which returns the most frequent value in a list of field values. ## Alerting @@ -74,9 +74,9 @@ By popular demand Grafana now includes support for an alert notification channel {{< docs-imagebox img="/img/docs/v52/dashboard_save_modal.png" max-width="800px" class="docs-image--right" >}} -Starting from Grafana v5.2 a modified time range or variable are no longer saved by default. To save a modified -time range or variable you'll need to actively select that when saving a dashboard, see screenshot. -This should hopefully make it easier to have sane defaults of time and variables in dashboards and make it more explicit +Starting from Grafana v5.2, a modified time range or variable are no longer saved by default. To save a modified +time range or variable, you'll need to actively select that when saving a dashboard, see screenshot. +This should hopefully make it easier to have sane defaults for time and variables in dashboards and make it more explicit when you actually want to overwrite those settings.
@@ -85,13 +85,13 @@ when you actually want to overwrite those settings. {{< docs-imagebox img="/img/docs/v52/dashboard_import.png" max-width="800px" class="docs-image--right" >}} -Grafana v5.2 adds support for specifying an existing folder or create a new one when importing a dashboard, a long awaited feature since -Grafana v5.0 introduced support for dashboard folders and permissions. The import dashboard page have also got some general improvements +Grafana v5.2 adds support for specifying an existing folder or creating a new one when importing a dashboard - a long-awaited feature since +Grafana v5.0 introduced support for dashboard folders and permissions. The import dashboard page has also got some general improvements and should now make it more clear if a possible import will overwrite an existing dashboard, or not. -This release also adds some improvements for those users only having editor or admin permissions in certain folders. Now the links to -*Create Dashboard* and *Import Dashboard* is available in side navigation, dashboard search and manage dashboards/folder page for a -user that has editor role in an organization or edit permission in at least one folder. +This release also adds some improvements for those users only having editor or admin permissions in certain folders. The links to +*Create Dashboard* and *Import Dashboard* are now available in the side navigation, in dashboard search and on the manage dashboards/folder page for a +user that has editor role in an organization or the edit permission in at least one folder.
From f1ab1a26e9f48224fea2d5d83f0ed480db25539d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 14:48:55 +0200 Subject: [PATCH 12/80] changelog: update [skip ci] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a09d3b89f0f..29d4ae2e006 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,10 @@ * **Dashboard list panel**: Search dashboards by folder [#11525](https://github.com/grafana/grafana/issues/11525) * **Sidenav**: Always show server admin link in sidenav if grafana admin [#11657](https://github.com/grafana/grafana/issues/11657) +# 5.1.5 (2018-06-27) + +* **Docker**: Config keys ending with _FILE are not respected [#170](https://github.com/grafana/grafana-docker/issues/170) + # 5.1.4 (2018-06-19) * **Permissions**: Important security fix for API keys with viewer role [#12343](https://github.com/grafana/grafana/issues/12343) From 18032b374b705db52afa2195bd770de818cfd5c1 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 15:11:44 +0200 Subject: [PATCH 13/80] changelog: add notes about closing #11920 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29d4ae2e006..abcf1884976 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,6 +63,7 @@ * **Elasticsearch**: Alerting support [#5893](https://github.com/grafana/grafana/issues/5893), thx [@WPH95](https://github.com/WPH95) * **Login**: Change admin password after first login [#11882](https://github.com/grafana/grafana/issues/11882) * **Alert list panel**: Updated to support filtering alerts by name, dashboard title, folder, tags [#11500](https://github.com/grafana/grafana/issues/11500), [#8168](https://github.com/grafana/grafana/issues/8168), [#6541](https://github.com/grafana/grafana/issues/6541) +* **Build**: Crosscompile and packages Grafana on arm, windows, linux and darwin [#11920](https://github.com/grafana/grafana/pull/11920), thx [@fg2it](https://github.com/fg2it) ### Minor From 3cf6a1a94bbe3f5c4985c20912109574a04eee4f Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 27 Jun 2018 15:17:15 +0200 Subject: [PATCH 14/80] changelog: add notes about closing #11920 [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abcf1884976..c943b2b9776 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,9 +61,9 @@ ### New Features * **Elasticsearch**: Alerting support [#5893](https://github.com/grafana/grafana/issues/5893), thx [@WPH95](https://github.com/WPH95) +* **Build**: Crosscompile and packages Grafana on arm, windows, linux and darwin [#11920](https://github.com/grafana/grafana/pull/11920), thx [@fg2it](https://github.com/fg2it) * **Login**: Change admin password after first login [#11882](https://github.com/grafana/grafana/issues/11882) * **Alert list panel**: Updated to support filtering alerts by name, dashboard title, folder, tags [#11500](https://github.com/grafana/grafana/issues/11500), [#8168](https://github.com/grafana/grafana/issues/8168), [#6541](https://github.com/grafana/grafana/issues/6541) -* **Build**: Crosscompile and packages Grafana on arm, windows, linux and darwin [#11920](https://github.com/grafana/grafana/pull/11920), thx [@fg2it](https://github.com/fg2it) ### Minor From c2381f088f69637c48ee8ced84d8604e71436013 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Thu, 28 Jun 2018 12:02:49 +0200 Subject: [PATCH 15/80] Add disabled styles for checked checkbox (#12422) --- public/sass/components/_switch.scss | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/public/sass/components/_switch.scss b/public/sass/components/_switch.scss index c7eb1914103..6eb01ecc32d 100644 --- a/public/sass/components/_switch.scss +++ b/public/sass/components/_switch.scss @@ -64,8 +64,8 @@ } input + label::before { - font-family: "FontAwesome"; - content: "\f096"; // square-o + font-family: 'FontAwesome'; + content: '\f096'; // square-o color: $text-color-weak; transition: transform 0.4s; backface-visibility: hidden; @@ -73,11 +73,11 @@ } input + label::after { - content: "\f046"; // check-square-o + content: '\f046'; // check-square-o color: $orange; text-shadow: $text-shadow-strong; - font-family: "FontAwesome"; + font-family: 'FontAwesome'; transition: transform 0.4s; transform: rotateY(180deg); backface-visibility: hidden; @@ -154,7 +154,8 @@ gf-form-switch[disabled] { .gf-form-switch input + label { cursor: default; pointer-events: none !important; - &::before { + &::before, + &::after { color: $text-color-faint; text-shadow: none; } From 3056d9a80eda8414539ff9c96ba2f32a219d2149 Mon Sep 17 00:00:00 2001 From: Dan Cech Date: Thu, 28 Jun 2018 12:08:32 +0200 Subject: [PATCH 16/80] support passing api token in Basic auth password (#12416) --- docs/sources/http_api/auth.md | 8 ++++++++ pkg/middleware/auth.go | 6 ++++++ pkg/middleware/middleware_test.go | 26 ++++++++++++++++++++++++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/sources/http_api/auth.md b/docs/sources/http_api/auth.md index 166a5a4fdb9..8ff40b5ef04 100644 --- a/docs/sources/http_api/auth.md +++ b/docs/sources/http_api/auth.md @@ -44,6 +44,14 @@ Authorization: Bearer eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk The `Authorization` header value should be `Bearer `. +The API Token can also be passed as a Basic authorization password with the special username `api_key`: + +curl example: +```bash +?curl http://api_key:eyJrIjoiT0tTcG1pUlY2RnVKZTFVaDFsNFZXdE9ZWmNrMkZYbk@localhost:3000/api/org +{"id":1,"name":"Main Org."} +``` + # Auth HTTP resources / actions ## Api Keys diff --git a/pkg/middleware/auth.go b/pkg/middleware/auth.go index 37e79c01071..5faee1e3fa7 100644 --- a/pkg/middleware/auth.go +++ b/pkg/middleware/auth.go @@ -9,6 +9,7 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/services/session" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" ) type AuthOptions struct { @@ -34,6 +35,11 @@ func getApiKey(c *m.ReqContext) string { return key } + username, password, err := util.DecodeBasicAuthHeader(header) + if err == nil && username == "api_key" { + return password + } + return "" } diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index 0b50358ad73..d82b7313585 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -82,7 +82,7 @@ func TestMiddlewareContext(t *testing.T) { setting.BasicAuthEnabled = true authHeader := util.GetBasicAuthHeader("myUser", "myPass") - sc.fakeReq("GET", "/").withAuthoriziationHeader(authHeader).exec() + sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec() Convey("Should init middleware context with user", func() { So(sc.context.IsSignedIn, ShouldEqual, true) @@ -128,6 +128,28 @@ func TestMiddlewareContext(t *testing.T) { }) }) + middlewareScenario("Valid api key via Basic auth", func(sc *scenarioContext) { + keyhash := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd") + + bus.AddHandler("test", func(query *m.GetApiKeyByNameQuery) error { + query.Result = &m.ApiKey{OrgId: 12, Role: m.ROLE_EDITOR, Key: keyhash} + return nil + }) + + authHeader := util.GetBasicAuthHeader("api_key", "eyJrIjoidjVuQXdwTWFmRlA2em5hUzR1cmhkV0RMUzU1MTFNNDIiLCJuIjoiYXNkIiwiaWQiOjF9") + sc.fakeReq("GET", "/").withAuthorizationHeader(authHeader).exec() + + Convey("Should return 200", func() { + So(sc.resp.Code, ShouldEqual, 200) + }) + + Convey("Should init middleware context", func() { + So(sc.context.IsSignedIn, ShouldEqual, true) + So(sc.context.OrgId, ShouldEqual, 12) + So(sc.context.OrgRole, ShouldEqual, m.ROLE_EDITOR) + }) + }) + middlewareScenario("UserId in session", func(sc *scenarioContext) { sc.fakeReq("GET", "/").handler(func(c *m.ReqContext) { @@ -473,7 +495,7 @@ func (sc *scenarioContext) withInvalidApiKey() *scenarioContext { return sc } -func (sc *scenarioContext) withAuthoriziationHeader(authHeader string) *scenarioContext { +func (sc *scenarioContext) withAuthorizationHeader(authHeader string) *scenarioContext { sc.authHeader = authHeader return sc } From 443ff5deb4b010f321d41f04f676543c6c2bdb8e Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Thu, 28 Jun 2018 12:10:36 +0200 Subject: [PATCH 17/80] Karma to Jest: value_select_dropdown (#12435) * Begin Karma 2 Jest: value_select_dropdown * Add return for Promise * Remove Karma test --- .../core/directives/value_select_dropdown.ts | 4 +- .../core/specs/value_select_dropdown.jest.ts | 159 ++++++++++++++++ .../core/specs/value_select_dropdown_specs.ts | 171 ------------------ 3 files changed, 161 insertions(+), 173 deletions(-) create mode 100644 public/app/core/specs/value_select_dropdown.jest.ts delete mode 100644 public/app/core/specs/value_select_dropdown_specs.ts diff --git a/public/app/core/directives/value_select_dropdown.ts b/public/app/core/directives/value_select_dropdown.ts index d6c6c3af5c5..d384904c2d8 100644 --- a/public/app/core/directives/value_select_dropdown.ts +++ b/public/app/core/directives/value_select_dropdown.ts @@ -93,7 +93,7 @@ export class ValueSelectDropdownCtrl { tagValuesPromise = this.$q.when(tag.values); } - tagValuesPromise.then(values => { + return tagValuesPromise.then(values => { tag.values = values; tag.valuesText = values.join(' + '); _.each(this.options, option => { @@ -132,7 +132,7 @@ export class ValueSelectDropdownCtrl { this.highlightIndex = (this.highlightIndex + direction) % this.search.options.length; } - selectValue(option, event, commitChange, excludeOthers) { + selectValue(option, event, commitChange?, excludeOthers?) { if (!option) { return; } diff --git a/public/app/core/specs/value_select_dropdown.jest.ts b/public/app/core/specs/value_select_dropdown.jest.ts new file mode 100644 index 00000000000..3cc310435b7 --- /dev/null +++ b/public/app/core/specs/value_select_dropdown.jest.ts @@ -0,0 +1,159 @@ +import 'app/core/directives/value_select_dropdown'; +import { ValueSelectDropdownCtrl } from '../directives/value_select_dropdown'; +import q from 'q'; + +describe('SelectDropdownCtrl', () => { + let tagValuesMap: any = {}; + + ValueSelectDropdownCtrl.prototype.onUpdated = jest.fn(); + let ctrl; + + describe('Given simple variable', () => { + beforeEach(() => { + ctrl = new ValueSelectDropdownCtrl(q); + ctrl.variable = { + current: { text: 'hej', value: 'hej' }, + getValuesForTag: key => { + return Promise.resolve(tagValuesMap[key]); + }, + }; + ctrl.init(); + }); + + it('Should init labelText and linkText', () => { + expect(ctrl.linkText).toBe('hej'); + }); + }); + + describe('Given variable with tags and dropdown is opened', () => { + beforeEach(() => { + ctrl = new ValueSelectDropdownCtrl(q); + ctrl.variable = { + current: { text: 'server-1', value: 'server-1' }, + options: [ + { text: 'server-1', value: 'server-1', selected: true }, + { text: 'server-2', value: 'server-2' }, + { text: 'server-3', value: 'server-3' }, + ], + tags: ['key1', 'key2', 'key3'], + getValuesForTag: key => { + return Promise.resolve(tagValuesMap[key]); + }, + multi: true, + }; + tagValuesMap.key1 = ['server-1', 'server-3']; + tagValuesMap.key2 = ['server-2', 'server-3']; + tagValuesMap.key3 = ['server-1', 'server-2', 'server-3']; + ctrl.init(); + ctrl.show(); + }); + + it('should init tags model', () => { + expect(ctrl.tags.length).toBe(3); + expect(ctrl.tags[0].text).toBe('key1'); + }); + + it('should init options model', () => { + expect(ctrl.options.length).toBe(3); + }); + + it('should init selected values array', () => { + expect(ctrl.selectedValues.length).toBe(1); + }); + + it('should set linkText', () => { + expect(ctrl.linkText).toBe('server-1'); + }); + + describe('after adititional value is selected', () => { + beforeEach(() => { + ctrl.selectValue(ctrl.options[2], {}); + ctrl.commitChanges(); + }); + + it('should update link text', () => { + expect(ctrl.linkText).toBe('server-1 + server-3'); + }); + }); + + describe('When tag is selected', () => { + beforeEach(async () => { + await ctrl.selectTag(ctrl.tags[0]); + ctrl.commitChanges(); + }); + + it('should select tag', () => { + expect(ctrl.selectedTags.length).toBe(1); + }); + + it('should select values', () => { + expect(ctrl.options[0].selected).toBe(true); + expect(ctrl.options[2].selected).toBe(true); + }); + + it('link text should not include tag values', () => { + expect(ctrl.linkText).toBe(''); + }); + + describe('and then dropdown is opened and closed without changes', () => { + beforeEach(() => { + ctrl.show(); + ctrl.commitChanges(); + }); + + it('should still have selected tag', () => { + expect(ctrl.selectedTags.length).toBe(1); + }); + }); + + describe('and then unselected', () => { + beforeEach(async () => { + await ctrl.selectTag(ctrl.tags[0]); + }); + + it('should deselect tag', () => { + expect(ctrl.selectedTags.length).toBe(0); + }); + }); + + describe('and then value is unselected', () => { + beforeEach(() => { + ctrl.selectValue(ctrl.options[0], {}); + }); + + it('should deselect tag', () => { + expect(ctrl.selectedTags.length).toBe(0); + }); + }); + }); + }); + + describe('Given variable with selected tags', () => { + beforeEach(() => { + ctrl = new ValueSelectDropdownCtrl(q); + ctrl.variable = { + current: { + text: 'server-1', + value: 'server-1', + tags: [{ text: 'key1', selected: true }], + }, + options: [ + { text: 'server-1', value: 'server-1' }, + { text: 'server-2', value: 'server-2' }, + { text: 'server-3', value: 'server-3' }, + ], + tags: ['key1', 'key2', 'key3'], + getValuesForTag: key => { + return Promise.resolve(tagValuesMap[key]); + }, + multi: true, + }; + ctrl.init(); + ctrl.show(); + }); + + it('should set tag as selected', () => { + expect(ctrl.tags[0].selected).toBe(true); + }); + }); +}); diff --git a/public/app/core/specs/value_select_dropdown_specs.ts b/public/app/core/specs/value_select_dropdown_specs.ts deleted file mode 100644 index 8f6408fb389..00000000000 --- a/public/app/core/specs/value_select_dropdown_specs.ts +++ /dev/null @@ -1,171 +0,0 @@ -import { describe, beforeEach, it, expect, angularMocks, sinon } from 'test/lib/common'; -import 'app/core/directives/value_select_dropdown'; - -describe('SelectDropdownCtrl', function() { - var scope; - var ctrl; - var tagValuesMap: any = {}; - var rootScope; - var q; - - beforeEach(angularMocks.module('grafana.core')); - beforeEach( - angularMocks.inject(function($controller, $rootScope, $q, $httpBackend) { - rootScope = $rootScope; - q = $q; - scope = $rootScope.$new(); - ctrl = $controller('ValueSelectDropdownCtrl', { $scope: scope }); - ctrl.onUpdated = sinon.spy(); - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); - - describe('Given simple variable', function() { - beforeEach(function() { - ctrl.variable = { - current: { text: 'hej', value: 'hej' }, - getValuesForTag: function(key) { - return q.when(tagValuesMap[key]); - }, - }; - ctrl.init(); - }); - - it('Should init labelText and linkText', function() { - expect(ctrl.linkText).to.be('hej'); - }); - }); - - describe('Given variable with tags and dropdown is opened', function() { - beforeEach(function() { - ctrl.variable = { - current: { text: 'server-1', value: 'server-1' }, - options: [ - { text: 'server-1', value: 'server-1', selected: true }, - { text: 'server-2', value: 'server-2' }, - { text: 'server-3', value: 'server-3' }, - ], - tags: ['key1', 'key2', 'key3'], - getValuesForTag: function(key) { - return q.when(tagValuesMap[key]); - }, - multi: true, - }; - tagValuesMap.key1 = ['server-1', 'server-3']; - tagValuesMap.key2 = ['server-2', 'server-3']; - tagValuesMap.key3 = ['server-1', 'server-2', 'server-3']; - ctrl.init(); - ctrl.show(); - }); - - it('should init tags model', function() { - expect(ctrl.tags.length).to.be(3); - expect(ctrl.tags[0].text).to.be('key1'); - }); - - it('should init options model', function() { - expect(ctrl.options.length).to.be(3); - }); - - it('should init selected values array', function() { - expect(ctrl.selectedValues.length).to.be(1); - }); - - it('should set linkText', function() { - expect(ctrl.linkText).to.be('server-1'); - }); - - describe('after adititional value is selected', function() { - beforeEach(function() { - ctrl.selectValue(ctrl.options[2], {}); - ctrl.commitChanges(); - }); - - it('should update link text', function() { - expect(ctrl.linkText).to.be('server-1 + server-3'); - }); - }); - - describe('When tag is selected', function() { - beforeEach(function() { - ctrl.selectTag(ctrl.tags[0]); - rootScope.$digest(); - ctrl.commitChanges(); - }); - - it('should select tag', function() { - expect(ctrl.selectedTags.length).to.be(1); - }); - - it('should select values', function() { - expect(ctrl.options[0].selected).to.be(true); - expect(ctrl.options[2].selected).to.be(true); - }); - - it('link text should not include tag values', function() { - expect(ctrl.linkText).to.be(''); - }); - - describe('and then dropdown is opened and closed without changes', function() { - beforeEach(function() { - ctrl.show(); - ctrl.commitChanges(); - rootScope.$digest(); - }); - - it('should still have selected tag', function() { - expect(ctrl.selectedTags.length).to.be(1); - }); - }); - - describe('and then unselected', function() { - beforeEach(function() { - ctrl.selectTag(ctrl.tags[0]); - rootScope.$digest(); - }); - - it('should deselect tag', function() { - expect(ctrl.selectedTags.length).to.be(0); - }); - }); - - describe('and then value is unselected', function() { - beforeEach(function() { - ctrl.selectValue(ctrl.options[0], {}); - }); - - it('should deselect tag', function() { - expect(ctrl.selectedTags.length).to.be(0); - }); - }); - }); - }); - - describe('Given variable with selected tags', function() { - beforeEach(function() { - ctrl.variable = { - current: { - text: 'server-1', - value: 'server-1', - tags: [{ text: 'key1', selected: true }], - }, - options: [ - { text: 'server-1', value: 'server-1' }, - { text: 'server-2', value: 'server-2' }, - { text: 'server-3', value: 'server-3' }, - ], - tags: ['key1', 'key2', 'key3'], - getValuesForTag: function(key) { - return q.when(tagValuesMap[key]); - }, - multi: true, - }; - ctrl.init(); - ctrl.show(); - }); - - it('should set tag as selected', function() { - expect(ctrl.tags[0].selected).to.be(true); - }); - }); -}); From 7a7c6f8fab04d622fda04d65fdca1a7914b3f5a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 28 Jun 2018 04:38:23 -0700 Subject: [PATCH 18/80] fix: log close/flush was done too early, before server shutdown log message was called, fixes #12438 --- pkg/cmd/grafana-server/main.go | 4 ---- pkg/cmd/grafana-server/server.go | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index 976c027d749..e64f42a7320 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -14,7 +14,6 @@ import ( "net/http" _ "net/http/pprof" - "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/setting" @@ -88,9 +87,6 @@ func main() { err := server.Run() - trace.Stop() - log.Close() - server.Exit(err) } diff --git a/pkg/cmd/grafana-server/server.go b/pkg/cmd/grafana-server/server.go index 6444528f7f0..a4543ef1f20 100644 --- a/pkg/cmd/grafana-server/server.go +++ b/pkg/cmd/grafana-server/server.go @@ -185,6 +185,8 @@ func (g *GrafanaServerImpl) Exit(reason error) { } g.log.Error("Server shutdown", "reason", reason) + + log.Close() os.Exit(code) } From af0a4a60394821b605d03575f3e756c1f0c16b6e Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Thu, 28 Jun 2018 13:43:23 +0200 Subject: [PATCH 19/80] Karma to Jest: 3 test files (#12414) * Karma to Jest: viewstate_srv * Karma to Jest: annotations_srv * Remove comments * Karma to Jest: series_override_ctrl * Remove unnecessary code * Class to function and fix lint error * Fix ngInject --- ...s_srv_specs.ts => annotations_srv.jest.ts} | 22 +- .../dashboard/specs/viewstate_srv.jest.ts | 67 ++++ .../dashboard/specs/viewstate_srv_specs.ts | 65 ---- .../panel/graph/series_overrides_ctrl.ts | 292 +++++++++--------- .../graph/specs/series_override_ctrl.jest.ts | 42 +++ .../graph/specs/series_override_ctrl_specs.ts | 55 ---- 6 files changed, 265 insertions(+), 278 deletions(-) rename public/app/features/annotations/specs/{annotations_srv_specs.ts => annotations_srv.jest.ts} (52%) create mode 100644 public/app/features/dashboard/specs/viewstate_srv.jest.ts delete mode 100644 public/app/features/dashboard/specs/viewstate_srv_specs.ts create mode 100644 public/app/plugins/panel/graph/specs/series_override_ctrl.jest.ts delete mode 100644 public/app/plugins/panel/graph/specs/series_override_ctrl_specs.ts diff --git a/public/app/features/annotations/specs/annotations_srv_specs.ts b/public/app/features/annotations/specs/annotations_srv.jest.ts similarity index 52% rename from public/app/features/annotations/specs/annotations_srv_specs.ts rename to public/app/features/annotations/specs/annotations_srv.jest.ts index 932fcf9415c..7db7b6c9f05 100644 --- a/public/app/features/annotations/specs/annotations_srv_specs.ts +++ b/public/app/features/annotations/specs/annotations_srv.jest.ts @@ -1,17 +1,17 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; import '../annotations_srv'; -import helpers from 'test/specs/helpers'; import 'app/features/dashboard/time_srv'; +import { AnnotationsSrv } from '../annotations_srv'; describe('AnnotationsSrv', function() { - var ctx = new helpers.ServiceTestContext(); + let $rootScope = { + onAppEvent: jest.fn(), + }; + let $q; + let datasourceSrv; + let backendSrv; + let timeSrv; - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(ctx.createService('timeSrv')); - beforeEach(() => { - ctx.createService('annotationsSrv'); - }); + let annotationsSrv = new AnnotationsSrv($rootScope, $q, datasourceSrv, backendSrv, timeSrv); describe('When translating the query result', () => { const annotationSource = { @@ -30,11 +30,11 @@ describe('AnnotationsSrv', function() { let translatedAnnotations; beforeEach(() => { - translatedAnnotations = ctx.service.translateQueryResult(annotationSource, annotations); + translatedAnnotations = annotationsSrv.translateQueryResult(annotationSource, annotations); }); it('should set defaults', () => { - expect(translatedAnnotations[0].source).to.eql(annotationSource); + expect(translatedAnnotations[0].source).toEqual(annotationSource); }); }); }); diff --git a/public/app/features/dashboard/specs/viewstate_srv.jest.ts b/public/app/features/dashboard/specs/viewstate_srv.jest.ts new file mode 100644 index 00000000000..08166c6f2bd --- /dev/null +++ b/public/app/features/dashboard/specs/viewstate_srv.jest.ts @@ -0,0 +1,67 @@ +//import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; +import 'app/features/dashboard/view_state_srv'; +import config from 'app/core/config'; +import { DashboardViewState } from '../view_state_srv'; + +describe('when updating view state', () => { + let location = { + replace: jest.fn(), + search: jest.fn(), + }; + + let $scope = { + onAppEvent: jest.fn(() => {}), + dashboard: { + meta: {}, + panels: [], + }, + }; + + let $rootScope = {}; + let viewState; + + beforeEach(() => { + config.bootData = { + user: { + orgId: 1, + }, + }; + }); + + describe('to fullscreen true and edit true', () => { + beforeEach(() => { + location.search = jest.fn(() => { + return { fullscreen: true, edit: true, panelId: 1 }; + }); + viewState = new DashboardViewState($scope, location, {}, $rootScope); + }); + + it('should update querystring and view state', () => { + var updateState = { fullscreen: true, edit: true, panelId: 1 }; + + viewState.update(updateState); + + expect(location.search).toHaveBeenCalledWith({ + edit: true, + editview: null, + fullscreen: true, + orgId: 1, + panelId: 1, + }); + expect(viewState.dashboard.meta.fullscreen).toBe(true); + expect(viewState.state.fullscreen).toBe(true); + }); + }); + + describe('to fullscreen false', () => { + beforeEach(() => { + viewState = new DashboardViewState($scope, location, {}, $rootScope); + }); + it('should remove params from query string', () => { + viewState.update({ fullscreen: true, panelId: 1, edit: true }); + viewState.update({ fullscreen: false }); + expect(viewState.dashboard.meta.fullscreen).toBe(false); + expect(viewState.state.fullscreen).toBe(null); + }); + }); +}); diff --git a/public/app/features/dashboard/specs/viewstate_srv_specs.ts b/public/app/features/dashboard/specs/viewstate_srv_specs.ts deleted file mode 100644 index d34b15b9113..00000000000 --- a/public/app/features/dashboard/specs/viewstate_srv_specs.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; -import 'app/features/dashboard/view_state_srv'; -import config from 'app/core/config'; - -describe('when updating view state', function() { - var viewState, location; - var timeSrv = {}; - var templateSrv = {}; - var contextSrv = { - user: { - orgId: 19, - }, - }; - beforeEach(function() { - config.bootData = { - user: { - orgId: 1, - }, - }; - }); - beforeEach(angularMocks.module('grafana.services')); - beforeEach( - angularMocks.module(function($provide) { - $provide.value('timeSrv', timeSrv); - $provide.value('templateSrv', templateSrv); - $provide.value('contextSrv', contextSrv); - }) - ); - - beforeEach( - angularMocks.inject(function(dashboardViewStateSrv, $location, $rootScope) { - $rootScope.onAppEvent = function() {}; - $rootScope.dashboard = { - meta: {}, - panels: [], - }; - viewState = dashboardViewStateSrv.create($rootScope); - location = $location; - }) - ); - - describe('to fullscreen true and edit true', function() { - it('should update querystring and view state', function() { - var updateState = { fullscreen: true, edit: true, panelId: 1 }; - viewState.update(updateState); - expect(location.search()).to.eql({ - fullscreen: true, - edit: true, - panelId: 1, - orgId: 1, - }); - expect(viewState.dashboard.meta.fullscreen).to.be(true); - expect(viewState.state.fullscreen).to.be(true); - }); - }); - - describe('to fullscreen false', function() { - it('should remove params from query string', function() { - viewState.update({ fullscreen: true, panelId: 1, edit: true }); - viewState.update({ fullscreen: false }); - expect(viewState.dashboard.meta.fullscreen).to.be(false); - expect(viewState.state.fullscreen).to.be(null); - }); - }); -}); diff --git a/public/app/plugins/panel/graph/series_overrides_ctrl.ts b/public/app/plugins/panel/graph/series_overrides_ctrl.ts index ecf79a8a4fb..5958c80bac9 100644 --- a/public/app/plugins/panel/graph/series_overrides_ctrl.ts +++ b/public/app/plugins/panel/graph/series_overrides_ctrl.ts @@ -1,160 +1,158 @@ import _ from 'lodash'; import angular from 'angular'; -export class SeriesOverridesCtrl { - /** @ngInject */ - constructor($scope, $element, popoverSrv) { - $scope.overrideMenu = []; - $scope.currentOverrides = []; - $scope.override = $scope.override || {}; +/** @ngInject */ +export function SeriesOverridesCtrl($scope, $element, popoverSrv) { + $scope.overrideMenu = []; + $scope.currentOverrides = []; + $scope.override = $scope.override || {}; - $scope.addOverrideOption = function(name, propertyName, values) { - var option = { - text: name, - propertyName: propertyName, - index: $scope.overrideMenu.lenght, - values: values, - submenu: _.map(values, function(value) { - return { text: String(value), value: value }; - }), - }; - - $scope.overrideMenu.push(option); + $scope.addOverrideOption = function(name, propertyName, values) { + var option = { + text: name, + propertyName: propertyName, + index: $scope.overrideMenu.lenght, + values: values, + submenu: _.map(values, function(value) { + return { text: String(value), value: value }; + }), }; - $scope.setOverride = function(item, subItem) { - // handle color overrides - if (item.propertyName === 'color') { - $scope.openColorSelector($scope.override['color']); + $scope.overrideMenu.push(option); + }; + + $scope.setOverride = function(item, subItem) { + // handle color overrides + if (item.propertyName === 'color') { + $scope.openColorSelector($scope.override['color']); + return; + } + + $scope.override[item.propertyName] = subItem.value; + + // automatically disable lines for this series and the fill below to series + // can be removed by the user if they still want lines + if (item.propertyName === 'fillBelowTo') { + $scope.override['lines'] = false; + $scope.ctrl.addSeriesOverride({ alias: subItem.value, lines: false }); + } + + $scope.updateCurrentOverrides(); + $scope.ctrl.render(); + }; + + $scope.colorSelected = function(color) { + $scope.override['color'] = color; + $scope.updateCurrentOverrides(); + $scope.ctrl.render(); + }; + + $scope.openColorSelector = function(color) { + var fakeSeries = { color: color }; + popoverSrv.show({ + element: $element.find('.dropdown')[0], + position: 'top center', + openOn: 'click', + template: '', + model: { + autoClose: true, + colorSelected: $scope.colorSelected, + series: fakeSeries, + }, + onClose: function() { + $scope.ctrl.render(); + }, + }); + }; + + $scope.removeOverride = function(option) { + delete $scope.override[option.propertyName]; + $scope.updateCurrentOverrides(); + $scope.ctrl.refresh(); + }; + + $scope.getSeriesNames = function() { + return _.map($scope.ctrl.seriesList, function(series) { + return series.alias; + }); + }; + + $scope.updateCurrentOverrides = function() { + $scope.currentOverrides = []; + _.each($scope.overrideMenu, function(option) { + var value = $scope.override[option.propertyName]; + if (_.isUndefined(value)) { return; } - - $scope.override[item.propertyName] = subItem.value; - - // automatically disable lines for this series and the fill below to series - // can be removed by the user if they still want lines - if (item.propertyName === 'fillBelowTo') { - $scope.override['lines'] = false; - $scope.ctrl.addSeriesOverride({ alias: subItem.value, lines: false }); - } - - $scope.updateCurrentOverrides(); - $scope.ctrl.render(); - }; - - $scope.colorSelected = function(color) { - $scope.override['color'] = color; - $scope.updateCurrentOverrides(); - $scope.ctrl.render(); - }; - - $scope.openColorSelector = function(color) { - var fakeSeries = { color: color }; - popoverSrv.show({ - element: $element.find('.dropdown')[0], - position: 'top center', - openOn: 'click', - template: '', - model: { - autoClose: true, - colorSelected: $scope.colorSelected, - series: fakeSeries, - }, - onClose: function() { - $scope.ctrl.render(); - }, + $scope.currentOverrides.push({ + name: option.text, + propertyName: option.propertyName, + value: String(value), }); - }; + }); + }; - $scope.removeOverride = function(option) { - delete $scope.override[option.propertyName]; - $scope.updateCurrentOverrides(); - $scope.ctrl.refresh(); - }; - - $scope.getSeriesNames = function() { - return _.map($scope.ctrl.seriesList, function(series) { - return series.alias; - }); - }; - - $scope.updateCurrentOverrides = function() { - $scope.currentOverrides = []; - _.each($scope.overrideMenu, function(option) { - var value = $scope.override[option.propertyName]; - if (_.isUndefined(value)) { - return; - } - $scope.currentOverrides.push({ - name: option.text, - propertyName: option.propertyName, - value: String(value), - }); - }); - }; - - $scope.addOverrideOption('Bars', 'bars', [true, false]); - $scope.addOverrideOption('Lines', 'lines', [true, false]); - $scope.addOverrideOption('Line fill', 'fill', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - $scope.addOverrideOption('Line width', 'linewidth', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - $scope.addOverrideOption('Null point mode', 'nullPointMode', ['connected', 'null', 'null as zero']); - $scope.addOverrideOption('Fill below to', 'fillBelowTo', $scope.getSeriesNames()); - $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]); - $scope.addOverrideOption('Dashes', 'dashes', [true, false]); - $scope.addOverrideOption('Dash Length', 'dashLength', [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - ]); - $scope.addOverrideOption('Dash Space', 'spaceLength', [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15, - 16, - 17, - 18, - 19, - 20, - ]); - $scope.addOverrideOption('Points', 'points', [true, false]); - $scope.addOverrideOption('Points Radius', 'pointradius', [1, 2, 3, 4, 5]); - $scope.addOverrideOption('Stack', 'stack', [true, false, 'A', 'B', 'C', 'D']); - $scope.addOverrideOption('Color', 'color', ['change']); - $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]); - $scope.addOverrideOption('Z-index', 'zindex', [-3, -2, -1, 0, 1, 2, 3]); - $scope.addOverrideOption('Transform', 'transform', ['negative-Y']); - $scope.addOverrideOption('Legend', 'legend', [true, false]); - $scope.updateCurrentOverrides(); - } + $scope.addOverrideOption('Bars', 'bars', [true, false]); + $scope.addOverrideOption('Lines', 'lines', [true, false]); + $scope.addOverrideOption('Line fill', 'fill', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + $scope.addOverrideOption('Line width', 'linewidth', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + $scope.addOverrideOption('Null point mode', 'nullPointMode', ['connected', 'null', 'null as zero']); + $scope.addOverrideOption('Fill below to', 'fillBelowTo', $scope.getSeriesNames()); + $scope.addOverrideOption('Staircase line', 'steppedLine', [true, false]); + $scope.addOverrideOption('Dashes', 'dashes', [true, false]); + $scope.addOverrideOption('Dash Length', 'dashLength', [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ]); + $scope.addOverrideOption('Dash Space', 'spaceLength', [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + ]); + $scope.addOverrideOption('Points', 'points', [true, false]); + $scope.addOverrideOption('Points Radius', 'pointradius', [1, 2, 3, 4, 5]); + $scope.addOverrideOption('Stack', 'stack', [true, false, 'A', 'B', 'C', 'D']); + $scope.addOverrideOption('Color', 'color', ['change']); + $scope.addOverrideOption('Y-axis', 'yaxis', [1, 2]); + $scope.addOverrideOption('Z-index', 'zindex', [-3, -2, -1, 0, 1, 2, 3]); + $scope.addOverrideOption('Transform', 'transform', ['negative-Y']); + $scope.addOverrideOption('Legend', 'legend', [true, false]); + $scope.updateCurrentOverrides(); } angular.module('grafana.controllers').controller('SeriesOverridesCtrl', SeriesOverridesCtrl); diff --git a/public/app/plugins/panel/graph/specs/series_override_ctrl.jest.ts b/public/app/plugins/panel/graph/specs/series_override_ctrl.jest.ts new file mode 100644 index 00000000000..2e7456a132a --- /dev/null +++ b/public/app/plugins/panel/graph/specs/series_override_ctrl.jest.ts @@ -0,0 +1,42 @@ +import '../series_overrides_ctrl'; +import { SeriesOverridesCtrl } from '../series_overrides_ctrl'; + +describe('SeriesOverridesCtrl', () => { + let popoverSrv = {}; + let $scope; + + beforeEach(() => { + $scope = { + ctrl: { + refresh: jest.fn(), + render: jest.fn(), + seriesList: [], + }, + render: jest.fn(() => {}), + }; + SeriesOverridesCtrl($scope, {}, popoverSrv); + }); + + describe('When setting an override', () => { + beforeEach(() => { + $scope.setOverride({ propertyName: 'lines' }, { value: true }); + }); + + it('should set override property', () => { + expect($scope.override.lines).toBe(true); + }); + + it('should update view model', () => { + expect($scope.currentOverrides[0].name).toBe('Lines'); + expect($scope.currentOverrides[0].value).toBe('true'); + }); + }); + + describe('When removing overide', () => { + it('click should include option and value index', () => { + $scope.setOverride(1, 0); + $scope.removeOverride({ propertyName: 'lines' }); + expect($scope.currentOverrides.length).toBe(0); + }); + }); +}); diff --git a/public/app/plugins/panel/graph/specs/series_override_ctrl_specs.ts b/public/app/plugins/panel/graph/specs/series_override_ctrl_specs.ts deleted file mode 100644 index 9e311c0775e..00000000000 --- a/public/app/plugins/panel/graph/specs/series_override_ctrl_specs.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { describe, beforeEach, it, expect, sinon, angularMocks } from 'test/lib/common'; -import '../series_overrides_ctrl'; -import helpers from 'test/specs/helpers'; - -describe('SeriesOverridesCtrl', function() { - var ctx = new helpers.ControllerTestContext(); - var popoverSrv = {}; - - beforeEach(angularMocks.module('grafana.services')); - beforeEach(angularMocks.module('grafana.controllers')); - - beforeEach( - ctx.providePhase({ - popoverSrv: popoverSrv, - }) - ); - - beforeEach( - angularMocks.inject(function($rootScope, $controller) { - ctx.scope = $rootScope.$new(); - ctx.scope.ctrl = { - refresh: sinon.spy(), - render: sinon.spy(), - seriesList: [], - }; - ctx.scope.render = function() {}; - ctx.controller = $controller('SeriesOverridesCtrl', { - $scope: ctx.scope, - }); - }) - ); - - describe('When setting an override', function() { - beforeEach(function() { - ctx.scope.setOverride({ propertyName: 'lines' }, { value: true }); - }); - - it('should set override property', function() { - expect(ctx.scope.override.lines).to.be(true); - }); - - it('should update view model', function() { - expect(ctx.scope.currentOverrides[0].name).to.be('Lines'); - expect(ctx.scope.currentOverrides[0].value).to.be('true'); - }); - }); - - describe('When removing overide', function() { - it('click should include option and value index', function() { - ctx.scope.setOverride(1, 0); - ctx.scope.removeOverride({ propertyName: 'lines' }); - expect(ctx.scope.currentOverrides.length).to.be(0); - }); - }); -}); From 54420363d3640570dafdcba24afda5b6fdc04c1d Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 28 Jun 2018 14:02:42 +0200 Subject: [PATCH 20/80] fix footer css issue --- public/sass/components/_footer.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/public/sass/components/_footer.scss b/public/sass/components/_footer.scss index 4a77ec37605..3c30f5a5798 100644 --- a/public/sass/components/_footer.scss +++ b/public/sass/components/_footer.scss @@ -45,9 +45,11 @@ } @include media-breakpoint-up(md) { - .footer { - bottom: $spacer; - position: absolute; - padding: 5rem 0 1rem 0; + .login-page { + .footer { + bottom: $spacer; + position: absolute; + padding: 5rem 0 1rem 0; + } } } From 4c4bd2ebba839b6d043bc69606b9977fef3efe39 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 28 Jun 2018 14:27:41 +0200 Subject: [PATCH 21/80] changelog: add notes about closing #12430 [skip ci] --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c943b2b9776..e149b420353 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ * **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley) * **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248) +# 5.2.1 (unreleased) + +### Minor + +* **UI**: Fix - Grafana footer overlapping page [#12430](https://github.com/grafana/grafana/issues/12430) + # 5.2.0-stable (2018-06-27) ### Minor From 7a2be69abcd07d22d48815e554e02a25bea14a22 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 28 Jun 2018 14:46:40 +0200 Subject: [PATCH 22/80] changelog: adds note for #11892 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e149b420353..7be0564bc96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley) * **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248) +* **Singlestat**: Make colorization of prefix and postfix optional in singlestat [#11892](https://github.com/grafana/grafana/pull/11892), thx [@ApsOps](https://github.com/ApsOps) # 5.2.1 (unreleased) From 8af5da738340e55ded1ee5e1a3b14287264d0451 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 28 Jun 2018 15:43:33 +0200 Subject: [PATCH 23/80] Revert "auth proxy: use real ip when validating white listed ip's" --- pkg/middleware/auth_proxy.go | 20 +++++------ pkg/middleware/middleware_test.go | 55 ------------------------------- 2 files changed, 8 insertions(+), 67 deletions(-) diff --git a/pkg/middleware/auth_proxy.go b/pkg/middleware/auth_proxy.go index eff532b0da2..144a0ae3a69 100644 --- a/pkg/middleware/auth_proxy.go +++ b/pkg/middleware/auth_proxy.go @@ -2,6 +2,7 @@ package middleware import ( "fmt" + "net" "net/mail" "reflect" "strings" @@ -28,7 +29,7 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool { } // if auth proxy ip(s) defined, check if request comes from one of those - if err := checkAuthenticationProxy(ctx.RemoteAddr(), proxyHeaderValue); err != nil { + if err := checkAuthenticationProxy(ctx.Req.RemoteAddr, proxyHeaderValue); err != nil { ctx.Handle(407, "Proxy authentication required", err) return true } @@ -196,23 +197,18 @@ func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error return nil } - // Multiple ip addresses? Right-most IP address is the IP address of the most recent proxy - if strings.Contains(remoteAddr, ",") { - sourceIPs := strings.Split(remoteAddr, ",") - remoteAddr = strings.TrimSpace(sourceIPs[len(sourceIPs)-1]) - } - - remoteAddr = strings.TrimPrefix(remoteAddr, "[") - remoteAddr = strings.TrimSuffix(remoteAddr, "]") - proxies := strings.Split(setting.AuthProxyWhitelist, ",") + sourceIP, _, err := net.SplitHostPort(remoteAddr) + if err != nil { + return err + } // Compare allowed IP addresses to actual address for _, proxyIP := range proxies { - if remoteAddr == strings.TrimSpace(proxyIP) { + if sourceIP == strings.TrimSpace(proxyIP) { return nil } } - return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, remoteAddr) + return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP) } diff --git a/pkg/middleware/middleware_test.go b/pkg/middleware/middleware_test.go index d82b7313585..87c23a7b49a 100644 --- a/pkg/middleware/middleware_test.go +++ b/pkg/middleware/middleware_test.go @@ -315,61 +315,6 @@ func TestMiddlewareContext(t *testing.T) { }) }) - middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is not trusted", func(sc *scenarioContext) { - setting.AuthProxyEnabled = true - setting.AuthProxyHeaderName = "X-WEBAUTH-USER" - setting.AuthProxyHeaderProperty = "username" - setting.AuthProxyWhitelist = "192.168.1.1, 2001::23" - - bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { - query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} - return nil - }) - - bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error { - cmd.Result = &m.User{Id: 33} - return nil - }) - - sc.fakeReq("GET", "/") - sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") - sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.1, 192.168.1.2") - sc.exec() - - Convey("should return 407 status code", func() { - So(sc.resp.Code, ShouldEqual, 407) - So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.1.2 is not from the authentication proxy") - }) - }) - - middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is trusted", func(sc *scenarioContext) { - setting.AuthProxyEnabled = true - setting.AuthProxyHeaderName = "X-WEBAUTH-USER" - setting.AuthProxyHeaderProperty = "username" - setting.AuthProxyWhitelist = "192.168.1.1, 2001::23" - - bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error { - query.Result = &m.SignedInUser{OrgId: 4, UserId: 33} - return nil - }) - - bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error { - cmd.Result = &m.User{Id: 33} - return nil - }) - - sc.fakeReq("GET", "/") - sc.req.Header.Add("X-WEBAUTH-USER", "torkelo") - sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.2, 192.168.1.1") - sc.exec() - - Convey("Should init context with user info", func() { - So(sc.context.IsSignedIn, ShouldBeTrue) - So(sc.context.UserId, ShouldEqual, 33) - So(sc.context.OrgId, ShouldEqual, 4) - }) - }) - middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) { setting.AuthProxyEnabled = true setting.AuthProxyHeaderName = "X-WEBAUTH-USER" From fb2b2c9f656f9d8f894edea70418b78efd3f22f3 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 28 Jun 2018 16:39:38 +0200 Subject: [PATCH 24/80] changelog: add notes about closing #12444 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7be0564bc96..aaa9dec5c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ ### Minor * **UI**: Fix - Grafana footer overlapping page [#12430](https://github.com/grafana/grafana/issues/12430) +* **Auth Proxy**: Revert of "Whitelist proxy IP address instead of client IP address" introduced in 5.2.0-beta2 [#12444](https://github.com/grafana/grafana/pull/12444) # 5.2.0-stable (2018-06-27) From a0e8437f8f70f33490d6c57dce46bf0de441048b Mon Sep 17 00:00:00 2001 From: Austin Winstanley Date: Fri, 29 Jun 2018 03:48:14 +0000 Subject: [PATCH 25/80] Used PostgreSQL TSDB as a model the set up the __timeFilter, __timeFrom, and __timeTo macros for Microsoft SQL and MySQL --- pkg/tsdb/mssql/macros.go | 6 +++--- pkg/tsdb/mssql/macros_test.go | 18 +++++++++--------- pkg/tsdb/mysql/macros.go | 6 +++--- pkg/tsdb/mysql/macros_test.go | 18 +++++++++--------- yarn.lock | 12 ++++++------ 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/pkg/tsdb/mssql/macros.go b/pkg/tsdb/mssql/macros.go index 2621110ee99..b136bdfb459 100644 --- a/pkg/tsdb/mssql/macros.go +++ b/pkg/tsdb/mssql/macros.go @@ -83,11 +83,11 @@ func (m *MsSqlMacroEngine) evaluateMacro(name string, args []string) (string, er return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], time.Unix(m.TimeRange.GetFromAsSecondsEpoch(), 0), time.Unix(m.TimeRange.GetToAsSecondsEpoch(), 0)), 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": - return fmt.Sprintf("'%s'", time.Unix(m.TimeRange.GetFromAsSecondsEpoch(), 0)), nil + return fmt.Sprintf("'%s'", m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil case "__timeTo": - return fmt.Sprintf("'%s'", time.Unix(m.TimeRange.GetToAsSecondsEpoch(), 0)), nil + return fmt.Sprintf("'%s'", m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) diff --git a/pkg/tsdb/mssql/macros_test.go b/pkg/tsdb/mssql/macros_test.go index 46f67e7d169..67ee1da7561 100644 --- a/pkg/tsdb/mssql/macros_test.go +++ b/pkg/tsdb/mssql/macros_test.go @@ -49,7 +49,7 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { @@ -96,14 +96,14 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -137,21 +137,21 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -185,21 +185,21 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { diff --git a/pkg/tsdb/mysql/macros.go b/pkg/tsdb/mysql/macros.go index 6dfe6ad952f..1855670e863 100644 --- a/pkg/tsdb/mysql/macros.go +++ b/pkg/tsdb/mysql/macros.go @@ -78,11 +78,11 @@ func (m *MySqlMacroEngine) evaluateMacro(name string, args []string) (string, er return "", fmt.Errorf("missing time column argument for macro %v", name) } - return fmt.Sprintf("%s BETWEEN '%s' AND '%s'", args[0], time.Unix(m.TimeRange.GetFromAsSecondsEpoch(), 0), time.Unix(m.TimeRange.GetToAsSecondsEpoch(), 0)), 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": - return fmt.Sprintf("'%s'", time.Unix(m.TimeRange.GetFromAsSecondsEpoch(), 0)), nil + return fmt.Sprintf("'%s'", m.TimeRange.GetFromAsTimeUTC().Format(time.RFC3339)), nil case "__timeTo": - return fmt.Sprintf("'%s'", time.Unix(m.TimeRange.GetToAsSecondsEpoch(), 0)), nil + return fmt.Sprintf("'%s'", m.TimeRange.GetToAsTimeUTC().Format(time.RFC3339)), nil case "__timeGroup": if len(args) < 2 { return "", fmt.Errorf("macro %v needs time column and interval", name) diff --git a/pkg/tsdb/mysql/macros_test.go b/pkg/tsdb/mysql/macros_test.go index 11ac6d8b2f3..6af1d180737 100644 --- a/pkg/tsdb/mysql/macros_test.go +++ b/pkg/tsdb/mysql/macros_test.go @@ -54,21 +54,21 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -102,21 +102,21 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { @@ -150,21 +150,21 @@ func TestMacroEngine(t *testing.T) { sql, err := engine.Interpolate(query, timeRange, "WHERE $__timeFilter(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("WHERE time_column BETWEEN '%s' AND '%s'", time.Unix(from.Unix(), 0), time.Unix(to.Unix(), 0))) + 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() { sql, err := engine.Interpolate(query, timeRange, "select $__timeFrom(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(from.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", from.Format(time.RFC3339))) }) Convey("interpolate __timeTo function", func() { sql, err := engine.Interpolate(query, timeRange, "select $__timeTo(time_column)") So(err, ShouldBeNil) - So(sql, ShouldEqual, fmt.Sprintf("select '%s'", time.Unix(to.Unix(), 0))) + So(sql, ShouldEqual, fmt.Sprintf("select '%s'", to.Format(time.RFC3339))) }) Convey("interpolate __unixEpochFilter function", func() { diff --git a/yarn.lock b/yarn.lock index 6cc48a7c79d..6772d7c14a4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,7 +1114,7 @@ babel-plugin-istanbul@^4.1.5, babel-plugin-istanbul@^4.1.6: istanbul-lib-instrument "^1.10.1" test-exclude "^4.2.1" -babel-plugin-jest-hoist@^22.4.4: +babel-plugin-jest-hoist@^22.4.3, babel-plugin-jest-hoist@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b" @@ -6498,7 +6498,7 @@ jest-docblock@^22.4.0, jest-docblock@^22.4.3: dependencies: detect-newline "^2.1.0" -jest-environment-jsdom@^22.4.1: +jest-environment-jsdom@^22.4.1, jest-environment-jsdom@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" dependencies: @@ -6506,7 +6506,7 @@ jest-environment-jsdom@^22.4.1: jest-util "^22.4.3" jsdom "^11.5.1" -jest-environment-node@^22.4.1: +jest-environment-node@^22.4.1, jest-environment-node@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" dependencies: @@ -6533,7 +6533,7 @@ jest-haste-map@^22.4.2: micromatch "^2.3.11" sane "^2.0.0" -jest-jasmine2@^22.4.4: +jest-jasmine2@^22.4.3, jest-jasmine2@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz#c55f92c961a141f693f869f5f081a79a10d24e23" dependencies: @@ -6587,7 +6587,7 @@ jest-resolve-dependencies@^22.1.0: dependencies: jest-regex-util "^22.4.3" -jest-resolve@^22.4.2: +jest-resolve@^22.4.2, jest-resolve@^22.4.3: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" dependencies: @@ -6671,7 +6671,7 @@ jest-validate@^21.1.0: leven "^2.1.0" pretty-format "^21.2.1" -jest-validate@^22.4.4: +jest-validate@^22.4.3, jest-validate@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d" dependencies: From 9823da1cc51e3a6e0222cd8c9c36a97b4f978fd3 Mon Sep 17 00:00:00 2001 From: Austin Winstanley Date: Fri, 29 Jun 2018 03:54:43 +0000 Subject: [PATCH 26/80] Reverted yarn.lock to master --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6772d7c14a4..6cc48a7c79d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1114,7 +1114,7 @@ babel-plugin-istanbul@^4.1.5, babel-plugin-istanbul@^4.1.6: istanbul-lib-instrument "^1.10.1" test-exclude "^4.2.1" -babel-plugin-jest-hoist@^22.4.3, babel-plugin-jest-hoist@^22.4.4: +babel-plugin-jest-hoist@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.4.tgz#b9851906eab34c7bf6f8c895a2b08bea1a844c0b" @@ -6498,7 +6498,7 @@ jest-docblock@^22.4.0, jest-docblock@^22.4.3: dependencies: detect-newline "^2.1.0" -jest-environment-jsdom@^22.4.1, jest-environment-jsdom@^22.4.3: +jest-environment-jsdom@^22.4.1: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" dependencies: @@ -6506,7 +6506,7 @@ jest-environment-jsdom@^22.4.1, jest-environment-jsdom@^22.4.3: jest-util "^22.4.3" jsdom "^11.5.1" -jest-environment-node@^22.4.1, jest-environment-node@^22.4.3: +jest-environment-node@^22.4.1: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" dependencies: @@ -6533,7 +6533,7 @@ jest-haste-map@^22.4.2: micromatch "^2.3.11" sane "^2.0.0" -jest-jasmine2@^22.4.3, jest-jasmine2@^22.4.4: +jest-jasmine2@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.4.tgz#c55f92c961a141f693f869f5f081a79a10d24e23" dependencies: @@ -6587,7 +6587,7 @@ jest-resolve-dependencies@^22.1.0: dependencies: jest-regex-util "^22.4.3" -jest-resolve@^22.4.2, jest-resolve@^22.4.3: +jest-resolve@^22.4.2: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" dependencies: @@ -6671,7 +6671,7 @@ jest-validate@^21.1.0: leven "^2.1.0" pretty-format "^21.2.1" -jest-validate@^22.4.3, jest-validate@^22.4.4: +jest-validate@^22.4.4: version "22.4.4" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.4.tgz#1dd0b616ef46c995de61810d85f57119dbbcec4d" dependencies: From ce88e4b927a4d417c91829e2e0bc227756ebb915 Mon Sep 17 00:00:00 2001 From: bergquist Date: Fri, 29 Jun 2018 09:35:31 +0200 Subject: [PATCH 27/80] fixes typos closes #12453 --- CHANGELOG.md | 2 +- public/app/partials/login.html | 2 +- yarn.lock | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaa9dec5c4d..fcb1cf6d19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1336,7 +1336,7 @@ Grafana 2.x is fundamentally different from 1.x; it now ships with an integrated **New features** - [Issue #1623](https://github.com/grafana/grafana/issues/1623). Share Dashboard: Dashboard snapshot sharing (dash and data snapshot), save to local or save to public snapshot dashboard snapshots.raintank.io site - [Issue #1622](https://github.com/grafana/grafana/issues/1622). Share Panel: The share modal now has an embed option, gives you an iframe that you can use to embedd a single graph on another web site -- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes in between the user is promted with a warning if he really wants to overwrite the other's changes +- [Issue #718](https://github.com/grafana/grafana/issues/718). Dashboard: When saving a dashboard and another user has made changes in between the user is prompted with a warning if he really wants to overwrite the other's changes - [Issue #1331](https://github.com/grafana/grafana/issues/1331). Graph & Singlestat: New axis/unit format selector and more units (kbytes, Joule, Watt, eV), and new design for graph axis & grid tab and single stat options tab views - [Issue #1241](https://github.com/grafana/grafana/issues/1242). Timepicker: New option in timepicker (under dashboard settings), to change ``now`` to be for example ``now-1m``, useful when you want to ignore last minute because it contains incomplete data - [Issue #171](https://github.com/grafana/grafana/issues/171). Panel: Different time periods, panels can override dashboard relative time and/or add a time shift diff --git a/public/app/partials/login.html b/public/app/partials/login.html index 8be9e777b9f..1919759334b 100644 --- a/public/app/partials/login.html +++ b/public/app/partials/login.html @@ -89,7 +89,7 @@
Skip - If you skip you will be promted to change password next time you login. + If you skip you will be prompted to change password next time you login. @@ -26,42 +26,80 @@

Team Members

- +
Add member - - + +
- - - - - - - - - - - - - - -
UsernameEmail
{{member.login}}{{member.email}} - - - -
-
- - This team has no members yet. - -
+ + + + Username + Email + + + + + + + + {{member.login}} + {{member.email}} + + + + + + + +
+ + This team has no members yet. + +
+
+ +
+ +

Team Group Mapping

+
+
+ Add group + +
+
+ +
+
+ + + + + + + + + + + + +
Group
{{group.groupId}} + + + +
+
+ + This team has no associated groups yet. + +
+ +
diff --git a/public/app/features/org/team_details_ctrl.ts b/public/app/features/org/team_details_ctrl.ts index 3d193880635..35fa04f55d3 100644 --- a/public/app/features/org/team_details_ctrl.ts +++ b/public/app/features/org/team_details_ctrl.ts @@ -1,15 +1,21 @@ import coreModule from 'app/core/core_module'; +import config from 'app/core/config'; export default class TeamDetailsCtrl { team: Team; teamMembers: User[] = []; navModel: any; + teamGroups: TeamGroup[] = []; + newGroupId: string; + enterprise: boolean; /** @ngInject **/ constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) { this.navModel = navModelSrv.getNav('cfg', 'teams', 0); this.userPicked = this.userPicked.bind(this); this.get = this.get.bind(this); + this.newGroupId = ''; + this.enterprise = config.enterprise; this.get(); } @@ -18,9 +24,16 @@ export default class TeamDetailsCtrl { this.backendSrv.get(`/api/teams/${this.$routeParams.id}`).then(result => { this.team = result; }); + this.backendSrv.get(`/api/teams/${this.$routeParams.id}/members`).then(result => { this.teamMembers = result; }); + + if (config.enterprise) { + this.backendSrv.get(`/api/teams/${this.$routeParams.id}/groups`).then(result => { + this.teamGroups = result; + }); + } } } @@ -57,6 +70,20 @@ export default class TeamDetailsCtrl { this.get(); }); } + + addGroup() { + this.backendSrv.post(`/api/teams/${this.$routeParams.id}/groups`, { groupId: this.newGroupId }).then(() => { + this.get(); + }); + } + + removeGroup(group: TeamGroup) { + this.backendSrv.delete(`/api/teams/${this.$routeParams.id}/groups/${group.groupId}`).then(this.get); + } +} + +export interface TeamGroup { + groupId: string; } export interface Team { diff --git a/scripts/build/build_enterprise.sh b/scripts/build/build_enterprise.sh index 02d8c78c885..cda3952c36a 100755 --- a/scripts/build/build_enterprise.sh +++ b/scripts/build/build_enterprise.sh @@ -14,9 +14,9 @@ cd /go/src/github.com/grafana/grafana echo "current dir: $(pwd)" cd .. -git clone -b ee_build --single-branch git@github.com:grafana/grafana-enterprise.git --depth 10 +git clone -b master --single-branch git@github.com:grafana/grafana-enterprise.git --depth 10 cd grafana-enterprise -git checkout 7fbae9c1be3467c4a39cf6ad85278a6896ceb49f +#git checkout 7fbae9c1be3467c4a39cf6ad85278a6896ceb49f ./build.sh cd ../grafana From aa1b5959da01c2bb283c30ab49c89c230ba300c9 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Tanda Date: Mon, 2 Jul 2018 14:56:24 +0900 Subject: [PATCH 47/80] omit extra template refresh (#12454) --- public/app/features/templating/variable_srv.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/app/features/templating/variable_srv.ts b/public/app/features/templating/variable_srv.ts index 8a096dd9ad2..8ad3c2845e2 100644 --- a/public/app/features/templating/variable_srv.ts +++ b/public/app/features/templating/variable_srv.ts @@ -38,7 +38,11 @@ export class VariableSrv { }); } - onDashboardRefresh() { + onDashboardRefresh(evt, payload) { + if (payload && payload.fromVariableValueUpdated) { + return Promise.resolve({}); + } + var promises = this.variables.filter(variable => variable.refresh === 2).map(variable => { var previousOptions = variable.options.slice(); @@ -130,7 +134,7 @@ export class VariableSrv { return this.$q.all(promises).then(() => { if (emitChangeEvents) { this.$rootScope.$emit('template-variable-value-updated'); - this.$rootScope.$broadcast('refresh'); + this.$rootScope.$broadcast('refresh', { fromVariableValueUpdated: true }); } }); } From 10e86eda690db3934a21ad95fe9df9b05c32d3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Sun, 1 Jul 2018 23:35:50 -0700 Subject: [PATCH 48/80] fix: #12461 introduced issues with route registration ordering, adding plugin static routes before plugins package had been initiated (#12474) --- pkg/api/api.go | 12 ------------ pkg/api/app_routes.go | 2 +- pkg/api/http_server.go | 25 ++++++++++++++++++++++--- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pkg/api/api.go b/pkg/api/api.go index 78c7aaf3f39..829b7735343 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -9,16 +9,7 @@ import ( m "github.com/grafana/grafana/pkg/models" ) -func (hs *HTTPServer) applyRoutes() { - hs.RouteRegister.Register(hs.macaron) - - InitAppPluginRoutes(hs.macaron) - - hs.macaron.NotFound(NotFoundHandler) -} - func (hs *HTTPServer) registerRoutes() { - macaronR := hs.macaron reqSignedIn := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true}) reqGrafanaAdmin := middleware.Auth(&middleware.AuthOptions{ReqSignedIn: true, ReqGrafanaAdmin: true}) reqEditorRole := middleware.RoleAuth(m.ROLE_EDITOR, m.ROLE_ADMIN) @@ -28,9 +19,6 @@ func (hs *HTTPServer) registerRoutes() { quota := middleware.Quota bind := binding.Bind - // automatically set HEAD for every GET - macaronR.SetAutoHead(true) - r := hs.RouteRegister // not logged in views diff --git a/pkg/api/app_routes.go b/pkg/api/app_routes.go index 0b7dcd32ce3..a2137089fc6 100644 --- a/pkg/api/app_routes.go +++ b/pkg/api/app_routes.go @@ -18,7 +18,7 @@ import ( var pluginProxyTransport *http.Transport -func InitAppPluginRoutes(r *macaron.Macaron) { +func (hs *HTTPServer) initAppPluginRoutes(r *macaron.Macaron) { pluginProxyTransport = &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: setting.PluginAppsSkipVerifyTLS, diff --git a/pkg/api/http_server.go b/pkg/api/http_server.go index 627192bb69b..0de63ce5e08 100644 --- a/pkg/api/http_server.go +++ b/pkg/api/http_server.go @@ -69,6 +69,7 @@ func (hs *HTTPServer) Run(ctx context.Context) error { var err error hs.context = ctx + hs.applyRoutes() hs.streamManager.Run(ctx) @@ -169,6 +170,26 @@ func (hs *HTTPServer) newMacaron() *macaron.Macaron { macaron.Env = setting.Env m := macaron.New() + // automatically set HEAD for every GET + m.SetAutoHead(true) + + return m +} + +func (hs *HTTPServer) applyRoutes() { + // start with middlewares & static routes + hs.addMiddlewaresAndStaticRoutes() + // then add view routes & api routes + hs.RouteRegister.Register(hs.macaron) + // then custom app proxy routes + hs.initAppPluginRoutes(hs.macaron) + // lastly not found route + hs.macaron.NotFound(NotFoundHandler) +} + +func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() { + m := hs.macaron + m.Use(middleware.Logger()) if setting.EnableGzip { @@ -180,7 +201,7 @@ func (hs *HTTPServer) newMacaron() *macaron.Macaron { for _, route := range plugins.StaticRoutes { pluginRoute := path.Join("/public/plugins/", route.PluginId) hs.log.Debug("Plugins: Adding route", "route", pluginRoute, "dir", route.Directory) - hs.mapStatic(m, route.Directory, "", pluginRoute) + hs.mapStatic(hs.macaron, route.Directory, "", pluginRoute) } hs.mapStatic(m, setting.StaticRootPath, "build", "public/build") @@ -209,8 +230,6 @@ func (hs *HTTPServer) newMacaron() *macaron.Macaron { } m.Use(middleware.AddDefaultResponseHeaders()) - - return m } func (hs *HTTPServer) metricsEndpoint(ctx *macaron.Context) { From 2941dff42887c750c75d3cf2e26fd825de2cce89 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Mon, 2 Jul 2018 13:18:23 +0200 Subject: [PATCH 49/80] Karma to Jest: datasource_srv (#12456) * Karma to Jest: datasource_srv * Sort function differs between Karma and Jest * Fix error based on .sort() implementation * Remove Karma test * alerting: only log when screenshot been uploaded * Remove comments * Karma to Jest: datasource_srv * Sort function differs between Karma and Jest * Fix error based on .sort() implementation * Remove Karma test * Remove comments * Remove console.log * Remove console.log * Change sorting and add test for default data source --- public/app/features/plugins/datasource_srv.ts | 25 ++++---- .../plugins/specs/datasource_srv.jest.ts | 59 +++++++++++++++++ .../plugins/specs/datasource_srv_specs.ts | 64 ------------------- 3 files changed, 73 insertions(+), 75 deletions(-) create mode 100644 public/app/features/plugins/specs/datasource_srv.jest.ts delete mode 100644 public/app/features/plugins/specs/datasource_srv_specs.ts diff --git a/public/app/features/plugins/datasource_srv.ts b/public/app/features/plugins/datasource_srv.ts index 19bdf599ce1..b5e0316163c 100644 --- a/public/app/features/plugins/datasource_srv.ts +++ b/public/app/features/plugins/datasource_srv.ts @@ -91,10 +91,20 @@ export class DatasourceSrv { _.each(config.datasources, function(value, key) { if (value.meta && value.meta.metrics) { - metricSources.push({ value: key, name: key, meta: value.meta }); + let metricSource = { value: key, name: key, meta: value.meta, sort: key }; + + //Make sure grafana and mixed are sorted at the bottom + if (value.meta.id === 'grafana') { + metricSource.sort = String.fromCharCode(253); + } else if (value.meta.id === 'mixed') { + metricSource.sort = String.fromCharCode(254); + } + + metricSources.push(metricSource); if (key === config.defaultDatasource) { - metricSources.push({ value: null, name: 'default', meta: value.meta }); + metricSource = { value: null, name: 'default', meta: value.meta, sort: key }; + metricSources.push(metricSource); } } }); @@ -104,17 +114,10 @@ export class DatasourceSrv { } metricSources.sort(function(a, b) { - // these two should always be at the bottom - if (a.meta.id === 'mixed' || a.meta.id === 'grafana') { + if (a.sort.toLowerCase() > b.sort.toLowerCase()) { return 1; } - if (b.meta.id === 'mixed' || b.meta.id === 'grafana') { - return -1; - } - if (a.name.toLowerCase() > b.name.toLowerCase()) { - return 1; - } - if (a.name.toLowerCase() < b.name.toLowerCase()) { + if (a.sort.toLowerCase() < b.sort.toLowerCase()) { return -1; } return 0; diff --git a/public/app/features/plugins/specs/datasource_srv.jest.ts b/public/app/features/plugins/specs/datasource_srv.jest.ts new file mode 100644 index 00000000000..f261c4e2249 --- /dev/null +++ b/public/app/features/plugins/specs/datasource_srv.jest.ts @@ -0,0 +1,59 @@ +import config from 'app/core/config'; +import 'app/features/plugins/datasource_srv'; +import { DatasourceSrv } from 'app/features/plugins/datasource_srv'; + +describe('datasource_srv', function() { + let _datasourceSrv = new DatasourceSrv({}, {}, {}, {}); + let metricSources; + + describe('when loading metric sources', () => { + let unsortedDatasources = { + mmm: { + type: 'test-db', + meta: { metrics: { m: 1 } }, + }, + '--Grafana--': { + type: 'grafana', + meta: { builtIn: true, metrics: { m: 1 }, id: 'grafana' }, + }, + '--Mixed--': { + type: 'test-db', + meta: { builtIn: true, metrics: { m: 1 }, id: 'mixed' }, + }, + ZZZ: { + type: 'test-db', + meta: { metrics: { m: 1 } }, + }, + aaa: { + type: 'test-db', + meta: { metrics: { m: 1 } }, + }, + BBB: { + type: 'test-db', + meta: { metrics: { m: 1 } }, + }, + }; + beforeEach(() => { + config.datasources = unsortedDatasources; + metricSources = _datasourceSrv.getMetricSources({ skipVariables: true }); + }); + + 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'; + }); + + it('should set default data source', () => { + expect(metricSources[2].name).toBe('default'); + expect(metricSources[2].sort).toBe('BBB'); + }); + }); +}); diff --git a/public/app/features/plugins/specs/datasource_srv_specs.ts b/public/app/features/plugins/specs/datasource_srv_specs.ts deleted file mode 100644 index 85a66b59ee7..00000000000 --- a/public/app/features/plugins/specs/datasource_srv_specs.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; -import config from 'app/core/config'; -import 'app/features/plugins/datasource_srv'; - -describe('datasource_srv', function() { - var _datasourceSrv; - var metricSources; - var templateSrv = {}; - - beforeEach(angularMocks.module('grafana.core')); - beforeEach( - angularMocks.module(function($provide) { - $provide.value('templateSrv', templateSrv); - }) - ); - beforeEach(angularMocks.module('grafana.services')); - beforeEach( - angularMocks.inject(function(datasourceSrv) { - _datasourceSrv = datasourceSrv; - }) - ); - - describe('when loading metric sources', function() { - var unsortedDatasources = { - mmm: { - type: 'test-db', - meta: { metrics: { m: 1 } }, - }, - '--Grafana--': { - type: 'grafana', - meta: { builtIn: true, metrics: { m: 1 }, id: 'grafana' }, - }, - '--Mixed--': { - type: 'test-db', - meta: { builtIn: true, metrics: { m: 1 }, id: 'mixed' }, - }, - ZZZ: { - type: 'test-db', - meta: { metrics: { m: 1 } }, - }, - aaa: { - type: 'test-db', - meta: { metrics: { m: 1 } }, - }, - BBB: { - type: 'test-db', - meta: { metrics: { m: 1 } }, - }, - }; - beforeEach(function() { - config.datasources = unsortedDatasources; - metricSources = _datasourceSrv.getMetricSources({ skipVariables: true }); - }); - - it('should return a list of sources sorted case insensitively with builtin sources last', function() { - expect(metricSources[0].name).to.be('aaa'); - expect(metricSources[1].name).to.be('BBB'); - expect(metricSources[2].name).to.be('mmm'); - expect(metricSources[3].name).to.be('ZZZ'); - expect(metricSources[4].name).to.be('--Grafana--'); - expect(metricSources[5].name).to.be('--Mixed--'); - }); - }); -}); From b8724ae0c400a9d5f62d2390bce1a5a601540339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 2 Jul 2018 04:33:39 -0700 Subject: [PATCH 50/80] refactoring: enterprise build/hooks refactorings (#12478) --- pkg/api/dtos/index.go | 1 + pkg/api/frontendsettings.go | 2 +- pkg/api/index.go | 1 + pkg/cmd/grafana-server/main.go | 5 +- pkg/extensions/main.go | 2 +- pkg/metrics/metrics.go | 9 +++ pkg/setting/setting.go | 7 +- public/app/core/config.ts | 19 +++++- public/app/core/services/context_srv.ts | 4 -- .../features/dashboard/specs/exporter.jest.ts | 4 +- .../features/org/partials/team_details.html | 66 +++++++++---------- public/app/features/org/team_details_ctrl.ts | 6 +- public/views/index.template.html | 2 +- 13 files changed, 73 insertions(+), 55 deletions(-) diff --git a/pkg/api/dtos/index.go b/pkg/api/dtos/index.go index 8c7f505277d..77004899fc3 100644 --- a/pkg/api/dtos/index.go +++ b/pkg/api/dtos/index.go @@ -13,6 +13,7 @@ type IndexViewData struct { Theme string NewGrafanaVersionExists bool NewGrafanaVersion string + AppName string } type PluginCss struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 06e6405baaf..da3c88566c1 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -132,7 +132,6 @@ func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) { } jsonObj := map[string]interface{}{ - "enterprise": setting.Enterprise, "defaultDatasource": defaultDatasource, "datasources": datasources, "panels": panels, @@ -154,6 +153,7 @@ func getFrontendSettingsMap(c *m.ReqContext) (map[string]interface{}, error) { "latestVersion": plugins.GrafanaLatestVersion, "hasUpdate": plugins.GrafanaHasUpdate, "env": setting.Env, + "isEnterprise": setting.IsEnterprise, }, } diff --git a/pkg/api/index.go b/pkg/api/index.go index a52bd3e77b0..ea10940d3ba 100644 --- a/pkg/api/index.go +++ b/pkg/api/index.go @@ -76,6 +76,7 @@ func setIndexViewData(c *m.ReqContext) (*dtos.IndexViewData, error) { BuildCommit: setting.BuildCommit, NewGrafanaVersion: plugins.GrafanaLatestVersion, NewGrafanaVersionExists: plugins.GrafanaHasUpdate, + AppName: setting.ApplicationName, } if setting.DisableGravatar { diff --git a/pkg/cmd/grafana-server/main.go b/pkg/cmd/grafana-server/main.go index 7b90cf8b1e8..f00e6bba0fd 100644 --- a/pkg/cmd/grafana-server/main.go +++ b/pkg/cmd/grafana-server/main.go @@ -18,7 +18,7 @@ import ( "github.com/grafana/grafana/pkg/metrics" "github.com/grafana/grafana/pkg/setting" - _ "github.com/grafana/grafana/pkg/extensions" + extensions "github.com/grafana/grafana/pkg/extensions" _ "github.com/grafana/grafana/pkg/services/alerting/conditions" _ "github.com/grafana/grafana/pkg/services/alerting/notifiers" _ "github.com/grafana/grafana/pkg/tsdb/cloudwatch" @@ -35,7 +35,6 @@ import ( var version = "5.0.0" var commit = "NA" var buildstamp string -var enterprise string var configFile = flag.String("config", "", "path to config file") var homePath = flag.String("homepath", "", "path to grafana install/home path, defaults to working directory") @@ -78,7 +77,7 @@ func main() { setting.BuildVersion = version setting.BuildCommit = commit setting.BuildStamp = buildstampInt64 - setting.Enterprise, _ = strconv.ParseBool(enterprise) + setting.IsEnterprise = extensions.IsEnterprise metrics.M_Grafana_Version.WithLabelValues(version).Set(1) diff --git a/pkg/extensions/main.go b/pkg/extensions/main.go index 34ac9da7e86..6e3461da8a8 100644 --- a/pkg/extensions/main.go +++ b/pkg/extensions/main.go @@ -1,3 +1,3 @@ package extensions -import _ "github.com/pkg/errors" +var IsEnterprise bool = false diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 3d3cfc2e1b6..4dd84c12151 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -334,6 +334,14 @@ func updateTotalStats() { var usageStatsURL = "https://stats.grafana.org/grafana-usage-report" +func getEdition() string { + if setting.IsEnterprise { + return "enterprise" + } else { + return "oss" + } +} + func sendUsageStats() { if !setting.ReportingEnabled { return @@ -349,6 +357,7 @@ func sendUsageStats() { "metrics": metrics, "os": runtime.GOOS, "arch": runtime.GOARCH, + "edition": getEdition(), } statsQuery := models.GetSystemStatsQuery{} diff --git a/pkg/setting/setting.go b/pkg/setting/setting.go index e71a3619aa5..d8c8e6431c0 100644 --- a/pkg/setting/setting.go +++ b/pkg/setting/setting.go @@ -18,9 +18,10 @@ import ( "github.com/go-macaron/session" + "time" + "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/util" - "time" ) type Scheme string @@ -49,7 +50,7 @@ var ( BuildVersion string BuildCommit string BuildStamp int64 - Enterprise bool + IsEnterprise bool ApplicationName string // Paths @@ -517,7 +518,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error { Raw = cfg.Raw ApplicationName = "Grafana" - if Enterprise { + if IsEnterprise { ApplicationName += " Enterprise" } diff --git a/public/app/core/config.ts b/public/app/core/config.ts index 83ef17662c8..e065ddb22fb 100644 --- a/public/app/core/config.ts +++ b/public/app/core/config.ts @@ -1,11 +1,18 @@ import _ from 'lodash'; -class Settings { +export interface BuildInfo { + version: string; + commit: string; + isEnterprise: boolean; + env: string; +} + +export class Settings { datasources: any; panels: any; appSubUrl: string; window_title_prefix: string; - buildInfo: any; + buildInfo: BuildInfo; new_panel_title: string; bootData: any; externalUserMngLinkUrl: string; @@ -22,7 +29,6 @@ class Settings { disableUserSignUp: boolean; loginHint: any; loginError: any; - enterprise: boolean; constructor(options) { var defaults = { @@ -33,7 +39,14 @@ class Settings { playlist_timespan: '1m', unsaved_changes_warning: true, appSubUrl: '', + buildInfo: { + version: 'v1.0', + commit: '1', + env: 'production', + isEnterprise: false, + }, }; + _.extend(this, defaults, options); } } diff --git a/public/app/core/services/context_srv.ts b/public/app/core/services/context_srv.ts index be8a0af7b7b..8959573e731 100644 --- a/public/app/core/services/context_srv.ts +++ b/public/app/core/services/context_srv.ts @@ -34,14 +34,10 @@ export class ContextSrv { constructor() { this.sidemenu = store.getBool('grafana.sidemenu', true); - if (!config.buildInfo) { - config.buildInfo = {}; - } if (!config.bootData) { config.bootData = { user: {}, settings: {} }; } - this.version = config.buildInfo.version; this.user = new User(); this.isSignedIn = this.user.isSignedIn; this.isGrafanaAdmin = this.user.isGrafanaAdmin; diff --git a/public/app/features/dashboard/specs/exporter.jest.ts b/public/app/features/dashboard/specs/exporter.jest.ts index aa574a4b85a..c7727a4af4d 100644 --- a/public/app/features/dashboard/specs/exporter.jest.ts +++ b/public/app/features/dashboard/specs/exporter.jest.ts @@ -86,9 +86,7 @@ describe('given dashboard with repeated panels', () => { ], }; - config.buildInfo = { - version: '3.0.2', - }; + config.buildInfo.version = '3.0.2'; //Stubs test function calls var datasourceSrvStub = { get: jest.fn(arg => getStub(arg)) }; diff --git a/public/app/features/org/partials/team_details.html b/public/app/features/org/partials/team_details.html index c5ac8bd37a3..3ce851d5546 100644 --- a/public/app/features/org/partials/team_details.html +++ b/public/app/features/org/partials/team_details.html @@ -67,39 +67,39 @@
-
+
-

Team Group Mapping

-
-
- Add group - -
-
- -
-
+

Mappings to external groups

+
+
+ Add group + +
+
+ +
+
- - - - - - - - - - - -
Group
{{group.groupId}} - - - -
-
- - This team has no associated groups yet. - -
+ + + + + + + + + + + +
Group
{{group.groupId}} + + + +
+
+ + This team has no associated groups yet. + +
-
+
diff --git a/public/app/features/org/team_details_ctrl.ts b/public/app/features/org/team_details_ctrl.ts index 35fa04f55d3..6e0fddafa9d 100644 --- a/public/app/features/org/team_details_ctrl.ts +++ b/public/app/features/org/team_details_ctrl.ts @@ -7,7 +7,7 @@ export default class TeamDetailsCtrl { navModel: any; teamGroups: TeamGroup[] = []; newGroupId: string; - enterprise: boolean; + isMappingsEnabled: boolean; /** @ngInject **/ constructor(private $scope, private backendSrv, private $routeParams, navModelSrv) { @@ -15,7 +15,7 @@ export default class TeamDetailsCtrl { this.userPicked = this.userPicked.bind(this); this.get = this.get.bind(this); this.newGroupId = ''; - this.enterprise = config.enterprise; + this.isMappingsEnabled = config.buildInfo.isEnterprise; this.get(); } @@ -29,7 +29,7 @@ export default class TeamDetailsCtrl { this.teamMembers = result; }); - if (config.enterprise) { + if (this.isMappingsEnabled) { this.backendSrv.get(`/api/teams/${this.$routeParams.id}/groups`).then(result => { this.teamGroups = result; }); diff --git a/public/views/index.template.html b/public/views/index.template.html index 79da1d7179c..4140321d633 100644 --- a/public/views/index.template.html +++ b/public/views/index.template.html @@ -65,7 +65,7 @@
  • - Grafana + [[.AppName]] v[[.BuildVersion]] (commit: [[.BuildCommit]])
  • [[if .NewGrafanaVersionExists]] From 6286c31d4b217eb3526475bfd7e560c6c4a0199a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 2 Jul 2018 14:10:39 +0200 Subject: [PATCH 51/80] refactoring: making api wrap public --- build.go | 1 - pkg/api/api.go | 272 +++++++++++++++++++++++----------------------- pkg/api/common.go | 2 +- 3 files changed, 137 insertions(+), 138 deletions(-) diff --git a/build.go b/build.go index 3f92f8833a2..77cbde50c41 100644 --- a/build.go +++ b/build.go @@ -465,7 +465,6 @@ func ldflags() string { b.WriteString(fmt.Sprintf(" -X main.version=%s", version)) b.WriteString(fmt.Sprintf(" -X main.commit=%s", getGitSha())) b.WriteString(fmt.Sprintf(" -X main.buildstamp=%d", buildStamp())) - b.WriteString(fmt.Sprintf(" -X main.enterprise=%t", enterprise)) return b.String() } diff --git a/pkg/api/api.go b/pkg/api/api.go index 829b7735343..8870b9b095e 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -24,7 +24,7 @@ func (hs *HTTPServer) registerRoutes() { // not logged in views r.Get("/", reqSignedIn, Index) r.Get("/logout", Logout) - r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), wrap(LoginPost)) + r.Post("/login", quota("session"), bind(dtos.LoginCommand{}), Wrap(LoginPost)) r.Get("/login/:name", quota("session"), OAuthLogin) r.Get("/login", LoginView) r.Get("/invite/:code", Index) @@ -83,20 +83,20 @@ func (hs *HTTPServer) registerRoutes() { // sign up r.Get("/signup", Index) - r.Get("/api/user/signup/options", wrap(GetSignUpOptions)) - r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), wrap(SignUp)) - r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), wrap(SignUpStep2)) + r.Get("/api/user/signup/options", Wrap(GetSignUpOptions)) + r.Post("/api/user/signup", quota("user"), bind(dtos.SignUpForm{}), Wrap(SignUp)) + r.Post("/api/user/signup/step2", bind(dtos.SignUpStep2Form{}), Wrap(SignUpStep2)) // invited - r.Get("/api/user/invite/:code", wrap(GetInviteInfoByCode)) - r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), wrap(CompleteInvite)) + r.Get("/api/user/invite/:code", Wrap(GetInviteInfoByCode)) + r.Post("/api/user/invite/complete", bind(dtos.CompleteInviteForm{}), Wrap(CompleteInvite)) // reset password r.Get("/user/password/send-reset-email", Index) r.Get("/user/password/reset", Index) - r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), wrap(SendResetPasswordEmail)) - r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), wrap(ResetPassword)) + r.Post("/api/user/password/send-reset-email", bind(dtos.SendResetPasswordEmailForm{}), Wrap(SendResetPasswordEmail)) + r.Post("/api/user/password/reset", bind(dtos.ResetUserPasswordForm{}), Wrap(ResetPassword)) // dashboard snapshots r.Get("/dashboard/snapshot/*", Index) @@ -106,8 +106,8 @@ func (hs *HTTPServer) registerRoutes() { r.Post("/api/snapshots/", bind(m.CreateDashboardSnapshotCommand{}), CreateDashboardSnapshot) r.Get("/api/snapshot/shared-options/", GetSharingOptions) r.Get("/api/snapshots/:key", GetDashboardSnapshot) - r.Get("/api/snapshots-delete/:deleteKey", wrap(DeleteDashboardSnapshotByDeleteKey)) - r.Delete("/api/snapshots/:key", reqEditorRole, wrap(DeleteDashboardSnapshot)) + r.Get("/api/snapshots-delete/:deleteKey", Wrap(DeleteDashboardSnapshotByDeleteKey)) + r.Delete("/api/snapshots/:key", reqEditorRole, Wrap(DeleteDashboardSnapshot)) // api renew session based on remember cookie r.Get("/api/login/ping", quota("session"), LoginAPIPing) @@ -117,138 +117,138 @@ func (hs *HTTPServer) registerRoutes() { // user (signed in) apiRoute.Group("/user", func(userRoute routing.RouteRegister) { - userRoute.Get("/", wrap(GetSignedInUser)) - userRoute.Put("/", bind(m.UpdateUserCommand{}), wrap(UpdateSignedInUser)) - userRoute.Post("/using/:id", wrap(UserSetUsingOrg)) - userRoute.Get("/orgs", wrap(GetSignedInUserOrgList)) + userRoute.Get("/", Wrap(GetSignedInUser)) + userRoute.Put("/", bind(m.UpdateUserCommand{}), Wrap(UpdateSignedInUser)) + userRoute.Post("/using/:id", Wrap(UserSetUsingOrg)) + userRoute.Get("/orgs", Wrap(GetSignedInUserOrgList)) - userRoute.Post("/stars/dashboard/:id", wrap(StarDashboard)) - userRoute.Delete("/stars/dashboard/:id", wrap(UnstarDashboard)) + userRoute.Post("/stars/dashboard/:id", Wrap(StarDashboard)) + userRoute.Delete("/stars/dashboard/:id", Wrap(UnstarDashboard)) - userRoute.Put("/password", bind(m.ChangeUserPasswordCommand{}), wrap(ChangeUserPassword)) - userRoute.Get("/quotas", wrap(GetUserQuotas)) - userRoute.Put("/helpflags/:id", wrap(SetHelpFlag)) + userRoute.Put("/password", bind(m.ChangeUserPasswordCommand{}), Wrap(ChangeUserPassword)) + userRoute.Get("/quotas", Wrap(GetUserQuotas)) + userRoute.Put("/helpflags/:id", Wrap(SetHelpFlag)) // For dev purpose - userRoute.Get("/helpflags/clear", wrap(ClearHelpFlags)) + userRoute.Get("/helpflags/clear", Wrap(ClearHelpFlags)) - userRoute.Get("/preferences", wrap(GetUserPreferences)) - userRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateUserPreferences)) + userRoute.Get("/preferences", Wrap(GetUserPreferences)) + userRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateUserPreferences)) }) // users (admin permission required) apiRoute.Group("/users", func(usersRoute routing.RouteRegister) { - usersRoute.Get("/", wrap(SearchUsers)) - usersRoute.Get("/search", wrap(SearchUsersWithPaging)) - usersRoute.Get("/:id", wrap(GetUserByID)) - usersRoute.Get("/:id/orgs", wrap(GetUserOrgList)) + usersRoute.Get("/", Wrap(SearchUsers)) + usersRoute.Get("/search", Wrap(SearchUsersWithPaging)) + usersRoute.Get("/:id", Wrap(GetUserByID)) + usersRoute.Get("/:id/orgs", Wrap(GetUserOrgList)) // query parameters /users/lookup?loginOrEmail=admin@example.com - usersRoute.Get("/lookup", wrap(GetUserByLoginOrEmail)) - usersRoute.Put("/:id", bind(m.UpdateUserCommand{}), wrap(UpdateUser)) - usersRoute.Post("/:id/using/:orgId", wrap(UpdateUserActiveOrg)) + usersRoute.Get("/lookup", Wrap(GetUserByLoginOrEmail)) + usersRoute.Put("/:id", bind(m.UpdateUserCommand{}), Wrap(UpdateUser)) + usersRoute.Post("/:id/using/:orgId", Wrap(UpdateUserActiveOrg)) }, reqGrafanaAdmin) // team (admin permission required) apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { - teamsRoute.Post("/", bind(m.CreateTeamCommand{}), wrap(CreateTeam)) - teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), wrap(UpdateTeam)) - teamsRoute.Delete("/:teamId", wrap(DeleteTeamByID)) - teamsRoute.Get("/:teamId/members", wrap(GetTeamMembers)) - teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), wrap(AddTeamMember)) - teamsRoute.Delete("/:teamId/members/:userId", wrap(RemoveTeamMember)) + teamsRoute.Post("/", bind(m.CreateTeamCommand{}), Wrap(CreateTeam)) + teamsRoute.Put("/:teamId", bind(m.UpdateTeamCommand{}), Wrap(UpdateTeam)) + teamsRoute.Delete("/:teamId", Wrap(DeleteTeamByID)) + teamsRoute.Get("/:teamId/members", Wrap(GetTeamMembers)) + teamsRoute.Post("/:teamId/members", bind(m.AddTeamMemberCommand{}), Wrap(AddTeamMember)) + teamsRoute.Delete("/:teamId/members/:userId", Wrap(RemoveTeamMember)) }, reqOrgAdmin) // team without requirement of user to be org admin apiRoute.Group("/teams", func(teamsRoute routing.RouteRegister) { - teamsRoute.Get("/:teamId", wrap(GetTeamByID)) - teamsRoute.Get("/search", wrap(SearchTeams)) + teamsRoute.Get("/:teamId", Wrap(GetTeamByID)) + teamsRoute.Get("/search", Wrap(SearchTeams)) }) // org information available to all users. apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { - orgRoute.Get("/", wrap(GetOrgCurrent)) - orgRoute.Get("/quotas", wrap(GetOrgQuotas)) + orgRoute.Get("/", Wrap(GetOrgCurrent)) + orgRoute.Get("/quotas", Wrap(GetOrgQuotas)) }) // current org apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { - orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrgCurrent)) - orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddressCurrent)) - orgRoute.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), wrap(AddOrgUserToCurrentOrg)) - orgRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUserForCurrentOrg)) - orgRoute.Delete("/users/:userId", wrap(RemoveOrgUserForCurrentOrg)) + orgRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrgCurrent)) + orgRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddressCurrent)) + orgRoute.Post("/users", quota("user"), bind(m.AddOrgUserCommand{}), Wrap(AddOrgUserToCurrentOrg)) + orgRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), Wrap(UpdateOrgUserForCurrentOrg)) + orgRoute.Delete("/users/:userId", Wrap(RemoveOrgUserForCurrentOrg)) // invites - orgRoute.Get("/invites", wrap(GetPendingOrgInvites)) - orgRoute.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), wrap(AddOrgInvite)) - orgRoute.Patch("/invites/:code/revoke", wrap(RevokeInvite)) + orgRoute.Get("/invites", Wrap(GetPendingOrgInvites)) + orgRoute.Post("/invites", quota("user"), bind(dtos.AddInviteForm{}), Wrap(AddOrgInvite)) + orgRoute.Patch("/invites/:code/revoke", Wrap(RevokeInvite)) // prefs - orgRoute.Get("/preferences", wrap(GetOrgPreferences)) - orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), wrap(UpdateOrgPreferences)) + orgRoute.Get("/preferences", Wrap(GetOrgPreferences)) + orgRoute.Put("/preferences", bind(dtos.UpdatePrefsCmd{}), Wrap(UpdateOrgPreferences)) }, reqOrgAdmin) // current org without requirement of user to be org admin apiRoute.Group("/org", func(orgRoute routing.RouteRegister) { - orgRoute.Get("/users", wrap(GetOrgUsersForCurrentOrg)) + orgRoute.Get("/users", Wrap(GetOrgUsersForCurrentOrg)) }) // create new org - apiRoute.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), wrap(CreateOrg)) + apiRoute.Post("/orgs", quota("org"), bind(m.CreateOrgCommand{}), Wrap(CreateOrg)) // search all orgs - apiRoute.Get("/orgs", reqGrafanaAdmin, wrap(SearchOrgs)) + apiRoute.Get("/orgs", reqGrafanaAdmin, Wrap(SearchOrgs)) // orgs (admin routes) apiRoute.Group("/orgs/:orgId", func(orgsRoute routing.RouteRegister) { - orgsRoute.Get("/", wrap(GetOrgByID)) - orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), wrap(UpdateOrg)) - orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), wrap(UpdateOrgAddress)) - orgsRoute.Delete("/", wrap(DeleteOrgByID)) - orgsRoute.Get("/users", wrap(GetOrgUsers)) - orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), wrap(AddOrgUser)) - orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), wrap(UpdateOrgUser)) - orgsRoute.Delete("/users/:userId", wrap(RemoveOrgUser)) - orgsRoute.Get("/quotas", wrap(GetOrgQuotas)) - orgsRoute.Put("/quotas/:target", bind(m.UpdateOrgQuotaCmd{}), wrap(UpdateOrgQuota)) + orgsRoute.Get("/", Wrap(GetOrgByID)) + orgsRoute.Put("/", bind(dtos.UpdateOrgForm{}), Wrap(UpdateOrg)) + orgsRoute.Put("/address", bind(dtos.UpdateOrgAddressForm{}), Wrap(UpdateOrgAddress)) + orgsRoute.Delete("/", Wrap(DeleteOrgByID)) + orgsRoute.Get("/users", Wrap(GetOrgUsers)) + orgsRoute.Post("/users", bind(m.AddOrgUserCommand{}), Wrap(AddOrgUser)) + orgsRoute.Patch("/users/:userId", bind(m.UpdateOrgUserCommand{}), Wrap(UpdateOrgUser)) + orgsRoute.Delete("/users/:userId", Wrap(RemoveOrgUser)) + orgsRoute.Get("/quotas", Wrap(GetOrgQuotas)) + orgsRoute.Put("/quotas/:target", bind(m.UpdateOrgQuotaCmd{}), Wrap(UpdateOrgQuota)) }, reqGrafanaAdmin) // orgs (admin routes) apiRoute.Group("/orgs/name/:name", func(orgsRoute routing.RouteRegister) { - orgsRoute.Get("/", wrap(GetOrgByName)) + orgsRoute.Get("/", Wrap(GetOrgByName)) }, reqGrafanaAdmin) // auth api keys apiRoute.Group("/auth/keys", func(keysRoute routing.RouteRegister) { - keysRoute.Get("/", wrap(GetAPIKeys)) - keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), wrap(AddAPIKey)) - keysRoute.Delete("/:id", wrap(DeleteAPIKey)) + keysRoute.Get("/", Wrap(GetAPIKeys)) + keysRoute.Post("/", quota("api_key"), bind(m.AddApiKeyCommand{}), Wrap(AddAPIKey)) + keysRoute.Delete("/:id", Wrap(DeleteAPIKey)) }, reqOrgAdmin) // Preferences apiRoute.Group("/preferences", func(prefRoute routing.RouteRegister) { - prefRoute.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), wrap(SetHomeDashboard)) + prefRoute.Post("/set-home-dash", bind(m.SavePreferencesCommand{}), Wrap(SetHomeDashboard)) }) // Data sources apiRoute.Group("/datasources", func(datasourceRoute routing.RouteRegister) { - datasourceRoute.Get("/", wrap(GetDataSources)) - datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), wrap(AddDataSource)) - datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), wrap(UpdateDataSource)) - datasourceRoute.Delete("/:id", wrap(DeleteDataSourceByID)) - datasourceRoute.Delete("/name/:name", wrap(DeleteDataSourceByName)) - datasourceRoute.Get("/:id", wrap(GetDataSourceByID)) - datasourceRoute.Get("/name/:name", wrap(GetDataSourceByName)) + datasourceRoute.Get("/", Wrap(GetDataSources)) + datasourceRoute.Post("/", quota("data_source"), bind(m.AddDataSourceCommand{}), Wrap(AddDataSource)) + datasourceRoute.Put("/:id", bind(m.UpdateDataSourceCommand{}), Wrap(UpdateDataSource)) + datasourceRoute.Delete("/:id", Wrap(DeleteDataSourceByID)) + datasourceRoute.Delete("/name/:name", Wrap(DeleteDataSourceByName)) + datasourceRoute.Get("/:id", Wrap(GetDataSourceByID)) + datasourceRoute.Get("/name/:name", Wrap(GetDataSourceByName)) }, reqOrgAdmin) - apiRoute.Get("/datasources/id/:name", wrap(GetDataSourceIDByName), reqSignedIn) + apiRoute.Get("/datasources/id/:name", Wrap(GetDataSourceIDByName), reqSignedIn) - apiRoute.Get("/plugins", wrap(GetPluginList)) - apiRoute.Get("/plugins/:pluginId/settings", wrap(GetPluginSettingByID)) - apiRoute.Get("/plugins/:pluginId/markdown/:name", wrap(GetPluginMarkdown)) + apiRoute.Get("/plugins", Wrap(GetPluginList)) + apiRoute.Get("/plugins/:pluginId/settings", Wrap(GetPluginSettingByID)) + apiRoute.Get("/plugins/:pluginId/markdown/:name", Wrap(GetPluginMarkdown)) apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) { - pluginRoute.Get("/:pluginId/dashboards/", wrap(GetPluginDashboards)) - pluginRoute.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), wrap(UpdatePluginSetting)) + pluginRoute.Get("/:pluginId/dashboards/", Wrap(GetPluginDashboards)) + pluginRoute.Post("/:pluginId/settings", bind(m.UpdatePluginSettingCmd{}), Wrap(UpdatePluginSetting)) }, reqOrgAdmin) apiRoute.Get("/frontend/settings/", GetFrontendSettings) @@ -257,106 +257,106 @@ func (hs *HTTPServer) registerRoutes() { // Folders apiRoute.Group("/folders", func(folderRoute routing.RouteRegister) { - folderRoute.Get("/", wrap(GetFolders)) - folderRoute.Get("/id/:id", wrap(GetFolderByID)) - folderRoute.Post("/", bind(m.CreateFolderCommand{}), wrap(CreateFolder)) + folderRoute.Get("/", Wrap(GetFolders)) + folderRoute.Get("/id/:id", Wrap(GetFolderByID)) + folderRoute.Post("/", bind(m.CreateFolderCommand{}), Wrap(CreateFolder)) folderRoute.Group("/:uid", func(folderUidRoute routing.RouteRegister) { - folderUidRoute.Get("/", wrap(GetFolderByUID)) - folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), wrap(UpdateFolder)) - folderUidRoute.Delete("/", wrap(DeleteFolder)) + folderUidRoute.Get("/", Wrap(GetFolderByUID)) + folderUidRoute.Put("/", bind(m.UpdateFolderCommand{}), Wrap(UpdateFolder)) + folderUidRoute.Delete("/", Wrap(DeleteFolder)) folderUidRoute.Group("/permissions", func(folderPermissionRoute routing.RouteRegister) { - folderPermissionRoute.Get("/", wrap(GetFolderPermissionList)) - folderPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), wrap(UpdateFolderPermissions)) + folderPermissionRoute.Get("/", Wrap(GetFolderPermissionList)) + folderPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), Wrap(UpdateFolderPermissions)) }) }) }) // Dashboard apiRoute.Group("/dashboards", func(dashboardRoute routing.RouteRegister) { - dashboardRoute.Get("/uid/:uid", wrap(GetDashboard)) - dashboardRoute.Delete("/uid/:uid", wrap(DeleteDashboardByUID)) + dashboardRoute.Get("/uid/:uid", Wrap(GetDashboard)) + dashboardRoute.Delete("/uid/:uid", Wrap(DeleteDashboardByUID)) - dashboardRoute.Get("/db/:slug", wrap(GetDashboard)) - dashboardRoute.Delete("/db/:slug", wrap(DeleteDashboard)) + dashboardRoute.Get("/db/:slug", Wrap(GetDashboard)) + dashboardRoute.Delete("/db/:slug", Wrap(DeleteDashboard)) - dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), wrap(CalculateDashboardDiff)) + dashboardRoute.Post("/calculate-diff", bind(dtos.CalculateDiffOptions{}), Wrap(CalculateDashboardDiff)) - dashboardRoute.Post("/db", bind(m.SaveDashboardCommand{}), wrap(PostDashboard)) - dashboardRoute.Get("/home", wrap(GetHomeDashboard)) + dashboardRoute.Post("/db", bind(m.SaveDashboardCommand{}), Wrap(PostDashboard)) + dashboardRoute.Get("/home", Wrap(GetHomeDashboard)) dashboardRoute.Get("/tags", GetDashboardTags) - dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), wrap(ImportDashboard)) + dashboardRoute.Post("/import", bind(dtos.ImportDashboardCommand{}), Wrap(ImportDashboard)) dashboardRoute.Group("/id/:dashboardId", func(dashIdRoute routing.RouteRegister) { - dashIdRoute.Get("/versions", wrap(GetDashboardVersions)) - dashIdRoute.Get("/versions/:id", wrap(GetDashboardVersion)) - dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), wrap(RestoreDashboardVersion)) + dashIdRoute.Get("/versions", Wrap(GetDashboardVersions)) + dashIdRoute.Get("/versions/:id", Wrap(GetDashboardVersion)) + dashIdRoute.Post("/restore", bind(dtos.RestoreDashboardVersionCommand{}), Wrap(RestoreDashboardVersion)) dashIdRoute.Group("/permissions", func(dashboardPermissionRoute routing.RouteRegister) { - dashboardPermissionRoute.Get("/", wrap(GetDashboardPermissionList)) - dashboardPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), wrap(UpdateDashboardPermissions)) + dashboardPermissionRoute.Get("/", Wrap(GetDashboardPermissionList)) + dashboardPermissionRoute.Post("/", bind(dtos.UpdateDashboardAclCommand{}), Wrap(UpdateDashboardPermissions)) }) }) }) // Dashboard snapshots apiRoute.Group("/dashboard/snapshots", func(dashboardRoute routing.RouteRegister) { - dashboardRoute.Get("/", wrap(SearchDashboardSnapshots)) + dashboardRoute.Get("/", Wrap(SearchDashboardSnapshots)) }) // Playlist apiRoute.Group("/playlists", func(playlistRoute routing.RouteRegister) { - playlistRoute.Get("/", wrap(SearchPlaylists)) - playlistRoute.Get("/:id", ValidateOrgPlaylist, wrap(GetPlaylist)) - playlistRoute.Get("/:id/items", ValidateOrgPlaylist, wrap(GetPlaylistItems)) - playlistRoute.Get("/:id/dashboards", ValidateOrgPlaylist, wrap(GetPlaylistDashboards)) - playlistRoute.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, wrap(DeletePlaylist)) - playlistRoute.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistCommand{}), ValidateOrgPlaylist, wrap(UpdatePlaylist)) - playlistRoute.Post("/", reqEditorRole, bind(m.CreatePlaylistCommand{}), wrap(CreatePlaylist)) + playlistRoute.Get("/", Wrap(SearchPlaylists)) + playlistRoute.Get("/:id", ValidateOrgPlaylist, Wrap(GetPlaylist)) + playlistRoute.Get("/:id/items", ValidateOrgPlaylist, Wrap(GetPlaylistItems)) + playlistRoute.Get("/:id/dashboards", ValidateOrgPlaylist, Wrap(GetPlaylistDashboards)) + playlistRoute.Delete("/:id", reqEditorRole, ValidateOrgPlaylist, Wrap(DeletePlaylist)) + playlistRoute.Put("/:id", reqEditorRole, bind(m.UpdatePlaylistCommand{}), ValidateOrgPlaylist, Wrap(UpdatePlaylist)) + playlistRoute.Post("/", reqEditorRole, bind(m.CreatePlaylistCommand{}), Wrap(CreatePlaylist)) }) // Search apiRoute.Get("/search/", Search) // metrics - apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), wrap(QueryMetrics)) - apiRoute.Get("/tsdb/testdata/scenarios", wrap(GetTestDataScenarios)) - apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, wrap(GenerateSQLTestData)) - apiRoute.Get("/tsdb/testdata/random-walk", wrap(GetTestDataRandomWalk)) + apiRoute.Post("/tsdb/query", bind(dtos.MetricRequest{}), Wrap(QueryMetrics)) + apiRoute.Get("/tsdb/testdata/scenarios", Wrap(GetTestDataScenarios)) + apiRoute.Get("/tsdb/testdata/gensql", reqGrafanaAdmin, Wrap(GenerateSQLTestData)) + apiRoute.Get("/tsdb/testdata/random-walk", Wrap(GetTestDataRandomWalk)) apiRoute.Group("/alerts", func(alertsRoute routing.RouteRegister) { - alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), wrap(AlertTest)) - alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), wrap(PauseAlert)) - alertsRoute.Get("/:alertId", ValidateOrgAlert, wrap(GetAlert)) - alertsRoute.Get("/", wrap(GetAlerts)) - alertsRoute.Get("/states-for-dashboard", wrap(GetAlertStatesForDashboard)) + alertsRoute.Post("/test", bind(dtos.AlertTestCommand{}), Wrap(AlertTest)) + alertsRoute.Post("/:alertId/pause", reqEditorRole, bind(dtos.PauseAlertCommand{}), Wrap(PauseAlert)) + alertsRoute.Get("/:alertId", ValidateOrgAlert, Wrap(GetAlert)) + alertsRoute.Get("/", Wrap(GetAlerts)) + alertsRoute.Get("/states-for-dashboard", Wrap(GetAlertStatesForDashboard)) }) - apiRoute.Get("/alert-notifications", wrap(GetAlertNotifications)) - apiRoute.Get("/alert-notifiers", wrap(GetAlertNotifiers)) + apiRoute.Get("/alert-notifications", Wrap(GetAlertNotifications)) + apiRoute.Get("/alert-notifiers", Wrap(GetAlertNotifiers)) apiRoute.Group("/alert-notifications", func(alertNotifications routing.RouteRegister) { - alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), wrap(NotificationTest)) - alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), wrap(CreateAlertNotification)) - alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), wrap(UpdateAlertNotification)) - alertNotifications.Get("/:notificationId", wrap(GetAlertNotificationByID)) - alertNotifications.Delete("/:notificationId", wrap(DeleteAlertNotification)) + alertNotifications.Post("/test", bind(dtos.NotificationTestCommand{}), Wrap(NotificationTest)) + alertNotifications.Post("/", bind(m.CreateAlertNotificationCommand{}), Wrap(CreateAlertNotification)) + alertNotifications.Put("/:notificationId", bind(m.UpdateAlertNotificationCommand{}), Wrap(UpdateAlertNotification)) + alertNotifications.Get("/:notificationId", Wrap(GetAlertNotificationByID)) + alertNotifications.Delete("/:notificationId", Wrap(DeleteAlertNotification)) }, reqEditorRole) - apiRoute.Get("/annotations", wrap(GetAnnotations)) - apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), wrap(DeleteAnnotations)) + apiRoute.Get("/annotations", Wrap(GetAnnotations)) + apiRoute.Post("/annotations/mass-delete", reqOrgAdmin, bind(dtos.DeleteAnnotationsCmd{}), Wrap(DeleteAnnotations)) apiRoute.Group("/annotations", func(annotationsRoute routing.RouteRegister) { - annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), wrap(PostAnnotation)) - annotationsRoute.Delete("/:annotationId", wrap(DeleteAnnotationByID)) - annotationsRoute.Put("/:annotationId", bind(dtos.UpdateAnnotationsCmd{}), wrap(UpdateAnnotation)) - annotationsRoute.Delete("/region/:regionId", wrap(DeleteAnnotationRegion)) - annotationsRoute.Post("/graphite", reqEditorRole, bind(dtos.PostGraphiteAnnotationsCmd{}), wrap(PostGraphiteAnnotation)) + annotationsRoute.Post("/", bind(dtos.PostAnnotationsCmd{}), Wrap(PostAnnotation)) + annotationsRoute.Delete("/:annotationId", Wrap(DeleteAnnotationByID)) + annotationsRoute.Put("/:annotationId", bind(dtos.UpdateAnnotationsCmd{}), Wrap(UpdateAnnotation)) + annotationsRoute.Delete("/region/:regionId", Wrap(DeleteAnnotationRegion)) + annotationsRoute.Post("/graphite", reqEditorRole, bind(dtos.PostGraphiteAnnotationsCmd{}), Wrap(PostGraphiteAnnotation)) }) // error test - r.Get("/metrics/error", wrap(GenerateError)) + r.Get("/metrics/error", Wrap(GenerateError)) }, reqSignedIn) @@ -367,10 +367,10 @@ func (hs *HTTPServer) registerRoutes() { adminRoute.Put("/users/:id/password", bind(dtos.AdminUpdateUserPasswordForm{}), AdminUpdateUserPassword) adminRoute.Put("/users/:id/permissions", bind(dtos.AdminUpdateUserPermissionsForm{}), AdminUpdateUserPermissions) adminRoute.Delete("/users/:id", AdminDeleteUser) - adminRoute.Get("/users/:id/quotas", wrap(GetUserQuotas)) - adminRoute.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), wrap(UpdateUserQuota)) + adminRoute.Get("/users/:id/quotas", Wrap(GetUserQuotas)) + adminRoute.Put("/users/:id/quotas/:target", bind(m.UpdateUserQuotaCmd{}), Wrap(UpdateUserQuota)) adminRoute.Get("/stats", AdminGetStats) - adminRoute.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), wrap(PauseAllAlerts)) + adminRoute.Post("/pause-all-alerts", bind(dtos.PauseAllAlertsCommand{}), Wrap(PauseAllAlerts)) }, reqGrafanaAdmin) // rendering diff --git a/pkg/api/common.go b/pkg/api/common.go index 97f41ff7c72..7973c72c8fa 100644 --- a/pkg/api/common.go +++ b/pkg/api/common.go @@ -30,7 +30,7 @@ type NormalResponse struct { err error } -func wrap(action interface{}) macaron.Handler { +func Wrap(action interface{}) macaron.Handler { return func(c *m.ReqContext) { var res Response From 8d6797c367bbb45228cf7e198808981fa2c0bffe Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 2 Jul 2018 16:05:23 +0200 Subject: [PATCH 52/80] ci: publishes grafana enterprise to s3. --- .circleci/config.yml | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b729ecfa15..7c8fad40905 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -160,7 +160,7 @@ jobs: steps: - checkout - run: - name: build and package grafana + name: build, test and package grafana enterprise command: './scripts/build/build_enterprise.sh' - run: name: sign packages @@ -168,6 +168,26 @@ jobs: - run: name: sha-sum packages command: 'go run build.go sha-dist' + -run: + name: move enterprise packages into their own folder + command: 'mv dist enterprise-dist' + - persist_to_workspace: + root: . + paths: + - enterprise-dist/grafana* + + deploy-enterprise-master: + docker: + - image: circleci/python:2.7-stretch + steps: + - attach_workspace: + at: . + - run: + name: install awscli + command: 'sudo pip install awscli' + - run: + name: deploy to s3 + command: 'aws s3 sync ./enterprise-dist s3://$ENTERPRISE_BUCKET_NAME/master' deploy-master: docker: @@ -247,6 +267,20 @@ workflows: filters: branches: only: master + - deploy-enterprise-master: + requires: + - build-all + - test-backend + - test-frontend + - codespell + - gometalinter + - mysql-integration-test + - postgres-integration-test + - build-enterprise + filters: + branches: + only: master + release: jobs: - build-all: From 4245f0ce119f8f016243844bd58de01c9adc1b51 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 2 Jul 2018 16:10:20 +0200 Subject: [PATCH 53/80] ci: typo --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 7c8fad40905..0217c129a62 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -168,7 +168,7 @@ jobs: - run: name: sha-sum packages command: 'go run build.go sha-dist' - -run: + - run: name: move enterprise packages into their own folder command: 'mv dist enterprise-dist' - persist_to_workspace: From 5e08bf5130e5f118afb1c43b36e62e116f369ec2 Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Mon, 2 Jul 2018 17:13:59 +0200 Subject: [PATCH 54/80] test: fixed usage of wrap in tests. --- pkg/api/alerting_test.go | 2 +- pkg/api/annotations_test.go | 6 +++--- pkg/api/common_test.go | 4 ++-- pkg/api/dashboard_permission_test.go | 2 +- pkg/api/dashboard_test.go | 4 ++-- pkg/api/folder_permission_test.go | 2 +- pkg/api/folder_test.go | 4 ++-- 7 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/api/alerting_test.go b/pkg/api/alerting_test.go index abfdfb66322..9eba0e0d5b6 100644 --- a/pkg/api/alerting_test.go +++ b/pkg/api/alerting_test.go @@ -135,7 +135,7 @@ func postAlertScenario(desc string, url string, routePattern string, role m.Role defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.UserId = TestUserID sc.context.OrgId = TestOrgID diff --git a/pkg/api/annotations_test.go b/pkg/api/annotations_test.go index e5f63ce022b..6590eb19ff2 100644 --- a/pkg/api/annotations_test.go +++ b/pkg/api/annotations_test.go @@ -223,7 +223,7 @@ func postAnnotationScenario(desc string, url string, routePattern string, role m defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.UserId = TestUserID sc.context.OrgId = TestOrgID @@ -246,7 +246,7 @@ func putAnnotationScenario(desc string, url string, routePattern string, role m. defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.UserId = TestUserID sc.context.OrgId = TestOrgID @@ -269,7 +269,7 @@ func deleteAnnotationsScenario(desc string, url string, routePattern string, rol defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.UserId = TestUserID sc.context.OrgId = TestOrgID diff --git a/pkg/api/common_test.go b/pkg/api/common_test.go index 40c438b607a..8b66a7a468b 100644 --- a/pkg/api/common_test.go +++ b/pkg/api/common_test.go @@ -23,7 +23,7 @@ func loggedInUserScenarioWithRole(desc string, method string, url string, routeP defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.UserId = TestUserID sc.context.OrgId = TestOrgID @@ -51,7 +51,7 @@ func anonymousUserScenario(desc string, method string, url string, routePattern defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c if sc.handlerFunc != nil { return sc.handlerFunc(sc.context) diff --git a/pkg/api/dashboard_permission_test.go b/pkg/api/dashboard_permission_test.go index 24f0bdca365..f65c5f1f5fa 100644 --- a/pkg/api/dashboard_permission_test.go +++ b/pkg/api/dashboard_permission_test.go @@ -194,7 +194,7 @@ func updateDashboardPermissionScenario(desc string, url string, routePattern str sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.OrgId = TestOrgID sc.context.UserId = TestUserID diff --git a/pkg/api/dashboard_test.go b/pkg/api/dashboard_test.go index ccde2382787..50a2e314f5c 100644 --- a/pkg/api/dashboard_test.go +++ b/pkg/api/dashboard_test.go @@ -882,7 +882,7 @@ func postDashboardScenario(desc string, url string, routePattern string, mock *d defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{OrgId: cmd.OrgId, UserId: cmd.UserId} @@ -907,7 +907,7 @@ func postDiffScenario(desc string, url string, routePattern string, cmd dtos.Cal defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{ OrgId: TestOrgID, diff --git a/pkg/api/folder_permission_test.go b/pkg/api/folder_permission_test.go index f7458af6dce..64a746ca937 100644 --- a/pkg/api/folder_permission_test.go +++ b/pkg/api/folder_permission_test.go @@ -226,7 +226,7 @@ func updateFolderPermissionScenario(desc string, url string, routePattern string sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.OrgId = TestOrgID sc.context.UserId = TestUserID diff --git a/pkg/api/folder_test.go b/pkg/api/folder_test.go index 0d9b9495686..6e24e432535 100644 --- a/pkg/api/folder_test.go +++ b/pkg/api/folder_test.go @@ -152,7 +152,7 @@ func createFolderScenario(desc string, url string, routePattern string, mock *fa defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID} @@ -181,7 +181,7 @@ func updateFolderScenario(desc string, url string, routePattern string, mock *fa defer bus.ClearBusHandlers() sc := setupScenarioContext(url) - sc.defaultHandler = wrap(func(c *m.ReqContext) Response { + sc.defaultHandler = Wrap(func(c *m.ReqContext) Response { sc.context = c sc.context.SignedInUser = &m.SignedInUser{OrgId: TestOrgID, UserId: TestUserID} From 37efa934f16de14630c28c190162105a45040dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Mon, 2 Jul 2018 17:25:44 +0200 Subject: [PATCH 55/80] ldap: improved ldap test env with more structured users and groups --- .gitignore | 1 + docker/blocks/openldap/entrypoint.sh | 10 +++++++++- docker/blocks/openldap/prepopulate/admin.ldif | 10 ---------- docker/blocks/openldap/prepopulate/adminsgroup.ldif | 5 ----- docker/blocks/openldap/prepopulate/editor.ldif | 10 ---------- docker/blocks/openldap/prepopulate/groups/admins.ldif | 5 +++++ .../blocks/openldap/prepopulate/groups/backend.ldif | 5 +++++ docker/blocks/openldap/prepopulate/groups/editor.ldif | 5 +++++ .../blocks/openldap/prepopulate/groups/frontend.ldif | 5 +++++ docker/blocks/openldap/prepopulate/units/groups.ldif | 3 +++ docker/blocks/openldap/prepopulate/units/users.ldif | 3 +++ .../blocks/openldap/prepopulate/users/ldap-admin.ldif | 11 +++++++++++ .../openldap/prepopulate/users/ldap-editor.ldif | 10 ++++++++++ .../openldap/prepopulate/users/ldap-frontend-1.ldif | 10 ++++++++++ .../{viewer.ldif => users/ldap-viewer.ldif} | 8 ++++---- docker/blocks/openldap/prepopulate/usersgroup.ldif | 5 ----- 16 files changed, 71 insertions(+), 35 deletions(-) delete mode 100644 docker/blocks/openldap/prepopulate/admin.ldif delete mode 100644 docker/blocks/openldap/prepopulate/adminsgroup.ldif delete mode 100644 docker/blocks/openldap/prepopulate/editor.ldif create mode 100644 docker/blocks/openldap/prepopulate/groups/admins.ldif create mode 100644 docker/blocks/openldap/prepopulate/groups/backend.ldif create mode 100644 docker/blocks/openldap/prepopulate/groups/editor.ldif create mode 100644 docker/blocks/openldap/prepopulate/groups/frontend.ldif create mode 100644 docker/blocks/openldap/prepopulate/units/groups.ldif create mode 100644 docker/blocks/openldap/prepopulate/units/users.ldif create mode 100644 docker/blocks/openldap/prepopulate/users/ldap-admin.ldif create mode 100644 docker/blocks/openldap/prepopulate/users/ldap-editor.ldif create mode 100644 docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif rename docker/blocks/openldap/prepopulate/{viewer.ldif => users/ldap-viewer.ldif} (52%) delete mode 100644 docker/blocks/openldap/prepopulate/usersgroup.ldif diff --git a/.gitignore b/.gitignore index 25325b37890..accc24d84cd 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ fig.yml docker-compose.yml docker-compose.yaml /conf/provisioning/**/custom.yaml +/conf/ldap_dev.toml profile.cov /grafana /local diff --git a/docker/blocks/openldap/entrypoint.sh b/docker/blocks/openldap/entrypoint.sh index d560b78d388..2f383355907 100755 --- a/docker/blocks/openldap/entrypoint.sh +++ b/docker/blocks/openldap/entrypoint.sh @@ -80,7 +80,15 @@ EOF done fi - for file in `ls /etc/ldap/prepopulate/*.ldif`; do + for file in `ls /etc/ldap/prepopulate/units/*.ldif`; do + slapadd -F /etc/ldap/slapd.d -l "$file" + done + + for file in `ls /etc/ldap/prepopulate/groups/*.ldif`; do + slapadd -F /etc/ldap/slapd.d -l "$file" + done + + for file in `ls /etc/ldap/prepopulate/users/*.ldif`; do slapadd -F /etc/ldap/slapd.d -l "$file" done diff --git a/docker/blocks/openldap/prepopulate/admin.ldif b/docker/blocks/openldap/prepopulate/admin.ldif deleted file mode 100644 index 3f4406d5810..00000000000 --- a/docker/blocks/openldap/prepopulate/admin.ldif +++ /dev/null @@ -1,10 +0,0 @@ -dn: cn=ldapadmin,dc=grafana,dc=org -mail: ldapadmin@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldapadmin -cn: ldapadmin -memberOf: cn=admins,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/adminsgroup.ldif b/docker/blocks/openldap/prepopulate/adminsgroup.ldif deleted file mode 100644 index d8dece4e458..00000000000 --- a/docker/blocks/openldap/prepopulate/adminsgroup.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=admins,dc=grafana,dc=org -cn: admins -member: cn=ldapadmin,dc=grafana,dc=org -objectClass: groupOfNames -objectClass: top diff --git a/docker/blocks/openldap/prepopulate/editor.ldif b/docker/blocks/openldap/prepopulate/editor.ldif deleted file mode 100644 index eba3adc4352..00000000000 --- a/docker/blocks/openldap/prepopulate/editor.ldif +++ /dev/null @@ -1,10 +0,0 @@ -dn: cn=ldapeditor,dc=grafana,dc=org -mail: ldapeditor@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldapeditor -cn: ldapeditor -memberOf: cn=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/admins.ldif b/docker/blocks/openldap/prepopulate/groups/admins.ldif new file mode 100644 index 00000000000..50d3a0ea4a8 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/groups/admins.ldif @@ -0,0 +1,5 @@ +dn: cn=admins,ou=groups,dc=grafana,dc=org +cn: admins +objectClass: groupOfNames +objectClass: top +member: cn=ldap-admin,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/backend.ldif b/docker/blocks/openldap/prepopulate/groups/backend.ldif new file mode 100644 index 00000000000..09a661adea0 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/groups/backend.ldif @@ -0,0 +1,5 @@ +dn: cn=backend,ou=groups,dc=grafana,dc=org +cn: backend +objectClass: groupOfNames +objectClass: top +member: cn=ldap-editor,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/editor.ldif b/docker/blocks/openldap/prepopulate/groups/editor.ldif new file mode 100644 index 00000000000..331ecc94141 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/groups/editor.ldif @@ -0,0 +1,5 @@ +dn: cn=editors,ou=groups,dc=grafana,dc=org +cn: editors +objectClass: groupOfNames +objectClass: top +member: cn=ldap-editor,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/frontend.ldif b/docker/blocks/openldap/prepopulate/groups/frontend.ldif new file mode 100644 index 00000000000..c410b96c7ad --- /dev/null +++ b/docker/blocks/openldap/prepopulate/groups/frontend.ldif @@ -0,0 +1,5 @@ +dn: cn=frontend,ou=groups,dc=grafana,dc=org +cn: frontend +objectClass: groupOfNames +objectClass: top +member: cn=ldap-frontend-1,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/units/groups.ldif b/docker/blocks/openldap/prepopulate/units/groups.ldif new file mode 100644 index 00000000000..64e21ad744f --- /dev/null +++ b/docker/blocks/openldap/prepopulate/units/groups.ldif @@ -0,0 +1,3 @@ +dn: ou=groups,dc=grafana,dc=org +objectclass: top +objectclass: organizationalUnit diff --git a/docker/blocks/openldap/prepopulate/units/users.ldif b/docker/blocks/openldap/prepopulate/units/users.ldif new file mode 100644 index 00000000000..76fc50dd299 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/units/users.ldif @@ -0,0 +1,3 @@ +dn: ou=users,dc=grafana,dc=org +objectclass: top +objectclass: organizationalUnit diff --git a/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif b/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif new file mode 100644 index 00000000000..1704a15c3db --- /dev/null +++ b/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif @@ -0,0 +1,11 @@ +dn: cn=ldap-admin,ou=users,dc=grafana,dc=org +mail: ldap-admin@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-admin +cn: ldap-admin +memberOf: cn=admins,ou=groups,dc=grafana,dc=org +memberOf: cn=editors,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif b/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif new file mode 100644 index 00000000000..d0de99f8c16 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif @@ -0,0 +1,10 @@ +dn: cn=ldap-editor,ou=users,dc=grafana,dc=org +mail: ldap-editor@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-editor +cn: ldap-editor +memberOf: cn=editors,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif b/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif new file mode 100644 index 00000000000..f5ebe0b41c4 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif @@ -0,0 +1,10 @@ +dn: cn=ldap-frontend-1,ou=users,dc=grafana,dc=org +mail: ldap-frontend-1@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-frontend-1 +cn: ldap-frontend-1 +memberOf: cn=frontend,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/viewer.ldif b/docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif similarity index 52% rename from docker/blocks/openldap/prepopulate/viewer.ldif rename to docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif index f699a7df57b..07066355a13 100644 --- a/docker/blocks/openldap/prepopulate/viewer.ldif +++ b/docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif @@ -1,9 +1,9 @@ -dn: cn=ldapviewer,dc=grafana,dc=org -mail: ldapviewer@grafana.com +dn: cn=ldap-viewer,ou=users,dc=grafana,dc=org +mail: ldap-viewer@grafana.com userPassword: grafana objectClass: person objectClass: top objectClass: inetOrgPerson objectClass: organizationalPerson -sn: ldapviewer -cn: ldapviewer +sn: ldap-viewer +cn: ldap-viewer diff --git a/docker/blocks/openldap/prepopulate/usersgroup.ldif b/docker/blocks/openldap/prepopulate/usersgroup.ldif deleted file mode 100644 index a1de3a50d38..00000000000 --- a/docker/blocks/openldap/prepopulate/usersgroup.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=users,dc=grafana,dc=org -cn: users -member: cn=ldapeditor,dc=grafana,dc=org -objectClass: groupOfNames -objectClass: top From a89351e8e0c8a594de31065a9410675d9bdd9c8d Mon Sep 17 00:00:00 2001 From: zicklam Date: Mon, 2 Jul 2018 18:01:42 +0200 Subject: [PATCH 56/80] correct example (#12481) foo.bar.com in description, but foo.bar in configuration. Updated description, makes more sense then changing the configuration :) --- docs/sources/installation/behind_proxy.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/sources/installation/behind_proxy.md b/docs/sources/installation/behind_proxy.md index 89711aecb46..6e3884456ac 100644 --- a/docs/sources/installation/behind_proxy.md +++ b/docs/sources/installation/behind_proxy.md @@ -26,7 +26,7 @@ Otherwise Grafana will not behave correctly. See example below. ## Examples Here are some example configurations for running Grafana behind a reverse proxy. -### Grafana configuration (ex http://foo.bar.com) +### Grafana configuration (ex http://foo.bar) ```bash [server] @@ -47,7 +47,7 @@ server { } ``` -### Examples with **sub path** (ex http://foo.bar.com/grafana) +### Examples with **sub path** (ex http://foo.bar/grafana) #### Grafana configuration with sub path ```bash From 0d1f7c8782f3cc7ad9cdd20c5257fdc087362f61 Mon Sep 17 00:00:00 2001 From: David Date: Mon, 2 Jul 2018 20:04:36 +0200 Subject: [PATCH 57/80] Fix bar width issue in aligned prometheus queries (#12483) * Fix bar width issue in aligned prometheus queries This was broken because null values were filled in with unaligned times. * use aligned times for result transformation * add tests An earlier version of this fix aligned the times again in the transformer, but I think it's safe to only deal with aligned times in the response. * Fixed prometheus heatmap tranformer test The interval needs to be 1 to prevent step alignment. --- public/app/core/specs/time_series.jest.ts | 14 ++++ .../datasource/prometheus/datasource.ts | 4 +- .../prometheus/specs/datasource.jest.ts | 2 +- .../specs/result_transformer.jest.ts | 78 +++++++++++++++++++ 4 files changed, 95 insertions(+), 3 deletions(-) diff --git a/public/app/core/specs/time_series.jest.ts b/public/app/core/specs/time_series.jest.ts index f5245476218..bf50d807e03 100644 --- a/public/app/core/specs/time_series.jest.ts +++ b/public/app/core/specs/time_series.jest.ts @@ -119,6 +119,20 @@ describe('TimeSeries', function() { series.getFlotPairs('null'); expect(series.stats.avg).toBe(null); }); + + it('calculates timeStep', function() { + series = new TimeSeries({ + datapoints: [[null, 1], [null, 2], [null, 3]], + }); + series.getFlotPairs('null'); + expect(series.stats.timeStep).toBe(1); + + series = new TimeSeries({ + datapoints: [[0, 1530529290], [0, 1530529305], [0, 1530529320]], + }); + series.getFlotPairs('null'); + expect(series.stats.timeStep).toBe(15); + }); }); describe('When checking if ms resolution is needed', function() { diff --git a/public/app/plugins/datasource/prometheus/datasource.ts b/public/app/plugins/datasource/prometheus/datasource.ts index 46431a08ab1..d7d33264c99 100644 --- a/public/app/plugins/datasource/prometheus/datasource.ts +++ b/public/app/plugins/datasource/prometheus/datasource.ts @@ -162,8 +162,8 @@ export class PrometheusDatasource { format: activeTargets[index].format, step: queries[index].step, legendFormat: activeTargets[index].legendFormat, - start: start, - end: end, + start: queries[index].start, + end: queries[index].end, query: queries[index].expr, responseListLength: responseList.length, responseIndex: index, diff --git a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts index 0157322da58..219b990e5dd 100644 --- a/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/datasource.jest.ts @@ -68,7 +68,7 @@ describe('PrometheusDatasource', () => { ctx.query = { range: { from: moment(1443454528000), to: moment(1443454528000) }, targets: [{ expr: 'test{job="testjob"}', format: 'heatmap', legendFormat: '{{le}}' }], - interval: '60s', + interval: '1s', }; }); diff --git a/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts b/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts index 56a05d5aedb..b94cca79059 100644 --- a/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts +++ b/public/app/plugins/datasource/prometheus/specs/result_transformer.jest.ts @@ -127,4 +127,82 @@ describe('Prometheus Result Transformer', () => { ]); }); }); + + describe('When resultFormat is time series', () => { + it('should transform matrix into timeseries', () => { + const response = { + status: 'success', + data: { + resultType: 'matrix', + result: [ + { + metric: { __name__: 'test', job: 'testjob' }, + values: [[0, '10'], [1, '10'], [2, '0']], + }, + ], + }, + }; + let result = []; + let options = { + format: 'timeseries', + start: 0, + end: 2, + }; + + ctx.resultTransformer.transform(result, { data: response }, options); + expect(result).toEqual([{ target: 'test{job="testjob"}', datapoints: [[10, 0], [10, 1000], [0, 2000]] }]); + }); + + it('should fill timeseries with null values', () => { + const response = { + status: 'success', + data: { + resultType: 'matrix', + result: [ + { + metric: { __name__: 'test', job: 'testjob' }, + values: [[1, '10'], [2, '0']], + }, + ], + }, + }; + let result = []; + let options = { + format: 'timeseries', + step: 1, + start: 0, + end: 2, + }; + + ctx.resultTransformer.transform(result, { data: response }, options); + expect(result).toEqual([{ target: 'test{job="testjob"}', datapoints: [[null, 0], [10, 1000], [0, 2000]] }]); + }); + + it('should align null values with step', () => { + const response = { + status: 'success', + data: { + resultType: 'matrix', + result: [ + { + metric: { __name__: 'test', job: 'testjob' }, + values: [[4, '10'], [8, '10']], + }, + ], + }, + }; + let result = []; + let options = { + format: 'timeseries', + step: 2, + start: 0, + end: 8, + }; + + ctx.resultTransformer.transform(result, { data: response }, options); + expect(result).toEqual([ + { target: 'test{job="testjob"}', datapoints: [[null, 0], [null, 2000], [10, 4000], [null, 6000], [10, 8000]] }, + ]); + }); + }); }); From 6046c8b4ca528bd4b2bb914f6d398914b6b29299 Mon Sep 17 00:00:00 2001 From: Martin Packman Date: Mon, 2 Jul 2018 20:14:41 +0200 Subject: [PATCH 58/80] Make table sorting stable when null values exist (#12362) Currently if a null appears in a table column, for instance in data returned by postgres, sorting on that gives an arbitrary order. This is due to null being neither greater or less than any string, which makes the sort unstable. Change the table sort function to compare on nullness first. Note this is a slight behaviour change for numbers, which would otherwise treat null and 0 as equivalent. Signed-off-by: Martin Packman --- public/app/core/specs/table_model.jest.ts | 35 +++++++++++++++++++++++ public/app/core/table_model.ts | 17 ++++------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/public/app/core/specs/table_model.jest.ts b/public/app/core/specs/table_model.jest.ts index a2c1eb5e1af..3d4c526cfea 100644 --- a/public/app/core/specs/table_model.jest.ts +++ b/public/app/core/specs/table_model.jest.ts @@ -44,3 +44,38 @@ describe('when sorting table asc', () => { expect(table.rows[2][1]).toBe(15); }); }); + +describe('when sorting with nulls', () => { + var table; + var values; + + beforeEach(() => { + table = new TableModel(); + table.columns = [{}, {}]; + table.rows = [[42, ''], [19, 'a'], [null, 'b'], [0, 'd'], [null, null], [2, 'c'], [0, null], [-8, '']]; + }); + + it('numbers with nulls at end with asc sort', () => { + table.sort({ col: 0, desc: false }); + values = table.rows.map(row => row[0]); + expect(values).toEqual([-8, 0, 0, 2, 19, 42, null, null]); + }); + + it('numbers with nulls at start with desc sort', () => { + table.sort({ col: 0, desc: true }); + values = table.rows.map(row => row[0]); + expect(values).toEqual([null, null, 42, 19, 2, 0, 0, -8]); + }); + + it('strings with nulls at end with asc sort', () => { + table.sort({ col: 1, desc: false }); + values = table.rows.map(row => row[1]); + expect(values).toEqual(['', '', 'a', 'b', 'c', 'd', null, null]); + }); + + it('strings with nulls at start with desc sort', () => { + table.sort({ col: 1, desc: true }); + values = table.rows.map(row => row[1]); + expect(values).toEqual([null, null, 'd', 'c', 'b', 'a', '', '']); + }); +}); diff --git a/public/app/core/table_model.ts b/public/app/core/table_model.ts index 5716aac2be6..04857eb806d 100644 --- a/public/app/core/table_model.ts +++ b/public/app/core/table_model.ts @@ -19,23 +19,16 @@ export default class TableModel { this.rows.sort(function(a, b) { a = a[options.col]; b = b[options.col]; - if (a < b) { - return -1; - } - if (a > b) { - return 1; - } - return 0; + // Sort null or undefined seperately from comparable values + return +(a == null) - +(b == null) || +(a > b) || -(a < b); }); - this.columns[options.col].sort = true; - if (options.desc) { this.rows.reverse(); - this.columns[options.col].desc = true; - } else { - this.columns[options.col].desc = false; } + + this.columns[options.col].sort = true; + this.columns[options.col].desc = options.desc; } addColumn(col) { From 1586a42a719f0c0724843e54ed9c4a015d8d00dc Mon Sep 17 00:00:00 2001 From: Leonard Gram Date: Tue, 3 Jul 2018 09:25:15 +0200 Subject: [PATCH 59/80] ci: Only publish grafana enterprise packages tagged with enterprise. --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0217c129a62..9b2f436adee 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -174,7 +174,7 @@ jobs: - persist_to_workspace: root: . paths: - - enterprise-dist/grafana* + - enterprise-dist/grafana-enterprise* deploy-enterprise-master: docker: From 1f97df46c136939e31b6eb3a72661190817a2fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Tue, 3 Jul 2018 12:57:48 +0200 Subject: [PATCH 60/80] devenv: open ldap docker block now prepopulating data with correct member groups --- docker/blocks/openldap/Dockerfile | 4 +- docker/blocks/openldap/entrypoint.sh | 17 ++-- docker/blocks/openldap/notes.md | 26 +++++- docker/blocks/openldap/prepopulate.sh | 14 ++++ .../blocks/openldap/prepopulate/1_units.ldif | 9 +++ .../blocks/openldap/prepopulate/2_users.ldif | 80 +++++++++++++++++++ .../blocks/openldap/prepopulate/3_groups.ldif | 25 ++++++ .../openldap/prepopulate/groups/admins.ldif | 5 -- .../openldap/prepopulate/groups/backend.ldif | 5 -- .../openldap/prepopulate/groups/editor.ldif | 5 -- .../openldap/prepopulate/groups/frontend.ldif | 5 -- .../openldap/prepopulate/units/groups.ldif | 3 - .../openldap/prepopulate/units/users.ldif | 3 - .../prepopulate/users/ldap-admin.ldif | 11 --- .../prepopulate/users/ldap-editor.ldif | 10 --- .../prepopulate/users/ldap-frontend-1.ldif | 10 --- .../prepopulate/users/ldap-viewer.ldif | 9 --- pkg/login/ext_user.go | 2 + 18 files changed, 163 insertions(+), 80 deletions(-) create mode 100755 docker/blocks/openldap/prepopulate.sh create mode 100644 docker/blocks/openldap/prepopulate/1_units.ldif create mode 100644 docker/blocks/openldap/prepopulate/2_users.ldif create mode 100644 docker/blocks/openldap/prepopulate/3_groups.ldif delete mode 100644 docker/blocks/openldap/prepopulate/groups/admins.ldif delete mode 100644 docker/blocks/openldap/prepopulate/groups/backend.ldif delete mode 100644 docker/blocks/openldap/prepopulate/groups/editor.ldif delete mode 100644 docker/blocks/openldap/prepopulate/groups/frontend.ldif delete mode 100644 docker/blocks/openldap/prepopulate/units/groups.ldif delete mode 100644 docker/blocks/openldap/prepopulate/units/users.ldif delete mode 100644 docker/blocks/openldap/prepopulate/users/ldap-admin.ldif delete mode 100644 docker/blocks/openldap/prepopulate/users/ldap-editor.ldif delete mode 100644 docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif delete mode 100644 docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif diff --git a/docker/blocks/openldap/Dockerfile b/docker/blocks/openldap/Dockerfile index c9b928ad56a..76172e133a4 100644 --- a/docker/blocks/openldap/Dockerfile +++ b/docker/blocks/openldap/Dockerfile @@ -8,7 +8,8 @@ ENV OPENLDAP_VERSION 2.4.40 RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \ - slapd=${OPENLDAP_VERSION}* && \ + slapd=${OPENLDAP_VERSION}* \ + ldap-utils && \ apt-get clean && \ rm -rf /var/lib/apt/lists/* @@ -22,6 +23,7 @@ COPY modules/ /etc/ldap.dist/modules COPY prepopulate/ /etc/ldap.dist/prepopulate COPY entrypoint.sh /entrypoint.sh +COPY prepopulate.sh /prepopulate.sh ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/blocks/openldap/entrypoint.sh b/docker/blocks/openldap/entrypoint.sh index 2f383355907..d202ed14b31 100755 --- a/docker/blocks/openldap/entrypoint.sh +++ b/docker/blocks/openldap/entrypoint.sh @@ -76,21 +76,14 @@ EOF IFS=","; declare -a modules=($SLAPD_ADDITIONAL_MODULES); unset IFS for module in "${modules[@]}"; do - slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1 + echo "Adding module ${module}" + slapadd -n0 -F /etc/ldap/slapd.d -l "/etc/ldap/modules/${module}.ldif" >/dev/null 2>&1 done fi - for file in `ls /etc/ldap/prepopulate/units/*.ldif`; do - slapadd -F /etc/ldap/slapd.d -l "$file" - done - - for file in `ls /etc/ldap/prepopulate/groups/*.ldif`; do - slapadd -F /etc/ldap/slapd.d -l "$file" - done - - for file in `ls /etc/ldap/prepopulate/users/*.ldif`; do - slapadd -F /etc/ldap/slapd.d -l "$file" - done + # This needs to run in background + # Will prepopulate entries after ldap daemon has started + ./prepopulate.sh & chown -R openldap:openldap /etc/ldap/slapd.d/ /var/lib/ldap/ /var/run/slapd/ else diff --git a/docker/blocks/openldap/notes.md b/docker/blocks/openldap/notes.md index 483266f0d88..8de23d5ccf2 100644 --- a/docker/blocks/openldap/notes.md +++ b/docker/blocks/openldap/notes.md @@ -1,6 +1,6 @@ # Notes on OpenLdap Docker Block -Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database. +Any ldif files added to the prepopulate subdirectory will be automatically imported into the OpenLdap database. The ldif files add three users, `ldapviewer`, `ldapeditor` and `ldapadmin`. Two groups, `admins` and `users`, are added that correspond with the group mappings in the default conf/ldap.toml. `ldapadmin` is a member of `admins` and `ldapeditor` is a member of `users`. @@ -22,3 +22,27 @@ enabled = true config_file = conf/ldap.toml ; allow_sign_up = true ``` + +Test groups & users + +admins + ldap-admin + ldap-torkel + ldap-daniel +backend + ldap-carl + ldap-torkel + ldap-leo +frontend + ldap-torkel + ldap-tobias + ldap-daniel +editors + ldap-editors + + +no groups + ldap-viewer + + + diff --git a/docker/blocks/openldap/prepopulate.sh b/docker/blocks/openldap/prepopulate.sh new file mode 100755 index 00000000000..aa11f8aba4f --- /dev/null +++ b/docker/blocks/openldap/prepopulate.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +echo "Pre-populating ldap entries, first waiting for ldap to start" + +sleep 3 + +adminUserDn="cn=admin,dc=grafana,dc=org" +adminPassword="grafana" + +for file in `ls /etc/ldap/prepopulate/*.ldif`; do + ldapadd -x -D $adminUserDn -w $adminPassword -f "$file" +done + + diff --git a/docker/blocks/openldap/prepopulate/1_units.ldif b/docker/blocks/openldap/prepopulate/1_units.ldif new file mode 100644 index 00000000000..22e06303688 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/1_units.ldif @@ -0,0 +1,9 @@ +dn: ou=groups,dc=grafana,dc=org +ou: Groups +objectclass: top +objectclass: organizationalUnit + +dn: ou=users,dc=grafana,dc=org +ou: Users +objectclass: top +objectclass: organizationalUnit diff --git a/docker/blocks/openldap/prepopulate/2_users.ldif b/docker/blocks/openldap/prepopulate/2_users.ldif new file mode 100644 index 00000000000..52e74b1e4b1 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/2_users.ldif @@ -0,0 +1,80 @@ +# ldap-admin +dn: cn=ldap-admin,ou=users,dc=grafana,dc=org +mail: ldap-admin@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-admin +cn: ldap-admin + +dn: cn=ldap-editor,ou=users,dc=grafana,dc=org +mail: ldap-editor@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-editor +cn: ldap-editor + +dn: cn=ldap-viewer,ou=users,dc=grafana,dc=org +mail: ldap-viewer@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-viewer +cn: ldap-viewer + +dn: cn=ldap-carl,ou=users,dc=grafana,dc=org +mail: ldap-carl@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-carl +cn: ldap-carl + +dn: cn=ldap-daniel,ou=users,dc=grafana,dc=org +mail: ldap-daniel@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-daniel +cn: ldap-daniel + +dn: cn=ldap-leo,ou=users,dc=grafana,dc=org +mail: ldap-leo@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-leo +cn: ldap-leo + +dn: cn=ldap-tobias,ou=users,dc=grafana,dc=org +mail: ldap-tobias@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-tobias +cn: ldap-tobias + +dn: cn=ldap-torkel,ou=users,dc=grafana,dc=org +mail: ldap-torkel@grafana.com +userPassword: grafana +objectClass: person +objectClass: top +objectClass: inetOrgPerson +objectClass: organizationalPerson +sn: ldap-torkel +cn: ldap-torkel diff --git a/docker/blocks/openldap/prepopulate/3_groups.ldif b/docker/blocks/openldap/prepopulate/3_groups.ldif new file mode 100644 index 00000000000..8638a089cc8 --- /dev/null +++ b/docker/blocks/openldap/prepopulate/3_groups.ldif @@ -0,0 +1,25 @@ +dn: cn=admins,ou=groups,dc=grafana,dc=org +cn: admins +objectClass: groupOfNames +objectClass: top +member: cn=ldap-admin,ou=users,dc=grafana,dc=org +member: cn=ldap-torkel,ou=users,dc=grafana,dc=org + +dn: cn=editors,ou=groups,dc=grafana,dc=org +cn: editors +objectClass: groupOfNames +member: cn=ldap-editor,ou=users,dc=grafana,dc=org + +dn: cn=backend,ou=groups,dc=grafana,dc=org +cn: backend +objectClass: groupOfNames +member: cn=ldap-carl,ou=users,dc=grafana,dc=org +member: cn=ldap-leo,ou=users,dc=grafana,dc=org +member: cn=ldap-torkel,ou=users,dc=grafana,dc=org + +dn: cn=frontend,ou=groups,dc=grafana,dc=org +cn: frontend +objectClass: groupOfNames +member: cn=ldap-torkel,ou=users,dc=grafana,dc=org +member: cn=ldap-daniel,ou=users,dc=grafana,dc=org +member: cn=ldap-leo,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/admins.ldif b/docker/blocks/openldap/prepopulate/groups/admins.ldif deleted file mode 100644 index 50d3a0ea4a8..00000000000 --- a/docker/blocks/openldap/prepopulate/groups/admins.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=admins,ou=groups,dc=grafana,dc=org -cn: admins -objectClass: groupOfNames -objectClass: top -member: cn=ldap-admin,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/backend.ldif b/docker/blocks/openldap/prepopulate/groups/backend.ldif deleted file mode 100644 index 09a661adea0..00000000000 --- a/docker/blocks/openldap/prepopulate/groups/backend.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=backend,ou=groups,dc=grafana,dc=org -cn: backend -objectClass: groupOfNames -objectClass: top -member: cn=ldap-editor,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/editor.ldif b/docker/blocks/openldap/prepopulate/groups/editor.ldif deleted file mode 100644 index 331ecc94141..00000000000 --- a/docker/blocks/openldap/prepopulate/groups/editor.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=editors,ou=groups,dc=grafana,dc=org -cn: editors -objectClass: groupOfNames -objectClass: top -member: cn=ldap-editor,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/groups/frontend.ldif b/docker/blocks/openldap/prepopulate/groups/frontend.ldif deleted file mode 100644 index c410b96c7ad..00000000000 --- a/docker/blocks/openldap/prepopulate/groups/frontend.ldif +++ /dev/null @@ -1,5 +0,0 @@ -dn: cn=frontend,ou=groups,dc=grafana,dc=org -cn: frontend -objectClass: groupOfNames -objectClass: top -member: cn=ldap-frontend-1,ou=users,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/units/groups.ldif b/docker/blocks/openldap/prepopulate/units/groups.ldif deleted file mode 100644 index 64e21ad744f..00000000000 --- a/docker/blocks/openldap/prepopulate/units/groups.ldif +++ /dev/null @@ -1,3 +0,0 @@ -dn: ou=groups,dc=grafana,dc=org -objectclass: top -objectclass: organizationalUnit diff --git a/docker/blocks/openldap/prepopulate/units/users.ldif b/docker/blocks/openldap/prepopulate/units/users.ldif deleted file mode 100644 index 76fc50dd299..00000000000 --- a/docker/blocks/openldap/prepopulate/units/users.ldif +++ /dev/null @@ -1,3 +0,0 @@ -dn: ou=users,dc=grafana,dc=org -objectclass: top -objectclass: organizationalUnit diff --git a/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif b/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif deleted file mode 100644 index 1704a15c3db..00000000000 --- a/docker/blocks/openldap/prepopulate/users/ldap-admin.ldif +++ /dev/null @@ -1,11 +0,0 @@ -dn: cn=ldap-admin,ou=users,dc=grafana,dc=org -mail: ldap-admin@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldap-admin -cn: ldap-admin -memberOf: cn=admins,ou=groups,dc=grafana,dc=org -memberOf: cn=editors,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif b/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif deleted file mode 100644 index d0de99f8c16..00000000000 --- a/docker/blocks/openldap/prepopulate/users/ldap-editor.ldif +++ /dev/null @@ -1,10 +0,0 @@ -dn: cn=ldap-editor,ou=users,dc=grafana,dc=org -mail: ldap-editor@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldap-editor -cn: ldap-editor -memberOf: cn=editors,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif b/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif deleted file mode 100644 index f5ebe0b41c4..00000000000 --- a/docker/blocks/openldap/prepopulate/users/ldap-frontend-1.ldif +++ /dev/null @@ -1,10 +0,0 @@ -dn: cn=ldap-frontend-1,ou=users,dc=grafana,dc=org -mail: ldap-frontend-1@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldap-frontend-1 -cn: ldap-frontend-1 -memberOf: cn=frontend,ou=groups,dc=grafana,dc=org diff --git a/docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif b/docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif deleted file mode 100644 index 07066355a13..00000000000 --- a/docker/blocks/openldap/prepopulate/users/ldap-viewer.ldif +++ /dev/null @@ -1,9 +0,0 @@ -dn: cn=ldap-viewer,ou=users,dc=grafana,dc=org -mail: ldap-viewer@grafana.com -userPassword: grafana -objectClass: person -objectClass: top -objectClass: inetOrgPerson -objectClass: organizationalPerson -sn: ldap-viewer -cn: ldap-viewer diff --git a/pkg/login/ext_user.go b/pkg/login/ext_user.go index d2f1aa1ff52..d6eaf9a975e 100644 --- a/pkg/login/ext_user.go +++ b/pkg/login/ext_user.go @@ -21,6 +21,7 @@ func UpsertUser(cmd *m.UpsertUserCommand) error { Email: extUser.Email, Login: extUser.Login, } + err := bus.Dispatch(userQuery) if err != m.ErrUserNotFound && err != nil { return err @@ -90,6 +91,7 @@ func createUser(extUser *m.ExternalUserInfo) (*m.User, error) { Name: extUser.Name, SkipOrgSetup: len(extUser.OrgRoles) > 0, } + if err := bus.Dispatch(cmd); err != nil { return nil, err } From 27aa1cd568c8ffdb748fcfdd311fa836f93bc9f2 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:41:34 +0200 Subject: [PATCH 61/80] changelog: add notes about closing #12362 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3ba9076774..3dd520f8d76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * **Api**: Delete nonexistent datasource should return 404 [#12313](https://github.com/grafana/grafana/issues/12313), thx [@AustinWinstanley](https://github.com/AustinWinstanley) * **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248) * **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) # 5.2.1 (2018-06-29) From 60792d57ddb256b266a7c89f37b390ba4c6fceeb Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:44:21 +0200 Subject: [PATCH 62/80] changelog: add notes about closing #12379 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dd520f8d76..d9150128289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * **Dashboard**: Fix selecting current dashboard from search should not reload dashboard [#12248](https://github.com/grafana/grafana/issues/12248) * **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) +* **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) # 5.2.1 (2018-06-29) From 512e7f8567e0ce07df5882b26c3daf697bb458e0 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:50:21 +0200 Subject: [PATCH 63/80] changelog: add notes about closing #8186 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9150128289..717d9fcaa5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ * **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) * **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) +* **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) # 5.2.1 (2018-06-29) From faf2ab4249ee8859c52fb83db48268c30e05bac9 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:54:09 +0200 Subject: [PATCH 64/80] changelog: add notes about closing #12460 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 717d9fcaa5f..a7a4f6ca3e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * **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) * **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) # 5.2.1 (2018-06-29) From 634c77469d975fe4ef0536b2fc043de9d21342b5 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:58:19 +0200 Subject: [PATCH 65/80] changelog: add notes about closing #11818 [skip ci] --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7a4f6ca3e7..879dc23eaed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) * **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) +- **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.1 (2018-06-29) From 723a894fc5d077b2a11d8685d777e3812bdf77bb Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Tue, 3 Jul 2018 19:59:34 +0200 Subject: [PATCH 66/80] changelog: update [skip ci] --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 879dc23eaed..6ddc9497dbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ * **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) * **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) -- **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.1 (2018-06-29) From d2f31a716f44ce2da7836d2d359a5de8aa8dbb4a Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 4 Jul 2018 12:16:39 +0200 Subject: [PATCH 67/80] remove unnecessary conversions --- pkg/services/alerting/extractor_test.go | 8 ++++---- .../client/search_request_test.go | 18 +++++++++--------- pkg/tsdb/mssql/mssql_test.go | 8 ++++---- pkg/tsdb/mysql/mysql_test.go | 10 +++++----- pkg/tsdb/postgres/postgres_test.go | 8 ++++---- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/pkg/services/alerting/extractor_test.go b/pkg/services/alerting/extractor_test.go index 861e9b9cbfc..c7212e48174 100644 --- a/pkg/services/alerting/extractor_test.go +++ b/pkg/services/alerting/extractor_test.go @@ -50,7 +50,7 @@ func TestAlertRuleExtraction(t *testing.T) { So(err, ShouldBeNil) Convey("Extractor should not modify the original json", func() { - dashJson, err := simplejson.NewJson([]byte(json)) + dashJson, err := simplejson.NewJson(json) So(err, ShouldBeNil) dash := m.NewDashboardFromJson(dashJson) @@ -79,7 +79,7 @@ func TestAlertRuleExtraction(t *testing.T) { Convey("Parsing and validating dashboard containing graphite alerts", func() { - dashJson, err := simplejson.NewJson([]byte(json)) + dashJson, err := simplejson.NewJson(json) So(err, ShouldBeNil) dash := m.NewDashboardFromJson(dashJson) @@ -143,7 +143,7 @@ func TestAlertRuleExtraction(t *testing.T) { panelWithoutId, err := ioutil.ReadFile("./test-data/panels-missing-id.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson([]byte(panelWithoutId)) + dashJson, err := simplejson.NewJson(panelWithoutId) So(err, ShouldBeNil) dash := m.NewDashboardFromJson(dashJson) extractor := NewDashAlertExtractor(dash, 1) @@ -159,7 +159,7 @@ func TestAlertRuleExtraction(t *testing.T) { panelWithIdZero, err := ioutil.ReadFile("./test-data/panel-with-id-0.json") So(err, ShouldBeNil) - dashJson, err := simplejson.NewJson([]byte(panelWithIdZero)) + dashJson, err := simplejson.NewJson(panelWithIdZero) So(err, ShouldBeNil) dash := m.NewDashboardFromJson(dashJson) extractor := NewDashAlertExtractor(dash, 1) diff --git a/pkg/tsdb/elasticsearch/client/search_request_test.go b/pkg/tsdb/elasticsearch/client/search_request_test.go index b026578d64f..862b8058cba 100644 --- a/pkg/tsdb/elasticsearch/client/search_request_test.go +++ b/pkg/tsdb/elasticsearch/client/search_request_test.go @@ -32,7 +32,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) So(json.Get("size").MustInt(500), ShouldEqual, 0) So(json.Get("sort").Interface(), ShouldBeNil) @@ -81,7 +81,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) So(json.Get("size").MustInt(0), ShouldEqual, 200) @@ -124,7 +124,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) scriptFields, err := json.Get("script_fields").Map() @@ -163,7 +163,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) So(json.Get("aggs").MustMap(), ShouldHaveLength, 2) @@ -200,7 +200,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) So(json.Get("aggs").MustMap(), ShouldHaveLength, 1) @@ -251,7 +251,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) topAggOne := json.GetPath("aggs", "1") @@ -300,7 +300,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) topAgg := json.GetPath("aggs", "1") @@ -364,7 +364,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) termsAgg := json.GetPath("aggs", "1") @@ -419,7 +419,7 @@ func TestSearchRequest(t *testing.T) { Convey("When marshal to JSON should generate correct json", func() { body, err := json.Marshal(sr) So(err, ShouldBeNil) - json, err := simplejson.NewJson([]byte(body)) + json, err := simplejson.NewJson(body) So(err, ShouldBeNil) scriptFields, err := json.Get("script_fields").Map() diff --git a/pkg/tsdb/mssql/mssql_test.go b/pkg/tsdb/mssql/mssql_test.go index 2ecd3cd9e96..db04d6d1f02 100644 --- a/pkg/tsdb/mssql/mssql_test.go +++ b/pkg/tsdb/mssql/mssql_test.go @@ -531,7 +531,7 @@ func TestMSSQL(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query using epoch (float32 nullable) as time column and value column (float32 nullable) should return metric with time in milliseconds", func() { @@ -553,7 +553,7 @@ func TestMSSQL(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { @@ -930,7 +930,7 @@ func TestMSSQL(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(int64), ShouldEqual, int64(dt.Unix()*1000)) + So(columns[0].(int64), ShouldEqual, dt.Unix()*1000) }) Convey("When doing an annotation query with a time column in epoch second format (int) should return ms", func() { @@ -960,7 +960,7 @@ func TestMSSQL(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(int64), ShouldEqual, int64(dt.Unix()*1000)) + So(columns[0].(int64), ShouldEqual, dt.Unix()*1000) }) Convey("When doing an annotation query with a time column in epoch millisecond format should return ms", func() { diff --git a/pkg/tsdb/mysql/mysql_test.go b/pkg/tsdb/mysql/mysql_test.go index 22e98ac63ca..850a37617e2 100644 --- a/pkg/tsdb/mysql/mysql_test.go +++ b/pkg/tsdb/mysql/mysql_test.go @@ -132,8 +132,8 @@ func TestMySQL(t *testing.T) { So(column[7].(float64), ShouldEqual, 1.11) So(column[8].(float64), ShouldEqual, 2.22) So(*column[9].(*float32), ShouldEqual, 3.33) - So(column[10].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now()) - So(column[11].(time.Time), ShouldHappenWithin, time.Duration(10*time.Second), time.Now()) + So(column[10].(time.Time), ShouldHappenWithin, 10*time.Second, time.Now()) + So(column[11].(time.Time), ShouldHappenWithin, 10*time.Second, time.Now()) So(column[12].(string), ShouldEqual, "11:11:11") So(column[13].(int64), ShouldEqual, 2018) So(*column[14].(*[]byte), ShouldHaveSameTypeAs, []byte{1}) @@ -578,7 +578,7 @@ func TestMySQL(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query using epoch (float32 nullable) as time column and value column (float32 nullable) should return metric with time in milliseconds", func() { @@ -600,7 +600,7 @@ func TestMySQL(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { @@ -817,7 +817,7 @@ func TestMySQL(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(int64), ShouldEqual, int64(dt.Unix()*1000)) + So(columns[0].(int64), ShouldEqual, dt.Unix()*1000) }) Convey("When doing an annotation query with a time column in epoch millisecond format should return ms", func() { diff --git a/pkg/tsdb/postgres/postgres_test.go b/pkg/tsdb/postgres/postgres_test.go index 18ceecb1080..a3a6d6546df 100644 --- a/pkg/tsdb/postgres/postgres_test.go +++ b/pkg/tsdb/postgres/postgres_test.go @@ -512,7 +512,7 @@ func TestPostgres(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query using epoch (float32 nullable) as time column and value column (float32 nullable) should return metric with time in milliseconds", func() { @@ -534,7 +534,7 @@ func TestPostgres(t *testing.T) { So(queryResult.Error, ShouldBeNil) So(len(queryResult.Series), ShouldEqual, 1) - So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float64(float32(tInitial.Unix())))*1e3) + So(queryResult.Series[0].Points[0][1].Float64, ShouldEqual, float64(float32(tInitial.Unix()))*1e3) }) Convey("When doing a metric query grouping by time and select metric column should return correct series", func() { @@ -721,7 +721,7 @@ func TestPostgres(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(int64), ShouldEqual, int64(dt.Unix()*1000)) + So(columns[0].(int64), ShouldEqual, dt.Unix()*1000) }) Convey("When doing an annotation query with a time column in epoch second format (int) should return ms", func() { @@ -751,7 +751,7 @@ func TestPostgres(t *testing.T) { columns := queryResult.Tables[0].Rows[0] //Should be in milliseconds - So(columns[0].(int64), ShouldEqual, int64(dt.Unix()*1000)) + So(columns[0].(int64), ShouldEqual, dt.Unix()*1000) }) Convey("When doing an annotation query with a time column in epoch millisecond format should return ms", func() { From 01fc6c5d958f98fe952fb7b40f88ced8cf5a28e1 Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 4 Jul 2018 12:23:10 +0200 Subject: [PATCH 68/80] fix json indentation --- docs/sources/http_api/admin.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index 0194c69caac..16b9115e8b9 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -36,11 +36,10 @@ HTTP/1.1 200 Content-Type: application/json { -"DEFAULT": -{ - "app_mode":"production"}, - "analytics": - { + "DEFAULT": { + "app_mode":"production" + }, + "analytics": { "google_analytics_ua_id":"", "reporting_enabled":"false" }, @@ -340,4 +339,4 @@ HTTP/1.1 200 Content-Type: application/json {state: "new state", message: "alerts pause/un paused", "alertsAffected": 100} -``` \ No newline at end of file +``` From 991a4b16637ea8fed191b08649d9f044987ab768 Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 4 Jul 2018 12:26:30 +0200 Subject: [PATCH 69/80] update stats admin doc --- docs/sources/http_api/admin.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index 16b9115e8b9..2e03611b125 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -194,15 +194,16 @@ HTTP/1.1 200 Content-Type: application/json { - "user_count":2, - "org_count":1, - "dashboard_count":4, - "db_snapshot_count":2, - "db_tag_count":6, - "data_source_count":1, - "playlist_count":1, - "starred_db_count":2, - "grafana_admin_count":2 + "users":2, + "orgs":1, + "dashboards":4, + "snapshots":2, + "tags":6, + "datasources":1, + "playlists":1, + "stars":2, + "alerts":2, + "activeUsers":1 } ``` From 86a574cc5031f78156879d1cac9315a71bb123a0 Mon Sep 17 00:00:00 2001 From: Augustin Husson Date: Wed, 4 Jul 2018 12:30:23 +0200 Subject: [PATCH 70/80] refix the settings indentation --- docs/sources/http_api/admin.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sources/http_api/admin.md b/docs/sources/http_api/admin.md index 2e03611b125..2d4be21bb78 100644 --- a/docs/sources/http_api/admin.md +++ b/docs/sources/http_api/admin.md @@ -37,7 +37,7 @@ Content-Type: application/json { "DEFAULT": { - "app_mode":"production" + "app_mode":"production" }, "analytics": { "google_analytics_ua_id":"", From a8970a4de95eed9cdb28267fee884207b4649703 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 4 Jul 2018 13:09:42 +0200 Subject: [PATCH 71/80] run enterprise build only on master for now --- .circleci/config.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b2f436adee..f351040fe2f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,9 @@ aliases: - &filter-not-release tags: ignore: /^v[0-9]+(\.[0-9]+){2}(-.+|[^-.]*)$/ + - &filter-only-master + branches: + only: master version: 2 @@ -242,7 +245,7 @@ workflows: - build-all: filters: *filter-not-release - build-enterprise: - filters: *filter-not-release + filters: *filter-only-master - codespell: filters: *filter-not-release - gometalinter: @@ -277,9 +280,7 @@ workflows: - mysql-integration-test - postgres-integration-test - build-enterprise - filters: - branches: - only: master + filters: *filter-only-master release: jobs: From bd417bedb238650b10a7bd62e9abc2e74c0d1a90 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Tue, 3 Jul 2018 10:12:07 +0200 Subject: [PATCH 72/80] Start elastic ds test conversion --- ...datasource_specs.ts => datasource.jest.ts} | 116 ++++++++++-------- 1 file changed, 65 insertions(+), 51 deletions(-) rename public/app/plugins/datasource/elasticsearch/specs/{datasource_specs.ts => datasource.jest.ts} (75%) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts similarity index 75% rename from public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts rename to public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index 558bccf3d0f..9a3d57f1d4f 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource_specs.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -2,31 +2,45 @@ import _ from 'lodash'; import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; import moment from 'moment'; import angular from 'angular'; -import helpers from 'test/specs/helpers'; import { ElasticDatasource } from '../datasource'; +import { TimeSrv } from 'app/features/dashboard/time_srv'; +import $q from 'q'; describe('ElasticDatasource', function() { - var ctx = new helpers.ServiceTestContext(); + //var ctx = new helpers.ServiceTestContext(); + let backendSrv = { + datasourceRequest: jest.fn() + }; - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv'])); + let $rootScope = { + $on: jest.fn(), + appEvent: jest.fn(), + }; - beforeEach( - angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - ctx.$q = $q; - ctx.$httpBackend = $httpBackend; - ctx.$rootScope = $rootScope; - ctx.$injector = $injector; - $httpBackend.when('GET', /\.html$/).respond(''); - }) - ); + let timeSrv = new TimeSrv($rootScope,jest.fn(),{},{},{}); + + let ctx = { + $rootScope, + backendSrv, + $q + }; + // beforeEach(angularMocks.module('grafana.core')); + // beforeEach(angularMocks.module('grafana.services')); + // beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv'])); + + // beforeEach( + // angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { + // ctx.$q = $q; + // ctx.$httpBackend = $httpBackend; + // ctx.$rootScope = $rootScope; + // ctx.$injector = $injector; + // $httpBackend.when('GET', /\.html$/).respond(''); + // }) + // ); function createDatasource(instanceSettings) { instanceSettings.jsonData = instanceSettings.jsonData || {}; - ctx.ds = ctx.$injector.instantiate(ElasticDatasource, { - instanceSettings: instanceSettings, - }); + ctx.ds = new ElasticDatasource(instanceSettings, {}, backendSrv, { replace: jest.fn() }, timeSrv); } describe('When testing datasource with index pattern', function() { @@ -40,13 +54,13 @@ describe('ElasticDatasource', function() { it('should translate index pattern to current day', function() { var requestOptions; - ctx.backendSrv.datasourceRequest = function(options) { + ctx.backendSrv.datasourceRequest = jest.fn((options) => { requestOptions = options; - return ctx.$q.when({ data: {} }); - }; + return Promise.resolve({ data: {} }); + }); ctx.ds.testDatasource(); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); var today = moment.utc().format('YYYY.MM.DD'); expect(requestOptions.url).to.be('http://es.com/asd-' + today + '/_mapping'); @@ -63,10 +77,10 @@ describe('ElasticDatasource', function() { jsonData: { interval: 'Daily', esVersion: '2' }, }); - ctx.backendSrv.datasourceRequest = function(options) { + ctx.backendSrv.datasourceRequest = jest.fn((options) => { requestOptions = options; - return ctx.$q.when({ data: { responses: [] } }); - }; + return Promise.resolve({ data: { responses: [] } }); + }); ctx.ds.query({ range: { @@ -82,19 +96,19 @@ describe('ElasticDatasource', function() { ], }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); it('should translate index pattern to current day', function() { - expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']); + expect(header.index).toEqual(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']); }); it('should json escape lucene query', function() { var body = angular.fromJson(parts[1]); - expect(body.query.bool.filter[1].query_string.query).to.be('escape\\:test'); + expect(body.query.bool.filter[1].query_string.query).toBe('escape\\:test'); }); }); @@ -108,10 +122,10 @@ describe('ElasticDatasource', function() { jsonData: { esVersion: '2' }, }); - ctx.backendSrv.datasourceRequest = function(options) { + ctx.backendSrv.datasourceRequest = jest.fn((options) => { requestOptions = options; - return ctx.$q.when({ data: { responses: [] } }); - }; + return Promise.resolve({ data: { responses: [] } }); + }); ctx.ds.query({ range: { @@ -127,18 +141,18 @@ describe('ElasticDatasource', function() { ], }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); it('should set search type to query_then_fetch', function() { - expect(header.search_type).to.eql('query_then_fetch'); + expect(header.search_type).toEqual('query_then_fetch'); }); it('should set size', function() { var body = angular.fromJson(parts[1]); - expect(body.size).to.be(500); + expect(body.size).toBe(500); }); }); @@ -146,8 +160,8 @@ describe('ElasticDatasource', function() { beforeEach(function() { createDatasource({ url: 'http://es.com', index: 'metricbeat' }); - ctx.backendSrv.datasourceRequest = function(options) { - return ctx.$q.when({ + ctx.backendSrv.datasourceRequest = jest.fn((options) => { + return Promise.resolve({ data: { metricbeat: { mappings: { @@ -190,7 +204,7 @@ describe('ElasticDatasource', function() { }, }, }); - }; + }) }); it('should return nested fields', function() { @@ -201,7 +215,7 @@ describe('ElasticDatasource', function() { }) .then(fieldObjects => { var fields = _.map(fieldObjects, 'text'); - expect(fields).to.eql([ + expect(fields).toEqual([ '@timestamp', 'beat.name.raw', 'beat.name', @@ -212,7 +226,7 @@ describe('ElasticDatasource', function() { 'system.process.name', ]); }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); }); it('should return fields related to query type', function() { @@ -224,7 +238,7 @@ describe('ElasticDatasource', function() { }) .then(fieldObjects => { var fields = _.map(fieldObjects, 'text'); - expect(fields).to.eql(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']); + expect(fields).toEqual(['system.cpu.system', 'system.cpu.user', 'system.process.cpu.total']); }); ctx.ds @@ -235,10 +249,10 @@ describe('ElasticDatasource', function() { }) .then(fieldObjects => { var fields = _.map(fieldObjects, 'text'); - expect(fields).to.eql(['@timestamp']); + expect(fields).toEqual(['@timestamp']); }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); }); }); @@ -271,18 +285,18 @@ describe('ElasticDatasource', function() { ], }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); it('should not set search type to count', function() { - expect(header.search_type).to.not.eql('count'); + expect(header.search_type).not.toEqual('count'); }); it('should set size to 0', function() { var body = angular.fromJson(parts[1]); - expect(body.size).to.be(0); + expect(body.size).toBe(0); }); }); @@ -324,7 +338,7 @@ describe('ElasticDatasource', function() { results = res; }); - ctx.$rootScope.$apply(); + // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); @@ -332,24 +346,24 @@ describe('ElasticDatasource', function() { }); it('should get results', function() { - expect(results.length).to.eql(2); + expect(results.length).toEqual(2); }); it('should use key or key_as_string', function() { - expect(results[0].text).to.eql('test'); - expect(results[1].text).to.eql('test2_as_string'); + expect(results[0].text).toEqual('test'); + expect(results[1].text).toEqual('test2_as_string'); }); it('should not set search type to count', function() { - expect(header.search_type).to.not.eql('count'); + expect(header.search_type).not.toEqual('count'); }); it('should set size to 0', function() { - expect(body.size).to.be(0); + expect(body.size).toBe(0); }); it('should not set terms aggregation size to 0', function() { - expect(body['aggs']['1']['terms'].size).to.not.be(0); + expect(body['aggs']['1']['terms'].size).not.toBe(0); }); }); }); From 12e76ad1972fe04fb97e870bab50524a3dcf014b Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Tue, 3 Jul 2018 10:47:50 +0200 Subject: [PATCH 73/80] Remove logs and comments --- .../elasticsearch/specs/datasource.jest.ts | 80 +++++++------------ 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index 9a3d57f1d4f..def60ecdcb3 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -1,15 +1,14 @@ import _ from 'lodash'; -import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common'; import moment from 'moment'; import angular from 'angular'; import { ElasticDatasource } from '../datasource'; import { TimeSrv } from 'app/features/dashboard/time_srv'; +import { TemplateSrv } from 'app/features/templating/template_srv'; import $q from 'q'; describe('ElasticDatasource', function() { - //var ctx = new helpers.ServiceTestContext(); let backendSrv = { - datasourceRequest: jest.fn() + datasourceRequest: jest.fn(), }; let $rootScope = { @@ -17,30 +16,21 @@ describe('ElasticDatasource', function() { appEvent: jest.fn(), }; - let timeSrv = new TimeSrv($rootScope,jest.fn(),{},{},{}); + let templateSrv = new TemplateSrv(); + + templateSrv.variables = []; + + let timeSrv = new TimeSrv($rootScope, jest.fn(), {}, {}, {}); let ctx = { $rootScope, backendSrv, - $q + $q, }; - // beforeEach(angularMocks.module('grafana.core')); - // beforeEach(angularMocks.module('grafana.services')); - // beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv'])); - - // beforeEach( - // angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { - // ctx.$q = $q; - // ctx.$httpBackend = $httpBackend; - // ctx.$rootScope = $rootScope; - // ctx.$injector = $injector; - // $httpBackend.when('GET', /\.html$/).respond(''); - // }) - // ); function createDatasource(instanceSettings) { instanceSettings.jsonData = instanceSettings.jsonData || {}; - ctx.ds = new ElasticDatasource(instanceSettings, {}, backendSrv, { replace: jest.fn() }, timeSrv); + ctx.ds = new ElasticDatasource(instanceSettings, {}, backendSrv, templateSrv, timeSrv); } describe('When testing datasource with index pattern', function() { @@ -54,35 +44,34 @@ describe('ElasticDatasource', function() { it('should translate index pattern to current day', function() { var requestOptions; - ctx.backendSrv.datasourceRequest = jest.fn((options) => { + ctx.backendSrv.datasourceRequest = jest.fn(options => { requestOptions = options; return Promise.resolve({ data: {} }); }); ctx.ds.testDatasource(); - // ctx.$rootScope.$apply(); var today = moment.utc().format('YYYY.MM.DD'); - expect(requestOptions.url).to.be('http://es.com/asd-' + today + '/_mapping'); + expect(requestOptions.url).toBe('http://es.com/asd-' + today + '/_mapping'); }); }); describe('When issuing metric query with interval pattern', function() { var requestOptions, parts, header; - beforeEach(function() { + beforeEach(async () => { createDatasource({ url: 'http://es.com', index: '[asd-]YYYY.MM.DD', jsonData: { interval: 'Daily', esVersion: '2' }, }); - ctx.backendSrv.datasourceRequest = jest.fn((options) => { + ctx.backendSrv.datasourceRequest = jest.fn(options => { requestOptions = options; return Promise.resolve({ data: { responses: [] } }); }); - ctx.ds.query({ + await ctx.ds.query({ range: { from: moment.utc([2015, 4, 30, 10]), to: moment.utc([2015, 5, 1, 10]), @@ -96,8 +85,6 @@ describe('ElasticDatasource', function() { ], }); - // ctx.$rootScope.$apply(); - parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); @@ -122,7 +109,7 @@ describe('ElasticDatasource', function() { jsonData: { esVersion: '2' }, }); - ctx.backendSrv.datasourceRequest = jest.fn((options) => { + ctx.backendSrv.datasourceRequest = jest.fn(options => { requestOptions = options; return Promise.resolve({ data: { responses: [] } }); }); @@ -141,7 +128,6 @@ describe('ElasticDatasource', function() { ], }); - // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); @@ -157,10 +143,10 @@ describe('ElasticDatasource', function() { }); describe('When getting fields', function() { - beforeEach(function() { + beforeEach(() => { createDatasource({ url: 'http://es.com', index: 'metricbeat' }); - ctx.backendSrv.datasourceRequest = jest.fn((options) => { + ctx.backendSrv.datasourceRequest = jest.fn(options => { return Promise.resolve({ data: { metricbeat: { @@ -204,7 +190,7 @@ describe('ElasticDatasource', function() { }, }, }); - }) + }); }); it('should return nested fields', function() { @@ -226,7 +212,6 @@ describe('ElasticDatasource', function() { 'system.process.name', ]); }); - // ctx.$rootScope.$apply(); }); it('should return fields related to query type', function() { @@ -251,8 +236,6 @@ describe('ElasticDatasource', function() { var fields = _.map(fieldObjects, 'text'); expect(fields).toEqual(['@timestamp']); }); - - // ctx.$rootScope.$apply(); }); }); @@ -266,10 +249,10 @@ describe('ElasticDatasource', function() { jsonData: { esVersion: '5' }, }); - ctx.backendSrv.datasourceRequest = function(options) { + ctx.backendSrv.datasourceRequest = jest.fn(options => { requestOptions = options; - return ctx.$q.when({ data: { responses: [] } }); - }; + return Promise.resolve({ data: { responses: [] } }); + }); ctx.ds.query({ range: { @@ -285,7 +268,6 @@ describe('ElasticDatasource', function() { ], }); - // ctx.$rootScope.$apply(); parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); }); @@ -303,16 +285,16 @@ describe('ElasticDatasource', function() { describe('When issuing metricFind query on es5.x', function() { var requestOptions, parts, header, body, results; - beforeEach(function() { + beforeEach(() => { createDatasource({ url: 'http://es.com', index: 'test', jsonData: { esVersion: '5' }, }); - ctx.backendSrv.datasourceRequest = function(options) { + ctx.backendSrv.datasourceRequest = jest.fn(options => { requestOptions = options; - return ctx.$q.when({ + return Promise.resolve({ data: { responses: [ { @@ -332,37 +314,35 @@ describe('ElasticDatasource', function() { ], }, }); - }; + }); ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}').then(res => { results = res; }); - // ctx.$rootScope.$apply(); - parts = requestOptions.data.split('\n'); header = angular.fromJson(parts[0]); body = angular.fromJson(parts[1]); }); - it('should get results', function() { + it('should get results', () => { expect(results.length).toEqual(2); }); - it('should use key or key_as_string', function() { + it('should use key or key_as_string', () => { expect(results[0].text).toEqual('test'); expect(results[1].text).toEqual('test2_as_string'); }); - it('should not set search type to count', function() { + it('should not set search type to count', () => { expect(header.search_type).not.toEqual('count'); }); - it('should set size to 0', function() { + it('should set size to 0', () => { expect(body.size).toBe(0); }); - it('should not set terms aggregation size to 0', function() { + it('should not set terms aggregation size to 0', () => { expect(body['aggs']['1']['terms'].size).not.toBe(0); }); }); From 69c85e0d7efc4bb2096b9855c75274240087947a Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Tue, 3 Jul 2018 10:55:48 +0200 Subject: [PATCH 74/80] Remove async --- .../plugins/datasource/elasticsearch/specs/datasource.jest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index def60ecdcb3..32b5a4dbbd5 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -59,7 +59,7 @@ describe('ElasticDatasource', function() { describe('When issuing metric query with interval pattern', function() { var requestOptions, parts, header; - beforeEach(async () => { + beforeEach(() => { createDatasource({ url: 'http://es.com', index: '[asd-]YYYY.MM.DD', @@ -71,7 +71,7 @@ describe('ElasticDatasource', function() { return Promise.resolve({ data: { responses: [] } }); }); - await ctx.ds.query({ + ctx.ds.query({ range: { from: moment.utc([2015, 4, 30, 10]), to: moment.utc([2015, 5, 1, 10]), From 80b2f5c7567cdef4c6cb288001ceb44f164d48be Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Tue, 3 Jul 2018 11:55:23 +0200 Subject: [PATCH 75/80] Add Jest stubs --- .../elasticsearch/specs/datasource.jest.ts | 10 ++-- public/test/specs/helpers.ts | 51 ++++++++++++++++--- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index 32b5a4dbbd5..bfd55544d7b 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -2,8 +2,8 @@ import _ from 'lodash'; import moment from 'moment'; import angular from 'angular'; import { ElasticDatasource } from '../datasource'; -import { TimeSrv } from 'app/features/dashboard/time_srv'; -import { TemplateSrv } from 'app/features/templating/template_srv'; +import { jestTimeSrvStub } from 'test/specs/helpers'; +import { jestTemplateSrvStub } from 'test/specs/helpers'; import $q from 'q'; describe('ElasticDatasource', function() { @@ -16,11 +16,9 @@ describe('ElasticDatasource', function() { appEvent: jest.fn(), }; - let templateSrv = new TemplateSrv(); + let templateSrv = new jestTemplateSrvStub(); - templateSrv.variables = []; - - let timeSrv = new TimeSrv($rootScope, jest.fn(), {}, {}, {}); + let timeSrv = new jestTimeSrvStub(); let ctx = { $rootScope, diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index dd8bd39846e..d98e79ff06b 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -195,13 +195,52 @@ export function TemplateSrvStub() { }; } +export function jestTemplateSrvStub() { + this.variables = []; + this.templateSettings = { interpolate: /\[\[([\s\S]+?)\]\]/g }; + this.data = {}; + this.replace = jest.fn(text => _.template(text, this.templateSettings)(this.data)); + this.init = jest.fn(); + this.getAdhocFilters = jest.fn(() => []); + this.fillVariableValuesForUrl = jest.fn(); + this.updateTemplateData = jest.fn(); + this.variableExists = jest.fn(() => false); + this.variableInitialized = jest.fn(); + this.highlightVariablesAsHtml = jest.fn(str => str); + this.setGrafanaVariable = jest.fn((name, value) => { + this.data[name] = value; + }); +} + +export function jestTimeSrvStub() { + this.init = jest.fn(); + this.time = { from: 'now-1h', to: 'now' }; + this.timeRange = jest.fn(parse => { + if (parse === false) { + return this.time; + } + return { + from: dateMath.parse(this.time.from, false), + to: dateMath.parse(this.time.to, true), + }; + }); + + this.replace = jest.fn(target => target); + + this.setTime = jest.fn(time => { + this.time = time; + }); +} + var allDeps = { - ContextSrvStub: ContextSrvStub, - TemplateSrvStub: TemplateSrvStub, - TimeSrvStub: TimeSrvStub, - ControllerTestContext: ControllerTestContext, - ServiceTestContext: ServiceTestContext, - DashboardViewStateStub: DashboardViewStateStub, + ContextSrvStub, + TemplateSrvStub, + TimeSrvStub, + ControllerTestContext, + ServiceTestContext, + DashboardViewStateStub, + jestTimeSrvStub, + jestTemplateSrvStub, }; // for legacy From 8f1bcd91178375c007688e022677611cd5d4c9f7 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Tue, 3 Jul 2018 13:33:30 +0200 Subject: [PATCH 76/80] Remove q and stub --- .../datasource/elasticsearch/specs/datasource.jest.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index bfd55544d7b..b6d19d003da 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -2,9 +2,7 @@ import _ from 'lodash'; import moment from 'moment'; import angular from 'angular'; import { ElasticDatasource } from '../datasource'; -import { jestTimeSrvStub } from 'test/specs/helpers'; -import { jestTemplateSrvStub } from 'test/specs/helpers'; -import $q from 'q'; +import { jestTimeSrvStub, TemplateSrvStub } from 'test/specs/helpers'; describe('ElasticDatasource', function() { let backendSrv = { @@ -16,14 +14,13 @@ describe('ElasticDatasource', function() { appEvent: jest.fn(), }; - let templateSrv = new jestTemplateSrvStub(); + let templateSrv = new TemplateSrvStub(); let timeSrv = new jestTimeSrvStub(); let ctx = { $rootScope, backendSrv, - $q, }; function createDatasource(instanceSettings) { From 12d158f391d0256802ff5be4bb0fb1f9335f4b66 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Wed, 4 Jul 2018 10:43:36 +0200 Subject: [PATCH 77/80] Add mocks in test file --- .../elasticsearch/specs/datasource.jest.ts | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index b6d19d003da..e9f7a61a572 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -2,7 +2,8 @@ import _ from 'lodash'; import moment from 'moment'; import angular from 'angular'; import { ElasticDatasource } from '../datasource'; -import { jestTimeSrvStub, TemplateSrvStub } from 'test/specs/helpers'; + +import * as dateMath from 'app/core/utils/datemath'; describe('ElasticDatasource', function() { let backendSrv = { @@ -14,9 +15,23 @@ describe('ElasticDatasource', function() { appEvent: jest.fn(), }; - let templateSrv = new TemplateSrvStub(); + let templateSrv = { + replace: jest.fn(text => text), + getAdhocFilters: jest.fn(() => []), + }; - let timeSrv = new jestTimeSrvStub(); + let timeSrv = { + time: { from: 'now-1h', to: 'now' }, + timeRange: jest.fn(parse => { + return { + from: dateMath.parse(this.time.from, false), + to: dateMath.parse(this.time.to, true), + }; + }), + setTime: jest.fn(time => { + this.time = time; + }), + }; let ctx = { $rootScope, From d3c213973c7d6eeb0eb2affbf2178bad2713c641 Mon Sep 17 00:00:00 2001 From: Tobias Skarhed Date: Wed, 4 Jul 2018 11:23:12 +0200 Subject: [PATCH 78/80] Basic cleanup --- .../elasticsearch/specs/datasource.jest.ts | 2 +- public/test/specs/helpers.ts | 39 ------------------- 2 files changed, 1 insertion(+), 40 deletions(-) diff --git a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts index e9f7a61a572..36e7a63a005 100644 --- a/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts +++ b/public/app/plugins/datasource/elasticsearch/specs/datasource.jest.ts @@ -22,7 +22,7 @@ describe('ElasticDatasource', function() { let timeSrv = { time: { from: 'now-1h', to: 'now' }, - timeRange: jest.fn(parse => { + timeRange: jest.fn(() => { return { from: dateMath.parse(this.time.from, false), to: dateMath.parse(this.time.to, true), diff --git a/public/test/specs/helpers.ts b/public/test/specs/helpers.ts index d98e79ff06b..677419f3f75 100644 --- a/public/test/specs/helpers.ts +++ b/public/test/specs/helpers.ts @@ -195,43 +195,6 @@ export function TemplateSrvStub() { }; } -export function jestTemplateSrvStub() { - this.variables = []; - this.templateSettings = { interpolate: /\[\[([\s\S]+?)\]\]/g }; - this.data = {}; - this.replace = jest.fn(text => _.template(text, this.templateSettings)(this.data)); - this.init = jest.fn(); - this.getAdhocFilters = jest.fn(() => []); - this.fillVariableValuesForUrl = jest.fn(); - this.updateTemplateData = jest.fn(); - this.variableExists = jest.fn(() => false); - this.variableInitialized = jest.fn(); - this.highlightVariablesAsHtml = jest.fn(str => str); - this.setGrafanaVariable = jest.fn((name, value) => { - this.data[name] = value; - }); -} - -export function jestTimeSrvStub() { - this.init = jest.fn(); - this.time = { from: 'now-1h', to: 'now' }; - this.timeRange = jest.fn(parse => { - if (parse === false) { - return this.time; - } - return { - from: dateMath.parse(this.time.from, false), - to: dateMath.parse(this.time.to, true), - }; - }); - - this.replace = jest.fn(target => target); - - this.setTime = jest.fn(time => { - this.time = time; - }); -} - var allDeps = { ContextSrvStub, TemplateSrvStub, @@ -239,8 +202,6 @@ var allDeps = { ControllerTestContext, ServiceTestContext, DashboardViewStateStub, - jestTimeSrvStub, - jestTemplateSrvStub, }; // for legacy From 240cf63ba6ad8c07a137d1c01f5034ee90422379 Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Wed, 4 Jul 2018 20:07:43 +0200 Subject: [PATCH 79/80] changelog: add notes about closing #11618 #11619 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ddc9497dbc..0177546b643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,10 @@ * **Prometheus**: Fix graph panel bar width issue in aligned prometheus queries [#12379](https://github.com/grafana/grafana/issues/12379) * **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) +* **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) + # 5.2.1 (2018-06-29) ### Minor From 50a522d418406955d82d0e08c21d28a4f0069fea Mon Sep 17 00:00:00 2001 From: Marcus Efraimsson Date: Thu, 5 Jul 2018 13:01:10 +0200 Subject: [PATCH 80/80] fix caret for help button is ds http settings --- public/app/features/plugins/partials/ds_http_settings.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/app/features/plugins/partials/ds_http_settings.html b/public/app/features/plugins/partials/ds_http_settings.html index 3af185d862c..6d014af567c 100644 --- a/public/app/features/plugins/partials/ds_http_settings.html +++ b/public/app/features/plugins/partials/ds_http_settings.html @@ -32,8 +32,8 @@