Compare commits

..

11 Commits

Author SHA1 Message Date
Arve Knudsen
4d88c1a264 Merge pull request #28 from grafana/7.2.3_fix
7.2.3 SAML vulnerability
2020-12-14 11:44:27 +01:00
bergquist
1b3c05077d saml vulnerability
Signed-off-by: bergquist <carl.bergquist@gmail.com>
2020-12-14 09:57:41 +01:00
Marcus Efraimsson
ad9d408ac2 E2E: Update e2e docker image in 7.2.x branch (#28424) 2020-10-21 09:26:59 +02:00
Torkel Ödegaard
9f7f13e02a E2E: Update e2e docker image in 7.2.x branch (#28407) 2020-10-20 17:34:53 +02:00
Marcus Efraimsson
d9540bc368 Release v7.2.2 2020-10-20 16:15:17 +02:00
Marcus Efraimsson
b1543b5676 Go dependency update 2020-10-20 16:15:17 +02:00
Arve Knudsen
0b4aa8b909 CloudWatch: Fix custom metrics (#28391)
* CloudWatch: Fix querying for custom metrics

Co-authored by Mitch McKenzie <mitch.mckenzie@outlook.com>

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit af17f9fd9b)
2020-10-20 16:15:17 +02:00
Carl Bergquist
4d21ade9cd Instrumentation: Add histogram for request duration (#28364)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit edbaa9d681)
2020-10-20 16:15:17 +02:00
Carl Bergquist
36c8a3bd93 remove status label from histogram (#28387)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit b036112444)
2020-10-20 16:15:17 +02:00
Carl Bergquist
589be2872b Instrumentation: Add counters and histograms for database queries (#28236)
Signed-off-by: bergquist <carl.bergquist@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 74d1d3c6a8)
2020-10-20 16:15:17 +02:00
Carl Bergquist
f60edaf029 Instrumentation: Adds environment_info metric (#28355)
Signed-off-by: bergquist <carl.bergquist@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 89ebb97fca)
2020-10-20 16:15:17 +02:00
27 changed files with 334 additions and 102 deletions

View File

@@ -30,7 +30,7 @@ executors:
- image: cimg/go:1.14
e2e:
docker:
- image: srclosson/grafana-plugin-ci-e2e:latest
- image: grafana/ci-e2e:12.19.0-1
grafana-build:
docker:
- image: grafana/build-container:1.2.26

View File

@@ -646,6 +646,12 @@ disable_total_stats = false
basic_auth_username =
basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
#exampleLabel2 = exampleValue2
# Send internal Grafana metrics to graphite
[metrics.graphite]
# Enable by setting the address setting (ex localhost:2003)

View File

@@ -640,6 +640,12 @@
; basic_auth_username =
; basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# can expose more information about the Grafana instance.
[metrics.environment_info]
#exampleLabel1 = exampleValue1
#exampleLabel2 = exampleValue2
# Send internal metrics to Graphite
[metrics.graphite]
# Enable by setting the address setting (ex localhost:2003)

View File

@@ -1041,6 +1041,15 @@ If both are set, then basic authentication is required to access the metrics end
<hr>
## [metrics.environment_info]
Adds dimensions to the `grafana_environment_info` metric, which can expose more information about the Grafana instance.
```
; exampleLabel1 = exampleValue1
; exampleLabel2 = exampleValue2
```
## [metrics.graphite]
Use these options if you want to send internal Grafana metrics to Graphite.

8
go.mod
View File

@@ -19,7 +19,7 @@ require (
github.com/benbjohnson/clock v0.0.0-20161215174838-7dc76406b6d3
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b
github.com/centrifugal/centrifuge v0.11.0
github.com/crewjam/saml v0.4.1
github.com/crewjam/saml v0.4.4-0.20201214083806-0dd2422c212e
github.com/davecgh/go-spew v1.1.1
github.com/deepmap/oapi-codegen v1.3.11 // indirect
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec
@@ -29,6 +29,7 @@ require (
github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/fatih/color v1.9.0
github.com/gchaincl/sqlhooks v1.3.0
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659
@@ -49,7 +50,7 @@ require (
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
github.com/influxdata/influxdb-client-go/v2 v2.0.1
github.com/jmespath/go-jmespath v0.3.0
github.com/jonboulle/clockwork v0.2.1 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/jung-kurt/gofpdf v1.10.1
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 // indirect
github.com/lib/pq v1.3.0
@@ -67,7 +68,6 @@ require (
github.com/rainycape/unidecode v0.0.0-20150907023854-cb7f23ec59be // indirect
github.com/robfig/cron v0.0.0-20180505203441-b41be1df6967
github.com/robfig/cron/v3 v3.0.0
github.com/russellhaering/goxmldsig v0.0.0-20200902171629-2e1fbc2c5593 // indirect
github.com/smartystreets/goconvey v1.6.4
github.com/stretchr/testify v1.6.1
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
@@ -80,7 +80,7 @@ require (
github.com/yudai/gojsondiff v1.0.0
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208

21
go.sum
View File

@@ -176,10 +176,10 @@ github.com/cenkalti/backoff v1.0.0/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQ
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/centrifugal/centrifuge v0.10.0 h1:Eften1Akke0mgHJwHPqhG8oR/6q/SO4y/+KKdz+Qr14=
github.com/centrifugal/centrifuge v0.10.0/go.mod h1:mkuUdBuYFqz2GincQhkwWmHuVs9VWEHhMjzMcdUk6oA=
github.com/centrifugal/protocol v0.3.3 h1:GCNee3RFsjQu6SyKBX0Ir7ByUrp+Gw0MU/PsIc2CM2s=
github.com/centrifugal/protocol v0.3.3/go.mod h1:2YbBCaDwQHl37ErRdMrKSj18X2yVvpkQYtSX6aVbe5A=
github.com/centrifugal/centrifuge v0.11.0 h1:aaG74PBO+rBmz3hE9W8bCGz8kofiam1LNE4EAVtiMaA=
github.com/centrifugal/centrifuge v0.11.0/go.mod h1:jdFw/2dBFpME3OTisc5FVAC1ilqodnyMnQFjem0k0yg=
github.com/centrifugal/protocol v0.3.4 h1:9q22iSp4CQOdQahfopvfmWWxDbj1Lo7ERG2j56mAxkE=
github.com/centrifugal/protocol v0.3.4/go.mod h1:2YbBCaDwQHl37ErRdMrKSj18X2yVvpkQYtSX6aVbe5A=
github.com/cespare/xxhash v0.0.0-20181017004759-096ff4a8a059/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -226,6 +226,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da/go.mod h1:+rmNIXRvYMqLQeR4DHyTvs6y0MEMymTz4vyFpFkKTPs=
github.com/crewjam/saml v0.4.1 h1:ZNSRJvdbypQDY2uApMngeIHNcxS6UCRAgiw3S+pmgRU=
github.com/crewjam/saml v0.4.1/go.mod h1:vHcshzXm2WkPOV1dcToZa99cCB1h3nPiKLtLYK+erBE=
github.com/crewjam/saml v0.4.4-0.20201214083806-0dd2422c212e h1:CFIpybPh+vrxRD6R3t2BCV9hdtlOQudsj1vB1ECXOo4=
github.com/crewjam/saml v0.4.4-0.20201214083806-0dd2422c212e/go.mod h1:qCJQpUtZte9R1ZjUBcW8qtCNlinbO363ooNl02S68bk=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
@@ -317,6 +319,8 @@ github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03D
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y=
github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34=
github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -698,6 +702,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/jonboulle/clockwork v0.2.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.1 h1:S/EaQvW6FpWMYAvYvY+OBDvpaM+izu0oiwo5y0MH7U0=
github.com/jonboulle/clockwork v0.2.1/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/joncrlsn/dque v2.2.1-0.20200515025108-956d14155fa2+incompatible/go.mod h1:hDZb8oMj3Kp8MxtbNLg9vrtAUDHjgI1yZvqivT4O8Iw=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -790,6 +796,8 @@ github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ=
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e h1:qqXczln0qwkVGcpQ+sQuPOVntt2FytYarXXxYSNJkgw=
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattetti/filebuffer v1.0.0 h1:ixTvQ0JjBTwWbdpDZ98lLrydo7KRi8xNRIi5RFszsbY=
github.com/mattetti/filebuffer v1.0.0/go.mod h1:X6nyAIge2JGVmuJt2MFCqmHrb/5IHiphfHtot0s5cnI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -1030,6 +1038,8 @@ github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUc
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
github.com/russellhaering/goxmldsig v0.0.0-20200902171629-2e1fbc2c5593 h1:wkyiSzH81tsd3tSoznvnXMIJo0cpHjbFuJhs/E9t/B8=
github.com/russellhaering/goxmldsig v0.0.0-20200902171629-2e1fbc2c5593/go.mod h1:QK8GhXPB3+AfuCrfo0oRISa9NfzeCpWmxeGnqEpDF9o=
github.com/russellhaering/goxmldsig v1.1.0 h1:lK/zeJie2sqG52ZAlPNn1oBBqsIsEKypUUBGpYYF6lk=
github.com/russellhaering/goxmldsig v1.1.0/go.mod h1:QK8GhXPB3+AfuCrfo0oRISa9NfzeCpWmxeGnqEpDF9o=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
@@ -1241,6 +1251,8 @@ golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9 h1:sYNJzB4J8toYPQTM6pAkcmBRgw9SnQKP9oXCHfgy604=
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1422,6 +1434,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200724161237-0e2f3a69832c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d h1:QQrM/CCYEzTs91GZylDCQjGHudbPTxF/1fvXdVh5lMo=
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -2,5 +2,5 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
"version": "7.2.1"
"version": "7.2.2"
}

View File

@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
"version": "7.2.1",
"version": "7.2.3",
"repository": "github:grafana/grafana",
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/data",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana Data Library",
"keywords": [
"typescript"

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e-selectors",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana End-to-End Test Selectors Library",
"keywords": [
"cli",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana End-to-End Test Library",
"keywords": [
"cli",
@@ -44,7 +44,7 @@
"types": "src/index.ts",
"dependencies": {
"@cypress/webpack-preprocessor": "4.1.3",
"@grafana/e2e-selectors": "7.2.1",
"@grafana/e2e-selectors": "7.2.2",
"@grafana/tsconfig": "^1.0.0-rc1",
"@mochajs/json-file-reporter": "^1.2.0",
"blink-diff": "1.0.13",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/runtime",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana Runtime Library",
"keywords": [
"grafana",
@@ -22,8 +22,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@grafana/data": "7.2.1",
"@grafana/ui": "7.2.1",
"@grafana/data": "7.2.2",
"@grafana/ui": "7.2.2",
"systemjs": "0.20.19",
"systemjs-plugin-css": "0.1.37"
},

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/toolkit",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana Toolkit",
"keywords": [
"grafana",

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/ui",
"version": "7.2.1",
"version": "7.2.2",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -27,8 +27,8 @@
},
"dependencies": {
"@emotion/core": "^10.0.27",
"@grafana/data": "7.2.1",
"@grafana/e2e-selectors": "7.2.1",
"@grafana/data": "7.2.2",
"@grafana/e2e-selectors": "7.2.2",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "1.1.4",

View File

@@ -1,6 +1,6 @@
{
"name": "@jaegertracing/jaeger-ui-components",
"version": "7.2.1",
"version": "7.2.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -14,8 +14,8 @@
"typescript": "3.9.3"
},
"dependencies": {
"@grafana/data": "7.2.1",
"@grafana/ui": "7.2.1",
"@grafana/data": "7.2.2",
"@grafana/ui": "7.2.2",
"@types/classnames": "^2.2.7",
"@types/deep-freeze": "^0.1.1",
"@types/hoist-non-react-statics": "^3.3.1",

View File

@@ -513,6 +513,26 @@ func SetBuildInformation(version, revision, branch string) {
grafanaBuildVersion.WithLabelValues(version, revision, branch, runtime.Version(), edition).Set(1)
}
// SetEnvironmentInformation exposes environment values provided by the operators as an `_info` metric.
// If there are no environment metrics labels configured, this metric will not be exposed.
func SetEnvironmentInformation(labels map[string]string) error {
if len(labels) == 0 {
return nil
}
grafanaEnvironmentInfo := prometheus.NewGauge(prometheus.GaugeOpts{
Name: "environment_info",
Help: "A metric with a constant '1' value labeled by environment information about the running instance.",
Namespace: ExporterName,
ConstLabels: labels,
})
prometheus.MustRegister(grafanaEnvironmentInfo)
grafanaEnvironmentInfo.Set(1)
return nil
}
func SetPluginBuildInformation(pluginID, pluginType, version string) {
grafanaPluginBuildInfoDesc.WithLabelValues(pluginID, pluginType, version).Set(1)
}

View File

@@ -7,12 +7,14 @@ import (
"time"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
"gopkg.in/macaron.v1"
)
var (
httpRequestsInFlight prometheus.Gauge
httpRequestsInFlight prometheus.Gauge
httpRequestDurationHistogram *prometheus.HistogramVec
)
func init() {
@@ -23,33 +25,52 @@ func init() {
},
)
prometheus.MustRegister(httpRequestsInFlight)
httpRequestDurationHistogram = prometheus.NewHistogramVec(
prometheus.HistogramOpts{
Namespace: "grafana",
Name: "http_request_duration_seconds",
Help: "Histogram of latencies for HTTP requests.",
Buckets: []float64{.1, .2, .4, 1, 3, 8, 20, 60, 120},
},
[]string{"handler"},
)
prometheus.MustRegister(httpRequestsInFlight, httpRequestDurationHistogram)
}
// RequestMetrics is a middleware handler that instruments the request
func RequestMetrics(handler string) macaron.Handler {
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
rw := res.(macaron.ResponseWriter)
now := time.Now()
httpRequestsInFlight.Inc()
defer httpRequestsInFlight.Dec()
c.Next()
func RequestMetrics(cfg *setting.Cfg) func(handler string) macaron.Handler {
return func(handler string) macaron.Handler {
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
rw := res.(macaron.ResponseWriter)
now := time.Now()
httpRequestsInFlight.Inc()
defer httpRequestsInFlight.Dec()
c.Next()
status := rw.Status()
status := rw.Status()
code := sanitizeCode(status)
method := sanitizeMethod(req.Method)
metrics.MHttpRequestTotal.WithLabelValues(handler, code, method).Inc()
duration := time.Since(now).Nanoseconds() / int64(time.Millisecond)
metrics.MHttpRequestSummary.WithLabelValues(handler, code, method).Observe(float64(duration))
code := sanitizeCode(status)
method := sanitizeMethod(req.Method)
metrics.MHttpRequestTotal.WithLabelValues(handler, code, method).Inc()
switch {
case strings.HasPrefix(req.RequestURI, "/api/datasources/proxy"):
countProxyRequests(status)
case strings.HasPrefix(req.RequestURI, "/api/"):
countApiRequests(status)
default:
countPageRequests(status)
duration := time.Since(now).Nanoseconds() / int64(time.Millisecond)
// enable histogram and disable summaries for http requests.
if cfg.IsHTTPRequestHistogramEnabled() {
httpRequestDurationHistogram.WithLabelValues(handler).Observe(float64(duration))
} else {
metrics.MHttpRequestSummary.WithLabelValues(handler, code, method).Observe(float64(duration))
}
switch {
case strings.HasPrefix(req.RequestURI, "/api/datasources/proxy"):
countProxyRequests(status)
case strings.HasPrefix(req.RequestURI, "/api/"):
countApiRequests(status)
default:
countPageRequests(status)
}
}
}
}

View File

@@ -22,6 +22,7 @@ import (
_ "github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/metrics"
_ "github.com/grafana/grafana/pkg/infra/metrics"
_ "github.com/grafana/grafana/pkg/infra/remotecache"
_ "github.com/grafana/grafana/pkg/infra/serverlock"
@@ -117,6 +118,9 @@ func (s *Server) init(cfg *Config) error {
s.loadConfiguration()
s.writePIDFile()
if err := metrics.SetEnvironmentInformation(s.cfg.MetricsGrafanaEnvironmentInfo); err != nil {
return err
}
login.Init()
social.NewOAuthService()
@@ -269,7 +273,7 @@ func (s *Server) buildServiceGraph(services []*registry.Descriptor) error {
objs := []interface{}{
bus.GetBus(),
s.cfg,
routing.NewRouteRegister(middleware.RequestMetrics, middleware.RequestTracing),
routing.NewRouteRegister(middleware.RequestMetrics(s.cfg), middleware.RequestTracing),
localcache.New(5*time.Minute, 10*time.Minute),
s,
}

View File

@@ -0,0 +1,110 @@
package sqlstore
import (
"context"
"database/sql"
"database/sql/driver"
"time"
"github.com/gchaincl/sqlhooks"
"github.com/go-sql-driver/mysql"
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
"github.com/lib/pq"
"github.com/mattn/go-sqlite3"
"github.com/prometheus/client_golang/prometheus"
"xorm.io/core"
)
var (
databaseQueryCounter *prometheus.CounterVec
databaseQueryHistogram prometheus.Histogram
)
func init() {
databaseQueryCounter = prometheus.NewCounterVec(prometheus.CounterOpts{
Namespace: "grafana",
Name: "database_queries_total",
Help: "The total amount of Database queries",
}, []string{"status"})
databaseQueryHistogram = prometheus.NewHistogram(prometheus.HistogramOpts{
Namespace: "grafana",
Name: "database_queries_duration_seconds",
Help: "Database query histogram",
Buckets: prometheus.ExponentialBuckets(0.0001, 4, 9),
})
prometheus.MustRegister(databaseQueryCounter, databaseQueryHistogram)
}
// WrapDatabaseDriverWithHooks creates a fake database driver that
// executes pre and post functions which we use to gather metrics about
// database queries.
func WrapDatabaseDriverWithHooks(dbType string) string {
drivers := map[string]driver.Driver{
migrator.SQLITE: &sqlite3.SQLiteDriver{},
migrator.MYSQL: &mysql.MySQLDriver{},
migrator.POSTGRES: &pq.Driver{},
}
d, exist := drivers[dbType]
if !exist {
return dbType
}
driverWithHooks := dbType + "WithHooks"
sql.Register(driverWithHooks, sqlhooks.Wrap(d, &databaseQueryWrapper{log: log.New("sqlstore.metrics")}))
core.RegisterDriver(driverWithHooks, &databaseQueryWrapperParser{dbType: dbType})
return driverWithHooks
}
// databaseQueryWrapper satisfies the sqlhook.databaseQueryWrapper interface
// which allow us to wrap all SQL queries with a `Before` & `After` hook.
type databaseQueryWrapper struct {
log log.Logger
}
// databaseQueryWrapperKey is used as key to save values in `context.Context`
type databaseQueryWrapperKey struct{}
// Before hook will print the query with its args and return the context with the timestamp
func (h *databaseQueryWrapper) Before(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
return context.WithValue(ctx, databaseQueryWrapperKey{}, time.Now()), nil
}
// After hook will get the timestamp registered on the Before hook and print the elapsed time
func (h *databaseQueryWrapper) After(ctx context.Context, query string, args ...interface{}) (context.Context, error) {
begin := ctx.Value(databaseQueryWrapperKey{}).(time.Time)
elapsed := time.Since(begin)
databaseQueryCounter.WithLabelValues("success").Inc()
databaseQueryHistogram.Observe(elapsed.Seconds())
h.log.Debug("query finished", "status", "success", "elapsed time", elapsed, "sql", query)
return ctx, nil
}
// OnError will be called if any error happens
func (h *databaseQueryWrapper) OnError(ctx context.Context, err error, query string, args ...interface{}) error {
status := "error"
// https://golang.org/pkg/database/sql/driver/#ErrSkip
if err == nil || err == driver.ErrSkip {
status = "success"
}
begin := ctx.Value(databaseQueryWrapperKey{}).(time.Time)
elapsed := time.Since(begin)
databaseQueryCounter.WithLabelValues(status).Inc()
databaseQueryHistogram.Observe(elapsed.Seconds())
h.log.Debug("query finished", "status", status, "elapsed time", elapsed, "sql", query, "error", err)
return err
}
type databaseQueryWrapperParser struct {
dbType string
}
func (hp *databaseQueryWrapperParser) Parse(string, string) (*core.Uri, error) {
return &core.Uri{
DbType: core.DbType(hp.dbType),
}, nil
}

View File

@@ -52,15 +52,21 @@ type Dialect interface {
IsDeadlock(err error) bool
}
type dialectFunc func(*xorm.Engine) Dialect
var supportedDialects = map[string]dialectFunc{
MYSQL: NewMysqlDialect,
SQLITE: NewSqlite3Dialect,
POSTGRES: NewPostgresDialect,
MYSQL + "WithHooks": NewMysqlDialect,
SQLITE + "WithHooks": NewSqlite3Dialect,
POSTGRES + "WithHooks": NewPostgresDialect,
}
func NewDialect(engine *xorm.Engine) Dialect {
name := engine.DriverName()
switch name {
case MYSQL:
return NewMysqlDialect(engine)
case SQLITE:
return NewSqlite3Dialect(engine)
case POSTGRES:
return NewPostgresDialect(engine)
if fn, exist := supportedDialects[name]; exist {
return fn(engine)
}
panic("Unsupported database type: " + name)

View File

@@ -14,7 +14,7 @@ type Mysql struct {
BaseDialect
}
func NewMysqlDialect(engine *xorm.Engine) *Mysql {
func NewMysqlDialect(engine *xorm.Engine) Dialect {
d := Mysql{}
d.BaseDialect.dialect = &d
d.BaseDialect.engine = engine

View File

@@ -15,7 +15,7 @@ type Postgres struct {
BaseDialect
}
func NewPostgresDialect(engine *xorm.Engine) *Postgres {
func NewPostgresDialect(engine *xorm.Engine) Dialect {
d := Postgres{}
d.BaseDialect.dialect = &d
d.BaseDialect.engine = engine

View File

@@ -11,7 +11,7 @@ type Sqlite3 struct {
BaseDialect
}
func NewSqlite3Dialect(engine *xorm.Engine) *Sqlite3 {
func NewSqlite3Dialect(engine *xorm.Engine) Dialect {
d := Sqlite3{}
d.BaseDialect.dialect = &d
d.BaseDialect.engine = engine

View File

@@ -238,6 +238,10 @@ func (ss *SqlStore) getEngine() (*xorm.Engine, error) {
return nil, err
}
if ss.Cfg.IsDatabaseMetricsEnabled() {
ss.dbCfg.Type = WrapDatabaseDriverWithHooks(ss.dbCfg.Type)
}
sqlog.Info("Connecting to DB", "dbtype", ss.dbCfg.Type)
if ss.dbCfg.Type == migrator.SQLITE && strings.HasPrefix(connectionString, "file:") {
exists, err := fs.Exists(ss.dbCfg.Path)

View File

@@ -16,6 +16,7 @@ import (
"time"
"github.com/go-macaron/session"
"github.com/prometheus/common/model"
ini "gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/components/gtime"
@@ -261,17 +262,20 @@ type Cfg struct {
CookieSameSiteDisabled bool
CookieSameSiteMode http.SameSite
TempDataLifetime time.Duration
TempDataLifetime time.Duration
PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
PluginSettings PluginSettings
PluginsAllowUnsigned []string
DisableSanitizeHtml bool
EnterpriseLicensePath string
// Metrics
MetricsEndpointEnabled bool
MetricsEndpointBasicAuthUsername string
MetricsEndpointBasicAuthPassword string
MetricsEndpointDisableTotalStats bool
PluginsEnableAlpha bool
PluginsAppsSkipVerifyTLS bool
PluginSettings PluginSettings
PluginsAllowUnsigned []string
DisableSanitizeHtml bool
EnterpriseLicensePath string
MetricsGrafanaEnvironmentInfo map[string]string
// Dashboards
DefaultHomeDashboardPath string
@@ -320,6 +324,14 @@ func (c Cfg) IsLiveEnabled() bool {
return c.FeatureToggles["live"]
}
func (c Cfg) IsDatabaseMetricsEnabled() bool {
return c.FeatureToggles["database_metrics"]
}
func (c Cfg) IsHTTPRequestHistogramEnabled() bool {
return c.FeatureToggles["http_request_histogram"]
}
type CommandLineArgs struct {
Config string
HomePath string
@@ -395,6 +407,29 @@ func applyEnvVariableOverrides(file *ini.File) error {
return nil
}
func (cfg *Cfg) readGrafanaEnvironmentMetrics() error {
environmentMetricsSection := cfg.Raw.Section("metrics.environment_info")
keys := environmentMetricsSection.Keys()
cfg.MetricsGrafanaEnvironmentInfo = make(map[string]string, len(keys))
for _, key := range keys {
labelName := model.LabelName(key.Name())
labelValue := model.LabelValue(key.Value())
if !labelName.IsValid() {
return fmt.Errorf("invalid label name in [metrics.environment_info] configuration. name %q", labelName)
}
if !labelValue.IsValid() {
return fmt.Errorf("invalid label value in [metrics.environment_info] configuration. name %q value %q", labelName, labelValue)
}
cfg.MetricsGrafanaEnvironmentInfo[string(labelName)] = string(labelValue)
}
return nil
}
func (cfg *Cfg) readAnnotationSettings() {
dashboardAnnotation := cfg.Raw.Section("annotations.dashboard")
apiIAnnotation := cfg.Raw.Section("annotations.api")
@@ -760,6 +795,9 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
cfg.readSmtpSettings()
cfg.readQuotaSettings()
cfg.readAnnotationSettings()
if err := cfg.readGrafanaEnvironmentMetrics(); err != nil {
return err
}
if VerifyEmailEnabled && !cfg.Smtp.Enabled {
log.Warnf("require_email_validation is enabled but smtp is disabled")

View File

@@ -412,10 +412,7 @@ func (e *cloudWatchExecutor) handleGetDimensions(ctx context.Context, parameters
}
} else {
var err error
dsInfo := e.getDSInfo(region)
dsInfo.Namespace = namespace
if dimensionValues, err = e.getDimensionsForCustomMetrics(region); err != nil {
if dimensionValues, err = e.getDimensionsForCustomMetrics(region, namespace); err != nil {
return nil, errutil.Wrap("unable to call AWS API", err)
}
}
@@ -700,15 +697,14 @@ func (e *cloudWatchExecutor) resourceGroupsGetResources(region string, filters [
return &resp, nil
}
func (e *cloudWatchExecutor) getAllMetrics(region string) (cloudwatch.ListMetricsOutput, error) {
func (e *cloudWatchExecutor) getAllMetrics(region, namespace string) (cloudwatch.ListMetricsOutput, error) {
client, err := e.getCWClient(region)
if err != nil {
return cloudwatch.ListMetricsOutput{}, err
}
dsInfo := e.getDSInfo(region)
params := &cloudwatch.ListMetricsInput{
Namespace: aws.String(dsInfo.Namespace),
Namespace: aws.String(namespace),
}
plog.Debug("Listing metrics pages")
@@ -737,7 +733,6 @@ func (e *cloudWatchExecutor) getMetricsForCustomMetrics(region, namespace string
defer metricsCacheLock.Unlock()
dsInfo := e.getDSInfo(region)
dsInfo.Namespace = namespace
if _, ok := customMetricsMetricsMap[dsInfo.Profile]; !ok {
customMetricsMetricsMap[dsInfo.Profile] = make(map[string]map[string]*customMetricsCache)
@@ -745,36 +740,36 @@ func (e *cloudWatchExecutor) getMetricsForCustomMetrics(region, namespace string
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region]; !ok {
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*customMetricsCache)
}
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &customMetricsCache{}
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
if _, ok := customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace]; !ok {
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace] = &customMetricsCache{}
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = make([]string, 0)
}
if customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
if customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Expire.After(time.Now()) {
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, nil
}
result, err := e.getAllMetrics(region)
result, err := e.getAllMetrics(region, namespace)
if err != nil {
return []string{}, err
}
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire = time.Now().Add(5 * time.Minute)
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = make([]string, 0)
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Expire = time.Now().Add(5 * time.Minute)
for _, metric := range result.Metrics {
if isDuplicate(customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *metric.MetricName) {
if isDuplicate(customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, *metric.MetricName) {
continue
}
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = append(
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *metric.MetricName)
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = append(
customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, *metric.MetricName)
}
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
return customMetricsMetricsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, nil
}
var dimensionsCacheLock sync.Mutex
func (e *cloudWatchExecutor) getDimensionsForCustomMetrics(region string) ([]string, error) {
func (e *cloudWatchExecutor) getDimensionsForCustomMetrics(region, namespace string) ([]string, error) {
dimensionsCacheLock.Lock()
defer dimensionsCacheLock.Unlock()
@@ -786,32 +781,32 @@ func (e *cloudWatchExecutor) getDimensionsForCustomMetrics(region string) ([]str
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region]; !ok {
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region] = make(map[string]*customMetricsCache)
}
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace]; !ok {
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace] = &customMetricsCache{}
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
if _, ok := customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace]; !ok {
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace] = &customMetricsCache{}
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = make([]string, 0)
}
if customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire.After(time.Now()) {
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
if customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Expire.After(time.Now()) {
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, nil
}
result, err := e.getAllMetrics(region)
result, err := e.getAllMetrics(region, namespace)
if err != nil {
return []string{}, err
}
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = make([]string, 0)
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Expire = time.Now().Add(5 * time.Minute)
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = make([]string, 0)
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Expire = time.Now().Add(5 * time.Minute)
for _, metric := range result.Metrics {
for _, dimension := range metric.Dimensions {
if isDuplicate(customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *dimension.Name) {
if isDuplicate(customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, *dimension.Name) {
continue
}
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache = append(
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, *dimension.Name)
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache = append(
customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, *dimension.Name)
}
}
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][dsInfo.Namespace].Cache, nil
return customMetricsDimensionsMap[dsInfo.Profile][dsInfo.Region][namespace].Cache, nil
}
func isDuplicate(nameList []string, target string) bool {

View File

@@ -1,6 +1,6 @@
{
"name": "@grafana-plugins/input-datasource",
"version": "7.2.1",
"version": "7.2.2",
"description": "Input Datasource",
"private": true,
"repository": {
@@ -16,9 +16,9 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"devDependencies": {
"@grafana/data": "7.2.1",
"@grafana/toolkit": "7.2.1",
"@grafana/ui": "7.2.1"
"@grafana/data": "7.2.2",
"@grafana/toolkit": "7.2.2",
"@grafana/ui": "7.2.2"
},
"volta": {
"node": "12.16.2"