Compare commits
57 Commits
wb/pkg-plu
...
v10.3.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
252761264e | ||
|
|
3f111809aa | ||
|
|
f0778dd147 | ||
|
|
069704b39a | ||
|
|
5c10afaf63 | ||
|
|
c0049dcbd6 | ||
|
|
be850d677a | ||
|
|
c03a7b9806 | ||
|
|
cf586a09ae | ||
|
|
18e510b027 | ||
|
|
5ff228aeb6 | ||
|
|
557b86804e | ||
|
|
3d2005cd9b | ||
|
|
7bdc53053b | ||
|
|
3377dc411c | ||
|
|
534b23eb9d | ||
|
|
e6d2e4d8fe | ||
|
|
537d7b46cf | ||
|
|
330d3adae2 | ||
|
|
3417e99a19 | ||
|
|
f2f9ece03a | ||
|
|
8abe04662b | ||
|
|
8ce96414b0 | ||
|
|
5fcfade7f0 | ||
|
|
4e69ce0830 | ||
|
|
1c8d8b35c2 | ||
|
|
f4cf504009 | ||
|
|
b6ce5e5885 | ||
|
|
403d00b0e4 | ||
|
|
509313715a | ||
|
|
3e6b3a69b4 | ||
|
|
34709d74cc | ||
|
|
ea8a2c4083 | ||
|
|
28cf2b300d | ||
|
|
835744673c | ||
|
|
0177550993 | ||
|
|
b55cd1288c | ||
|
|
04b3a236f3 | ||
|
|
c2f0203662 | ||
|
|
b168ca8a9a | ||
|
|
c57ba08e28 | ||
|
|
0ec97b3878 | ||
|
|
38732d474b | ||
|
|
35a2b14c4b | ||
|
|
d19e5ec49f | ||
|
|
bfbe603bb2 | ||
|
|
8bf3055cb6 | ||
|
|
21e1008976 | ||
|
|
22d91ddb0b | ||
|
|
570736eb23 | ||
|
|
7b06079f0d | ||
|
|
8ba22b11cf | ||
|
|
b0f6404e0a | ||
|
|
6870f2381e | ||
|
|
7c2e5376c8 | ||
|
|
4ba9afa45f | ||
|
|
be211fbccb |
133
.drone.yml
133
.drone.yml
@@ -5,7 +5,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-verify-drone
|
||||
node:
|
||||
@@ -55,7 +56,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-verify-starlark
|
||||
node:
|
||||
@@ -105,7 +107,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-test-frontend
|
||||
node:
|
||||
@@ -184,7 +187,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-lint-frontend
|
||||
node:
|
||||
@@ -273,7 +277,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-test-backend
|
||||
node:
|
||||
@@ -379,7 +384,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-lint-backend
|
||||
node:
|
||||
@@ -474,7 +480,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-build-e2e
|
||||
node:
|
||||
@@ -756,7 +763,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-integration-tests
|
||||
node:
|
||||
@@ -1020,7 +1028,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-docs
|
||||
node:
|
||||
@@ -1094,7 +1103,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-shellcheck
|
||||
node:
|
||||
@@ -1136,7 +1146,8 @@ clone:
|
||||
retries: 3
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-swagger-gen
|
||||
node:
|
||||
@@ -1199,7 +1210,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: pr-integration-benchmarks
|
||||
node:
|
||||
@@ -1375,7 +1387,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-docs
|
||||
node:
|
||||
@@ -1450,7 +1463,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-test-frontend
|
||||
node:
|
||||
@@ -1507,7 +1521,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-lint-frontend
|
||||
node:
|
||||
@@ -1574,7 +1589,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-test-backend
|
||||
node:
|
||||
@@ -1653,7 +1669,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-lint-backend
|
||||
node:
|
||||
@@ -1727,7 +1744,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-build-e2e-publish
|
||||
node:
|
||||
@@ -2127,7 +2145,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-integration-tests
|
||||
node:
|
||||
@@ -2370,7 +2389,8 @@ depends_on:
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-windows
|
||||
platform:
|
||||
@@ -2414,7 +2434,8 @@ depends_on:
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: main-trigger-downstream
|
||||
node:
|
||||
@@ -2497,7 +2518,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: publish-docker-public
|
||||
node:
|
||||
@@ -2603,7 +2625,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: publish-artifacts-public
|
||||
node:
|
||||
@@ -2672,7 +2695,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: publish-npm-packages-public
|
||||
node:
|
||||
@@ -2737,7 +2761,8 @@ depends_on:
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: publish-packages
|
||||
node:
|
||||
@@ -2825,7 +2850,8 @@ depends_on:
|
||||
- main-test-backend
|
||||
- main-test-frontend
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-main-prerelease
|
||||
node:
|
||||
@@ -2900,7 +2926,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: release-whatsnew-checker
|
||||
node:
|
||||
@@ -2944,7 +2971,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: release-test-frontend
|
||||
node:
|
||||
@@ -2999,7 +3027,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: release-test-backend
|
||||
node:
|
||||
@@ -3076,7 +3105,8 @@ depends_on:
|
||||
- release-test-backend
|
||||
- release-test-frontend
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-tag-prerelease
|
||||
node:
|
||||
@@ -3147,7 +3177,8 @@ clone:
|
||||
depends_on:
|
||||
- rgm-tag-prerelease
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-tag-prerelease-windows
|
||||
platform:
|
||||
@@ -3211,7 +3242,8 @@ depends_on:
|
||||
- rgm-tag-prerelease
|
||||
- rgm-tag-prerelease-windows
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-tag-verify-prerelease-assets
|
||||
node:
|
||||
@@ -3256,7 +3288,8 @@ depends_on:
|
||||
- release-test-backend
|
||||
- release-test-frontend
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-version-branch-prerelease
|
||||
node:
|
||||
@@ -3321,7 +3354,8 @@ clone:
|
||||
depends_on:
|
||||
- rgm-version-branch-prerelease
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-prerelease-verify-prerelease-assets
|
||||
node:
|
||||
@@ -3360,7 +3394,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: nightly-test-frontend
|
||||
node:
|
||||
@@ -3413,7 +3448,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: nightly-test-backend
|
||||
node:
|
||||
@@ -3488,7 +3524,8 @@ depends_on:
|
||||
- nightly-test-backend
|
||||
- nightly-test-frontend
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-nightly-build
|
||||
node:
|
||||
@@ -3595,7 +3632,8 @@ clone:
|
||||
depends_on:
|
||||
- rgm-nightly-build
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: rgm-nightly-publish
|
||||
node:
|
||||
@@ -3744,7 +3782,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: testing-test-backend-windows
|
||||
platform:
|
||||
@@ -3796,7 +3835,8 @@ depends_on: []
|
||||
environment:
|
||||
EDITION: oss
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: integration-tests
|
||||
node:
|
||||
@@ -4019,7 +4059,8 @@ clone:
|
||||
disable: true
|
||||
depends_on: []
|
||||
image_pull_secrets:
|
||||
- dockerconfigjson
|
||||
- gcr
|
||||
- gar
|
||||
kind: pipeline
|
||||
name: publish-ci-windows-test-image
|
||||
platform:
|
||||
@@ -4458,7 +4499,13 @@ get:
|
||||
name: .dockerconfigjson
|
||||
path: secret/data/common/gcr
|
||||
kind: secret
|
||||
name: dockerconfigjson
|
||||
name: gcr
|
||||
---
|
||||
get:
|
||||
name: .dockerconfigjson
|
||||
path: secret/data/common/gar
|
||||
kind: secret
|
||||
name: gar
|
||||
---
|
||||
get:
|
||||
name: pat
|
||||
@@ -4629,6 +4676,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: d7e383e4bf37190a97d695ef8f91755cb8cb70fb8088a5c1f63262adcf8e4f5e
|
||||
hmac: 6df571f0449f7220d208c81fe6bd5537d82afee03f2a773aaaf74af02978f56a
|
||||
|
||||
...
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
# Tags aren't necessarily made to the HEAD of the version branch.
|
||||
# The documentation to be published is always on the HEAD of the version branch.
|
||||
if: "steps.has-matching-release-tag.outputs.bool == 'true' && github.ref_type == 'tag'"
|
||||
run: "git switch --detach origin/${{ steps.target.output.target }}.x"
|
||||
run: "git switch --detach origin/${{ steps.target.outputs.target }}.x"
|
||||
|
||||
- name: "Publish to website repository (release)"
|
||||
if: "steps.has-matching-release-tag.outputs.bool == 'true'"
|
||||
|
||||
105
CHANGELOG.md
105
CHANGELOG.md
@@ -1,3 +1,108 @@
|
||||
<!-- 10.3.1 START -->
|
||||
|
||||
# 10.3.1 (2024-01-22)
|
||||
|
||||
To resolve a technical issue within the Grafana release package management process, we are releasing both Grafana 10.3.0 and Grafana 10.3.1 simultaneously. The 10.3.1 release contains no breaking or functional changes from 10.3.0. Please refer to the [What’s New](https://grafana.com/docs/grafana/latest/whatsnew/whats-new-in-v10-3/) post for Grafana 10.3.0 for details on new features and changes in this release.
|
||||
|
||||
<!-- 10.3.1 END -->
|
||||
<!-- 10.3.0 START -->
|
||||
|
||||
# 10.3.0 (2024-01-22)
|
||||
|
||||
To resolve a technical issue within the Grafana release package management process, we are releasing both Grafana 10.3.0 and Grafana 10.3.1 simultaneously. The 10.3.1 release contains no breaking or functional changes from 10.3.0. Please refer to the [What’s New](https://grafana.com/docs/grafana/latest/whatsnew/whats-new-in-v10-3/) post for Grafana 10.3.0 for details on new features and changes in this release.
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Alerting:** Guided legacy alerting upgrade dry-run. [#80071](https://github.com/grafana/grafana/issues/80071), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Explore:** Preserve time range when creating a dashboard panel from Explore. [#80070](https://github.com/grafana/grafana/issues/80070), [@Elfo404](https://github.com/Elfo404)
|
||||
- **Explore:** Init with mixed DS if there's no root DS in the URL and queries have multiple datasources. [#80068](https://github.com/grafana/grafana/issues/80068), [@Elfo404](https://github.com/Elfo404)
|
||||
- **QueryEditor:** Display error even if error field is empty. [#79943](https://github.com/grafana/grafana/issues/79943), [@idastambuk](https://github.com/idastambuk)
|
||||
- **K8s:** Enable api-server by default. [#79942](https://github.com/grafana/grafana/issues/79942), [@ryantxu](https://github.com/ryantxu)
|
||||
- **Parca:** Add standalone building configuration. [#79896](https://github.com/grafana/grafana/issues/79896), [@fabrizio-grafana](https://github.com/fabrizio-grafana)
|
||||
- **Auth:** Hide forgot password if grafana auth is disabled. [#79895](https://github.com/grafana/grafana/issues/79895), [@Jguer](https://github.com/Jguer)
|
||||
- **Plugins:** Add uninstall requested message for cloud plugins. [#79748](https://github.com/grafana/grafana/issues/79748), [@oshirohugo](https://github.com/oshirohugo)
|
||||
- **Loki:** Open log context in new tab. [#79723](https://github.com/grafana/grafana/issues/79723), [@svennergr](https://github.com/svennergr)
|
||||
- **Alerting:** Allow linking to library panels. [#79693](https://github.com/grafana/grafana/issues/79693), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Loki:** Drop all errors in volume requests. [#79686](https://github.com/grafana/grafana/issues/79686), [@svennergr](https://github.com/svennergr)
|
||||
- **Loki Logs volume:** Added a query splitting loading indicator to the Logs Volume graph. [#79681](https://github.com/grafana/grafana/issues/79681), [@matyax](https://github.com/matyax)
|
||||
- **Plugins:** Disable add new data source for incomplete install. [#79658](https://github.com/grafana/grafana/issues/79658), [@oshirohugo](https://github.com/oshirohugo)
|
||||
- **RBAC:** Render team, service account and user list when a user can see entities but not roles attached to them. [#79642](https://github.com/grafana/grafana/issues/79642), [@kalleep](https://github.com/kalleep)
|
||||
- **InfluxDB:** Use database input for SQL configuration instead of metadata. [#79579](https://github.com/grafana/grafana/issues/79579), [@itsmylife](https://github.com/itsmylife)
|
||||
- **Tempo:** Support special characters in identifiers. [#79565](https://github.com/grafana/grafana/issues/79565), [@fabrizio-grafana](https://github.com/fabrizio-grafana)
|
||||
- **Alerting:** Display "Show all" button for cloud rules. [#79512](https://github.com/grafana/grafana/issues/79512), [@VikaCep](https://github.com/VikaCep)
|
||||
- **React Hook Form:** Update to v 7.49.2. [#79493](https://github.com/grafana/grafana/issues/79493), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **Loki:** Add timeRange to labels requests in LogContext to reduce loading times. [#79478](https://github.com/grafana/grafana/issues/79478), [@svennergr](https://github.com/svennergr)
|
||||
- **InfluxDB:** Enable SQL support by default. [#79474](https://github.com/grafana/grafana/issues/79474), [@itsmylife](https://github.com/itsmylife)
|
||||
- **OAuth:** Remove accessTokenExpirationCheck feature toggle. [#79455](https://github.com/grafana/grafana/issues/79455), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Units:** Add scalable unit option. [#79411](https://github.com/grafana/grafana/issues/79411), [@Develer](https://github.com/Develer)
|
||||
- **Alerting:** Add export mute timings feature to the UI. [#79395](https://github.com/grafana/grafana/issues/79395), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Config:** Can add static headers to email messages. [#79365](https://github.com/grafana/grafana/issues/79365), [@owensmallwood](https://github.com/owensmallwood)
|
||||
- **Alerting:** Drop NamespaceID from responses on unstable ngalert API endpoints in favor of NamespaceUID. [#79359](https://github.com/grafana/grafana/issues/79359), [@alexweav](https://github.com/alexweav)
|
||||
- **Cloudwatch:** Update cloudwatchNewRegionsHandler to General Availability. [#79348](https://github.com/grafana/grafana/issues/79348), [@sarahzinger](https://github.com/sarahzinger)
|
||||
- **Plugins:** Include Azure settings as a part of Grafana config sent in plugin requests. [#79342](https://github.com/grafana/grafana/issues/79342), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Plugins:** Add hide_angular_deprecation setting. [#79296](https://github.com/grafana/grafana/issues/79296), [@xnyo](https://github.com/xnyo)
|
||||
- **Table:** Add select/unselect all column values to table filter. [#79290](https://github.com/grafana/grafana/issues/79290), [@ahuarte47](https://github.com/ahuarte47)
|
||||
- **Anonymous:** Add configurable device limit. [#79265](https://github.com/grafana/grafana/issues/79265), [@Jguer](https://github.com/Jguer)
|
||||
- **Frontend:** Detect new assets / versions / config changes. [#79258](https://github.com/grafana/grafana/issues/79258), [@ryantxu](https://github.com/ryantxu)
|
||||
- **Plugins:** Add option to disable TLS in the socks proxy. [#79246](https://github.com/grafana/grafana/issues/79246), [@PoorlyDefinedBehaviour](https://github.com/PoorlyDefinedBehaviour)
|
||||
- **Frontend:** Reload the browser when backend configuration/assets change. [#79057](https://github.com/grafana/grafana/issues/79057), [@torkelo](https://github.com/torkelo)
|
||||
- **Chore:** Refactor dataviz aria-label e2e selectors to data-testid. [#78938](https://github.com/grafana/grafana/issues/78938), [@khushijain21](https://github.com/khushijain21)
|
||||
- **SSO:** Add GitHub auth configuration page. [#78933](https://github.com/grafana/grafana/issues/78933), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **PublicDashboards:** Add setting to disable the feature. [#78894](https://github.com/grafana/grafana/issues/78894), [@AgnesToulet](https://github.com/AgnesToulet)
|
||||
- **Variables:** Interpolate variables used in custom variable definition. [#78800](https://github.com/grafana/grafana/issues/78800), [@torkelo](https://github.com/torkelo)
|
||||
- **Table:** Highlight row on shared crosshair. [#78392](https://github.com/grafana/grafana/issues/78392), [@mdvictor](https://github.com/mdvictor)
|
||||
- **Stat:** Add Percent Change Option. [#78250](https://github.com/grafana/grafana/issues/78250), [@drew08t](https://github.com/drew08t)
|
||||
- **Plugins:** Add Command Palette extension point. [#78098](https://github.com/grafana/grafana/issues/78098), [@sd2k](https://github.com/sd2k)
|
||||
- **Transformations:** Add frame source picker to allow transforming annotations. [#77842](https://github.com/grafana/grafana/issues/77842), [@leeoniya](https://github.com/leeoniya)
|
||||
- **Pyroscope:** Send start/end with profile types query. [#77523](https://github.com/grafana/grafana/issues/77523), [@bryanhuhta](https://github.com/bryanhuhta)
|
||||
- **Explore:** Create menu for short link button. [#77336](https://github.com/grafana/grafana/issues/77336), [@gelicia](https://github.com/gelicia)
|
||||
- **Alerting:** Don't record annotations for mapped NoData transitions, when NoData is mapped to OK. [#77164](https://github.com/grafana/grafana/issues/77164), [@alexweav](https://github.com/alexweav)
|
||||
- **Canvas:** Add Pan and Zoom. [#76705](https://github.com/grafana/grafana/issues/76705), [@drew08t](https://github.com/drew08t)
|
||||
- **Alerting:** In migration, create one label per channel. [#76527](https://github.com/grafana/grafana/issues/76527), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Separate overlapping legacy and UA alerting routes. [#76517](https://github.com/grafana/grafana/issues/76517), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Tooltip:** Improved Timeseries and Candlestick tooltips. [#75841](https://github.com/grafana/grafana/issues/75841), [@adela-almasan](https://github.com/adela-almasan)
|
||||
- **Alerting:** Support hysteresis command expression. [#75189](https://github.com/grafana/grafana/issues/75189), [@yuri-tceretian](https://github.com/yuri-tceretian)
|
||||
- **Plugins:** Add update for instance plugins. (Enterprise)
|
||||
- **React Hook Form:** Update to v 7.49.2. (Enterprise)
|
||||
- **Plugins:** Improve cloud plugins install error treatment. (Enterprise)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Transformations:** Fix bug where having NaN in the input to regression analysis transformation causes all predictions to be NaN. [#80079](https://github.com/grafana/grafana/issues/80079), [@oscarkilhed](https://github.com/oscarkilhed)
|
||||
- **Alerting:** Fix URL timestamp conversion in historian API in annotation mode. [#80026](https://github.com/grafana/grafana/issues/80026), [@alexweav](https://github.com/alexweav)
|
||||
- **Fix:** Switch component not being styled as disabled when is checked. [#80012](https://github.com/grafana/grafana/issues/80012), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Tempo:** Fix Spans table format. [#79938](https://github.com/grafana/grafana/issues/79938), [@fabrizio-grafana](https://github.com/fabrizio-grafana)
|
||||
- **Gauges:** Fixing broken auto sizing. [#79926](https://github.com/grafana/grafana/issues/79926), [@torkelo](https://github.com/torkelo)
|
||||
- **Barchart:** Fix percent stacking regression. [#79903](https://github.com/grafana/grafana/issues/79903), [@nmarrs](https://github.com/nmarrs)
|
||||
- **Alerting:** Fix reusing last url in tab when reopening a new tab in rule detail a…. [#79801](https://github.com/grafana/grafana/issues/79801), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Azure Monitor:** Fix multi-resource bug "Missing required region params, requested QueryParams: api-version:2017-12-01-preview...". [#79669](https://github.com/grafana/grafana/issues/79669), [@bossinc](https://github.com/bossinc)
|
||||
- **Explore:** Fix URL sync with async queries import . [#79584](https://github.com/grafana/grafana/issues/79584), [@Elfo404](https://github.com/Elfo404)
|
||||
- **Dashboards:** Skip inherited object variable names. [#79567](https://github.com/grafana/grafana/issues/79567), [@jarben](https://github.com/jarben)
|
||||
- **Alerting:** Fix queries and expressions in rule view details. [#79497](https://github.com/grafana/grafana/issues/79497), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Tempo:** Fix cache in TraceQL editor. [#79468](https://github.com/grafana/grafana/issues/79468), [@adrapereira](https://github.com/adrapereira)
|
||||
- **Nested Folders:** Fix /api/folders pagination. [#79447](https://github.com/grafana/grafana/issues/79447), [@papagian](https://github.com/papagian)
|
||||
- **Elasticsearch:** Fix modify query with backslashes. [#79430](https://github.com/grafana/grafana/issues/79430), [@svennergr](https://github.com/svennergr)
|
||||
- **Cloudwatch:** Fix errors while loading queries/datasource on Safari. [#79417](https://github.com/grafana/grafana/issues/79417), [@kevinwcyu](https://github.com/kevinwcyu)
|
||||
- **Stat:** Fix inconsistent center padding. [#79389](https://github.com/grafana/grafana/issues/79389), [@torkelo](https://github.com/torkelo)
|
||||
- **Tempo:** Fix autocompletion with strings. [#79370](https://github.com/grafana/grafana/issues/79370), [@fabrizio-grafana](https://github.com/fabrizio-grafana)
|
||||
- **Alerting:** Fix for data source filter on cloud rules. [#79327](https://github.com/grafana/grafana/issues/79327), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix UI inheriting mute timings from parent when calculating the polic…. [#79295](https://github.com/grafana/grafana/issues/79295), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Auth:** Fix a panic during logout when OAuth provider is not set. [#79271](https://github.com/grafana/grafana/issues/79271), [@dmihai](https://github.com/dmihai)
|
||||
- **Tempo:** Fix read-only assignment. [#79183](https://github.com/grafana/grafana/issues/79183), [@fabrizio-grafana](https://github.com/fabrizio-grafana)
|
||||
- **Templating:** Json interpolation of single-value default selection does not create valid json. [#79137](https://github.com/grafana/grafana/issues/79137), [@kaydelaney](https://github.com/kaydelaney)
|
||||
- **Heatmap:** Fix null options migration. [#79083](https://github.com/grafana/grafana/issues/79083), [@overvenus](https://github.com/overvenus)
|
||||
- **Dashboards:** Run shared queries even when source panel is in collapsed row. [#77792](https://github.com/grafana/grafana/issues/77792), [@kaydelaney](https://github.com/kaydelaney)
|
||||
- **PDF:** Fix support for large panels. (Enterprise)
|
||||
- **Reporting:** Fix daylight saving time support for custom schedules. (Enterprise)
|
||||
- **RBAC:** Fix role assignment removal . (Enterprise)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
Users who have InfluxDB datasource configured with SQL querying language must update their database information. They have to enter their `bucket name` into the database field. Issue [#79579](https://github.com/grafana/grafana/issues/79579)
|
||||
|
||||
Removes `NamespaceID` from responses of all GET routes underneath the path `/api/ruler/grafana/api/v1/rules` - 3 affected endpoints. All affected routes are not in the publicly documented or `stable` marked portion of the ngalert API. This only breaks clients who are directly using the unstable portion of the API. Such clients should use `NamespaceUID` rather than `NamespaceID` to identify namespaces. Issue [#79359](https://github.com/grafana/grafana/issues/79359)
|
||||
|
||||
<!-- 10.3.0 END -->
|
||||
<!-- 10.2.3 START -->
|
||||
|
||||
# 10.2.3 (2023-12-18)
|
||||
|
||||
@@ -246,7 +246,7 @@ reporting_distributor = grafana-labs
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
# for new versions of grafana. The check is used
|
||||
# in some UI views to notify that a grafana update exists.
|
||||
# This option does not cause any auto updates, nor send any information
|
||||
# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version.
|
||||
# only a GET request to https://grafana.com/api/grafana/versions/stable to get the latest version.
|
||||
;check_for_updates = true
|
||||
|
||||
# Set to false to disable all checks to https://grafana.com
|
||||
|
||||
@@ -26,6 +26,6 @@ Grafana employees can find more details in our internal docs.
|
||||
|
||||
## Announced deprecations.
|
||||
|
||||
| Name | Annoucement Date | Disabling date | Removal Date | Description | Status |
|
||||
| ------------------------------------------------------------------------ | ---------------- | -------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| [Support for Mysql 5.7](https://github.com/grafana/grafana/issues/68446) | 2023-05-15 | October 2023 | | MySQL 5.7 is being deprecated in October 2023 and Grafana's policy is to test against the officially supported version. | Planned |
|
||||
| Name | Announcement Date | Disabling date | Removal Date | Description | Status |
|
||||
| ------------------------------------------------------------------------ | ----------------- | -------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------- | ------- |
|
||||
| [Support for Mysql 5.7](https://github.com/grafana/grafana/issues/68446) | 2023-05-15 | October 2023 | | MySQL 5.7 is being deprecated in October 2023 and Grafana's policy is to test against the officially supported version. | Planned |
|
||||
|
||||
@@ -6,7 +6,13 @@
|
||||
# [Semantic versioning](https://semver.org/) is used to help the reader identify the significance of changes.
|
||||
# Changes are relevant to this script and the support docs.mk GNU Make interface.
|
||||
#
|
||||
|
||||
# ## 5.2.0 (2024-01-18)
|
||||
#
|
||||
# ### Changed
|
||||
#
|
||||
# - Updated `make vale` to use latest Vale style and configuration.
|
||||
# - Updated `make vale` to use platform appropriate image.
|
||||
#
|
||||
# ## 5.1.2 (2023-11-08)
|
||||
#
|
||||
# ### Added
|
||||
@@ -704,14 +710,14 @@ case "${image}" in
|
||||
"${PODMAN}" run \
|
||||
--init \
|
||||
--interactive \
|
||||
--platform linux/amd64 \
|
||||
--rm \
|
||||
--workdir /etc/vale \
|
||||
--tty \
|
||||
${volumes} \
|
||||
"${DOCS_IMAGE}" \
|
||||
"--minAlertLevel=${VALE_MINALERTLEVEL}" \
|
||||
--config=/etc/vale/.vale.ini \
|
||||
--output=line \
|
||||
'--glob=*.md' \
|
||||
--output=/etc/vale/rdjsonl.tmpl \
|
||||
/hugo/content/docs | sed "s#$(proj_dst "${proj}")#sources#"
|
||||
;;
|
||||
*)
|
||||
|
||||
@@ -42,6 +42,10 @@ title: Grafana open source documentation
|
||||
<img src="/static/img/logos/logo-docker.svg">
|
||||
<h5>Run Docker image</h5>
|
||||
</a>
|
||||
<a href="{{< relref "setup-grafana/installation/kubernetes/" >}}" class="nav-cards__item nav-cards__item--install">
|
||||
<img src="/static/img/logos/logo-kubernetes.svg">
|
||||
<h5>Run on Kubernetes</h5>
|
||||
</a>
|
||||
<a href="https://grafana.com/docs/grafana-cloud/" class="nav-cards__item nav-cards__item--install">
|
||||
<div class="nav-cards__icon fa fa-cloud">
|
||||
</div>
|
||||
@@ -77,8 +81,8 @@ title: Grafana open source documentation
|
||||
<h4>Provisioning</h4>
|
||||
<p>Learn how to automate your Grafana configuration.</p>
|
||||
</a>
|
||||
<a href="{{< relref "whatsnew/whats-new-in-v10-2/" >}}" class="nav-cards__item nav-cards__item--guide">
|
||||
<h4>What's new in v10.2</h4>
|
||||
<a href="{{< relref "whatsnew/whats-new-in-v10-3/" >}}" class="nav-cards__item nav-cards__item--guide">
|
||||
<h4>What's new in v10.3</h4>
|
||||
<p>Explore the features and enhancements in the latest release.</p>
|
||||
</a>
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ Define a query to get the data you want to measure and a condition that needs to
|
||||
All alert rules are managed by Grafana by default. If you want to switch to a data source-managed alert rule, click **Switch to data source-managed alert rule**.
|
||||
|
||||
1. Add one or more [expressions][expression-queries].
|
||||
|
||||
a. For each expression, select either **Classic condition** to create a single alert rule, or choose from the **Math**, **Reduce**, and **Resample** options to generate separate alert for each series.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
@@ -79,6 +80,14 @@ Define a query to get the data you want to measure and a condition that needs to
|
||||
|
||||
b. Click **Preview** to verify that the expression is successful.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
The recovery threshold feature is currently only available in OSS.
|
||||
{{% /admonition %}}
|
||||
|
||||
1. To add a recovery threshold, turn the **Custom recovery threshold** toggle on and fill in a value for when your alert rule should stop firing.
|
||||
|
||||
You can only add one recovery threshold in a query and it must be the alert condition.
|
||||
|
||||
1. Click **Set as alert condition** on the query or expression you want to set as your alert condition.
|
||||
|
||||
## Set alert evaluation behavior
|
||||
|
||||
@@ -128,6 +128,26 @@ When the queried data satisfies the defined condition, Grafana triggers the asso
|
||||
|
||||
By default, the last expression added is used as the alert condition.
|
||||
|
||||
## Recovery threshold
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
The recovery threshold feature is currently only available in OSS.
|
||||
{{% /admonition %}}
|
||||
|
||||
To reduce the noise of flapping alerts, you can set a recovery threshold different to the alert threshold.
|
||||
|
||||
Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications being generated.
|
||||
|
||||
Grafana-managed alert rules are evaluated for a specific interval of time. During each evaluation, the result of the query is checked against the threshold set in the alert rule. If the value of a metric is above the threshold, an alert rule fires and a notification is sent. When the value goes below the threshold and there is an active alert for this metric, the alert is resolved, and another notification is sent.
|
||||
|
||||
It can be tricky to create an alert rule for a noisy metric. That is, when the value of a metric continually goes above and below a threshold. This is called flapping and results in a series of firing - resolved - firing notifications and a noisy alert state history.
|
||||
|
||||
For example, if you have an alert for latency with a threshold of 1000ms and the number fluctuates around 1000 (say 980 ->1010 -> 990 -> 1020, and so on) then each of those will trigger a notification.
|
||||
|
||||
To solve this problem, you can set a (custom) recovery threshold, which basically means having two thresholds instead of one. An alert is triggered when the first threshold is crossed and is resolved only when the second threshold is crossed.
|
||||
|
||||
For example, you could set a threshold of 1000ms and a recovery threshold of 900ms. This way, an alert rule will only stop firing when it goes under 900ms and flapping is reduced.
|
||||
|
||||
{{% docs/reference %}}
|
||||
[data-source-alerting]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/alerting/fundamentals/data-source-alerting"
|
||||
[data-source-alerting]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/alerting-and-irm/alerting/fundamentals/data-source-alerting"
|
||||
|
||||
@@ -22,9 +22,11 @@ To do this, you need to make sure that your alert rule is in the right evaluatio
|
||||
|
||||
## Evaluation group
|
||||
|
||||
Every alert rule is part of an evaluation group. Each evaluation group contains an evaluation interval that determines how frequently the alert rule is checked. Alert rules within the same group are evaluated one after the other, while alert rules in different groups can be evaluated simultaneously.
|
||||
Every alert rule is part of an evaluation group. Each evaluation group contains an evaluation interval that determines how frequently the alert rule is checked.
|
||||
|
||||
This feature is especially useful for Prometheus/Mimir rules when you want to ensure that recording rules are evaluated before any alert rules.
|
||||
**Data-source managed** alert rules within the same group are evaluated one after the other, while alert rules in different groups can be evaluated simultaneously. This feature is especially useful when you want to ensure that recording rules are evaluated before any alert rules.
|
||||
|
||||
**Grafana-managed** alert rules are evaluated at the same time, regardless of alert rule group. The default evaluation interval is set at 10 seconds, which means that Grafana-managed alert rules are evaluated every 10 seconds to the closest 10-second window on the clock, for example, 10:00:00, 10:00:10, 10:00:20, and so on. You can also configure your own evaluation interval, if required.
|
||||
|
||||
**Note:**
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@ In the Contact points tab, you can see a list of your contact points.
|
||||
|
||||
**Note:** You can edit an existing contact by clicking the Edit icon.
|
||||
|
||||
2. Execute a template from one or more fields such as Message and Subject:
|
||||
1. Execute a template from one or more fields such as Message and Subject:
|
||||
|
||||
{{< figure max-width="940px" src="/static/img/docs/alerting/unified/use-notification-template-9-4.png" caption="Use notification template" >}}
|
||||
|
||||
For more information on how to write and execute templates, refer to [Using Go's templating language][using-go-templating-language] and [Create notification templates][create-notification-templates].
|
||||
|
||||
3. Click Save template.
|
||||
1. Click **Save contact point**.
|
||||
|
||||
{{% docs/reference %}}
|
||||
[create-notification-templates]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/alerting/manage-notifications/template-notifications/create-notification-templates"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
aliases:
|
||||
- ../provision-alerting-resources/
|
||||
canonical: https://grafana.com/docs/grafana/latest/alerting/set-up/provision-alerting-resources/
|
||||
description: Provision alerting resources
|
||||
description: Import and export alerting resources
|
||||
keywords:
|
||||
- grafana
|
||||
- alerting
|
||||
@@ -14,23 +14,23 @@ labels:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Provision Grafana Alerting resources
|
||||
title: Import and export Grafana Alerting resources
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Provision Grafana Alerting resources
|
||||
# Import and export Grafana Alerting resources
|
||||
|
||||
Alerting infrastructure is often complex, with many pieces of the pipeline that often live in different places. Scaling this across multiple teams and organizations is an especially challenging task. Grafana Alerting provisioning makes this process easier by enabling you to create, manage, and maintain your alerting data in a way that best suits your organization.
|
||||
Alerting infrastructure is often complex, with many pieces of the pipeline that often live in different places. Scaling this across multiple teams and organizations is an especially challenging task. Importing and exporting (or provisioning) your alerting resources in Grafana Alerting makes this process easier by enabling you to create, manage, and maintain your alerting data in a way that best suits your organization.
|
||||
|
||||
Provisioning for Grafana Alerting supports alert rules, contact points, notification policies, mute timings, and templates.
|
||||
You can import alert rules, contact points, notification policies, mute timings, and templates.
|
||||
|
||||
You cannot edit provisioned alerting resources in the Grafana UI in the same way as unprovisioned alerting resources. You can only edit provisioned contact points, notification policies, templates, and mute timings in the source where they were created. For example, if you provision your alerting resources using files from disk, you cannot edit the data in Terraform or from within Grafana.
|
||||
You cannot edit imported alerting resources in the Grafana UI in the same way as alerting resources that were not imported. You can only edit imported contact points, notification policies, templates, and mute timings in the source where they were created. For example, if you manage your alerting resources using files from disk, you cannot edit the data in Terraform or from within Grafana.
|
||||
|
||||
To modify provisioned alert rules, you can use the **Modify export** feature to edit and then export.
|
||||
To modify imported alert rules, you can use the **Modify export** feature to edit and then export.
|
||||
|
||||
Choose from the options below to provision your Grafana Alerting resources.
|
||||
Choose from the options below to import your Grafana Alerting resources.
|
||||
|
||||
1. Use file provisioning to provision your Grafana Alerting resources, such as alert rules and contact points, through files on disk.
|
||||
1. Use file provisioning to manage your Grafana Alerting resources, such as alert rules and contact points, through files on disk.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
File provisioning is not available in Grafana Cloud instances.
|
||||
|
||||
@@ -14,13 +14,13 @@ labels:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Create and manage alerting resources using file provisioning
|
||||
title: Use file provisioning to manage alerting resources
|
||||
weight: 100
|
||||
---
|
||||
|
||||
## Create and manage alerting resources using file provisioning
|
||||
## Use file provisioning to manage alerting resources
|
||||
|
||||
Provision your alerting resources using files from disk. When you start Grafana, the data from these files is created in your Grafana system. Grafana adds any new resources you created, updates any that you changed, and deletes old ones.
|
||||
Manage your alerting resources using files from disk. When you start Grafana, the data from these files is created in your Grafana system. Grafana adds any new resources you created, updates any that you changed, and deletes old ones.
|
||||
|
||||
Arrange your files in a directory in a way that best suits your use case. For example, you can choose a team-based layout where every team has its own file, you can have one big file for all your teams; or you can have one file per resource type.
|
||||
|
||||
@@ -28,9 +28,9 @@ Details on how to set up the files and which fields are required for each object
|
||||
|
||||
**Note:**
|
||||
|
||||
Provisioning takes place during the initial set up of your Grafana system, but you can re-run it at any time using the [Grafana Admin API][reload-provisioning-configurations].
|
||||
Importing takes place during the initial set up of your Grafana system, but you can re-run it at any time using the [Grafana Admin API][reload-provisioning-configurations].
|
||||
|
||||
### Provision alert rules
|
||||
### Import alert rules
|
||||
|
||||
Create or delete alert rules in your Grafana instance(s).
|
||||
|
||||
@@ -41,11 +41,11 @@ Create or delete alert rules in your Grafana instance(s).
|
||||
Example configuration files can be found below.
|
||||
|
||||
1. Ensure that your files are in the right directory on the node running the Grafana server, so that they deploy alongside your Grafana instance(s).
|
||||
1. Delete the alert rules in Grafana that will be provisioned.
|
||||
1. Delete the alert rules in Grafana that are going to be imported.
|
||||
|
||||
**Note:**
|
||||
|
||||
If you do not delete the alert rule, it will clash with the provisioned alert rule once uploaded.
|
||||
If you do not delete the alert rule, it will clash with the imported alert rule once uploaded.
|
||||
|
||||
Here is an example of a configuration file for creating alert rules.
|
||||
|
||||
@@ -133,7 +133,7 @@ deleteRules:
|
||||
uid: my_id_1
|
||||
```
|
||||
|
||||
### Provision contact points
|
||||
### Import contact points
|
||||
|
||||
Create or delete contact points in your Grafana instance(s).
|
||||
|
||||
@@ -492,7 +492,7 @@ settings:
|
||||
{{ template "default.title" . }}
|
||||
```
|
||||
|
||||
### Provision notification policies
|
||||
### Import notification policies
|
||||
|
||||
Create or reset the notification policy tree in your Grafana instance(s).
|
||||
|
||||
@@ -584,7 +584,7 @@ In Grafana, the entire notification policy tree is considered a single, large re
|
||||
|
||||
Since the policy tree is a single resource, applying it will overwrite a policy tree created through any other means.
|
||||
|
||||
### Provision templates
|
||||
### Import templates
|
||||
|
||||
Create or delete templates in your Grafana instance(s).
|
||||
|
||||
@@ -603,7 +603,7 @@ apiVersion: 1
|
||||
# List of templates to import or update
|
||||
templates:
|
||||
# <int> organization ID, default = 1
|
||||
- orgID: 1
|
||||
- orgId: 1
|
||||
# <string, required> name of the template, must be unique
|
||||
name: my_first_template
|
||||
# <string, required> content of the the template
|
||||
@@ -624,7 +624,7 @@ deleteTemplates:
|
||||
name: my_first_template
|
||||
```
|
||||
|
||||
### Provision mute timings
|
||||
### Import mute timings
|
||||
|
||||
Create or delete mute timings in your Grafana instance(s).
|
||||
|
||||
|
||||
@@ -13,11 +13,11 @@ labels:
|
||||
products:
|
||||
- enterprise
|
||||
- oss
|
||||
title: Create and manage alerting resources using Terraform
|
||||
title: Use Terraform to manage alerting resources
|
||||
weight: 200
|
||||
---
|
||||
|
||||
# Create and manage alerting resources using Terraform
|
||||
# Use Terraform to manage alerting resources
|
||||
|
||||
Use Terraform’s Grafana Provider to manage your alerting resources and provision them into your Grafana system. Terraform provider support for Grafana Alerting makes it easy to create, manage, and maintain your entire Grafana Alerting stack as code.
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ labels:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Manage provisioned resources in Grafana
|
||||
title: Manage provisioned alerting resources in Grafana
|
||||
menuTitle: Manage provisioned alerting resources
|
||||
title: Manage provisioned alerting resources
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Manage provisioned alerting resources in Grafana
|
||||
# Manage provisioned alerting resources
|
||||
|
||||
Verify that your alerting resources were created in Grafana, as well as edit or export your provisioned alerting resources.
|
||||
|
||||
|
||||
77
docs/sources/breaking-changes/breaking-changes-v10-3.md
Normal file
77
docs/sources/breaking-changes/breaking-changes-v10-3.md
Normal file
@@ -0,0 +1,77 @@
|
||||
---
|
||||
description: Breaking changes for Grafana v10.3
|
||||
keywords:
|
||||
- grafana
|
||||
- breaking changes
|
||||
- documentation
|
||||
- '10.3'
|
||||
- '10.2.3'
|
||||
- release notes
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: Breaking changes in Grafana v10.3
|
||||
weight: -2
|
||||
---
|
||||
|
||||
# Breaking changes in Grafana v10.3
|
||||
|
||||
Following are breaking changes that you should be aware of when upgrading to Grafana v10.3. Breaking changes that were introduced in release 10.2.3 are also included here and are marked with an asterisk.
|
||||
|
||||
For our purposes, a breaking change is any change that requires users or operators to do something. This includes:
|
||||
|
||||
- Changes in one part of the system that could cause other components to fail
|
||||
- Deprecations or removal of a feature
|
||||
- Changes to an API that could break automation
|
||||
- Changes that affect some plugins or functions of Grafana
|
||||
- Migrations that can’t be rolled back
|
||||
|
||||
For each change, the provided information:
|
||||
|
||||
- Helps you determine if you’re affected
|
||||
- Describes the change or relevant background information
|
||||
- Guides you in how to mitigate for the change or migrate
|
||||
- Provides more learning resources
|
||||
|
||||
For release highlights and deprecations, refer to our [v10.3 What’s new](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/whatsnew/whats-new-in-v10-3/). For the specific steps we recommend when you upgrade to v10.3, check out our [Upgrade guide](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/upgrade-guide/upgrade-v10.3/).
|
||||
|
||||
<!--
|
||||
## Feature
|
||||
|
||||
You must use relative references when linking to docs within the Grafana repo. Please do not use absolute URLs. For more information about relrefs, refer to [Links and references](/docs/writers-toolkit/writing-guide/references/).-->
|
||||
|
||||
## General breaking changes
|
||||
|
||||
### Transformations\*
|
||||
|
||||
In panels using the extract fields transformation, where one of the extracted names collides with one of the already existing fields, the extracted field will be renamed. Issue [#77569](https://github.com/grafana/grafana/issues/77569).
|
||||
|
||||
If you use the Table visualization, you might see some inconsistencies in your panels. We have updated the table column naming. This will potentially affect field transformations and/or field overrides. To resolve this, either:
|
||||
|
||||
- Update the transformation you are using
|
||||
- Update field override. Issue [#76899](https://github.com/grafana/grafana/issues/76899).
|
||||
|
||||
Users who have transformations with the Time field might see their transformations are not working. Those panels that have broken transformations will fail to render. This is because we changed the field key. See related PR: [#69865](https://github.com/grafana/grafana/pull/69865). To resolve this, either:
|
||||
|
||||
- Remove the affected panel and re-create it
|
||||
- Select the Time field again
|
||||
- Edit the time field as Time for transformation in panel.json or dashboard.json. Issue [#76641](https://github.com/grafana/grafana/issues/76641).
|
||||
|
||||
### Data source permissions\*
|
||||
|
||||
The following data source permission endpoints have been removed:
|
||||
|
||||
- `GET /datasources/:datasourceId/permissions`
|
||||
- `POST /api/datasources/:datasourceId/permissions`
|
||||
- `DELETE /datasources/:datasourceId/permissions`
|
||||
- `POST /datasources/:datasourceId/enable-permissions`
|
||||
- `POST /datasources/:datasourceId/disable-permissions`
|
||||
|
||||
Please use the following endpoints instead:
|
||||
|
||||
- `GET /api/access-control/datasources/:uid` for listing data source permissions
|
||||
- `POST /api/access-control/datasources/:uid/users/:id`, `POST /api/access-control/datasources/:uid/teams/:id`, and `POST /api/access-control/datasources/:uid/buildInRoles/:id` for adding or removing data source permissions
|
||||
|
||||
If you are using the Grafana provider for Terraform to manage data source permissions, you will need to upgrade your provider to [version 2.6.0](https://registry.terraform.io/providers/grafana/grafana/2.6.0/docs) or newer to ensure that data source permission provisioning keeps working. Issue [#5880](https://github.com/grafana/grafana-enterprise/pull/5880).
|
||||
@@ -44,7 +44,10 @@ Annotations are supported for the following visualization types:
|
||||
|
||||
Grafana comes with the ability to add annotation events directly from a panel using the [built-in annotation query](#built-in-query) that exists on all dashboards. Annotations that you create this way are stored in Grafana.
|
||||
|
||||
To add annotations directly in the panel, the built-in query must be enabled. Learn more in [Built-in query](#built-in-query)
|
||||
To add annotations directly in the panel:
|
||||
|
||||
- The dashboard must already be saved.
|
||||
- The built-in query must be enabled. Learn more in [Built-in query](#built-in-query).
|
||||
|
||||
### Add an annotation
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ labels:
|
||||
- oss
|
||||
menuTitle: Best practices
|
||||
title: Grafana dashboard best practices
|
||||
weight: 100
|
||||
weight: 800
|
||||
---
|
||||
|
||||
# Grafana dashboard best practices
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
aliases:
|
||||
- ../../reference/export_import/ # /docs/grafana/<GRAFANA_VERSION>/reference/export_import/
|
||||
- ../export-import/ # /docs/grafana/<GRAFANA_VERSION>/dashboards/export-import/
|
||||
canonical: https://grafana.com/docs/grafana/latest/dashboards/build-dashboards/import-dashboards/
|
||||
keywords:
|
||||
- grafana
|
||||
- dashboard
|
||||
- import
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Import dashboards
|
||||
title: Import dashboards
|
||||
description: Learn how to import dashboards and about Grafana's preconfigured dashboards
|
||||
weight: 5
|
||||
---
|
||||
|
||||
# Import dashboards
|
||||
|
||||
You can import preconfigured dashboards into your Grafana instance or Cloud stack using the UI or the [HTTP API][].
|
||||
|
||||
## Import a dashboard
|
||||
|
||||
To import a dashboard, follow these steps:
|
||||
|
||||
1. Click **Dashboards** in the primary menu.
|
||||
1. Click **New** and select **Import** in the drop-down menu.
|
||||
1. Perform one of the following steps:
|
||||
|
||||
- Upload a dashboard JSON file.
|
||||
- Paste a [Grafana.com dashboard](#discover-dashboards-on-grafanacom) URL or ID into the field provided.
|
||||
- Paste dashboard JSON text directly into the text area.
|
||||
|
||||
1. (Optional) Change the dashboard name, folder, or UID, and specify metric prefixes, if the dashboard uses any.
|
||||
1. Select a data source, if required.
|
||||
1. Click **Import**.
|
||||
1. Save the dashboard.
|
||||
|
||||
## Discover dashboards on grafana.com
|
||||
|
||||
The [Dashboards page](https://grafana.com/grafana/dashboards/) on grafana.com provides you with dashboards for common server applications. Browse our library of official and community-built dashboards and import them to quickly get up and running.
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-gcom-dashboards.png" alt="Preconfigured dashboards on grafana.com">}}
|
||||
|
||||
You can also add to this library by exporting one of your own dashboards. For more information, refer to [Share dashboards and panels][].
|
||||
|
||||
## More examples
|
||||
|
||||
Your Grafana Cloud stack comes with several default dashboards in the **Grafana Cloud** folder in **Dashboards**. If you're running your own installation of Grafana, you can find more example dashboards in the `public/dashboards/` directory.
|
||||
|
||||
{{% docs/reference %}}
|
||||
[HTTP API]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/http_api"
|
||||
[HTTP API]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/developer-resources/api-reference/http-api"
|
||||
|
||||
[Share dashboards and panels]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/share-dashboards-panels"
|
||||
[Share dashboards and panels]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/dashboards/share-dashboards-panels"
|
||||
{{% /docs/reference %}}
|
||||
@@ -107,10 +107,16 @@ You can include dynamic dashboards with panels or rows, set to repeat by a varia
|
||||
|
||||
By default, reports use the saved time range of the dashboard. You can change the time range of the report by:
|
||||
|
||||
- Saving a modified time range to the dashboard.
|
||||
- Saving a modified time range to the dashboard. Changing the dashboard time range without saving it doesn't change the time zone of the report.
|
||||
- Setting a time range via the **Time range** field in the report form. If specified, the custom time range overrides the time range from the report's dashboard.
|
||||
|
||||
The page header of the report displays the time range for the dashboard's data queries. Dashboards set to use the browser's time zone use the time zone on the Grafana server.
|
||||
The page header of the report displays the time range for the dashboard's data queries.
|
||||
|
||||
#### Report time zones
|
||||
|
||||
Reports use the time zone of the dashboard from which they’re generated. You can control the time zone for your reports by setting the dashboard to a specific time zone. Note that this affects the display of the dashboard for all users.
|
||||
|
||||
If a dashboard has the **Browser Time** setting, the reports generated from that dashboard use the time zone of the Grafana server. As a result, this time zone might not match the time zone of users creating or receiving the report.
|
||||
|
||||
If the time zone is set differently between your Grafana server and its remote image renderer, then the time ranges in the report might be different between the page header and the time axes in the panels. To avoid this, set the time zone to UTC for dashboards when using a remote renderer. Each dashboard's time zone setting is visible in the [time range controls][].
|
||||
|
||||
|
||||
@@ -3,23 +3,15 @@ aliases:
|
||||
- ../features/dashboard/dashboards/
|
||||
- ../panels/working-with-panels/organize-dashboard/
|
||||
- ../reference/dashboard_folders/
|
||||
- ../reference/export_import/
|
||||
- ../reference/timerange/
|
||||
- ../troubleshooting/troubleshoot-dashboards/
|
||||
- dashboard-folders/
|
||||
- dashboard-manage/
|
||||
- export-import/
|
||||
canonical: https://grafana.com/docs/grafana/latest/dashboards/manage-dashboards/
|
||||
keywords:
|
||||
- grafana
|
||||
- dashboard
|
||||
- dashboard folders
|
||||
- folder
|
||||
- folders
|
||||
- import
|
||||
- export
|
||||
- troubleshoot
|
||||
- time range
|
||||
- scripting
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
@@ -27,34 +19,31 @@ labels:
|
||||
- oss
|
||||
menuTitle: Manage dashboards
|
||||
title: Manage dashboards
|
||||
description: Learn about dashboard folders, generative AI features for dashboards, and troubleshooting
|
||||
description: Learn about dashboard management and generative AI features for dashboards
|
||||
weight: 8
|
||||
---
|
||||
|
||||
# Manage dashboards
|
||||
|
||||
This topic includes techniques you can use to manage your Grafana dashboards, including:
|
||||
On the **Dashboards** page, you can perform dashboard management tasks such as:
|
||||
|
||||
- [Creating and managing dashboard folders](#create-and-manage-dashboard-folders)
|
||||
- [Exporting and importing dashboards](#export-and-import-dashboards)
|
||||
- [Organizing dashboards](#organize-a-dashboard)
|
||||
- [Troubleshooting dashboards](#troubleshoot-dashboards)
|
||||
- [Browsing](#browse-dashboards) and [creating](#create-a-dashboard-folder) dashboard folders
|
||||
- [Managing folder permissions](#folder-permissions)
|
||||
- [Adding generative AI features to dashboards](#set-up-generative-ai-features-for-dashboards)
|
||||
|
||||
For more information about creating dashboards, refer to [Add and organize panels](../add-organize-panels).
|
||||
For more information about creating dashboards, refer to [Build dashboards][].
|
||||
|
||||
## Browse dashboards
|
||||
|
||||
On the **Dashboards** page, you can browse and manage folders and dashboards. This includes the options to:
|
||||
|
||||
- Create folders and dashboards
|
||||
- Create folders and dashboards.
|
||||
- Move dashboards between folders.
|
||||
- Delete multiple dashboards and folders.
|
||||
- Navigate to a folder.
|
||||
- Manage folder permissions. For more information, refer to [Dashboard permissions][].
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
As of Grafana 10.2, there is no longer a special **General** folder. Dashboards without a folder are now shown at the top level alongside folders.
|
||||
{{% /admonition %}}
|
||||
The page lists all the dashboards to which you have access, grouped into folders. Dashboards without a folder are displayed at the top level alongside folders.
|
||||
|
||||
## Create a dashboard folder
|
||||
|
||||
@@ -76,9 +65,9 @@ Alerts can't be placed in folders with slashes (\ /) in the name. If you wish to
|
||||
|
||||
**To edit the name of a folder:**
|
||||
|
||||
1. Click **Dashboards** in the main menu.
|
||||
1. Click **Dashboards** in the primary menu.
|
||||
1. Navigate to the folder by selecting it in the list, or searching for it.
|
||||
1. Click the pencil icon labelled **Edit title** in the header and update the name of the folder.
|
||||
1. Click the **Edit title** icon (pencil) in the header and update the name of the folder.
|
||||
|
||||
The new folder name is automatically saved.
|
||||
|
||||
@@ -88,7 +77,7 @@ You can assign permissions to a folder. Dashboards in the folder inherit any per
|
||||
|
||||
**To modify permissions for a folder:**
|
||||
|
||||
1. Click **Dashboards** in the main menu.
|
||||
1. Click **Dashboards** in the primary menu.
|
||||
1. Navigate to the folder by selecting it in the list, or searching for it.
|
||||
1. On the folder's page, click **Folder actions** and select **Manage permissions** in the drop-down.
|
||||
1. Update the permissions as desired.
|
||||
@@ -97,49 +86,6 @@ Changes are saved automatically.
|
||||
|
||||
For more information about dashboard permissions, refer to [Dashboard permissions][].
|
||||
|
||||
## Export and import dashboards
|
||||
|
||||
You can use the Grafana UI or the [HTTP API][] to export and import dashboards.
|
||||
|
||||
### Export a dashboard
|
||||
|
||||
The dashboard export action creates a Grafana JSON file that contains everything you need, including layout, variables, styles, data sources, queries, and so on, so that you can later import the dashboard.
|
||||
|
||||
1. Click **Dashboards** in the main menu.
|
||||
1. Open the dashboard you want to export.
|
||||
1. Click the **Share** icon in the top navigation bar.
|
||||
1. Click **Export**.
|
||||
|
||||
If you're exporting the dashboard to use in another instance, with different data source UIDs, enable the **Export for sharing externally** switch.
|
||||
|
||||
1. Click **Save to file**.
|
||||
|
||||
Grafana downloads a JSON file to your local machine.
|
||||
|
||||
#### Make a dashboard portable
|
||||
|
||||
If you want to export a dashboard for others to use, you can add template variables for things like a metric prefix (use a constant variable) and server name.
|
||||
|
||||
A template variable of the type `Constant` is automatically hidden in the dashboard, and is also added as a required input when the dashboard is imported.
|
||||
|
||||
### Import a dashboard
|
||||
|
||||
1. Click **Dashboards** in the left-side menu.
|
||||
1. Click **New** and select **Import** in the dropdown menu.
|
||||
1. Perform one of the following steps:
|
||||
|
||||
- Upload a dashboard JSON file
|
||||
- Paste a [Grafana.com](https://grafana.com) dashboard URL
|
||||
- Paste dashboard JSON text directly into the text area
|
||||
|
||||
The import process enables you to change the name of the dashboard, pick the data source you want the dashboard to use, and specify any metric prefixes (if the dashboard uses any).
|
||||
|
||||
### Discover dashboards on grafana.com
|
||||
|
||||
Find dashboards for common server applications at [Grafana.com/dashboards](https://grafana.com/dashboards).
|
||||
|
||||
{{< figure src="/media/docs/grafana/dashboards/screenshot-gcom-dashboards.png" alt="Preconfigured dashboards on grafana.com">}}
|
||||
|
||||
## Set up generative AI features for dashboards
|
||||
|
||||
{{< docs/public-preview product="Generative AI in dashboards" featureFlag="`dashgpt`" >}}
|
||||
@@ -153,56 +99,13 @@ To access these features, enable the `dashgpt` feature toggle. Then install and
|
||||
|
||||
When enabled, the **✨ Auto generate** option displays next to the **Title** and **Description** fields in your panels and dashboards, or when you press the **Save** button.
|
||||
|
||||
## Troubleshoot dashboards
|
||||
|
||||
This section provides information to help you solve common dashboard problems.
|
||||
|
||||
### Dashboard is slow
|
||||
|
||||
- Are you trying to render dozens (or hundreds or thousands) of time-series on a graph? This can cause the browser to lag. Try using functions like `highestMax` (in Graphite) to reduce the returned series.
|
||||
- Sometimes the series names can be very large. This causes larger response sizes. Try using `alias` to reduce the size of the returned series names.
|
||||
- Are you querying many time-series or for a long range of time? Both of these conditions can cause Grafana or your data source to pull in a lot of data, which may slow it down.
|
||||
- It could be high load on your network infrastructure. If the slowness isn't consistent, this may be the problem.
|
||||
|
||||
### Dashboard refresh rate issues
|
||||
|
||||
By default, Grafana queries your data source every 30 seconds. Setting a low refresh rate on your dashboards puts unnecessary stress on the backend. In many cases, querying this frequently isn't necessary because the data isn't being sent to the system such that changes would be seen.
|
||||
|
||||
We recommend the following:
|
||||
|
||||
- Only enable auto-refreshing on dashboards, panels, or variables unless if necessary. Users can refresh their browser manually, or you can set the refresh rate for a time period that makes sense (every ten minutes, every hour, and so on).
|
||||
- If it's required, then set the refresh rate to once a minute. Users can always refresh the dashboard manually.
|
||||
- If your dashboard has a longer time period (such as a week), then you really don't need automated refreshing.
|
||||
|
||||
#### Handling or rendering null data is wrong or confusing
|
||||
|
||||
Some applications publish data intermittently; for example, they only post a metric when an event occurs. By default, Grafana graphs connect lines between the data points. This can be very deceiving.
|
||||
|
||||
In the picture below we've enabled:
|
||||
|
||||
- Points and 3-point radius to highlight where data points are actually present.
|
||||
- **Connect null values\* is set to **Always\*\*.
|
||||
|
||||
{{< figure src="/static/img/docs/troubleshooting/grafana_null_connected.png" max-width="1200px" alt="Graph with null values connected" >}}
|
||||
|
||||
In this graph, we set graph to show bars instead of lines and set the **No value** under **Standard options** to **0**. There is a very big difference in the visuals.
|
||||
|
||||
{{< figure src="/static/img/docs/troubleshooting/grafana_null_zero.png" max-width="1200px" alt="Graph with null values not connected" >}}
|
||||
|
||||
### More examples
|
||||
|
||||
You can find more examples in `public/dashboards/` directory of your Grafana installation.
|
||||
|
||||
{{% docs/reference %}}
|
||||
[Dashboard permissions]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/administration/roles-and-permissions#dashboard-permissions"
|
||||
[Dashboard permissions]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/administration/roles-and-permissions#dashboard-permissions"
|
||||
|
||||
[panels]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations"
|
||||
[panels]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations"
|
||||
[Grafana LLM plugin documentation]: "/docs/grafana/ -> /docs/grafana-cloud/alerting-and-irm/machine-learning/configure/llm-plugin"
|
||||
[Grafana LLM plugin documentation]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/alerting-and-irm/machine-learning/configure/llm-plugin"
|
||||
|
||||
[HTTP API]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/developers/http_api"
|
||||
[HTTP API]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/developer-resources/api-reference/http-api"
|
||||
|
||||
[Grafana LLM plugin documentation]: "/docs/grafana/ -> /docs/grafana-cloud/alerting-and-irm/machine-learning/llm-plugin"
|
||||
[Grafana LLM plugin documentation]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/alerting-and-irm/machine-learning/llm-plugin"
|
||||
[Build dashboards]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/build-dashboards"
|
||||
[Build dashboards]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/dashboards/build-dashboards"
|
||||
{{% /docs/reference %}}
|
||||
|
||||
58
docs/sources/dashboards/troubleshoot-dashboards/index.md
Normal file
58
docs/sources/dashboards/troubleshoot-dashboards/index.md
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
aliases:
|
||||
- ../troubleshooting/troubleshoot-dashboards/
|
||||
- ../reference/timerange/
|
||||
canonical: https://grafana.com/docs/grafana/latest/dashboards/troubleshoot-dashboards/
|
||||
keywords:
|
||||
- grafana
|
||||
- dashboard
|
||||
- troubleshoot
|
||||
- time range
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
menuTitle: Troubleshoot dashboards
|
||||
title: Troubleshoot dashboards
|
||||
description: Learn how to troubleshoot common dashboard issues
|
||||
weight: 300
|
||||
---
|
||||
|
||||
# Troubleshoot dashboards
|
||||
|
||||
Use the following strategies to help you troubleshoot common dashboard problems.
|
||||
|
||||
## Dashboard is slow
|
||||
|
||||
- Are you trying to render dozens (or hundreds or thousands) of time series on a graph? This can cause the browser to lag. Try using functions like `highestMax` (in Graphite) to reduce the number of returned series.
|
||||
- Sometimes series names can be very large. This causes larger response sizes. Try using `alias` to reduce the size of the returned series names.
|
||||
- Are you querying many time series or a long time range? Both of these conditions can cause Grafana or your data source to pull in a lot of data, which may slow the dashboard down. Try reducing one or both of these.
|
||||
- There could be high load on your network infrastructure. If the slowness isn't consistent, this may be the problem.
|
||||
|
||||
## Dashboard refresh rate issues
|
||||
|
||||
By default, Grafana queries your data source every 30 seconds. However, setting a low refresh rate on your dashboards puts unnecessary stress on the backend. In many cases, querying this frequently isn't necessary because the data source isn't sending data often enough for there to be changes every 30 seconds.
|
||||
|
||||
We recommend the following:
|
||||
|
||||
- Only enable auto-refreshing on dashboards, panels, or variables if necessary. Users can refresh their browser manually.
|
||||
- If you require auto-refreshing, then set the refresh rate to a longer time period that makes sense, such as once a minute, every 10 minutes, or every hour.
|
||||
- Check the time range of your dashboard. If your dashboard has a longer time range, such as a week, then you really don't need automated refreshing and you should disable it.
|
||||
|
||||
## Handling or rendering null data is wrong or confusing
|
||||
|
||||
Some applications publish data intermittently; for example, they only post a metric when an event occurs. By default, Grafana graphs connect lines between the data points, but this can be deceptive.
|
||||
|
||||
The graph in the following image has:
|
||||
|
||||
- Points and 3-point radius enabled to highlight where data points are actually present.
|
||||
- **Connect null values** set to **Always**.
|
||||
|
||||
{{< figure src="/static/img/docs/troubleshooting/grafana_null_connected.png" max-width="1200px" alt="Graph with null values connected" >}}
|
||||
|
||||
The graph in this next image shows bars instead of lines and has the **No value** option under **Standard options** set to **0**.
|
||||
|
||||
{{< figure src="/static/img/docs/troubleshooting/grafana_null_zero.png" max-width="1200px" alt="Graph with null values not connected" >}}
|
||||
|
||||
As you can see, there's a significant difference in the visualizations.
|
||||
@@ -60,6 +60,7 @@ datasources:
|
||||
access: proxy
|
||||
url: http://localhost:3100
|
||||
jsonData:
|
||||
timeout: 60
|
||||
maxLines: 1000
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ weight: 1300
|
||||
|
||||
# Prometheus data source
|
||||
|
||||
Prometheus is an open-source database that uses an telemetry collector agent to scrape and store metrics used for monitoring and alerting. If you are just getting started with Prometheus, see [What is Prometheus?][intro-to-prometheus].
|
||||
Prometheus is an open-source database that uses a telemetry collector agent to scrape and store metrics used for monitoring and alerting. If you are just getting started with Prometheus, see [What is Prometheus?][intro-to-prometheus].
|
||||
|
||||
Grafana provides native support for Prometheus.
|
||||
For instructions on downloading Prometheus see [Get started with Grafana and Prometheus][get-started-prometheus].
|
||||
|
||||
@@ -20,11 +20,11 @@ weight: 1400
|
||||
|
||||
# Tempo data source
|
||||
|
||||
Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo/latest/), a high-volume, minimal-dependency trace storage, open-source tracing solution from Grafana Labs. This topic explains configuration and queries specific to the Tempo data source.
|
||||
Grafana ships with built-in support for [Tempo](https://grafana.com/docs/tempo/latest/), a high-volume, minimal-dependency trace storage, open source tracing solution from Grafana Labs. This topic explains configuration and queries specific to the Tempo data source.
|
||||
|
||||
For instructions on how to add a data source to Grafana, refer to the [administration documentation][data-source-management].
|
||||
Only users with the organization administrator role can add data sources.
|
||||
Administrators can also [configure the data source via YAML](#provision-the-data-source) with Grafana's provisioning system.
|
||||
Administrators can also [configure the data source via YAML][configure-tempo-data-source] with Grafana's provisioning system.
|
||||
|
||||
Once you've added the data source, you can [configure it]({{< relref "./configure-tempo-data-source/" >}}) so that your Grafana instance's users can create queries in its [query editor]({{< relref "./query-editor/" >}}) when they [build dashboards][build-dashboards] and use [Explore][explore].
|
||||
|
||||
@@ -37,6 +37,9 @@ Once you've added the data source, you can [configure it]({{< relref "./configur
|
||||
[configure-grafana-feature-toggles]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/configure-grafana#feature_toggles"
|
||||
[configure-grafana-feature-toggles]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/setup-grafana/configure-grafana#feature_toggles"
|
||||
|
||||
[configure-tempo-data-source]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/datasources/tempo/configure-tempo-data-source#provision-the-data-source"
|
||||
[configure-tempo-data-source]: "/docs/grafana-cloud/ -> docs/grafana-cloud/connect-externally-hosted/data-sources/tempo/configure-tempo-data-source#provision-the-data-source"
|
||||
|
||||
[data-source-management]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
[data-source-management]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/administration/data-source-management"
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ There are two ways to configure the trace to logs feature:
|
||||
You can also click **Open advanced data source picker** to see more options, including adding a data source.
|
||||
|
||||
1. Set start and end time shift. As the logs timestamps may not exactly match the timestamps of the spans in trace it may be necessary to search in larger or shifted time range to find the desired logs.
|
||||
1. Select which tags to use in the logs query. The tags you configure must be present in the spans attributes or resources for a trace to logs span link to appear. You can optionally configure a new name for the tag. This is useful for example if the tag has dots in the name and the target data source does not allow using dots in labels. In that case you can for example remap `http.status` to `http_status`.
|
||||
1. Select which tags to use in the logs query. The tags you configure must be present in the span's attributes or resources for a trace to logs span link to appear. You can optionally configure a new name for the tag. This is useful, for example, if the tag has dots in the name and the target data source does not allow using dots in labels. In that case, you can for example remap `http.status` (the span attribute) to `http_status` (the data source field). "Data source" in this context can refer to Loki, or another log data source.
|
||||
1. Optionally switch on the **Filter by trace ID** and/or **Filter by span ID** setting to further filter the logs if your logs consistently contain trace or span IDs.
|
||||
|
||||
### Configure a custom query
|
||||
|
||||
@@ -26,13 +26,37 @@ weight: 80
|
||||
|
||||
# Configure data links
|
||||
|
||||
You can use data link variables or data links to create links between panels.
|
||||
Data links allow you to provide more granular context to your links. You can create links that include the series name or even the value under the cursor. For example, if your visualization shows four servers, you can add a data link to one or two of them. You can also link panels using data links.
|
||||
|
||||
The link itself is accessible in different ways depending on the visualization. For the time series visualization you need to click a data point or line:
|
||||
|
||||

|
||||
|
||||
For visualizations like stat, gauge, or bar gauge you can click anywhere on the visualization to open the context menu:
|
||||
|
||||

|
||||
|
||||
If there's only one data link in the visualization, clicking anywhere on the visualization opens the link rather than the context menu.
|
||||
|
||||
## Supported visualizations
|
||||
|
||||
You can configure data links for the following visualizations:
|
||||
|
||||
| | | |
|
||||
| -------------------------- | ---------------------- | -------------------------------- |
|
||||
| [Bar chart][bar chart] | [Geomap][geomap] | [State timeline][state timeline] |
|
||||
| [Bar gauge][bar gauge] | [Heatmap][heatmap] | [Status history][status history] |
|
||||
| [Candlestick][candlestick] | [Histogram][histogram] | [Table][table] |
|
||||
| [Canvas][canvas] | [Pie chart][pie chart] | [Time series][time series] |
|
||||
| [Gauge][gauge] | [Stat][stat] | [Trend][trend] |
|
||||
|
||||
<!--Also xy chart -->
|
||||
|
||||
## Data link variables
|
||||
|
||||
You can use variables in data links to refer to series fields, labels, and values. For more information about data links, refer to [Data links](#data-links).
|
||||
Variables in data links let you send people to a detailed dashboard with preserved data filters. For example, you could use variables to specify a label, time range, series, or variable selection.
|
||||
|
||||
To see a list of available variables, type `$` in the data link **URL** field to see a list of variables that you can use.
|
||||
To see a list of available variables, enter `$` in the data link **URL** field.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
These variables changed in 6.4 so if you have an older version of Grafana, then use the version picker to select docs for an older version of Grafana.
|
||||
@@ -40,50 +64,61 @@ These variables changed in 6.4 so if you have an older version of Grafana, then
|
||||
|
||||
Azure Monitor, [CloudWatch][], and [Google Cloud Monitoring][] have pre-configured data links called _deep links_.
|
||||
|
||||
You can also use template variables in your data links URLs, refer to [Templates and variables][] for more information on template variables.
|
||||
You can also use template variables in your data links URLs. For more information, refer to [Templates and variables][].
|
||||
|
||||
## Time range panel variables
|
||||
### Time range panel variables
|
||||
|
||||
These variables allow you to include the current time range in the data link URL.
|
||||
These variables allow you to include the current time range in the data link URL:
|
||||
|
||||
- `__url_time_range` - current dashboard's time range (i.e. `?from=now-6h&to=now`)
|
||||
- `$__from and $__to` - For more information, refer to [Global variables][].
|
||||
| Variable | Description |
|
||||
| ------------------ | ------------------------------------------------------------------- |
|
||||
| `__url_time_range` | Current dashboard's time range (for example, `?from=now-6h&to=now`) |
|
||||
| `__from` | For more information, refer to [Global variables][]. |
|
||||
| `__to` | For more information, refer to [Global variables][]. |
|
||||
|
||||
## Series variables
|
||||
### Series variables
|
||||
|
||||
Series-specific variables are available under `__series` namespace:
|
||||
|
||||
- `__series.name` - series name to the URL
|
||||
| Variable | Description |
|
||||
| --------------- | ---------------------- |
|
||||
| `__series.name` | Series name to the URL |
|
||||
|
||||
## Field variables
|
||||
### Field variables
|
||||
|
||||
Field-specific variables are available under `__field` namespace:
|
||||
|
||||
- `__field.name` - the name of the field
|
||||
- `__field.labels.<LABEL>` - label's value to the URL. If your label contains dots, then use `__field.labels["<LABEL>"]` syntax.
|
||||
| Variable | Description |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
| `__field.name` | The name of the field |
|
||||
| `__field.labels.<LABEL>` | Label's value to the URL. If your label contains dots, then use `__field.labels["<LABEL>"]` syntax. |
|
||||
|
||||
## Value variables
|
||||
### Value variables
|
||||
|
||||
Value-specific variables are available under `__value` namespace:
|
||||
|
||||
- `__value.time` - value's timestamp (Unix ms epoch) to the URL (i.e. `?time=1560268814105`)
|
||||
- `__value.raw` - raw value
|
||||
- `__value.numeric` - numeric representation of a value
|
||||
- `__value.text` - text representation of a value
|
||||
- `__value.calc` - calculation name if the value is result of calculation
|
||||
| Variable | Description |
|
||||
| ----------------- | --------------------------------------------------------------------------------- |
|
||||
| `__value.time` | Value's timestamp (Unix ms epoch) to the URL (for example, `?time=1560268814105`) |
|
||||
| `__value.raw` | Raw value |
|
||||
| `__value.numeric` | Numeric representation of a value |
|
||||
| `__value.text` | Text representation of a value |
|
||||
| `__value.calc` | Calculation name if the value is result of calculation |
|
||||
|
||||
Using value-specific variables in data links can show different results depending on the set option of Tooltip mode.
|
||||
|
||||
## Data variables
|
||||
### Data variables
|
||||
|
||||
To access values and labels from other fields use:
|
||||
|
||||
- `${__data.fields[i]}` - value of field `i` (on the same row)
|
||||
- `${__data.fields["NameOfField"]}` - value of field using name instead of index
|
||||
- `${__data.fields["NameOfField"]}` - value of field using name instead of index
|
||||
- `${__data.fields[1].labels.cluster}` - access labels of another field
|
||||
| Variable | Description |
|
||||
| --------------------------------- | ------------------------------------------ |
|
||||
| `__data.fields[i]` | Value of field `i` (on the same row) |
|
||||
| `__data.fields["NameOfField"]` | Value of field using name instead of index |
|
||||
| `__data.fields["NameOfField"]` | Value of field using name instead of index |
|
||||
| `__data.fields[1].labels.cluster` | Access labels of another field |
|
||||
|
||||
## Template variables
|
||||
### Template variables
|
||||
|
||||
When linking to another dashboard that uses template variables, select variable values for whoever clicks the link.
|
||||
|
||||
@@ -97,54 +132,69 @@ When linking to another dashboard that uses template variables, select variable
|
||||
|
||||
If you want to add all of the current dashboard's variables to the URL, then use `${__all_variables}`.
|
||||
|
||||
## Data links
|
||||
## Add a data link
|
||||
|
||||
Data links allow you to provide more granular context to your links. You can create links that include the series name or even the value under the cursor. For example, if your visualization showed four servers, you could add a data link to one or two of them.
|
||||
|
||||
The link itself is accessible in different ways depending on the visualization. For the Graph you need to click on a data point or line, for a panel like
|
||||
Stat, Gauge, or Bar Gauge you can click anywhere on the visualization to open the context menu.
|
||||
|
||||
You can use variables in data links to send people to a detailed dashboard with preserved data filters. For example, you could use variables to specify a time range, series, and variable selection. For more information, refer to [Data link variables](#data-link-variables).
|
||||
|
||||
### Typeahead suggestions
|
||||
|
||||
When creating or updating a data link, press Cmd+Space or Ctrl+Space on your keyboard to open the typeahead suggestions to more easily add variables to your URL.
|
||||
|
||||
{{< figure src="/static/img/docs/data_link_typeahead.png" max-width= "800px" alt="Drop-down list with variable suggestions open from the URL field" >}}
|
||||
|
||||
### Add a data link
|
||||
|
||||
1. Hover over any part of the panel you want to which you want to add the data link to display the actions menu on the top right corner.
|
||||
1. Click the menu and select **Edit**.
|
||||
|
||||
To use a keyboard shortcut to open the panel, hover over the panel and press `e`.
|
||||
|
||||
1. Scroll down to the Data links section and expand it.
|
||||
1. Navigate to the panel to which you want to add the data link.
|
||||
1. Hover over any part of the panel to display the menu icon in the upper-right corner.
|
||||
1. Click the menu icon and select **Edit** to open the panel editor.
|
||||
1. In the panel edit pane, scroll down to the **Data links** section and expand it.
|
||||
1. Click **Add link**.
|
||||
1. Enter a **Title**. **Title** is a human-readable label for the link that will be displayed in the UI.
|
||||
1. Enter the **URL** you want to link to.
|
||||
1. In the dialog box that opens, enter a **Title**. This is a human-readable label for the link, which will be displayed in the UI.
|
||||
1. Enter the **URL** or variable to which you want to link.
|
||||
|
||||
You can even add one of the template variables defined in the dashboard. Click in the **URL** field and then type `$` or press Ctrl+Space or Cmd+Space to see a list of available variables. By adding template variables to your panel link, the link sends the user to the right context, with the relevant variables already set. For more information, refer to [Data link variables](#data-link-variables).
|
||||
To add a data link variable, click in the **URL** field and enter `$` or press Ctrl+Space or Cmd+Space to see a list of available variables.
|
||||
|
||||
1. If you want the link to open in a new tab, then select **Open in a new tab**.
|
||||
1. Click **Save** to save changes and close the window.
|
||||
1. Click **Save** in the upper right to save your changes to the dashboard.
|
||||
|
||||
### Update a data link
|
||||
|
||||
1. Scroll down to the Data links section, expand it, and find the link that you want to make changes to.
|
||||
1. Click the Edit (pencil) icon to open the Edit link window.
|
||||
1. Make any necessary changes.
|
||||
1. Click **Save** to save changes and close the window.
|
||||
1. Click **Save** in the upper right to save your changes to the dashboard.
|
||||
|
||||
### Delete a data link
|
||||
|
||||
1. Scroll down to the Data links section, expand it, and find the link that you want to delete.
|
||||
1. Click the **X** icon next to the link you want to delete.
|
||||
1. Click **Save** in the upper right to save your changes to the dashboard.
|
||||
1. If you want the link to open in a new tab, then toggle the **Open in a new tab** switch.
|
||||
1. Click **Save** to save changes and close the dialog box.
|
||||
1. Click **Apply** to see your changes in the dashboard.
|
||||
1. Click the **Save dashboard** icon to save your changes to the dashboard.
|
||||
|
||||
{{% docs/reference %}}
|
||||
[bar chart]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/bar-chart"
|
||||
[bar chart]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/bar-chart"
|
||||
|
||||
[bar gauge]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/bar-gauge"
|
||||
[bar gauge]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/bar-gauge"
|
||||
|
||||
[candlestick]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/candlestick"
|
||||
[candlestick]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/candlestick"
|
||||
|
||||
[canvas]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/canvas"
|
||||
[canvas]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/canvas"
|
||||
|
||||
[gauge]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/gauge"
|
||||
[gauge]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/gauge"
|
||||
|
||||
[geomap]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/geomap"
|
||||
[geomap]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/geomap"
|
||||
|
||||
[heatmap]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/heatmap"
|
||||
[heatmap]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/heatmap"
|
||||
|
||||
[histogram]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/histogram"
|
||||
[histogram]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/histogram"
|
||||
|
||||
[pie chart]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/pie-chart"
|
||||
[pie chart]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/pie-chart"
|
||||
|
||||
[stat]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/stat"
|
||||
[stat]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/stat"
|
||||
|
||||
[state timeline]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/state-timeline"
|
||||
[state timeline]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/state-timeline"
|
||||
|
||||
[status history]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/status-history"
|
||||
[status history]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/status-history"
|
||||
|
||||
[table]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/table"
|
||||
[table]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/table"
|
||||
|
||||
[time series]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/time-series"
|
||||
[time series]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series"
|
||||
|
||||
[trend]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/trend"
|
||||
[trend]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/trend"
|
||||
|
||||
[Cloudwatch]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/datasources/aws-cloudwatch/query-editor#deep-link-grafana-panels-to-the-cloudwatch-console-1"
|
||||
[Cloudwatch]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/connect-externally-hosted/data-sources/aws-cloudwatch/query-editor#deep-link-grafana-panels-to-the-cloudwatch-console-1"
|
||||
|
||||
@@ -152,8 +202,8 @@ When creating or updating a data link, press Cmd+Space or Ctrl+Space on your key
|
||||
[Google Cloud Monitoring]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/connect-externally-hosted/data-sources/google-cloud-monitoring/query-editor#deep-link-from-grafana-panels-to-the-google-cloud-console-metrics-explorer"
|
||||
|
||||
[Templates and variables]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables"
|
||||
[Templates and variables]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables"
|
||||
[Templates and variables]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/dashboards/variables"
|
||||
|
||||
[Global variables]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#**from-and-**to"
|
||||
[Global variables]: "/docs/grafana-cloud/ -> /docs/grafana/<GRAFANA VERSION>/dashboards/variables/add-template-variables#**from-and-**to"
|
||||
[Global variables]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/dashboards/variables/add-template-variables#**from-and-**to"
|
||||
{{% /docs/reference %}}
|
||||
|
||||
@@ -16,34 +16,55 @@ labels:
|
||||
- oss
|
||||
menuTitle: Configure value mappings
|
||||
title: Configure value mappings
|
||||
description: Configure value mappings to change the visual treatment of data in your visualizations
|
||||
description: Configure value mappings to change how data appears in your visualizations
|
||||
weight: 90
|
||||
---
|
||||
|
||||
# Configure value mappings
|
||||
|
||||
In addition to field overrides, value mapping is a technique that you can use to change the visual treatment of data that appears in a visualization.
|
||||
In addition to field overrides, value mapping is a technique you can use to change how data appears in a visualization.
|
||||
|
||||
Values mapped using value mappings bypass the unit formatting. This means that a text value mapped to a numerical value is not formatted using the configured unit.
|
||||
For example, the mapping applied in the following image causes the visualization to display the text `Cold`, `Good`, and `Hot` in blue, green, and red for ranges of temperatures rather than actual temperature values. Using value mappings this way can make data faster and easier to understand and interpret.
|
||||
|
||||

|
||||

|
||||
|
||||
If value mappings are present in a panel, then Grafana displays a summary in the side pane of the panel editor.
|
||||
Value mappings bypass unit formatting set in the **Standard options** section of panel editor, like color or number of decimal places displayed. When value mappings are present in a panel, Grafana displays a summary of them in the **Value mappings** section of the editor panel.
|
||||
|
||||
## Supported visualizations
|
||||
|
||||
You can configure value mappings for the following visualizations:
|
||||
|
||||
| | | |
|
||||
| -------------------------- | -------------------------------- | -------------------------------- |
|
||||
| [Bar chart][bar chart] | [Geomap][geomap] | [Status history][status history] |
|
||||
| [Bar gauge][bar gauge] | [Histogram][histogram] | [Table][table] |
|
||||
| [Candlestick][candlestick] | [Pie chart][pie chart] | [Time series][time series] |
|
||||
| [Canvas][canvas] | [Stat][stat] | [Trend][trend] |
|
||||
| [Gauge][gauge] | [State timeline][state timeline] | |
|
||||
|
||||
## Types of value mappings
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
The new value mappings are not compatible with some visualizations, such as Graph (old), Text, and Heatmap.
|
||||
{{% /admonition %}}
|
||||
Grafana supports the following value mapping types:
|
||||
|
||||
Grafana supports the following value mappings:
|
||||
### Value
|
||||
|
||||
- **Value:** Maps text values to a color or different display text. For example, you can configure a value mapping so that all instances of the value `10` appear as **Perfection!** rather than the number.
|
||||
- **Range:** Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display **Low** or **High** rather than the number.
|
||||
- **Regex:** Maps regular expressions to replacement text and a color. For example, if a value is `www.example.com`, you can configure a regex value mapping so that Grafana displays **www** and truncates the domain.
|
||||
- **Special** Maps special values like `Null`, `NaN` (not a number), and boolean values like `true` and `false` to a display text and color. For example, you can configure a special value mapping so that `null` values appear as **N/A**.
|
||||
A **Value** mapping maps specific values to text and a color. For example, you can configure a mapping so that all instances of the value `10` appear as **Perfection!** rather than the number. Use **Value** mapping when you want to format a single value.
|
||||

|
||||
|
||||
You can also use the dots on the left to drag and reorder value mappings in the list.
|
||||
### Range
|
||||
|
||||
A **Range** mapping maps numerical ranges to text and a color. For example, if a value is within a certain range, you can configure a range value mapping to display **Low** or **High** rather than the number. Use **Range** mapping when you want to format multiple, continuous values.
|
||||

|
||||
|
||||
### Regex
|
||||
|
||||
A **Regex** mapping maps regular expressions to text and a color. For example, if a value is `www.example.com`, you can configure a regular expression value mapping so that Grafana displays **www** and truncates the domain. Use the **Regex** mapping when you want to format the text and color of a regular expression value.
|
||||

|
||||
|
||||
### Special
|
||||
|
||||
A **Special** mapping maps special values like `Null`, `NaN` (not a number), and boolean values like `true` and `false` to text and color. For example, you can configure a special value mapping so that `null` values appear as **N/A**. Use the **Special** mapping when you want to format uncommon, boolean, or empty values.
|
||||

|
||||
|
||||
## Examples
|
||||
|
||||
@@ -51,19 +72,19 @@ Refer to the following examples to learn more about value mapping.
|
||||
|
||||
### Time series example
|
||||
|
||||
The following image shows a time series visualization with value mappings. Value mapping colors are not applied to this visualization, but the display text is shown on the axis.
|
||||
The following image shows a time series visualization with value mappings. Value mapping colors aren't applied to this visualization, but the display text is shown on the axis.
|
||||
|
||||

|
||||
|
||||
### Stat example
|
||||
|
||||
The following image shows a Stat visualization with value mappings and text colors applied. You can hide the sparkline so it doesn't interfere with the values.
|
||||
The following image shows a stat visualization with value mappings and text colors applied. You can hide the sparkline so it doesn't interfere with the values.
|
||||
|
||||

|
||||
|
||||
### Bar gauge example
|
||||
|
||||
The following image shows a bar gauge visualization with value mappings. The value mapping colors are applied to the text, but not to the gauges.
|
||||
The following image shows a bar gauge visualization with value mappings. Note that the value mapping colors are applied to the text, but not to the gauges.
|
||||
|
||||

|
||||
|
||||
@@ -73,64 +94,67 @@ The following image shows a table visualization with value mappings. If you want
|
||||
|
||||

|
||||
|
||||
## Map a value
|
||||
## Add a value mapping
|
||||
|
||||
Map a value when you want to format a single value.
|
||||
1. Navigate to the panel you want to update.
|
||||
1. Hover over any part of the panel you want to work on to display the menu on the top right corner.
|
||||
1. Click the menu and select **Edit**.
|
||||
1. Scroll to the **Value mappings** section and expand it.
|
||||
1. Click **Add value mappings**.
|
||||
1. Click **Add a new mapping** and then select one of the following:
|
||||
|
||||
- **Value** - Enter a single value to match.
|
||||
- **Range** - Enter the beginning and ending values of a range to match.
|
||||
- **Regex** - Enter a regular expression pattern to match.
|
||||
- **Special** - Select a special value to match.
|
||||
|
||||
1. Open a panel for which you want to map a value.
|
||||
1. In panel display options, locate the **Value mappings** section and click **Add value mappings**.
|
||||
1. Click **Add a new mapping** and then select **Value**.
|
||||
1. Enter the value for Grafana to match.
|
||||
1. (Optional) Enter display text.
|
||||
1. (Optional) Set the color.
|
||||
1. (Optional) Set an icon (canvas visualizations only).
|
||||
1. Click **Update** to save the value mapping.
|
||||
|
||||

|
||||
After you've added a mapping, the **Edit value mappings** button replaces the **Add value mappings** button. Click the edit button to add or update mappings.
|
||||
|
||||
## Map a range
|
||||
{{% docs/reference %}}
|
||||
[bar chart]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/bar-chart"
|
||||
[bar chart]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/bar-chart"
|
||||
|
||||
Map a range of values when you want to format multiple, continuous values.
|
||||
[bar gauge]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/bar-gauge"
|
||||
[bar gauge]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/bar-gauge"
|
||||
|
||||
1. Edit the panel for which you want to map a range of values.
|
||||
1. In panel display options, in the **Value mappings** section, click **Add value mappings**.
|
||||
1. Click **Add a new mapping** and then select **Range**.
|
||||
1. Enter the beginning and ending values in the range for Grafana to match.
|
||||
1. (Optional) Enter display text.
|
||||
1. (Optional) Set the color.
|
||||
1. Click **Update** to save the value mapping.
|
||||
[candlestick]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/candlestick"
|
||||
[candlestick]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/candlestick"
|
||||
|
||||

|
||||
[canvas]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/canvas"
|
||||
[canvas]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/canvas"
|
||||
|
||||
## Map a regular expression
|
||||
[gauge]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/gauge"
|
||||
[gauge]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/gauge"
|
||||
|
||||
Map a regular expression when you want to format the text and color of a regular expression value.
|
||||
[geomap]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/geomap"
|
||||
[geomap]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/geomap"
|
||||
|
||||
1. Edit the panel for which you want to map a regular expression.
|
||||
1. In the **Value mappings** section of the panel display options, click **Add value mappings**.
|
||||
1. Click **Add a new mapping** and then select **Regex**.
|
||||
1. Enter the regular expression pattern for Grafana to match.
|
||||
1. (Optional) Enter display text.
|
||||
1. (Optional) Set the color.
|
||||
1. Click **Update** to save the value mapping.
|
||||
[histogram]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/histogram"
|
||||
[histogram]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/histogram"
|
||||
|
||||
## Map a special value
|
||||
[pie chart]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/pie-chart"
|
||||
[pie chart]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/pie-chart"
|
||||
|
||||
Map a special value when you want to format uncommon, boolean, or empty values.
|
||||
[stat]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/stat"
|
||||
[stat]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/stat"
|
||||
|
||||
1. Edit the panel for which you want to map a special value.
|
||||
1. In panel display options, locate the **Value mappings** section and click **Add value mappings**.
|
||||
1. Click **Add a new mapping** and then select **Special**.
|
||||
1. Select the special value for Grafana to match.
|
||||
1. (Optional) Enter display text.
|
||||
1. (Optional) Set the color.
|
||||
1. Click **Update** to save the value mapping.
|
||||
[state timeline]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/state-timeline"
|
||||
[state timeline]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/state-timeline"
|
||||
|
||||

|
||||
[status history]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/status-history"
|
||||
[status history]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/status-history"
|
||||
|
||||
## Edit a value mapping
|
||||
[table]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/table"
|
||||
[table]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/table"
|
||||
|
||||
You can edit a value mapping at any time.
|
||||
[time series]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/time-series"
|
||||
[time series]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/time-series"
|
||||
|
||||
1. Edit the panel that contains the value mapping you want to edit.
|
||||
1. In the panel display options, in the **Value mappings** section, click **Edit value mappings**.
|
||||
1. Make the changes and click **Update**.
|
||||
[trend]: "/docs/grafana/ -> /docs/grafana/<GRAFANA VERSION>/panels-visualizations/visualizations/trend"
|
||||
[trend]: "/docs/grafana-cloud/ -> /docs/grafana-cloud/visualizations/panels-visualizations/visualizations/trend"
|
||||
{{% /docs/reference %}}
|
||||
|
||||
@@ -369,7 +369,7 @@ The maximum number of connections in the idle connection pool.
|
||||
|
||||
### max_open_conn
|
||||
|
||||
The maximum number of open connections to the database.
|
||||
The maximum number of open connections to the database. For MYSQL, configure this setting on both Grafana and the database. For more information, refer to [`sysvar_max_connections`](https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_max_connections).
|
||||
|
||||
### conn_max_lifetime
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ Experimental features might be changed or removed without prior notice.
|
||||
| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected |
|
||||
| `pluginsSkipHostEnvVars` | Disables passing host environment variable to plugin processes |
|
||||
| `tableSharedCrosshair` | Enables shared crosshair in table panel |
|
||||
| `newFolderPicker` | Enables the nested folder picker without having nested folders enabled |
|
||||
|
||||
## Development feature toggles
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@ Request security is available in Grafana Enterprise v7.4 and later versions.
|
||||
|
||||
Configure a firewall to restrict Grafana from making network requests to sensitive internal web services.
|
||||
|
||||
There are many firewall tools available, refer to the documentation for your specific security tool. For example, Linux users can use [iptables](https://en.wikipedia.org/wiki/Iptables).
|
||||
There are many firewall tools available. Refer to the documentation for your specific security tool. For example, Linux users can use [iptables](https://en.wikipedia.org/wiki/Iptables).
|
||||
|
||||
## Proxy server
|
||||
|
||||
Require all network requests being made by Grafana to go through a proxy server.
|
||||
You can require all network requests made by Grafana to go through a proxy server.
|
||||
|
||||
Self-hosted reverse proxy options include but are not limited to:
|
||||
|
||||
@@ -47,9 +47,9 @@ Self-hosted reverse proxy options include but are not limited to:
|
||||
|
||||
## Limit Viewer query permissions
|
||||
|
||||
Users with the Viewer role can enter _any possible query_ in _any_ of the data sources available in the **organization**, not just the queries that are defined on the dashboards for which the user has Viewer permissions.
|
||||
Users with the `Viewer role` can enter _any possible query_ in _any_ of the data sources available in the **organization**, not just the queries that are defined on the dashboards for which the user has Viewer permissions.
|
||||
|
||||
**For example:** In a Grafana instance with one data source, one dashboard, and one panel that has one query defined, you might assume that a Viewer can only see the result of the query defined in that panel. Actually, the Viewer has access to send any query to the data source. With a command-line tool like curl (there are lots of tools for this), the Viewer can make their own query to the data source and potentially access sensitive data.
|
||||
For example, in a Grafana instance with one data source, one dashboard, and one panel that has one query defined, you might assume that a Viewer can only see the result of the query defined in that panel. Actually, the Viewer has access to send any query to the data source. With a command-line tool like curl (there are many tools for this), the Viewer can make their own query to the data source and potentially access sensitive data.
|
||||
|
||||
To address this vulnerability, you can restrict data source query access in the following ways:
|
||||
|
||||
@@ -58,8 +58,10 @@ To address this vulnerability, you can restrict data source query access in the
|
||||
|
||||
## Implications of enabling anonymous access to dashboards
|
||||
|
||||
When you enable anonymous access to a dashboard, it is publicly available. This section lists the security implications of enabling Anonymous access.
|
||||
When you enable anonymous access in Grafana, any visitor or user can use Grafana as a Viewer without signing in. This section lists the security implications of enabling Anonymous access.
|
||||
|
||||
- Anyone with the URL can access the dashboard.
|
||||
- Anyone can make view calls to the API and list all folders, dashboards, and data sources.
|
||||
- Anyone with the URL of a dashboard accessible by the Viewer role can access that dashboard.
|
||||
- New dashboards are publicly available unless the dashboard creator hides them from **all Viewers**.
|
||||
- Anyone can edit or delete dashboards that have granted Edit or Admin abilities to Viewers.
|
||||
- Anyone can make `view` calls to the API and list all folders, dashboards, and data sources.
|
||||
- Anyone can make arbitrary queries to any data source that the Grafana instance is configured with.
|
||||
|
||||
@@ -326,6 +326,40 @@ Payload:
|
||||
|
||||
This section includes examples of setting up generic OAuth2 integration.
|
||||
|
||||
### Set up OAuth2 with Descope
|
||||
|
||||
To set up generic OAuth2 authentication with Descope, follow these steps:
|
||||
|
||||
1. Create a Descope Project [here](https://app.descope.com/gettingStarted), and go through the Getting Started Wizard to configure your authentication. You can skip step if you already have Descope project set up.
|
||||
|
||||
1. If you wish to use a flow besides `Sign Up or In`, go to the **IdP Applications** menu in the console, and select your IdP application. Then alter the **Flow Hosting URL** query parameter `?flow=sign-up-or-in` to change which flow id you wish to use.
|
||||
|
||||
1. Click **Save**.
|
||||
|
||||
1. Update the `[auth.generic_oauth]` section of the Grafana configuration file using the values from the **Settings** tab:
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
You can get your Client ID (Descope Project ID) under [Project Settings](https://app.descope.com/settings/project). Your Client Secret (Descope Access Key) can be generated under [Access Keys](https://app.descope.com/accesskeys).
|
||||
{{% /admonition %}}
|
||||
|
||||
```bash
|
||||
[auth.generic_oauth]
|
||||
enabled = true
|
||||
allow_sign_up = true
|
||||
auto_login = false
|
||||
team_ids =
|
||||
allowed_organizations =
|
||||
name = Descope
|
||||
client_id = <Descope Project ID>
|
||||
client_secret = <Descope Access Key>
|
||||
scopes = openid profile email descope.claims descope.custom_claims
|
||||
auth_url = https://api.descope.com/oauth2/v1/authorize
|
||||
token_url = https://api.descope.com/oauth2/v1/token
|
||||
api_url = https://api.descope.com/oauth2/v1/userinfo
|
||||
use_pkce = true
|
||||
use_refresh_token = true
|
||||
```
|
||||
|
||||
### Set up OAuth2 with Auth0
|
||||
|
||||
To set up generic OAuth2 authentication with Auth0, follow these steps:
|
||||
|
||||
@@ -18,9 +18,11 @@ weight: 300
|
||||
The LDAP integration in Grafana allows your Grafana users to login with their LDAP credentials. You can also specify mappings between LDAP
|
||||
group memberships and Grafana Organization user roles.
|
||||
|
||||
> [Enhanced LDAP authentication]({{< relref "../enhanced-ldap" >}}) is available in [Grafana Cloud](/docs/grafana-cloud/) and in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}).
|
||||
{{% admonition type="note" %}}
|
||||
[Enhanced LDAP authentication]({{< relref "../enhanced-ldap" >}}) is available in [Grafana Cloud](/docs/grafana-cloud/) and in [Grafana Enterprise]({{< relref "../../../../introduction/grafana-enterprise" >}}).
|
||||
{{% /admonition %}}
|
||||
|
||||
> Refer to [Role-based access control]({{< relref "../../../../administration/roles-and-permissions/access-control" >}}) to understand how you can control access with role-based permissions.
|
||||
Refer to [Role-based access control]({{< relref "../../../../administration/roles-and-permissions/access-control" >}}) to understand how you can control access with role-based permissions.
|
||||
|
||||
## Supported LDAP Servers
|
||||
|
||||
@@ -127,6 +129,10 @@ member_of = "memberOf"
|
||||
email = "email"
|
||||
```
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
Whenever you modify the ldap.toml file, you must restart Grafana in order for the change(s) to take effect.
|
||||
{{% /admonition %}}
|
||||
|
||||
### Using environment variables
|
||||
|
||||
You can interpolate variables in the TOML configuration from environment variables. For instance, you could externalize your `bind_password` that way:
|
||||
@@ -135,17 +141,19 @@ You can interpolate variables in the TOML configuration from environment variabl
|
||||
bind_password = "${LDAP_ADMIN_PASSWORD}"
|
||||
```
|
||||
|
||||
## LDAP Debug View
|
||||
## LDAP debug view
|
||||
|
||||
> Only available in Grafana v6.4+
|
||||
{{% admonition type="note" %}}
|
||||
Available in Grafana v6.4+
|
||||
{{% /admonition %}}
|
||||
|
||||
Grafana has an LDAP debug view built-in which allows you to test your LDAP configuration directly within Grafana. At the moment of writing, only Grafana admins can use the LDAP debug view.
|
||||
Grafana has an LDAP debug view built-in which allows you to test your LDAP configuration directly within Grafana. Only Grafana admins can use the LDAP debug view.
|
||||
|
||||
Within this view, you'll be able to see which LDAP servers are currently reachable and test your current configuration.
|
||||
|
||||
{{< figure src="/static/img/docs/ldap_debug.png" class="docs-image--no-shadow" max-width="600px" >}}
|
||||
|
||||
To use the debug view:
|
||||
To use the debug view, complete the following steps:
|
||||
|
||||
1. Type the username of a user that exists within any of your LDAP server(s)
|
||||
1. Then, press "Run"
|
||||
@@ -157,9 +165,7 @@ To use the debug view:
|
||||
|
||||
{{< figure src="/static/img/docs/ldap_sync_debug.png" class="docs-image--no-shadow" max-width="600px" >}}
|
||||
|
||||
### Bind
|
||||
|
||||
#### Bind and Bind Password
|
||||
### Bind and bind password
|
||||
|
||||
By default the configuration expects you to specify a bind DN and bind password. This should be a read only user that can perform LDAP searches.
|
||||
When the user DN is found a second bind is performed with the user provided username and password (in the normal Grafana login form).
|
||||
@@ -169,7 +175,7 @@ bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||
bind_password = "grafana"
|
||||
```
|
||||
|
||||
#### Single Bind Example
|
||||
#### Single bind example
|
||||
|
||||
If you can provide a single bind expression that matches all possible users, you can skip the second bind and bind against the user DN directly.
|
||||
This allows you to not specify a bind_password in the configuration file.
|
||||
@@ -183,7 +189,7 @@ The search filter and search bases settings are still needed to perform the LDAP
|
||||
|
||||
### POSIX schema
|
||||
|
||||
If your LDAP server does not support the memberOf attribute add these options:
|
||||
If your LDAP server does not support the `memberOf` attribute, add the following options:
|
||||
|
||||
```bash
|
||||
## Group search filter, to retrieve the groups of which the user is a member (only set if memberOf attribute is not available)
|
||||
@@ -194,7 +200,7 @@ group_search_base_dns = ["ou=groups,dc=grafana,dc=org"]
|
||||
group_search_filter_user_attribute = "uid"
|
||||
```
|
||||
|
||||
### Group Mappings
|
||||
### Group mappings
|
||||
|
||||
In `[[servers.group_mappings]]` you can map an LDAP group to a Grafana organization and role. These will be synced every time the user logs in, with LDAP being the authoritative source.
|
||||
|
||||
@@ -231,8 +237,12 @@ org_role = "Viewer"
|
||||
| `org_id` | No | The Grafana organization database id. Setting this allows for multiple group_dn's to be assigned to the same `org_role` provided the `org_id` differs | `1` (default org id) |
|
||||
| `grafana_admin` | No | When `true` makes user of `group_dn` Grafana server admin. A Grafana server admin has admin access over all organizations and users. Available in Grafana v5.3 and above | `false` |
|
||||
|
||||
Note: Commenting out a group mapping requires also commenting out the header of
|
||||
said group or it will fail validation as an empty mapping. Example:
|
||||
{{% admonition type="note" %}}
|
||||
Commenting out a group mapping requires also commenting out the header of
|
||||
said group or it will fail validation as an empty mapping.
|
||||
{{% /admonition %}}
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
[[servers]]
|
||||
@@ -265,7 +275,7 @@ To configure `group_search_filter`:
|
||||
**Active Directory example:**
|
||||
|
||||
Active Directory groups store the Distinguished Names (DNs) of members, so your filter will need to know the DN for the user based only on the submitted username.
|
||||
Multiple DN templates can be searched by combining filters with the LDAP OR-operator. Two examples:
|
||||
Multiple DN templates are searched by combining filters with the LDAP OR-operator. Two examples:
|
||||
|
||||
```bash
|
||||
group_search_filter = "(member:1.2.840.113556.1.4.1941:=%s)"
|
||||
@@ -281,10 +291,12 @@ group_search_filter_user_attribute = "cn"
|
||||
|
||||
For more information on AD searches see [Microsoft's Search Filter Syntax](https://docs.microsoft.com/en-us/windows/desktop/adsi/search-filter-syntax) documentation.
|
||||
|
||||
For troubleshooting, by changing `member_of` in `[servers.attributes]` to "dn" it will show you more accurate group memberships when [debug is enabled](#troubleshooting).
|
||||
For troubleshooting, changing `member_of` in `[servers.attributes]` to "dn" will show you more accurate group memberships when [debug is enabled](#troubleshooting).
|
||||
|
||||
## Configuration examples
|
||||
|
||||
The following examples describe different LDAP configuration options.
|
||||
|
||||
### OpenLDAP
|
||||
|
||||
[OpenLDAP](http://www.openldap.org/) is an open source directory service.
|
||||
|
||||
@@ -15,7 +15,7 @@ weight: 400
|
||||
|
||||
This topic guides you through installing Grafana via the official Docker images. Specifically, it covers running Grafana via the Docker command line interface (CLI) and docker-compose.
|
||||
|
||||
{{< youtube id="FlDfcMbSLXs" >}}
|
||||
{{< youtube id="FlDfcMbSLXs" start="703">}}
|
||||
|
||||
Grafana Docker images come in two editions:
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ weight: 500
|
||||
|
||||
On this page, you will find instructions for installing and running Grafana on Kubernetes using Kubernetes manifests for the setup. If Helm is your preferred option, refer to [Grafana Helm community charts](https://github.com/grafana/helm-charts).
|
||||
|
||||
Watch this video to learn more about installing Grafana on Kubernetes: {{< vimeo 871940219 >}}
|
||||
Watch this video to learn more about installing Grafana on Kubernetes: {{< youtube id="DEv5wtZxNCk" start="1872">}}
|
||||
|
||||
## Before you begin
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ To install Grafana on macOS using the standalone binaries, complete the followin
|
||||
./bin/grafana server
|
||||
```
|
||||
|
||||
Alternatively, watch the Grafana for Beginners video below:
|
||||
|
||||
{{< youtube id="T51Qa7eE3W8" >}}
|
||||
|
||||
## Next steps
|
||||
|
||||
- [Start the Grafana server]({{< relref "../../start-restart-grafana" >}})
|
||||
|
||||
@@ -86,6 +86,8 @@ Query results for both the editor and the builder are returned in a table. Selec
|
||||
|
||||
Selecting the trace ID from the returned results opens a trace diagram. Selecting a span from the returned results opens a trace diagram and reveals the relevant span in the trace diagram (above, the highlighted blue line).
|
||||
|
||||
In the trace diagram, the bold text on the left side of each span indicates the service name, for example `mythical-requester: requester`, and it is hidden when subsequent spans have the same service name (nested spans). Each service has a color assigned to it, which is visible to the left of the name and timeline in the graph. Spans with the same color belong to the same service. The grey text to the right of the service name indicates the span name.
|
||||
|
||||
### Streaming results
|
||||
|
||||
The Tempo data source supports streaming responses to TraceQL queries so you can see partial query results as they come in without waiting for the whole query to finish.
|
||||
|
||||
23
docs/sources/upgrade-guide/upgrade-v10.3/index.md
Normal file
23
docs/sources/upgrade-guide/upgrade-v10.3/index.md
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
description: Guide for upgrading to Grafana v10.3
|
||||
keywords:
|
||||
- grafana
|
||||
- configuration
|
||||
- documentation
|
||||
- upgrade
|
||||
- '10.3'
|
||||
- '10.2.3'
|
||||
title: Upgrade to Grafana v10.3
|
||||
menuTitle: Upgrade to v10.3
|
||||
weight: 1400
|
||||
---
|
||||
|
||||
# Upgrade to Grafana v10.3
|
||||
|
||||
{{< docs/shared lookup="upgrade/intro.md" source="grafana" version="<GRAFANA VERSION>" >}}
|
||||
|
||||
{{< docs/shared lookup="back-up/back-up-grafana.md" source="grafana" version="<GRAFANA VERSION>" leveloffset="+1" >}}
|
||||
|
||||
{{< docs/shared lookup="upgrade/upgrade-common-tasks.md" source="grafana" version="<GRAFANA VERSION>" >}}
|
||||
|
||||
## Technical notes
|
||||
@@ -76,6 +76,7 @@ For a complete list of every change, with links to pull requests and related iss
|
||||
|
||||
## Grafana 10
|
||||
|
||||
- [What's new in 10.3](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/whatsnew/whats-new-in-v10-3/)
|
||||
- [What's new in 10.2](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/whatsnew/whats-new-in-v10-2/)
|
||||
- [What's new in 10.1]({{< relref "whats-new-in-v10-1/" >}})
|
||||
- [What's new in 10.0]({{< relref "whats-new-in-v10-0/" >}})
|
||||
|
||||
@@ -100,7 +100,7 @@ You can now use generative AI to assist you in your Grafana dashboards. So far g
|
||||
- **Generate panel and dashboard titles and descriptions** - You can now generate a title and description for your panel or dashboard based on the data you've added to it. This is useful when you want to quickly visualize your data and don't want to spend time coming up with a title or description.
|
||||
- **Generate dashboard save changes summary** - You can now generate a summary of the changes you've made to a dashboard when you save it. This is great for effortlessly tracking the history of a dashboard.
|
||||
|
||||
To enable these features, you must first enable the `dashgpt` [feature toggle](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#experimental-feature-toggles). Then install and configure Grafana's LLM app plugin. For more information, refer to the [Grafana LLM app plugin documentation](https://grafana.com/docs/grafana-cloud/alerting-and-irm/machine-learning/llm-plugin/).
|
||||
To enable these features, you must first enable the `dashgpt` [feature toggle](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/#experimental-feature-toggles). Then install and configure Grafana's LLM app plugin. For more information, refer to the [Grafana LLM app plugin documentation](https://grafana.com/docs/grafana-cloud/alerting-and-irm/machine-learning/configure/llm-plugin/).
|
||||
|
||||
When enabled, look for the **✨ Auto generate** option next to the **Title** and **Description** fields in your panels and dashboards, or when you press the **Save** button.
|
||||
|
||||
|
||||
420
docs/sources/whatsnew/whats-new-in-v10-3.md
Normal file
420
docs/sources/whatsnew/whats-new-in-v10-3.md
Normal file
@@ -0,0 +1,420 @@
|
||||
---
|
||||
description: Feature and improvement highlights for Grafana v10.3
|
||||
keywords:
|
||||
- grafana
|
||||
- new
|
||||
- documentation
|
||||
- '10.3'
|
||||
- '10.2.3'
|
||||
- release notes
|
||||
labels:
|
||||
products:
|
||||
- cloud
|
||||
- enterprise
|
||||
- oss
|
||||
title: What's new in Grafana v10.3
|
||||
weight: -40
|
||||
---
|
||||
|
||||
# What’s new in Grafana v10.3
|
||||
|
||||
Welcome to Grafana 10.3! Read on to learn about changes to navigation, visualizations and transformations, alerting, profiling, and logs.
|
||||
|
||||
We've also included here features released in Grafana 10.2.3, as well as breaking changes from that release. Features that were included in the 10.2.3 release are marked with an asterisk.
|
||||
|
||||
For even more detail about all the changes in this release, refer to the [changelog](https://github.com/grafana/grafana/blob/master/CHANGELOG.md). For the specific steps we recommend when you upgrade to v10.3, check out our [Upgrade Guide](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/upgrade-guide/upgrade-v10.3/).
|
||||
|
||||
## Breaking changes
|
||||
|
||||
For Grafana v10.3, we've also provided a list of [breaking changes](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/breaking-changes/breaking-changes-v10-3) to help you upgrade with greater confidence. For information about these along with guidance on how to proceed, refer to [Breaking changes in Grafana v10.3](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/breaking-changes/breaking-changes-v10-3/).
|
||||
|
||||
<!-- Template below
|
||||
|
||||
## Feature
|
||||
<!-- Name of contributor -->
|
||||
<!--_[Generally available | Available in private/public preview | Experimental] in Grafana [Open Source, Enterprise, all editions of Grafana, some combination of self-managed and Cloud]_
|
||||
Description. Include an overview of the feature and problem it solves, and where to learn more (like a link to the docs).
|
||||
{{% admonition type="note" %}}
|
||||
Use full URLs for links. When linking to versioned docs, replace the version with the version interpolation placeholder (for example, <GRAFANA_VERSION>, <TEMPO_VERSION>, <MIMIR_VERSION>) so the system can determine the correct set of docs to point to. For example, "https://grafana.com/docs/grafana/latest/administration/" becomes "https://grafana.com/docs/grafana/<GRAFANA_VERSION>/administration/".
|
||||
{{% /admonition %}}
|
||||
|
||||
<!--Add an image, GIF or video as below-->
|
||||
|
||||
<!--{{< figure src="/media/docs/grafana/dashboards/WidgetVizSplit.png" max-width="750px" caption="DESCRIPTIVE CAPTION" >}}
|
||||
|
||||
<!--Learn how to upload images here: https://grafana.com/docs/writers-toolkit/write/image-guidelines/#where-to-store-media-assets-->
|
||||
<!---->
|
||||
|
||||
## Navigation updates\*
|
||||
|
||||
<!--Laura Benz-->
|
||||
|
||||
_Available in public preview in Grafana Open Source and Enterprise_
|
||||
|
||||
The improved navigation menu gives you a better overview by showing all levels of navigation items in a more compact design. We also implemented a better dock and improved scrolling behavior. Furthermore, we improved the structure of the nav menu and added several new items.
|
||||
|
||||
{{< youtube id="IhpghtVykLc" >}}
|
||||
|
||||
## Table data in PDF reports
|
||||
|
||||
<!--Agnès Toulet-->
|
||||
|
||||
_Available in public preview in Grafana Enterprise and Grafana Cloud_
|
||||
|
||||
We've improved the reporting experience with options to make all of your table data accessible in PDFs. Previously, if your dashboard included large table visualizations, you couldn't see all of the table data in your PDF report. Unlike in Grafana, you couldn't scroll in the PDF table visualization or click on the page numbers. With this new feature, you now have the option to see all the data directly in your PDF without losing your dashboard layout.
|
||||
|
||||
We've added two format options to the report creation form:
|
||||
|
||||
- **Include table data as PDF appendix** - Adds an appendix to your dashboard PDF.
|
||||
- **Attach a separate PDF of table data** - Generates a separate PDF file for your table panel data.
|
||||
|
||||
To try out this feature, enable the `pdfTables` [feature toggle](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/) or contact Grafana Support to have it enabled in on your Grafana Cloud stack.
|
||||
|
||||
{{< youtube id="1fzQQI8O838" >}}
|
||||
|
||||
## Dashboards and visualizations
|
||||
|
||||
### Moving average and trend lines using transformations
|
||||
|
||||
<!--Oscar Kilhed-->
|
||||
|
||||
_Available in public preview in all editions of Grafana_
|
||||
|
||||
#### Moving average\*
|
||||
|
||||
Sometimes your data is too noisy to quickly grasp what's going on. A common way to address this issue is to calculate the moving mean, or moving average, to filter out some of that noise. Luckily, many data sources already support calculating the moving mean, but when the support is lacking or you're not well versed in the query language, until now, you were stuck with the noise.
|
||||
|
||||
{{< figure src="/media/docs/grafana/transformations/noisy-sensor-data.png" caption="Noisy data can hide the general trend of your data." alt="Graph displaying noisy sensor data" max-width="300px" >}}
|
||||
|
||||
By selecting the **Window functions** mode and using **Mean** as the calculation for the **Add field from calculation** transformation, Grafana adds a field with the moving mean for your selected field.
|
||||
|
||||
{{< figure src="/media/docs/grafana/transformations/noisy-sensor-data-moving-average.png" caption="Calculating the moving mean of your data will make it easier to grasp what's going on." alt="Graph displaying the moving mean of noisy data" >}}
|
||||
|
||||
The **Window functions** mode also supports moving variance and moving standard deviation calculations if you need to analyze the volatility of your metric.
|
||||
|
||||
[Documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/transform-data/#add-field-from-calculation)
|
||||
|
||||
#### Trend lines\*
|
||||
|
||||
We're also adding some basic statistical analysis features as a way to help you visualize trends in your data. The **Regression analysis** transformation will fit a mathematical function to your data and display it as predicted data points in a separate data frame.
|
||||
|
||||
{{< figure src="/media/docs/grafana/transformations/trendlines.png" caption="Linear and polynomial regression trendlines" alt="Graph with trendlines" >}}
|
||||
|
||||
The transformation currently supports linear regression and polynomial regression to the fifth-degree.
|
||||
|
||||
### Canvas visualization supports pan and zoom
|
||||
|
||||
<!--Nathan Marrs-->
|
||||
|
||||
_Available in public preview in all editions of Grafana_
|
||||
|
||||
Canvas visualizations now support panning and zooming. This allows you to both create and navigate more complex designs.
|
||||
|
||||
To enable this feature, you must first enable the `canvasPanelPanZoom` [feature toggle](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/).
|
||||
|
||||
{{< youtube id="CF-HFkcytRA" >}}
|
||||
|
||||
[Documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/canvas/)
|
||||
|
||||
### Improved tooltips in visualizations\*
|
||||
|
||||
<!--Nathan Marrs-->
|
||||
|
||||
_Available in public preview in all editions of Grafana_
|
||||
|
||||
We've introduced enhanced tooltips as part of our standardization initiative, unifying the tooltip architecture for a consistent user experience across panels. Packed with features like color indicators, time uniformity, and improved support for long labels, these tooltips go beyond a cosmetic redesign, bringing fundamental changes to elevate your data visualization experience. Stay tuned for more updates!
|
||||
|
||||
To try out the new tooltips, enable the `newVizTooltips` [feature toggle](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/). Enhanced tooltips have been implemented for the following visualizations:
|
||||
|
||||
- Time series
|
||||
- Trend
|
||||
- Heatmap
|
||||
- Status history
|
||||
- Candlestick
|
||||
- State timeline
|
||||
- XY Chart
|
||||
- and more coming soon!
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
As this is an ongoing project, the dashboard shared cursor and annotations features are not yet fully supported.
|
||||
{{% /admonition %}}
|
||||
|
||||
{{< youtube id="0Rp6FYfHu6Q" >}}
|
||||
|
||||
### Plot enum values in your time series and state timeline visualizations\*
|
||||
|
||||
<!--Nathan Marrs-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
You can now plot enum values in your time series and state timeline visualizations. This feature is useful when you want to visualize the state of a system, such as the status of a service or the health of a device. For example, you can use this feature to visualize the status of a service as `ON`, `STANDBY`, or `OFF`. To display enum values you can [use the convert field transform](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/query-transform-data/transform-data/#convert-field-type).
|
||||
|
||||
{{< youtube id="FG0hBFfgpps" >}}
|
||||
|
||||
### View percent change in stat visualizations
|
||||
|
||||
<!--Nathan Marrs-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
You can now view percent change in stat visualizations. This makes it easier to understand your data by showing how metrics are changing over time.
|
||||
|
||||
{{< youtube id="mB9FU0myZo8" >}}
|
||||
|
||||
[Documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/visualizations/stat/#show-percent-change)
|
||||
|
||||
### Data visualization quality of life improvements
|
||||
|
||||
<!--Nathan Marrs-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
We’ve made a few smaller improvements to the data visualization experience in Grafana.
|
||||
|
||||
#### Apply data transformations to annotations
|
||||
|
||||
You can now apply data transformations to annotation data. For example, you can now configure how exemplar data is displayed in tooltips.
|
||||
|
||||
{{< video-embed src="/media/docs/grafana/screen-recording-10-3-data-transformations-annotation-support.mp4" caption="Configure how exemplar data appears in tooltip" >}}
|
||||
|
||||
#### Disable auto scaling units
|
||||
|
||||
By default, Grafana automatically scales the unit based on the magnitude of the value. For example, if you have a value of 0.14 kW, Grafana will display it as 140 W. You can now disable unit auto scaling. This is helpful in cases where you want to ensure that the same unit is shown in your visualization regardless of the magnitude of the data. See [the standard options documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/panels-visualizations/configure-standard-options/#scale-units) for more information.
|
||||
|
||||
{{< video-embed src="/media/docs/grafana/screen-recording-10-3-disable-unit-auto-scaling.mp4" caption="Disable auto scaling units" >}}
|
||||
|
||||
### New Transformations UI experience and documentation upgrades
|
||||
|
||||
<!--Jev Forsberg-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
We've revamped the Transformations user interface to make it cleaner, more user-friendly, and overall better for visualizing, selecting, and comprehending transformation options for your data.
|
||||
|
||||
#### Improved UI\*
|
||||
|
||||
In the past, transformations were applied through a dropdown menu, indicated solely by text names like Merge, Sort, JoinByLabels, etc. Now, we've introduced a much more user-friendly interface. A convenient drawer allows seamless access to all transformation options, each accompanied by visual/graphical representations and a brief description. These enhancements are designed to enhance the user's comprehension of their data transformation choices.
|
||||
|
||||
{{< figure src="/media/docs/grafana/transformations/transformations_ui_drawer_selector.png" caption="The new Transformation UI drawer" alt="Transformation UI drawer" >}}
|
||||
|
||||
#### In-App documentation
|
||||
|
||||
We've also streamlined the user experience by integrating documentation directly into the core Grafana application. Gone are the days of navigating to a separate browser page for Transformation documentation. Now, users can conveniently access documentation within the app interface, providing a more seamless and efficient way to understand and utilize various features. This enhancement aims to save time and enhance user convenience, ensuring that valuable information is readily available at their fingertips.
|
||||
|
||||
{{< figure src="/media/docs/grafana/transformations/transformations_internal_documentation.png" caption="Transformation documentation is now internally available inside the Grafana app itself." alt="Transformation documentation internally available" >}}
|
||||
|
||||
### Copy and paste time range
|
||||
|
||||
<!--Haris Rozajac-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
Copying and pasting time range in the time range picker is now available. For example, you can copy a time range in **Explore** and paste it into **Dashboards** and vice versa. You can also copy and paste a time range using the new keyboard shortcuts `t+c` and `t+v`, respectively.
|
||||
|
||||
## Profiles
|
||||
|
||||
### Trace to Profiles\*
|
||||
|
||||
<!--Joey Tawadrous-->
|
||||
|
||||
_Experimental in all editions of Grafana_
|
||||
|
||||
Using Trace to profiles, you can use Grafana’s ability to correlate different signals by adding the functionality to link between traces and profiles.
|
||||
|
||||
**Trace to profiles** lets you link your Grafana Pyroscope data source to tracing data. When configured, this connection lets you run queries from a trace span into the profile data.
|
||||
|
||||
There are two ways to configure the trace to profiles feature:
|
||||
|
||||
- Use a simplified configuration with default query, or
|
||||
- Configure a custom query where you can use a template language to interpolate variables from the trace or span.
|
||||
|
||||
{{< figure src="/static/img/docs/tempo/profiles/tempo-trace-to-profile.png" caption="Trace to profiles screenshot" alt="Trace to profiles screenshot" >}}
|
||||
|
||||
To try out **Trace to profiles**, enable the 'traceToProfiles' feature toggle.
|
||||
|
||||
If you would also like to try out the **Embedded Flame Graph** feature, please enable the 'tracesEmbeddedFlameGraph' feature toggle.
|
||||
|
||||
Note: in order to determine that there is a profile for a given span and render the 'Profiles for this span' button or the embedded flame graph in the span details, the 'pyroscope.profile.id' key-value pair must exist in your span tags.
|
||||
|
||||
{{< youtube id="AG8VzfFMLxo" >}}
|
||||
|
||||
[Documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/datasources/tempo/configure-tempo-data-source/#trace-to-profiles)
|
||||
|
||||
### FlameGraph: Collapsing similar items in the graph\*
|
||||
|
||||
<!--Andrej Ocenas-->
|
||||
|
||||
_Experimental in all editions of Grafana_
|
||||
|
||||
Sometimes profile stacks contain lots of levels with similar repeating items, for example long stacks of framework code that usually isn't of interest but takes up a lot of visual real estate. With this feature, instead of rendering all of the similar items we render only one and allow to expand those collapsed items on demand.
|
||||
|
||||
To try it out, enable the ‘traceToProfiles’ feature toggle. To enable it in your Grafana Cloud stack, contact Grafana Support.
|
||||
|
||||
{{< youtube id="Y1c32Cf5nSE" >}}
|
||||
|
||||
## Alerting
|
||||
|
||||
### Alerting insights\*
|
||||
|
||||
<!-- George Robinson -->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
Use Alerting insights to monitor your alerting data, discover key trends about your organization’s alert management performance, and find patterns in why things go wrong.
|
||||
|
||||
### Export alerting resources to Terraform
|
||||
|
||||
<!-- Yuri Tseretyan-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
Export your alerting resources, such as alert rules, contact points, and notification policies as Terraform resources. A new “Modify export” mode for alert rules enables you to edit provisioned alert rules and export a modified version.
|
||||
|
||||
### Contact points list view redesign
|
||||
|
||||
<!-- Brenda Muir -->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
The Contact points list view has been redesigned and split into two tabs: Contact Points and Notification Templates, making it easier to view all contact point information at a glance. You can now search for name and type of contact points and integrations, view how many notification policies each contact point is being used for, and navigate directly to the linked notification policies.
|
||||
|
||||
{{< youtube id="_eOhSmbYK8Q" >}}
|
||||
|
||||
### Create alerts from panels\*
|
||||
|
||||
<!-- Brenda Muir -->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
Create alerts from dashboard panels. You can reuse the panel queries and create alerts based on them.
|
||||
|
||||
### Support for adding responders to Opsgenie alerting contact point\*
|
||||
|
||||
<!--Ryan Kehoe-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
The Opsgenie contact point has been extended to allow users to optionally fill out responder information for their integration. Responders tell Opsgenie who an alert should notify according to their escalation policies and routing rules.
|
||||
|
||||
### Recovery thresholds for alerts
|
||||
|
||||
<!--Ryan Kehoe-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
To reduce the noise of flapping alerts, you can set a recovery threshold different to the alert threshold.
|
||||
|
||||
Flapping alerts occur when a metric hovers around the alert threshold condition and may lead to frequent state changes, resulting in too many notifications being generated.
|
||||
|
||||
## Logs
|
||||
|
||||
### Logs Table UI
|
||||
|
||||
<!--Galen Kistler-->
|
||||
|
||||
_Available in public preview in all editions of Grafana_
|
||||
|
||||
Table view was created to help facilitate ease of use in a point and click UI, as opposed to datasource specific query language formatting options, like loki's line_format.
|
||||
|
||||
Tables can be configured and shared with team members via explore URLs or by adding the table to a dashboard panel.
|
||||
|
||||
{{< youtube id="OAZeqqNpEjc" >}}
|
||||
|
||||
## Data sources
|
||||
|
||||
### Data source Admin permission\*
|
||||
|
||||
<!--Ieva Vasiljeva-->
|
||||
|
||||
_Generally available in Grafana Enterprise and Grafana Cloud_
|
||||
|
||||
In addition to `Query` and `Edit` access, you can now grant users, teams, or basic roles `Admin` access to data sources. Users with `Admin` access to a data source can grant and revoke permissions to the data source, as well as to manage query caching settings for the data source. Users are automatically granted `Admin` access to data sources that they create.
|
||||
|
||||
### Redshift and Athena: Async query caching
|
||||
|
||||
<!--Isabella Siu-->
|
||||
|
||||
_Generally available in Grafana Enterprise, Grafana Cloud Advanced and Cloud Pro_
|
||||
|
||||
Introducing query caching for async queries in the Athena and Redshift data source plugins. We previously introduced async queries for the Athena and Redshift plugins, and this feature adds support for caching those queries. To use this, you must have query caching enabled for the Athena or Redshift data source you wish to cache. This feature was previously available behind a feature toggle and is now generally available and enabled by default.
|
||||
|
||||
{{% admonition type="note" %}}
|
||||
|
||||
The `useCachingService` feature toggle must also be enabled to use this feature.
|
||||
|
||||
{{% /admonition %}}
|
||||
|
||||
### Loki data source improvements: "or" filter syntax, filter by label types, derived fields by labels
|
||||
|
||||
<!--Sven Grossmann-->
|
||||
<!--enablement videos to come?-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
Introducing several improvements to the Loki data source.
|
||||
|
||||
{{< youtube id="ievPSzmCrAk" >}}
|
||||
|
||||
#### Line filter "or" syntax\*
|
||||
|
||||
Loki's line filter syntax is great to find specific substrings of your log lines. If users want to find multiple different substrings it was cumbersome to use the regex `=~` operator. With this change it is possible to chain multiple strings with the existing filter operators.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
{app="foo"} |= "foo" or "bar" != "baz" or "qux"
|
||||
```
|
||||
|
||||
#### Filter based on label type\*
|
||||
|
||||
Grafana users can use the action buttons in the log details to filter for specific labels. Those would be always added as a LabelFilter expression regardless of the type of the label. Now, filtered labels will be added either to the stream selector if the label is an indexed label, or as a LabelFilter expression if the label is a parsed label or part of structured metadata.
|
||||
|
||||
#### Derived fields based on labels\*
|
||||
|
||||
Derived fields or data links are a concept to add correlations based on your log lines. Previously it was only possible to add derived fields based on a regular expression of your log line and doing it based on labels was not possible. With this change derived fields can be added either based on a regex of a log line or based on a label, parsed label or structured metadata.
|
||||
|
||||
The following example would add the derived field `traceID regex` based on a regular expression and another `app label` field based on the `app` label.
|
||||
|
||||
{{< figure src="/media/docs/grafana/2024-01-05_loki-derived-fields.png" >}}
|
||||
|
||||
### InfluxDB native SQL support
|
||||
|
||||
<!--Ismail Simsek-->
|
||||
|
||||
_Generally available in all editions of Grafana_
|
||||
|
||||
InfluxDB introduced [a new version, 3.0](https://www.influxdata.com/blog/introducing-influxdb-3-0/), in April. With this new version, InfluxDB has put [Flux in maintenance mode](https://www.influxdata.com/blog/the-plan-for-influxdb-3-0-open-source/#heading4). But with the new version we have a new querying language, [Native SQL](https://www.influxdata.com/products/sql/). With v10.3.0, Grafana has built-in support for SQL query language in InfluxDB.
|
||||
|
||||
All you need to do is set up your InfluxDB Cloud Account and create your InfluxDB data source on Grafana with the query language "SQL" selected.
|
||||
|
||||
{{< youtube id="jGclGsv5PBA" >}}
|
||||
|
||||
## Authentication and authorization
|
||||
|
||||
### Grafana Anonymous Access\*
|
||||
|
||||
<!--Eric Leijonmarck-->
|
||||
|
||||
_Generally available in Grafana Open Source and Enterprise_
|
||||
|
||||
We've identified a need for users who enable anonymous authentication to monitor the anonymous devices connected to their Grafana instance. This feature is part of our ongoing efforts to enhance control and transparency regarding anonymous usage.
|
||||
|
||||
Anonymous access now allows users, including those in open-source and enterprise self-managed environments, to view and monitor their anonymous access. They can also set a device limit, configuring a specific number of anonymous devices to connect to their instance.
|
||||
|
||||
Once this limit is reached, any new devices attempting to connect will be denied access until existing devices disconnect.
|
||||
|
||||
The anonymous devices feature improves the management and monitoring of anonymous access within your Grafana instance.
|
||||
|
||||
**Anonymous Device:**
|
||||
|
||||
When anonymous access has been enabled, any device which accesses Grafana in the last 30 days (without logging in) is considered an active anonymous device. Users can now view anonymous devices on the users page, anonymous usage statistics, including the count of devices and users over this period.
|
||||
|
||||
**Grafana UI:**
|
||||
|
||||
- Navigate to Administration -> Users to access the anonymous devices tab.
|
||||
|
||||
- A new statistic has been added to the Usage & Stats page, displaying active anonymous devices from the last 30 days.
|
||||
|
||||
{{< youtube id="B72X3_9e-ds" >}}
|
||||
|
||||
[Documentation](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/grafana/)
|
||||
@@ -1,3 +1,7 @@
|
||||
---
|
||||
draft: true
|
||||
---
|
||||
|
||||
# Contribute to 'What's new in Grafana Cloud'
|
||||
|
||||
To contribute to [What's new in Grafana Cloud](https://grafana.com/docs/grafana-cloud/whatsnew/), refer to [Contribute to What’s new or release notes](https://grafana.com/docs/writers-toolkit/contribute-documentation/contribute-release-notes/).
|
||||
|
||||
@@ -10,14 +10,6 @@ describe('Explore', () => {
|
||||
e2e.pages.Explore.General.container().should('have.length', 1);
|
||||
e2e.components.RefreshPicker.runButtonV2().should('have.length', 1);
|
||||
|
||||
// delete query history queries that would be unrelated
|
||||
e2e.components.QueryTab.queryHistoryButton().should('be.visible').click();
|
||||
cy.get('button[title="Delete query"]').each((button) => {
|
||||
button.trigger('click');
|
||||
});
|
||||
cy.get('button[title="Delete query"]').should('not.exist');
|
||||
e2e.components.QueryTab.queryHistoryButton().should('be.visible').click();
|
||||
|
||||
e2e.components.DataSource.TestData.QueryTab.scenarioSelectContainer()
|
||||
.scrollIntoView()
|
||||
.should('be.visible')
|
||||
@@ -26,17 +18,5 @@ describe('Explore', () => {
|
||||
});
|
||||
|
||||
cy.contains('CSV Metric Values').scrollIntoView().should('be.visible').click();
|
||||
|
||||
const canvases = cy.get('canvas');
|
||||
canvases.should('have.length', 1);
|
||||
|
||||
// Both queries above should have been run and be shown in the query history
|
||||
e2e.components.QueryTab.queryHistoryButton().should('be.visible').click();
|
||||
e2e.components.QueryHistory.queryText().should('have.length', 1).should('contain', 'csv_metric_values');
|
||||
|
||||
// delete all queries
|
||||
cy.get('button[title="Delete query"]').each((button) => {
|
||||
button.trigger('click');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
40
emails/templates/verify_email_update.mjml
Normal file
40
emails/templates/verify_email_update.mjml
Normal file
@@ -0,0 +1,40 @@
|
||||
<mjml>
|
||||
<!-- global variables -->
|
||||
<mj-include path="./partials/_globals.mjml" />
|
||||
<!-- css styling -->
|
||||
<mj-include path="./partials/layout/theme.css" type="css" css-inline="inline" />
|
||||
<mj-head>
|
||||
<!-- ⬇ Don't forget to specifify an email subject below! ⬇ -->
|
||||
<mj-title>
|
||||
{{ Subject .Subject .TemplateData "Verify your new email - {{.Name}}" }}
|
||||
</mj-title>
|
||||
<mj-include path="./partials/layout/head.mjml" />
|
||||
</mj-head>
|
||||
<mj-body>
|
||||
<mj-section>
|
||||
<mj-include path="./partials/layout/header.mjml" />
|
||||
</mj-section>
|
||||
<mj-section css-class="background">
|
||||
<mj-column>
|
||||
<mj-text>
|
||||
<h2>Hi {{ .Name }},</h2>
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
Please click the following link to verify your email within <strong>{{ .VerificationEmailLifetimeHours }} hour(s)</strong>.
|
||||
</mj-text>
|
||||
<mj-button href="{{ .AppUrl }}user/email/update?code={{ .Code }}">
|
||||
Verify Email
|
||||
</mj-button>
|
||||
<mj-text>
|
||||
You can also copy and paste this link into your browser directly:
|
||||
</mj-text>
|
||||
<mj-text>
|
||||
<a rel="noopener" href="{{ .AppUrl }}user/email/update?code={{ .Code }}">{{ .AppUrl }}user/email/update?code={{ .Code }}</a>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
<mj-section>
|
||||
<mj-include path="./partials/layout/footer.mjml" />
|
||||
</mj-section>
|
||||
</mj-body>
|
||||
</mjml>
|
||||
6
emails/templates/verify_email_update.txt
Normal file
6
emails/templates/verify_email_update.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
[[HiddenSubject .Subject "Verify your new email - [[.Name]]"]]
|
||||
|
||||
Hi [[.Name]],
|
||||
|
||||
Copy and paste the following link directly in your browser to verify your email within [[.VerificationEmailLifetimeHours]] hour(s).
|
||||
[[.AppUrl]]user/email/update?code=[[.Code]]
|
||||
2
go.mod
2
go.mod
@@ -306,7 +306,7 @@ require (
|
||||
github.com/buildkite/yaml v2.1.0+incompatible // indirect
|
||||
github.com/bwmarrin/snowflake v0.3.0 // @grafan/grafana-app-platform-squad
|
||||
github.com/centrifugal/protocol v0.10.0 // indirect
|
||||
github.com/cloudflare/circl v1.3.3 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cockroachdb/errors v1.9.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20211118104740-dabe8e521a4f // indirect
|
||||
github.com/cockroachdb/redact v1.1.3 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -888,8 +888,9 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
|
||||
github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"__message": "This file is now deprecated, and will be removed in a future release. No further updates should be made to this file",
|
||||
"stable": "10.2.3",
|
||||
"testing": "10.2.3"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"version": "10.3.0-pre"
|
||||
"version": "10.3.3"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"license": "AGPL-3.0-only",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"repository": "github:grafana/grafana",
|
||||
"scripts": {
|
||||
"prebuild": "yarn i18n:compile && yarn plugin:build",
|
||||
|
||||
@@ -36,7 +36,7 @@ Every commit to main that has changes within the `packages` directory is a subje
|
||||
|
||||
> All of the steps below must be performed on a release branch, according to Grafana Release Guide.
|
||||
|
||||
> You must be logged in to NPM as part of Grafana NPM org before attempting to publish to the npm registery.
|
||||
> You must be logged in to NPM as part of Grafana NPM org before attempting to publish to the npm registry.
|
||||
|
||||
1. Run `yarn packages:clean` script from the root directory. This will delete any previous builds of the packages.
|
||||
2. Run `yarn packages:prepare` script from the root directory. This performs tests on the packages and prompts for the version of the packages. The version should be the same as the one being released.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
@@ -36,7 +36,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "6.0.2",
|
||||
"@grafana/schema": "10.3.0-pre",
|
||||
"@grafana/schema": "10.3.3",
|
||||
"@types/d3-interpolate": "^3.0.0",
|
||||
"@types/string-hash": "1.1.1",
|
||||
"d3-interpolate": "3.0.1",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import { DataFrame, Field, TIME_SERIES_VALUE_FIELD_NAME, FieldType, TIME_SERIES_TIME_FIELD_NAME } from '../types';
|
||||
import { formatLabels } from '../utils/labels';
|
||||
|
||||
@@ -167,7 +165,7 @@ export function getUniqueFieldName(field: Field, frame?: DataFrame) {
|
||||
for (let i = 0; i < frame.fields.length; i++) {
|
||||
const otherField = frame.fields[i];
|
||||
|
||||
if (isEqual(field, otherField)) {
|
||||
if (field === otherField) {
|
||||
foundSelf = true;
|
||||
|
||||
if (dupeCount > 0) {
|
||||
|
||||
@@ -172,4 +172,5 @@ export interface FeatureToggles {
|
||||
alertStateHistoryAnnotationsFromLoki?: boolean;
|
||||
lokiQueryHints?: boolean;
|
||||
alertingPreviewUpgrade?: boolean;
|
||||
newFolderPicker?: boolean;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana End-to-End Test Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
@@ -63,8 +63,8 @@
|
||||
"@babel/core": "7.23.2",
|
||||
"@babel/preset-env": "7.23.2",
|
||||
"@cypress/webpack-preprocessor": "5.17.1",
|
||||
"@grafana/e2e-selectors": "10.3.0-pre",
|
||||
"@grafana/schema": "10.3.0-pre",
|
||||
"@grafana/e2e-selectors": "10.3.3",
|
||||
"@grafana/schema": "10.3.3",
|
||||
"@grafana/tsconfig": "^1.2.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"babel-loader": "9.1.3",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@grafana/eslint-plugin",
|
||||
"description": "ESLint rules for use within the Grafana repo. Not suitable (or supported) for external use.",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"main": "./index.cjs",
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/flamegraph",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana flamegraph visualization component",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -44,8 +44,8 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@emotion/css": "11.11.2",
|
||||
"@grafana/data": "10.3.0-pre",
|
||||
"@grafana/ui": "10.3.0-pre",
|
||||
"@grafana/data": "10.3.3",
|
||||
"@grafana/ui": "10.3.3",
|
||||
"@leeoniya/ufuzzy": "1.0.13",
|
||||
"d3": "^7.8.5",
|
||||
"lodash": "4.17.21",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "@grafana/plugin-configs",
|
||||
"description": "Shared dependencies and files for core plugins",
|
||||
"private": true,
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"dependencies": {
|
||||
"tslib": "2.6.0"
|
||||
},
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -37,10 +37,10 @@
|
||||
"postpack": "mv package.json.bak package.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "10.3.0-pre",
|
||||
"@grafana/e2e-selectors": "10.3.0-pre",
|
||||
"@grafana/data": "10.3.3",
|
||||
"@grafana/e2e-selectors": "10.3.3",
|
||||
"@grafana/faro-web-sdk": "^1.3.5",
|
||||
"@grafana/ui": "10.3.0-pre",
|
||||
"@grafana/ui": "10.3.3",
|
||||
"history": "4.10.1",
|
||||
"lodash": "4.17.21",
|
||||
"rxjs": "7.8.1",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/schema",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana Schema Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
limit: number;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip, common.OptionsWithTextFormatting {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
displayMode: common.BarGaugeDisplayMode;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export enum VizDisplayMode {
|
||||
Candles = 'candles',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export enum HorizontalConstraint {
|
||||
Center = 'center',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface MetricStat {
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
selectedSeries: number;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export type UpdateConfig = {
|
||||
render: boolean,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export type BucketAggregation = (DateHistogram | Histogram | Terms | Filters | GeoHashGrid | Nested);
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
minVizHeight: number;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
basemap: ui.MapLayerOptions;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export type PyroscopeQueryType = ('metrics' | 'profile' | 'both');
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
/**
|
||||
* Controls the color mode of the heatmap
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
dedupStrategy: common.LogsDedupStrategy;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export enum QueryEditorMode {
|
||||
Builder = 'builder',
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface ArcOption {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
/**
|
||||
* Select the pie chart display style.
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export enum QueryEditorMode {
|
||||
Builder = 'builder',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.SingleStatBaseOptions {
|
||||
colorMode: common.BigValueColorMode;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as ui from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options {
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface TempoQuery extends common.DataQuery {
|
||||
filters: Array<TraceqlFilter>;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export enum TextMode {
|
||||
Code = 'code',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
export interface Options extends common.OptionsWithTimezones {
|
||||
legend: common.VizLegendOptions;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
/**
|
||||
* Identical to timeseries... except it does not have timezone settings
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const pluginVersion = "10.3.0-pre";
|
||||
export const pluginVersion = "10.3.3";
|
||||
|
||||
/**
|
||||
* Auto is "table" in the UI
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "10.3.0-pre",
|
||||
"version": "10.3.3",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -50,10 +50,10 @@
|
||||
"@emotion/css": "11.11.2",
|
||||
"@emotion/react": "11.11.1",
|
||||
"@floating-ui/react": "0.26.4",
|
||||
"@grafana/data": "10.3.0-pre",
|
||||
"@grafana/e2e-selectors": "10.3.0-pre",
|
||||
"@grafana/data": "10.3.3",
|
||||
"@grafana/e2e-selectors": "10.3.3",
|
||||
"@grafana/faro-web-sdk": "^1.3.5",
|
||||
"@grafana/schema": "10.3.0-pre",
|
||||
"@grafana/schema": "10.3.3",
|
||||
"@leeoniya/ufuzzy": "1.0.13",
|
||||
"@monaco-editor/react": "4.6.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
|
||||
@@ -606,7 +606,7 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Annotations",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ac.ActionAnnotationsRead, Scope: ac.ScopeAnnotationsTypeOrganization},
|
||||
{Action: ac.ActionAnnotationsRead, Scope: dashboards.ScopeDashboardsAll},
|
||||
{Action: ac.ActionAnnotationsRead, Scope: dashboards.ScopeFoldersAll},
|
||||
},
|
||||
},
|
||||
Grants: []string{string(org.RoleAdmin)},
|
||||
@@ -620,11 +620,11 @@ func (hs *HTTPServer) declareFixedRoles() error {
|
||||
Group: "Annotations",
|
||||
Permissions: []ac.Permission{
|
||||
{Action: ac.ActionAnnotationsCreate, Scope: ac.ScopeAnnotationsTypeOrganization},
|
||||
{Action: ac.ActionAnnotationsCreate, Scope: dashboards.ScopeDashboardsAll},
|
||||
{Action: ac.ActionAnnotationsCreate, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionAnnotationsDelete, Scope: ac.ScopeAnnotationsTypeOrganization},
|
||||
{Action: ac.ActionAnnotationsDelete, Scope: dashboards.ScopeDashboardsAll},
|
||||
{Action: ac.ActionAnnotationsDelete, Scope: dashboards.ScopeFoldersAll},
|
||||
{Action: ac.ActionAnnotationsWrite, Scope: ac.ScopeAnnotationsTypeOrganization},
|
||||
{Action: ac.ActionAnnotationsWrite, Scope: dashboards.ScopeDashboardsAll},
|
||||
{Action: ac.ActionAnnotationsWrite, Scope: dashboards.ScopeFoldersAll},
|
||||
},
|
||||
},
|
||||
Grants: []string{string(org.RoleAdmin)},
|
||||
|
||||
@@ -193,6 +193,11 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
r.Post("/api/user/signup", quota(user.QuotaTargetSrv), quota(org.QuotaTargetSrv), routing.Wrap(hs.SignUp))
|
||||
r.Post("/api/user/signup/step2", routing.Wrap(hs.SignUpStep2))
|
||||
|
||||
// update user email
|
||||
if hs.Cfg.Smtp.Enabled && setting.VerifyEmailEnabled {
|
||||
r.Get("/user/email/update", reqSignedInNoAnonymous, routing.Wrap(hs.UpdateUserEmail))
|
||||
}
|
||||
|
||||
// invited
|
||||
r.Get("/api/user/invite/:code", routing.Wrap(hs.GetInviteInfoByCode))
|
||||
r.Post("/api/user/invite/complete", routing.Wrap(hs.CompleteInvite))
|
||||
|
||||
@@ -179,7 +179,7 @@ type HTTPServer struct {
|
||||
queryDataService query.Service
|
||||
serviceAccountsService serviceaccounts.Service
|
||||
authInfoService login.AuthInfoService
|
||||
NotificationService *notifications.NotificationService
|
||||
NotificationService notifications.Service
|
||||
DashboardService dashboards.DashboardService
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService
|
||||
folderService folder.Service
|
||||
@@ -242,7 +242,7 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dataSourcesService datasources.DataSourceService, queryDataService query.Service, pluginFileStore plugins.FileStore,
|
||||
serviceaccountsService serviceaccounts.Service,
|
||||
authInfoService login.AuthInfoService, storageService store.StorageService,
|
||||
notificationService *notifications.NotificationService, dashboardService dashboards.DashboardService,
|
||||
notificationService notifications.Service, dashboardService dashboards.DashboardService,
|
||||
dashboardProvisioningService dashboards.DashboardProvisioningService, folderService folder.Service,
|
||||
dsGuardian guardian.DatasourceGuardianProvider, alertNotificationService *alerting.AlertNotificationService,
|
||||
dashboardsnapshotsService dashboardsnapshots.Service, pluginSettings pluginSettings.Service,
|
||||
|
||||
190
pkg/api/user.go
190
pkg/api/user.go
@@ -4,17 +4,23 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/dtos"
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/grafana/grafana/pkg/web"
|
||||
)
|
||||
@@ -125,6 +131,7 @@ func (hs *HTTPServer) GetUserByLoginOrEmail(c *contextmodel.ReqContext) response
|
||||
// 200: okResponse
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 409: conflictError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
@@ -165,6 +172,7 @@ func (hs *HTTPServer) UpdateSignedInUser(c *contextmodel.ReqContext) response.Re
|
||||
// 401: unauthorisedError
|
||||
// 403: forbiddenError
|
||||
// 404: notFoundError
|
||||
// 409: conflictError
|
||||
// 500: internalServerError
|
||||
func (hs *HTTPServer) UpdateUser(c *contextmodel.ReqContext) response.Response {
|
||||
cmd := user.UpdateUserCommand{}
|
||||
@@ -228,6 +236,39 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
|
||||
return response.Err(user.ErrEmptyUsernameAndEmail.Errorf("user cannot be created with empty username and email"))
|
||||
}
|
||||
|
||||
// If email is being updated, we need to verify it. Likewise, if username is being updated and the new username
|
||||
// is an email, we also need to verify it.
|
||||
// To avoid breaking changes, email verification is implemented in a way that if the email field is being updated,
|
||||
// all the other fields being updated in the same request are disregarded. We do this because email might need to
|
||||
// be verified and if so, it goes through a different code flow.
|
||||
if hs.Cfg.Smtp.Enabled && setting.VerifyEmailEnabled {
|
||||
query := user.GetUserByIDQuery{ID: cmd.UserID}
|
||||
usr, err := hs.userService.GetByID(ctx, &query)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return response.Error(http.StatusNotFound, user.ErrUserNotFound.Error(), nil)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to get user", err)
|
||||
}
|
||||
|
||||
if len(cmd.Email) != 0 && usr.Email != cmd.Email {
|
||||
// Email is being updated
|
||||
newEmail, err := ValidateAndNormalizeEmail(cmd.Email)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusBadRequest, "Invalid email address", err)
|
||||
}
|
||||
|
||||
return hs.verifyEmailUpdate(ctx, newEmail, user.EmailUpdateAction, usr)
|
||||
}
|
||||
if len(cmd.Login) != 0 && usr.Login != cmd.Login {
|
||||
// Username is being updated. If it's an email, go through the email verification flow
|
||||
newEmailLogin, err := ValidateAndNormalizeEmail(cmd.Login)
|
||||
if err == nil && newEmailLogin != usr.Email {
|
||||
return hs.verifyEmailUpdate(ctx, newEmailLogin, user.LoginUpdateAction, usr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := hs.userService.Update(ctx, &cmd); err != nil {
|
||||
if errors.Is(err, user.ErrCaseInsensitive) {
|
||||
return response.Error(http.StatusConflict, "Update would result in user login conflict", err)
|
||||
@@ -238,6 +279,104 @@ func (hs *HTTPServer) handleUpdateUser(ctx context.Context, cmd user.UpdateUserC
|
||||
return response.Success("User updated")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) verifyEmailUpdate(ctx context.Context, email string, field user.UpdateEmailActionType, usr *user.User) response.Response {
|
||||
// Verify that email is not already being used
|
||||
query := user.GetUserByLoginQuery{LoginOrEmail: email}
|
||||
existingUsr, err := hs.userService.GetByLogin(ctx, &query)
|
||||
if err != nil && !errors.Is(err, user.ErrUserNotFound) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to validate if email is already in use", err)
|
||||
}
|
||||
if existingUsr != nil {
|
||||
return response.Error(http.StatusConflict, "Email is already being used", nil)
|
||||
}
|
||||
|
||||
// Invalidate any pending verifications for this user
|
||||
expireCmd := tempuser.ExpirePreviousVerificationsCommand{InvitedByUserID: usr.ID}
|
||||
err = hs.tempUserService.ExpirePreviousVerifications(ctx, &expireCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Could not invalidate pending email verifications", err)
|
||||
}
|
||||
|
||||
code, err := util.GetRandomString(20)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to generate random string", err)
|
||||
}
|
||||
|
||||
tempCmd := tempuser.CreateTempUserCommand{
|
||||
OrgID: -1,
|
||||
Email: email,
|
||||
Code: code,
|
||||
Status: tempuser.TmpUserEmailUpdateStarted,
|
||||
// used to fetch the User in the second step of the verification flow
|
||||
InvitedByUserID: usr.ID,
|
||||
// used to determine if the user was updating their email or username in the second step of the verification flow
|
||||
Name: string(field),
|
||||
}
|
||||
|
||||
tempUser, err := hs.tempUserService.CreateTempUser(ctx, &tempCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to create email change", err)
|
||||
}
|
||||
|
||||
emailCmd := notifications.SendVerifyEmailCommand{Email: tempUser.Email, Code: tempUser.Code, User: usr}
|
||||
err = hs.NotificationService.SendVerificationEmail(ctx, &emailCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to send verification email", err)
|
||||
}
|
||||
|
||||
// Record email as sent
|
||||
emailSentCmd := tempuser.UpdateTempUserWithEmailSentCommand{Code: tempUser.Code}
|
||||
err = hs.tempUserService.UpdateTempUserWithEmailSent(ctx, &emailSentCmd)
|
||||
if err != nil {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to record verification email", err)
|
||||
}
|
||||
|
||||
return response.Success("Email sent for verification")
|
||||
}
|
||||
|
||||
// swagger:route GET /user/email/update user updateUserEmail
|
||||
//
|
||||
// Update user email.
|
||||
//
|
||||
// Update the email of user given a verification code.
|
||||
//
|
||||
// Responses:
|
||||
// 302: okResponse
|
||||
func (hs *HTTPServer) UpdateUserEmail(c *contextmodel.ReqContext) response.Response {
|
||||
var err error
|
||||
|
||||
q := c.Req.URL.Query()
|
||||
code, err := url.QueryUnescape(q.Get("code"))
|
||||
if err != nil || code == "" {
|
||||
return hs.RedirectResponseWithError(c, errors.New("bad request data"))
|
||||
}
|
||||
|
||||
tempUser, err := hs.validateEmailCode(c.Req.Context(), code)
|
||||
if err != nil {
|
||||
return hs.RedirectResponseWithError(c, err)
|
||||
}
|
||||
|
||||
cmd, err := hs.updateCmdFromEmailVerification(c.Req.Context(), tempUser)
|
||||
if err != nil {
|
||||
return hs.RedirectResponseWithError(c, err)
|
||||
}
|
||||
|
||||
if err := hs.userService.Update(c.Req.Context(), cmd); err != nil {
|
||||
if errors.Is(err, user.ErrCaseInsensitive) {
|
||||
return hs.RedirectResponseWithError(c, errors.New("update would result in user login conflict"))
|
||||
}
|
||||
return hs.RedirectResponseWithError(c, errors.New("failed to update user"))
|
||||
}
|
||||
|
||||
// Mark temp user as completed
|
||||
updateTmpUserCmd := tempuser.UpdateTempUserStatusCommand{Code: code, Status: tempuser.TmpUserEmailUpdateCompleted}
|
||||
if err := hs.tempUserService.UpdateTempUserStatus(c.Req.Context(), &updateTmpUserCmd); err != nil {
|
||||
return hs.RedirectResponseWithError(c, errors.New("failed to update verification status"))
|
||||
}
|
||||
|
||||
return response.Redirect(hs.Cfg.AppSubURL + "/profile")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) isExternalUser(ctx context.Context, userID int64) (bool, error) {
|
||||
getAuthQuery := login.GetAuthInfoQuery{UserId: userID}
|
||||
var err error
|
||||
@@ -602,6 +741,57 @@ func getUserID(c *contextmodel.ReqContext) (int64, *response.NormalResponse) {
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) updateCmdFromEmailVerification(ctx context.Context, tempUser *tempuser.TempUserDTO) (*user.UpdateUserCommand, error) {
|
||||
userQuery := user.GetUserByLoginQuery{LoginOrEmail: tempUser.InvitedByLogin}
|
||||
usr, err := hs.userService.GetByLogin(ctx, &userQuery)
|
||||
if err != nil {
|
||||
if errors.Is(err, user.ErrUserNotFound) {
|
||||
return nil, user.ErrUserNotFound
|
||||
}
|
||||
return nil, errors.New("failed to get user")
|
||||
}
|
||||
|
||||
cmd := &user.UpdateUserCommand{UserID: usr.ID, Email: tempUser.Email}
|
||||
|
||||
switch tempUser.Name {
|
||||
case string(user.EmailUpdateAction):
|
||||
// User updated the email field
|
||||
if _, err := mail.ParseAddress(usr.Login); err == nil {
|
||||
// If username was also an email, we update it to keep it in sync with the email field
|
||||
cmd.Login = tempUser.Email
|
||||
}
|
||||
case string(user.LoginUpdateAction):
|
||||
// User updated the username field with a new email
|
||||
cmd.Login = tempUser.Email
|
||||
default:
|
||||
return nil, errors.New("trying to update email on unknown field")
|
||||
}
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) validateEmailCode(ctx context.Context, code string) (*tempuser.TempUserDTO, error) {
|
||||
tempUserQuery := tempuser.GetTempUserByCodeQuery{Code: code}
|
||||
tempUser, err := hs.tempUserService.GetTempUserByCode(ctx, &tempUserQuery)
|
||||
if err != nil {
|
||||
if errors.Is(err, tempuser.ErrTempUserNotFound) {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
return nil, errors.New("failed to read temp user")
|
||||
}
|
||||
|
||||
if tempUser.Status != tempuser.TmpUserEmailUpdateStarted {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
if !tempUser.EmailSent {
|
||||
return nil, errors.New("verification email was not recorded as sent")
|
||||
}
|
||||
if tempUser.EmailSentOn.Add(hs.Cfg.VerificationEmailMaxLifetime).Before(time.Now()) {
|
||||
return nil, errors.New("invalid email verification code")
|
||||
}
|
||||
|
||||
return tempUser, nil
|
||||
}
|
||||
|
||||
// swagger:parameters searchUsers
|
||||
type SearchUsersParams struct {
|
||||
// Limit the maximum number of users to return per page
|
||||
|
||||
@@ -5,9 +5,18 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/notifications"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/fakes"
|
||||
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
||||
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
|
||||
"github.com/grafana/grafana/pkg/web/webtest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -39,6 +48,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const newEmail = "newEmail@localhost"
|
||||
|
||||
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||
settings := setting.NewCfg()
|
||||
sqlStore := db.InitTestDB(t)
|
||||
@@ -69,7 +80,6 @@ func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||
hs.authInfoService = srv
|
||||
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sc.cfg, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
hs.userService = userSvc
|
||||
@@ -361,6 +371,681 @@ func TestHTTPServer_UpdateUser(t *testing.T) {
|
||||
}, hs)
|
||||
}
|
||||
|
||||
func setupUpdateEmailTests(t *testing.T, cfg *setting.Cfg) (*user.User, *HTTPServer, *notifications.NotificationServiceMock) {
|
||||
t.Helper()
|
||||
|
||||
sqlStore := db.InitTestDB(t)
|
||||
sqlStore.Cfg = cfg
|
||||
|
||||
tempUserService := tempuserimpl.ProvideService(sqlStore, cfg)
|
||||
orgSvc, err := orgimpl.ProvideService(sqlStore, cfg, quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, cfg, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create test user
|
||||
createUserCmd := user.CreateUserCommand{
|
||||
Email: "testuser@localhost",
|
||||
Name: "testuser",
|
||||
Login: "loginuser",
|
||||
Company: "testCompany",
|
||||
IsAdmin: true,
|
||||
}
|
||||
usr, err := userSvc.Create(context.Background(), &createUserCmd)
|
||||
require.NoError(t, err)
|
||||
|
||||
nsMock := notifications.MockNotificationService()
|
||||
|
||||
hs := &HTTPServer{
|
||||
Cfg: cfg,
|
||||
SQLStore: sqlStore,
|
||||
userService: userSvc,
|
||||
tempUserService: tempUserService,
|
||||
NotificationService: nsMock,
|
||||
}
|
||||
return usr, hs, nsMock
|
||||
}
|
||||
|
||||
func TestUser_UpdateEmail(t *testing.T) {
|
||||
cases := []struct {
|
||||
Name string
|
||||
Field user.UpdateEmailActionType
|
||||
}{
|
||||
{
|
||||
Name: "Updating Email field",
|
||||
Field: user.EmailUpdateAction,
|
||||
},
|
||||
{
|
||||
Name: "Updating Login (username) field",
|
||||
Field: user.LoginUpdateAction,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
t.Run("With verification disabled should update without verifying", func(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
smtpConfigured bool
|
||||
verifyEmailEnabled bool
|
||||
}{
|
||||
{
|
||||
name: "SMTP not configured",
|
||||
smtpConfigured: false,
|
||||
verifyEmailEnabled: true,
|
||||
},
|
||||
{
|
||||
name: "config verify_email_enabled = false",
|
||||
smtpConfigured: true,
|
||||
verifyEmailEnabled: false,
|
||||
},
|
||||
{
|
||||
name: "config verify_email_enabled = false and SMTP not configured",
|
||||
smtpConfigured: false,
|
||||
verifyEmailEnabled: false,
|
||||
},
|
||||
}
|
||||
for _, ttt := range tests {
|
||||
settings := setting.NewCfg()
|
||||
settings.Smtp.Enabled = ttt.smtpConfigured
|
||||
setting.VerifyEmailEnabled = ttt.verifyEmailEnabled
|
||||
|
||||
usr, hs, nsMock := setupUpdateEmailTests(t, settings)
|
||||
|
||||
updateUserCommand := user.UpdateUserCommand{
|
||||
Email: usr.Email,
|
||||
Name: "newName",
|
||||
Login: usr.Login,
|
||||
UserID: usr.ID,
|
||||
}
|
||||
|
||||
switch tt.Field {
|
||||
case user.LoginUpdateAction:
|
||||
updateUserCommand.Login = newEmail
|
||||
case user.EmailUpdateAction:
|
||||
updateUserCommand.Email = newEmail
|
||||
}
|
||||
|
||||
fn := func(sc *scenarioContext) {
|
||||
// User is internal
|
||||
sc.authInfoService.ExpectedError = user.ErrUserNotFound
|
||||
|
||||
sc.fakeReqWithParams("PUT", sc.url, nil).exec()
|
||||
assert.Equal(t, http.StatusOK, sc.resp.Code)
|
||||
|
||||
// Verify that no email has been sent after update
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
userQuery := user.GetUserByIDQuery{ID: usr.ID}
|
||||
updatedUsr, err := hs.userService.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify fields have been updated
|
||||
require.NotEqual(t, usr.Name, updatedUsr.Name)
|
||||
require.Equal(t, updateUserCommand.Name, updatedUsr.Name)
|
||||
|
||||
switch tt.Field {
|
||||
case user.LoginUpdateAction:
|
||||
require.Equal(t, usr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, usr.Login, updatedUsr.Login)
|
||||
require.Equal(t, updateUserCommand.Login, updatedUsr.Login)
|
||||
case user.EmailUpdateAction:
|
||||
require.Equal(t, usr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, usr.Email, updatedUsr.Email)
|
||||
require.Equal(t, updateUserCommand.Email, updatedUsr.Email)
|
||||
}
|
||||
|
||||
// Verify other fields have been kept
|
||||
require.Equal(t, usr.Company, updatedUsr.Company)
|
||||
}
|
||||
|
||||
updateUserScenario(t, updateUserContext{
|
||||
desc: ttt.name,
|
||||
url: fmt.Sprintf("/api/users/%d", usr.ID),
|
||||
routePattern: "/api/users/:id",
|
||||
cmd: updateUserCommand,
|
||||
fn: fn,
|
||||
}, hs)
|
||||
|
||||
updateSignedInUserScenario(t, updateUserContext{
|
||||
desc: ttt.name,
|
||||
url: "/api/user",
|
||||
routePattern: "/api/user",
|
||||
cmd: updateUserCommand,
|
||||
fn: fn,
|
||||
}, hs)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
doReq := func(req *http.Request, usr *user.User) (*http.Response, error) {
|
||||
r := webtest.RequestWithSignedInUser(
|
||||
req,
|
||||
authedUserWithPermissions(
|
||||
usr.ID,
|
||||
usr.OrgID,
|
||||
[]accesscontrol.Permission{
|
||||
{
|
||||
Action: accesscontrol.ActionUsersWrite,
|
||||
Scope: accesscontrol.ScopeGlobalUsersAll,
|
||||
},
|
||||
},
|
||||
),
|
||||
)
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}}
|
||||
return client.Do(r)
|
||||
}
|
||||
|
||||
sendUpdateReq := func(server *webtest.Server, usr *user.User, body string) {
|
||||
req := server.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/user",
|
||||
strings.NewReader(body),
|
||||
)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
res, err := doReq(req, usr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
|
||||
sendVerificationReq := func(server *webtest.Server, usr *user.User, code string) {
|
||||
url := fmt.Sprintf("/user/email/update?code=%s", url.QueryEscape(code))
|
||||
req := server.NewGetRequest(url)
|
||||
res, err := doReq(req, usr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusFound, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
}
|
||||
|
||||
getVerificationTempUser := func(tempUserSvc tempuser.Service, code string) *tempuser.TempUserDTO {
|
||||
tmpUserQuery := tempuser.GetTempUserByCodeQuery{Code: code}
|
||||
tmpUser, err := tempUserSvc.GetTempUserByCode(context.Background(), &tmpUserQuery)
|
||||
require.NoError(t, err)
|
||||
return tmpUser
|
||||
}
|
||||
|
||||
verifyEmailData := func(tempUserSvc tempuser.Service, nsMock *notifications.NotificationServiceMock, originalUsr *user.User, newEmail string) {
|
||||
verification := nsMock.EmailVerification
|
||||
tmpUsr := getVerificationTempUser(tempUserSvc, verification.Code)
|
||||
|
||||
require.True(t, nsMock.EmailVerified)
|
||||
require.Equal(t, newEmail, verification.Email)
|
||||
require.Equal(t, originalUsr.ID, verification.User.ID)
|
||||
require.Equal(t, tmpUsr.Code, verification.Code)
|
||||
}
|
||||
|
||||
verifyUserNotUpdated := func(userSvc user.Service, usr *user.User) {
|
||||
userQuery := user.GetUserByIDQuery{ID: usr.ID}
|
||||
checkUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, usr.Email, checkUsr.Email)
|
||||
require.Equal(t, usr.Login, checkUsr.Login)
|
||||
require.Equal(t, usr.Name, checkUsr.Name)
|
||||
}
|
||||
|
||||
setupScenario := func(cfg *setting.Cfg) (*webtest.Server, user.Service, tempuser.Service, *notifications.NotificationServiceMock) {
|
||||
setting.VerifyEmailEnabled = true
|
||||
settings := setting.NewCfg()
|
||||
settings.Smtp.Enabled = true
|
||||
settings.VerificationEmailMaxLifetime = 1 * time.Hour
|
||||
|
||||
if cfg != nil {
|
||||
settings = cfg
|
||||
}
|
||||
|
||||
nsMock := notifications.MockNotificationService()
|
||||
sqlStore := db.InitTestDB(t)
|
||||
sqlStore.Cfg = settings
|
||||
|
||||
tempUserSvc := tempuserimpl.ProvideService(sqlStore, settings)
|
||||
orgSvc, err := orgimpl.ProvideService(sqlStore, settings, quotatest.New(false, nil))
|
||||
require.NoError(t, err)
|
||||
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, settings, nil, nil, quotatest.New(false, nil), supportbundlestest.NewFakeBundleService())
|
||||
require.NoError(t, err)
|
||||
|
||||
server := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.Cfg = settings
|
||||
|
||||
hs.SQLStore = sqlStore
|
||||
hs.userService = userSvc
|
||||
hs.tempUserService = tempUserSvc
|
||||
hs.NotificationService = nsMock
|
||||
hs.SecretsService = fakes.NewFakeSecretsService()
|
||||
// User is internal
|
||||
hs.authInfoService = &authinfotest.FakeService{ExpectedError: user.ErrUserNotFound}
|
||||
})
|
||||
|
||||
return server, userSvc, tempUserSvc, nsMock
|
||||
}
|
||||
|
||||
createUser := func(userSvc user.Service, name string, email string, login string) *user.User {
|
||||
createUserCmd := user.CreateUserCommand{
|
||||
Email: email,
|
||||
Name: name,
|
||||
Login: login,
|
||||
Company: "testCompany",
|
||||
IsAdmin: true,
|
||||
}
|
||||
usr, err := userSvc.Create(context.Background(), &createUserCmd)
|
||||
require.NoError(t, err)
|
||||
return usr
|
||||
}
|
||||
|
||||
t.Run("Update Email and disregard other fields", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newName := "newName"
|
||||
body := fmt.Sprintf(`{"email": "%s", "name": "%s"}`, newEmail, newName)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
require.NotEqual(t, newName, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Email when Login was also an email should update both", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "email@localhost")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email and Login have been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login with an email should update Email too", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"login": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify Email and Login have been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login should not need verification if it is not an email", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newLogin"
|
||||
newName := "newName"
|
||||
body := fmt.Sprintf(`{"login": "%s", "name": "%s"}`, newLogin, newName)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify that email has not been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify Login has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, originalUsr.Name, updatedUsr.Name)
|
||||
require.Equal(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, newName, updatedUsr.Name)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
})
|
||||
|
||||
t.Run("Update Login should not need verification if it is being updated to the already configured email", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"login": "%s"}`, originalUsr.Email)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify that email has not been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify Login has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
})
|
||||
|
||||
t.Run("Update Login and Email with different email values at once should disregard the Login update", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newEmail2@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s", "login": "%s"}`, newEmail, newLogin)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify only Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.NotEqual(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Update Login and Email with different email values at once when Login was already an email should update both with Email", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "email@localhost")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
newLogin := "newEmail2@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s", "login": "%s"}`, newEmail, newLogin)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify only Email has been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.NotEqual(t, originalUsr.Login, updatedUsr.Login)
|
||||
require.NotEqual(t, newLogin, updatedUsr.Login)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, newEmail, updatedUsr.Login)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Name, updatedUsr.Name)
|
||||
})
|
||||
|
||||
t.Run("Email verification should expire", func(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
cfg.Smtp.Enabled = true
|
||||
cfg.VerificationEmailMaxLifetime = 0 // Expire instantly
|
||||
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(cfg)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow, when user clicks email button
|
||||
code := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, code)
|
||||
|
||||
// Verify user has not been updated
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, newEmail, updatedUsr.Email)
|
||||
require.Equal(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
})
|
||||
|
||||
t.Run("A new verification should revoke other pending verifications", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// First email verification
|
||||
firstNewEmail := "newEmail1@localhost"
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, firstNewEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, firstNewEmail)
|
||||
firstCode := nsMock.EmailVerification.Code
|
||||
|
||||
// Second email verification
|
||||
secondNewEmail := "newEmail2@localhost"
|
||||
body = fmt.Sprintf(`{"email": "%s"}`, secondNewEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, secondNewEmail)
|
||||
secondCode := nsMock.EmailVerification.Code
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Try to follow through with the first verification unsuccessfully
|
||||
sendVerificationReq(server, originalUsr, firstCode)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Follow through with second verification successfully
|
||||
sendVerificationReq(server, originalUsr, secondCode)
|
||||
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.NotEqual(t, originalUsr.Email, updatedUsr.Email)
|
||||
require.Equal(t, secondNewEmail, updatedUsr.Email)
|
||||
// Fields unchanged
|
||||
require.Equal(t, originalUsr.Login, updatedUsr.Login)
|
||||
})
|
||||
|
||||
t.Run("Email verification should fail if code is not valid", func(t *testing.T) {
|
||||
server, userSvc, tempUserSvc, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Start email update
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify email data
|
||||
verifyEmailData(tempUserSvc, nsMock, originalUsr, newEmail)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Second part of the verification flow should fail if using the wrong code
|
||||
sendVerificationReq(server, originalUsr, "notTheRightCode")
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
})
|
||||
|
||||
t.Run("Email verification code can only be used once", func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name", "email@localhost", "login")
|
||||
|
||||
// Start email update
|
||||
require.NotEqual(t, originalUsr.Email, newEmail)
|
||||
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, newEmail)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
|
||||
// Verify user has not been updated yet
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Use code to verify successfully
|
||||
codeToReuse := nsMock.EmailVerification.Code
|
||||
sendVerificationReq(server, originalUsr, codeToReuse)
|
||||
|
||||
// User should have an updated Email
|
||||
userQuery := user.GetUserByIDQuery{ID: originalUsr.ID}
|
||||
updatedUsr, err := userSvc.GetByID(context.Background(), &userQuery)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, newEmail, updatedUsr.Email)
|
||||
|
||||
// Change email back to what it was
|
||||
body = fmt.Sprintf(`{"email": "%s"}`, originalUsr.Email)
|
||||
sendUpdateReq(server, originalUsr, body)
|
||||
sendVerificationReq(server, originalUsr, nsMock.EmailVerification.Code)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
|
||||
// Re-use code to verify new email again, unsuccessfully
|
||||
sendVerificationReq(server, originalUsr, codeToReuse)
|
||||
verifyUserNotUpdated(userSvc, originalUsr)
|
||||
})
|
||||
|
||||
t.Run("Update Email with an email that is already being used should fail", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
description string
|
||||
clashLogin bool
|
||||
}{
|
||||
{
|
||||
description: "when Email clashes",
|
||||
clashLogin: false,
|
||||
},
|
||||
{
|
||||
description: "when Login clashes",
|
||||
clashLogin: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.description, func(t *testing.T) {
|
||||
server, userSvc, _, nsMock := setupScenario(nil)
|
||||
|
||||
originalUsr := createUser(userSvc, "name1", "email1@localhost", "login1@localhost")
|
||||
badUsr := createUser(userSvc, "name2", "email2@localhost", "login2")
|
||||
|
||||
// Verify that no email has been sent yet
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Update `badUsr` to use the same email as `originalUsr`
|
||||
body := fmt.Sprintf(`{"email": "%s"}`, originalUsr.Email)
|
||||
if tt.clashLogin {
|
||||
body = fmt.Sprintf(`{"login": "%s"}`, originalUsr.Login)
|
||||
}
|
||||
req := server.NewRequest(
|
||||
http.MethodPut,
|
||||
"/api/user",
|
||||
strings.NewReader(body),
|
||||
)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
res, err := doReq(req, badUsr)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusConflict, res.StatusCode)
|
||||
require.NoError(t, res.Body.Close())
|
||||
|
||||
// Verify that no email has been sent
|
||||
require.False(t, nsMock.EmailVerified)
|
||||
|
||||
// Verify user has not been updated
|
||||
verifyUserNotUpdated(userSvc, badUsr)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
type updateUserContext struct {
|
||||
desc string
|
||||
url string
|
||||
|
||||
@@ -116,6 +116,27 @@ var DashboardViewActions = []string{dashboards.ActionDashboardsRead}
|
||||
var DashboardEditActions = append(DashboardViewActions, []string{dashboards.ActionDashboardsWrite, dashboards.ActionDashboardsDelete}...)
|
||||
var DashboardAdminActions = append(DashboardEditActions, []string{dashboards.ActionDashboardsPermissionsRead, dashboards.ActionDashboardsPermissionsWrite}...)
|
||||
|
||||
func getDashboardViewActions(features featuremgmt.FeatureToggles) []string {
|
||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||
return append(DashboardViewActions, accesscontrol.ActionAnnotationsRead)
|
||||
}
|
||||
return DashboardViewActions
|
||||
}
|
||||
|
||||
func getDashboardEditActions(features featuremgmt.FeatureToggles) []string {
|
||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||
return append(DashboardEditActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
||||
}
|
||||
return DashboardEditActions
|
||||
}
|
||||
|
||||
func getDashboardAdminActions(features featuremgmt.FeatureToggles) []string {
|
||||
if features.IsEnabled(context.Background(), featuremgmt.FlagAnnotationPermissionUpdate) {
|
||||
return append(DashboardAdminActions, []string{accesscontrol.ActionAnnotationsRead, accesscontrol.ActionAnnotationsWrite, accesscontrol.ActionAnnotationsDelete, accesscontrol.ActionAnnotationsCreate}...)
|
||||
}
|
||||
return DashboardAdminActions
|
||||
}
|
||||
|
||||
func ProvideDashboardPermissions(
|
||||
features featuremgmt.FeatureToggles, router routing.RouteRegister, sql db.DB, ac accesscontrol.AccessControl,
|
||||
license licensing.Licensing, dashboardStore dashboards.Store, folderService folder.Service, service accesscontrol.Service,
|
||||
@@ -174,9 +195,9 @@ func ProvideDashboardPermissions(
|
||||
ServiceAccounts: true,
|
||||
},
|
||||
PermissionsToActions: map[string][]string{
|
||||
"View": DashboardViewActions,
|
||||
"Edit": DashboardEditActions,
|
||||
"Admin": DashboardAdminActions,
|
||||
"View": getDashboardViewActions(features),
|
||||
"Edit": getDashboardEditActions(features),
|
||||
"Admin": getDashboardAdminActions(features),
|
||||
},
|
||||
ReaderRoleName: "Dashboard permission reader",
|
||||
WriterRoleName: "Dashboard permission writer",
|
||||
@@ -239,9 +260,9 @@ func ProvideFolderPermissions(
|
||||
ServiceAccounts: true,
|
||||
},
|
||||
PermissionsToActions: map[string][]string{
|
||||
"View": append(DashboardViewActions, FolderViewActions...),
|
||||
"Edit": append(DashboardEditActions, FolderEditActions...),
|
||||
"Admin": append(DashboardAdminActions, FolderAdminActions...),
|
||||
"View": append(getDashboardViewActions(features), FolderViewActions...),
|
||||
"Edit": append(getDashboardEditActions(features), FolderEditActions...),
|
||||
"Admin": append(getDashboardAdminActions(features), FolderAdminActions...),
|
||||
},
|
||||
ReaderRoleName: "Folder permission reader",
|
||||
WriterRoleName: "Folder permission writer",
|
||||
@@ -283,7 +304,6 @@ func (e DatasourcePermissionsService) SetPermissions(ctx context.Context, orgID
|
||||
}
|
||||
|
||||
func (e DatasourcePermissionsService) DeleteResourcePermissions(ctx context.Context, orgID int64, resourceID string) error {
|
||||
// TODO: implement
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user