Compare commits

...

127 Commits

Author SHA1 Message Date
bergquist
f25d63954b fixes for saml vulnerability
closes https://github.com/grafana/grafana/issues/29875

Signed-off-by: bergquist <carl.bergquist@gmail.com>
2020-12-17 10:54:28 +01:00
Peter Holmberg
11f305f88a [v7.3.x] Fix: Correct panel edit uistate migration (#29413) (#29711)
* Fix: Correct panel edit uistate migration (#29413)

(cherry picked from commit 26e3b61f92)

* prettier
2020-12-09 17:02:57 +01:00
Grot (@grafanabot)
3795ad5af0 PanelEdit: Prevent the preview pane to be resized further than window height (#28370) (#29726)
* use percentage on topPanel, limit resize

* add relative size to rightPanel, resize split panes on resize

* lock min size of options pane to min 300px, adding debounce

* sorting imports

* assigning the ref, remove debounce

* set default uistate to number instead of string

* revert go.sum and go.mod

* fix go.mod

(cherry picked from commit f13c267f1c)

Co-authored-by: Peter Holmberg <peterholmberg@users.noreply.github.com>
2020-12-09 16:44:31 +01:00
Peter Holmberg
f5d8063da2 Fix: Migrate Panel edit uiState percentage strings to number (#29412) (#29723)
(cherry picked from commit 06e48cb869)
2020-12-09 15:49:30 +01:00
Grot (@grafanabot)
751f32c752 "Release: Updated versions in package to 7.3.5" (#29710) 2020-12-09 13:15:57 +01:00
Grot (@grafanabot)
8e4fb74e8f Chore: upgrading y18n to 4.0.1 for security reasons (#29523) (#29709)
(cherry picked from commit 60502463e2)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-12-09 12:18:00 +01:00
Grot (@grafanabot)
13c8669ac7 Panel: making sure we support all versions of chrome when detecting position of click event. (#29544) (#29708)
* making sure we are always using int value when compairing coordinates.

* removed unused import.

* Using Math.floor instead.

* removed unused dep.

(cherry picked from commit de395f24e4)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-12-09 12:17:41 +01:00
Grot (@grafanabot)
e9553ddd72 PanelEdit: making sure the correct datasource query editor is being rendered. (#29500) (#29707)
* making sure we try to use the selected db in the query row.

* fixing so we use || instead of ??.

(cherry picked from commit 863dbc7ba8)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-12-09 12:10:50 +01:00
Grot (@grafanabot)
7922b400ef [v7.3.x] Auth: Add SigV4 header allowlist to reduce chances of verification issues (#29705)
* Auth: Add SigV4 header allowlist to reduce chances of verification issues (#29650)

* enforce allowlist

* fix default auth selection

* add Host and comment

(cherry picked from commit 31d64d9074)

* fixup

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Will Browne <will.browne@grafana.com>
2020-12-09 11:54:46 +01:00
Grot (@grafanabot)
3ed851151f Alerting: Use correct time series name override from frame fields (#29693) (#29698)
* cater for empty labels and new DisplayNameFromDS field

* simplify

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* increase priority for ds display name

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 3de091edf1)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-12-08 22:22:15 +01:00
Grot (@grafanabot)
76d3a7a1e6 CloudWatch: namespace in search expression should be quoted if match exact is enabled #29109 (#29563) (#29687)
(cherry picked from commit 9668c172b3)

Co-authored-by: ying-jeanne <74549700+ying-jeanne@users.noreply.github.com>
2020-12-08 17:30:02 +01:00
Carl Bergquist
2ec763a0d4 Adds go dep used by an Enterprise feature. (#29645) (#29690)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit 609d61dece)
2020-12-08 17:05:13 +01:00
Grot (@grafanabot)
240417f82f instrumentation: align label name with our other projects (#29514) (#29685)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit e1d5fc8627)

Co-authored-by: Carl Bergquist <carl.bergquist@gmail.com>
2020-12-08 14:50:07 +01:00
Carl Bergquist
d95ac6c85c Instrumentation: Add examplars for request histograms (#29357) (#29682)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit b7aa6fed1d)
2020-12-08 14:25:58 +01:00
Grot (@grafanabot)
1e6c14a750 Login: Fixes typo in tooltip (#29604) (#29606)
See "log in" (verb) vs "login" (noun) : https://english.stackexchange.com/a/5304/354852

(cherry picked from commit 985c7934d9)

Co-authored-by: Mathieu Rollet <matletix@gmail.com>
2020-12-04 10:23:01 +01:00
Grot (@grafanabot)
f511cf6727 fixes bug with invalid handler name for metrics (#29529) (#29532)
closes https://github.com/grafana/grafana/issues/29487

Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit 4edb1364e9)

Co-authored-by: Carl Bergquist <carl.bergquist@gmail.com>
2020-12-02 14:38:47 +01:00
Grot (@grafanabot)
9b70e04cdf AzureMonitor: Unit MilliSeconds naming (#29399) (#29526)
(cherry picked from commit 63c8945db5)

Co-authored-by: Sebastian Poxhofer <secustor@users.noreply.github.com>
2020-12-02 13:50:23 +01:00
Grot (@grafanabot)
95a9fad671 Alarting: fix alarm messages in dingding (Fixes #29470) (#29482) (#29527)
(cherry picked from commit d33c4e5e7c)

Co-authored-by: Tomo Wang <tomo_wang@outlook.com>
2020-12-02 11:30:23 +01:00
Grot (@grafanabot)
e6327b0536 Bug: trace viewer doesn't show more than 300 spans (#29377) (#29504)
* Fix: trace viewer scroll issue

* Fix lazy loading

* Listview test fixes

* Move scrollElement to props

(cherry picked from commit 9913ac73fb)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-12-01 18:11:22 +01:00
Grot (@grafanabot)
df367b0634 Prometheus: don't override displayName property (#29441) (#29488)
* Prometheus: don't override displayName property

* Rename displayName to displayNameFromDS for consistency

(cherry picked from commit 207831fa39)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-12-01 10:57:46 +01:00
Will Browne
2069630cb3 resolve conflicts (#29415) 2020-11-26 15:40:43 +01:00
Arve Knudsen
a578cdf18c Drone: Upgrade build pipeline tool (#29365) (#29368)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 5773929953)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-25 11:22:15 +01:00
Grot (@grafanabot)
81e18c85bf Drone: Upload artifacts for release branch builds (#29297) (#29364)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 5cec4095b2)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-25 09:49:05 +01:00
Grot (@grafanabot)
d7208411cc Drone: Execute artifact publishing for both editions in parallel during release (#29362) (#29363)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit f7567f2266)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-25 09:28:13 +01:00
Grot (@grafanabot)
14c494085e Drone: Publish NPM packages after Storybook to avoid race condition (#29340) (#29343)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 7d8cb6869a)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-24 15:17:31 +01:00
Grot (@grafanabot)
3a50fc8db0 Docs: Fix editor role and alert notification channel description (#29301) (#29337)
* Docs: Fix editor role and alert notification channel description

* Update docs/sources/permissions/organization_roles.md

Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
Co-authored-by: Diana Payton <52059945+oddlittlebird@users.noreply.github.com>
(cherry picked from commit 27b4390484)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-11-24 11:52:49 +01:00
Grot (@grafanabot)
7472e37cf9 "Release: Updated versions in package to 7.3.4" (#29336) 2020-11-24 11:20:38 +01:00
Grot (@grafanabot)
be6425d461 Security: Fixes minor security issue with alert notification webhooks that allowed GET & DELETE requests #29330 (#29335)
(cherry picked from commit d796c61946)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-11-24 10:45:09 +01:00
Torkel Ödegaard
f34fecbca0 Backport of InfluxDB: update flux library and support boolean label values #29333 2020-11-24 10:41:15 +01:00
Torkel Ödegaard
509174df31 ReleaseNotes: Update link in package.json (#29328) 2020-11-24 10:24:03 +01:00
Grot (@grafanabot)
6ada37b445 Login: Fixes redirect url encoding issues of # %23 being unencoded after login (#29299) (#29323)
(cherry picked from commit 763e958d9d)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-24 07:52:04 +01:00
Grot (@grafanabot)
6d79790397 Drone: Upgrade build pipeline tool (#29308) (#29309)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit e293dd40b0)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-23 17:36:01 +01:00
Grot (@grafanabot)
089c636acf Annotations: fixing so when changing annotations query links submenu will be updated. (#28990) (#29285)
* fixing so changes to annotations query links will update submenu.

* Refactored so we dont use the events to trigger a refresh but instead recreating the annotations list instead of mutating the existing list.

* updated snapshot.

* uppdates according to feedback.

* fixed so it also works to update a annotation.

(cherry picked from commit 81859880d3)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-11-21 17:26:57 +01:00
Grot (@grafanabot)
fcd66eef29 Dashboard: Fixes kiosk state after being redirected to login page and back (#29273) (#29278)
* Login: Fixes issue where url parameters where modified by golang url code

* Add tests

* Fix test cases

* Update pkg/middleware/auth_test.go

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>

* fixed formatting

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
(cherry picked from commit 1076f47509)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-20 20:40:11 +01:00
Grot (@grafanabot)
cffb1cd98d Increase search limit on team add user and improve placeholder (#29258) (#29261)
(cherry picked from commit a312f1ae80)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-20 13:17:59 +01:00
Arve Knudsen
881a595651 Drone: Sync with master (#29205)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-18 16:00:58 +01:00
Arve Knudsen
004104717c Drone: Fix publish-packages invocation (#29179) (#29184)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-18 10:12:34 +01:00
Arve Knudsen
b345e28a2d Chore: Upgrade grafana/build-ci-deploy image to latest Go (#29171) (#29180)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-18 09:13:12 +01:00
Grot (@grafanabot)
fa1db4dc12 Table: Fix incorrect condtition for rendering table filter (#29165) (#29181)
(cherry picked from commit f28ba27ca2)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-18 09:05:14 +01:00
Grot (@grafanabot)
9a3fbb8782 DashboardLinks: will only refresh dashboard search when changing tags for link. (#29040) (#29177)
* fixing so we dont run multiple dashboard links searches when changing variables.

* changed so we fetch the list when open the dashboard links dropdown.

* removed verification of unneccesary requests.

(cherry picked from commit 4c0aa4acd5)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-11-18 07:28:57 +01:00
Arve Knudsen
7292e1508e Drone: Upgrade build pipeline tool and build image (#29161) (#29162)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-17 20:40:19 +01:00
Grot (@grafanabot)
2489dc4d3a Release: Updated versions in package to 7.3.3 (#29126) 2020-11-17 13:18:22 +01:00
Torkel Ödegaard
ca8de25f0c git cherry-pick -x 0f3bebb38d (#29155)
Backport of https://github.com/grafana/grafana/pull/29031
2020-11-17 12:05:11 +01:00
Grot (@grafanabot)
ff7c462600 Build: support custom build tags (#28609) (#29128)
* Build: support custom build tags

* Update build.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 63c230b670)

Co-authored-by: Leonard Gram <leo@xlson.com>
2020-11-17 11:09:19 +01:00
Torkel Ödegaard
41bbafe979 Revert "Graph: Fixes stacking issues like floating bars when data is not aligned (#29051) (#29088)" (#29151)
This reverts commit 697e4f7037.
2020-11-17 10:27:10 +01:00
Grot (@grafanabot)
37e4a19ea8 Provisioning: always pin app to the sidebar when enabled (#29084) (#29146)
(cherry picked from commit 4f9123ebe2)

Co-authored-by: Alexander Zobnin <alexanderzobnin@gmail.com>
2020-11-17 10:27:49 +03:00
Grot (@grafanabot)
f04131c9c6 build paths in an os independent way (#29143) (#29147)
Use filepath instead of path to manipulate filename paths in a way compatible with the target operating system-defined file paths.

(cherry picked from commit 4430ca2317)

Co-authored-by: Andrew Mattheisen <amattheisen@users.noreply.github.com>
2020-11-17 08:24:12 +01:00
Grot (@grafanabot)
7e6c34fc20 Chore: Upgrade Go dev tools (#29124) (#29132)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 115f38f5c3)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-16 08:34:17 +01:00
Torkel Ödegaard
0e5da44bc1 Automatin: set node version 2020-11-15 12:18:02 +01:00
Torkel Ödegaard
fc44872ca2 Automation: Adding version bump action 2020-11-15 11:52:34 +01:00
Grot (@grafanabot)
c88fc42fc6 Drone: Fix Drone config verification for enterprise on Windows (#29118) (#29119)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 01df8f177c)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-14 12:04:03 +01:00
Grot (@grafanabot)
d05c3462da [v7.3.x] Drone: Verify Drone config at beginning of pipelines (#29111)
* Drone: Verify Drone config at beginning of pipelines (#29071)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 113e288668)

* Sync .drone.yml

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

* Drone: Remove Windows testing of PRs

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-14 07:36:28 +01:00
Grot (@grafanabot)
8db22cef96 Test Datasource/Bug: Fixes division by zero in csv metric values scenario (#29029) (#29068)
Closes #8705

(cherry picked from commit 9659c98d61)

Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com>
2020-11-13 15:52:58 +01:00
Grot (@grafanabot)
edfe914cb0 [v7.3.x] StatPanel: Fixes hanging issue when all values are zero (#29087)
* StatPanel: Fixes hanging issue when all values are zero (#29077)

* StatPanel: Fixes hanging issue when all values are zero

* Minor tweak

(cherry picked from commit 01a4951da0)

* Fixed test

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-11-13 15:51:38 +01:00
Arve Knudsen
4cf6c916c7 Data source proxy: Convert 401 from data source to 400 (#28962) (#29095)
* Data source proxy: Convert 401 from data source to 400

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-11-13 15:06:38 +01:00
Grot (@grafanabot)
697e4f7037 Graph: Fixes stacking issues like floating bars when data is not aligned (#29051) (#29088)
* Graph: Remove interpolation logic from graph stacking

* Improving tooltip

(cherry picked from commit dff451992d)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-13 14:16:16 +01:00
Grot (@grafanabot)
ca4fdc46fd Auth: Enable more complete credential chain for SigV4 default SDK auth option (#29065) (#29086)
* Force more complete credential chain for default auth option

* simplify

* allow assume role for default

(cherry picked from commit eba046d3cb)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-11-13 11:23:52 +01:00
Grot (@grafanabot)
e086a96161 Fix for multi-value template variable for project selector (#29042) (#29054)
(cherry picked from commit 0c054d1a9f)

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
2020-11-12 11:54:08 +02:00
Grot (@grafanabot)
945573eb93 Thresholds: Fixes color assigned to null values (#29010) (#29018)
* Thresholds: Fixes color assigned to null values

* Updates

(cherry picked from commit 145901c0db)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-12 09:39:49 +01:00
Emil Tullstedt
b8ad4eaab3 [v7.3.x] Chore: Bump build pipeline version (#29025) 2020-11-11 14:20:01 +01:00
Leonard Gram
f034cbef50 Release v7.3.2 (#29024) 2020-11-11 14:09:57 +01:00
David
23be6e3898 Fix conflict (#29020) 2020-11-11 12:57:07 +01:00
Grot (@grafanabot)
7798f01cc9 StatPanels: Fixes auto min max when latest value is zero (#28982) (#29007)
(cherry picked from commit 10f226c4c2)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-11 11:56:33 +01:00
Grot (@grafanabot)
38b96278c8 Tracing: Add setting for sampling server (#29011) (#29015)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
(cherry picked from commit e3c7d66324)

Co-authored-by: Carl Bergquist <carl@grafana.com>
2020-11-11 10:42:38 +01:00
Marcus Andersson
f142752ad6 Gauge: making sure threshold panel json is correct before render (#28898) (#28984)
* making sure we work with a proper data structure.

* added test to verify functionality.

* removed unused variables.

Co-authored-by: Leonard Gram <leo@xlson.com>
2020-11-10 17:32:49 +01:00
Marcus Andersson
1cfe644d51 Variables: make sure that we support both old and new syntax for custom variables. (#28896) (#28985)
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-11-10 17:32:37 +01:00
Grot (@grafanabot)
d8ac457ebc Explore: Remove redundant decodeURI and fix urls (#28697) (#28963)
* Remove redundant path URIdecoding

* Remove redundant comments

(cherry picked from commit f71f03bf94)

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
2020-11-10 17:30:20 +01:00
Grot (@grafanabot)
3d4ff87721 [v7.3.x] Drone: Fix docs building (#28987)
* Drone: Fix docs building (#28986)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 9582e0afd9)

* Updated

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
2020-11-10 16:49:05 +01:00
Marcus Andersson
0c5524786b Alerting: Append appSubUrl to back button on channel form (#28282) (#28983)
Co-authored-by: Mikhail Snetkov <ufoproger@gmail.com>
2020-11-10 16:19:47 +01:00
Grot (@grafanabot)
c0adf1022b Plugins: allow override when allowing unsigned plugins (#28901) (#28927)
* Plugins: allow override when allowing unsigned plugins

* Update pkg/plugins/plugins.go

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>

* Plugins: removed java-style setter

* Plugins: cleanup

* Update pkg/plugins/plugins.go

Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>

Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
(cherry picked from commit 8fb06da34e)

Co-authored-by: Leonard Gram <leo@xlson.com>
2020-11-10 15:05:53 +01:00
Grot (@grafanabot)
706cc59d1d CloudWatch Logs: Change what we use to measure progress (#28912) (#28964)
(cherry picked from commit 76f4c11430)

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2020-11-10 12:12:26 +01:00
Sofia Papagiannaki
f9f3b9d953 Tracing: log traceID in request logger (#28952) (#28959)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
2020-11-10 09:33:48 +01:00
Grot (@grafanabot)
1b14c6b8db Panel inspect: Interpolate variables in panel inspect title (#28779) (#28801)
* Interpolate variables in panel inspect title

* Update public/app/features/dashboard/components/Inspector/InspectContent.tsx

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* fix typo

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
(cherry picked from commit 4036a44a80)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-10 09:23:26 +01:00
Grot (@grafanabot)
d8674013cb UsageStats: start tracking usage stats for tempo (#28948) (#28951)
Signed-off-by: bergquist <carl.bergquist@gmail.com>
(cherry picked from commit afb06ec21a)

Co-authored-by: Carl Bergquist <carl@grafana.com>
2020-11-10 09:20:13 +01:00
Grot (@grafanabot)
fc86862533 Short URL: Cleanup unvisited/stale short URLs (#28867) (#28944)
* cleanup stale short urls

* refactor test case names

* service injection

* fix query

* add docs

* remove comma

(cherry picked from commit a7ea8de47e)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-11-09 19:01:02 +01:00
Grot (@grafanabot)
3636749a43 Plugins signing: Fix docs urls (#28930) (#28934)
(cherry picked from commit 2ddda8f452)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-09 15:10:37 +01:00
Grot (@grafanabot)
f30f6d8a35 Chore: Fix spelling issue (#28904) (#28925)
(cherry picked from commit 5b00b500a7)

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
2020-11-09 10:10:41 +02:00
Grot (@grafanabot)
202b9e5ca9 API: replace SendLoginLogCommand with LoginHook (#28777) (#28891)
* API: replace SendLoginLogCommand with LoginHook

* LoginInfo: Query -> LoginUsername

(cherry picked from commit 2c246276fd)

Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
2020-11-06 11:33:27 +01:00
Grot (@grafanabot)
c9a6e09235 Elasticsearch: Exclude pipeline aggregations from order by options (#28620) (#28873)
(cherry picked from commit 135b83e17f)

Co-authored-by: Chris Cowan <chris@chriscowan.us>
2020-11-06 09:23:52 +00:00
Grot (@grafanabot)
c03fbac5db Dashboards / Folders: delete related data (permissions, stars, tags, versions, annotations) when deleting a dashboard or a folder (#28826) (#28890)
* Dashboard: delete related data when deleting a dashboard or a folder

* fix migrations order

* apply PR feedback

(cherry picked from commit f0421ed08e)

Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
2020-11-06 09:58:58 +01:00
Grot (@grafanabot)
de19ba97cb Disable selecting enterprise plugins with no license (#28758) (#28859)
* Add unlicensed property to plugins

* Disable selecting unlicensed plugin

* Add customizable plugin market place url

* License: workaround enabled only in enterprise

* linter

* Move licensing info to front end

* Update pkg/services/licensing/oss.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/services/licensing/oss.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/api/frontendsettings.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update sample.ini

* Update docs

* Update packages/grafana-runtime/src/config.ts

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update public/app/features/datasources/state/buildCategories.ts

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update pkg/api/frontendsettings.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update pkg/setting/setting.go

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Fix spelling

Co-authored-by: Leonard Gram <leo@xlson.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
(cherry picked from commit 9b90ff2961)

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
2020-11-05 13:22:02 +02:00
Grot (@grafanabot)
e5be54b0f0 Tempo: fix test data source (#28836) (#28856)
(cherry picked from commit 62138e8ad4)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-11-05 12:02:09 +01:00
Grot (@grafanabot)
70bf01f4f9 Prometheus: fix missing labels from value (#28842) (#28855)
(cherry picked from commit 9155f46315)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-11-05 12:01:07 +01:00
Grot (@grafanabot)
0ee8427386 Units: added support to handle negative fractional numbers. (#28849) (#28851)
(cherry picked from commit abe96f4f89)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-11-05 10:59:41 +01:00
Grot (@grafanabot)
7995ab999f increase blob column size for encrypted dashboard data (#28831) (#28832)
(cherry picked from commit 65554269ed)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-11-04 21:30:37 +01:00
Grot (@grafanabot)
b51303b83d Gauge: Improve font size auto sizing (#28797) (#28828)
* Gauge: Improved font size calculations

* Added some comments

* update

* Moving to variable

(cherry picked from commit 2b7a570273)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-04 18:36:27 +01:00
Grot (@grafanabot)
c14ee2d245 Variables: Fixes URL values for dependent variables (#28798) (#28800)
(cherry picked from commit 50b3409474)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-11-04 09:38:20 +01:00
Grot (@grafanabot)
d394be2555 grafana/toolkit: Extract CHANGELOG when building plugin (#28773) (#28774)
(cherry picked from commit 651ab5677c)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-03 15:18:20 +01:00
Grot (@grafanabot)
f523500df2 Templating: Custom variable edit UI, change text input into textarea (#28312) (#28322) (#28704)
(cherry picked from commit 0f97925c1a)

Co-authored-by: Darryl <5493333+darrylsepeda@users.noreply.github.com>
2020-11-03 12:54:35 +01:00
Grot (@grafanabot)
d55ca82f0b Cloudwatch: Fix issue with field calculation transform not working properly with Cloudwatch data (#28761) (#28775)
* Cloudwatch: Fix issue with reducer transform not working properly with Cloudwatch data

* Updated go sdk and updated code

* CloudWatch: Improve test

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit fa567de9b8)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-11-03 12:07:18 +01:00
Grot (@grafanabot)
7f9990c889 Plugin page: Fix dom validation warning (#28737) (#28741)
(cherry picked from commit e93cb06604)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-11-02 17:23:55 +01:00
Grot (@grafanabot)
6a2d9224f1 Dashboard: fix view panel mode for Safari / iOS (#28702) (#28755)
* fix(dashboard): flex-basis so child height percentages are respected

* fix(dashboard): reset scrollTop when entering view panel mode

(cherry picked from commit 87ba1b67b7)

Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
2020-11-02 17:23:31 +01:00
Grot (@grafanabot)
88ae4c0e64 Fix typo in unsigned plugin warning (#28709) (#28722)
Typo fix!

(cherry picked from commit 5b1dbe0b0c)

Co-authored-by: Éamon Ryan <eamonryan@users.noreply.github.com>
2020-11-02 11:13:01 +01:00
Grot (@grafanabot)
f11bfe95da TableFilters: Fixes filtering with field overrides (#28690) (#28727)
* TableFilters: Fixes filtering with field overrides

* Refactor: changes after PR comments

(cherry picked from commit ba12a6a42a)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-11-02 07:52:47 +01:00
Grot (@grafanabot)
49dd35f8d9 Templating: Speeds up certain variable queries for Postgres, MySql and MSSql (#28686) (#28726)
(cherry picked from commit 04565d497e)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-11-02 06:51:39 +01:00
Grot (@grafanabot)
557639a458 Prometheus: Fix copy paste behaving as cut and paste (#28622) (#28691)
(cherry picked from commit 43a0167b01)

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
2020-10-30 15:07:18 +01:00
Dominik Prokop
6668161a88 Release 7.3.1 2020-10-30 11:13:21 +01:00
Grot (@grafanabot)
f81257c2f2 Loki: Fix error when some queries return zero results (#28645) (#28689)
* Fix logSeriesToLogsModel when some data frames have empty fields

* Update test

* Update public/app/core/logs_model.ts

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
(cherry picked from commit 05644e7042)

Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
2020-10-30 10:05:27 +01:00
Grot (@grafanabot)
e8d399ef94 TextPanel: Fixes problems where text panel would show old content (#28643) (#28687)
(cherry picked from commit 16a1d2f744)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-30 08:44:14 +01:00
Grot (@grafanabot)
03563abba0 PanelMenu: Fixes panel submenu not being accessible for panels close to the right edge of the screen (#28666) (#28676)
* Dropdowns: Trying to fix dropdown menus

* Dropdowns: Trying to fix dropdown menus

* removed now unnessary wrapper ref

* Upodates

* Remove export

(cherry picked from commit 5a83fc574a)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 20:33:16 +01:00
Grot (@grafanabot)
43928c4c88 Cloudwatch: Fix duplicate metric data (#28642) (#28668)
* Cloudwatch: Fix duplicate metric data

* Refactor reduce function to for of

(cherry picked from commit 6dbf1f830a)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-10-29 17:31:14 +01:00
Grot (@grafanabot)
3069d764f8 Plugins: Fix descendent frontend plugin signature validation (#28638) (#28662)
* move plugin root check to earlier in validation process

* remove comment

* only check root if necessary

(cherry picked from commit b9d71f5cdd)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-29 15:28:20 +01:00
Grot (@grafanabot)
de9ac280b2 StatPanel: Fixed value being under graph and reduced likley hood for white and dark value text mixing (#28641) (#28655)
* StatPanel: Fixed value being under graph and reduced likley hood for white and dark value text mixing

* Updated snapshot

* Updated storybook config

(cherry picked from commit b46ac2891d)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 14:59:00 +01:00
Grot (@grafanabot)
4ff80d541b Table: Fix image cell mode so that it works with value mappings (#28644) (#28651)
(cherry picked from commit 1e51d33d85)

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
2020-10-29 14:58:38 +01:00
Grot (@grafanabot)
4f4fcf6331 Plugin signing: Fix copy on signed plugin notice (#28633) (#28637)
* Fix copy on signed plugin notice

* Update public/app/features/plugins/PluginPage.tsx

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Update public/app/features/plugins/PluginPage.tsx

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit a2b3e63705)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-10-29 11:32:12 +01:00
kyle
bf8837f6f8 Release v7.3.0 2020-10-28 08:03:43 -04:00
Grot (@grafanabot)
95a3c6a024 Live: updated the reference to use lazy loaded Monaco in code editor. (#28597) (#28599)
(cherry picked from commit c258ee86eb)

Co-authored-by: Marcus Andersson <marcus.andersson@grafana.com>
2020-10-28 07:59:22 -04:00
Grot (@grafanabot)
1ff5718af9 AddDatasource: Improve plugin categories (#28584) (#28589)
* add IoT category to datasources

* add more enterprise plugins

* add more enterprise plugins

(cherry picked from commit 33ef71d81e)

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2020-10-28 10:55:19 +01:00
Grot (@grafanabot)
e123e50d27 StatPanel: Fixes BizChart error max: yyy should not be less than min zzz (#28587) (#28588)
(cherry picked from commit 0d803613d6)

Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com>
2020-10-28 08:24:31 +01:00
Kyle Brandt
44f691ff91 Alerting: Log alert warnings for obsolete notifiers when extracting alerts and remove spammy error (#28162) (#28582)
* Lower level of notification translation messages

* API: Log alert warnings when saving dashboard

* Remove spammy error

* Rename function parameter

* Apply suggestions from code review

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* Apply suggestions from code review

* Fix test

* Change alertValidator return type

* Small fix

* Rename symbol

* Revert "Rename symbol"

This reverts commit 40b619b21a.

* Revert "Small fix"

This reverts commit 2df8319d1d.

* Revert "Change alertValidator return type"

This reverts commit ad933863e3.

* Revert "Fix test"

This reverts commit f728ece2db.

* Revert "Apply suggestions from code review"

This reverts commit f35c5f52af.

* Revert "Apply suggestions from code review"

This reverts commit 7f95800c5f.

* Revert "Rename function parameter"

This reverts commit 95d3e75b00.

* Revert "API: Log alert warnings when saving dashboard"

This reverts commit 1ac5c3f281.

* Conditionally log translation failures

* Fix issue causing test to fail

* Fix test

* Log instead of propagating translations failures due to database errors

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-27 15:57:01 -04:00
Grot (@grafanabot)
12a69876c7 Plugin signing: UI information (#28469) (#28563)
* first pass

* return list

* types and cleanup

* add to plugin page and add styles

* update comment

* update comment

* fix component path

* simplify error component

* simplify error struct

* fix tests

* don't export and fix string()

* update naming

* remove frontend

* introduce phantom loader

* track single error

* remove error from base

* remove unused struct

* remove unnecessary filter

* add errors endpoint

* Update set log to use id field

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* skip adding BE plugins

* remove errs from plugin + ds list

* remove unnecessary fields

* add signature state to panels

* Fetch plugins errors

* grafana/ui component tweaks

* DS Picker - add unsigned badge

* VizPicker - add unsigned badge

* PluginSignatureBadge tweaks

* Plugins list - add signatures info box

* New datasource page - add signatures info box

* Plugin page - add signatures info box

* Fix test

* Do not show Core label in viz picker

* Update public/app/features/plugins/PluginsErrorsInfo.tsx

Co-authored-by: Torkel Ödegaard <torkel@grafana.org>

* Update public/app/features/plugins/PluginListPage.test.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/plugins/PluginListPage.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Update public/app/features/datasources/NewDataSourcePage.tsx

Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>

* Review comments 1

* Review comments 2

* Update public/app/features/plugins/PluginsErrorsInfo.tsx

* Update public/app/features/plugins/PluginPage.tsx

* Prettier fix

* remove stale backend code

* Docs issues fix

Co-authored-by: Will Browne <will.browne@grafana.com>
Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.org>
Co-authored-by: Alex Khomenko <Clarity-89@users.noreply.github.com>
(cherry picked from commit 4468d41417)

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
2020-10-27 15:28:56 -04:00
Grot (@grafanabot)
5bb203df99 Short URL: Update last seen at when visiting a short URL (#28565) (#28573)
Ref #28248

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
(cherry picked from commit 60d40fa99b)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-27 12:16:56 -04:00
Grot (@grafanabot)
23ae63913f Grafana/ui: auto focus threshold editor input (#28360) (#28538)
* feat(grafana-ui): autofocus threshold editor input

* refactor(grafana-ui): remove commented out css

* feat(grafana-ui): use ref for autofocus new thresholds editor input

* refactor(grafana-ui): conditionally set input ref for latest threshold

* refactor(grafana-ui): put back createRef for input ref

(cherry picked from commit a8a3686785)

Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com>
2020-10-27 09:04:55 +01:00
Grot (@grafanabot)
9b3981fb3c API: add login username in SendLoginLogCommand (#28544) (#28545)
* API: add login username in Login actions

* LoginUser -> LoginUsername

* fix test

(cherry picked from commit 65b5086a59)

Co-authored-by: Agnès Toulet <35176601+AgnesToulet@users.noreply.github.com>
2020-10-26 18:52:48 +02:00
Will Browne
c933d58893 Docs: SigV4 What's New and AWS Elasticsearch documentation (#28506) (#28543)
* add what's new entry and elastic docs

* update docs

* remove caps

* fix image path

* fix md link

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>

* fix link case

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>

* fix md link

* docs fixes

* update config ref

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-26 14:52:29 +01:00
Grot (@grafanabot)
1c61f3fd45 AzureMonitor: Support decimal (as float64) type in analytics/logs (#28480) (#28540)
loss of precision but will make the response work instead of erroring
follows work already done in the ADX plugin
fixes #28278

(cherry picked from commit 96e6524a7d)

Co-authored-by: Kyle Brandt <kyle@grafana.com>
2020-10-26 08:46:08 -04:00
Grot (@grafanabot)
be4b32270a Auth: Fix SigV4 request verification step for Amazon Elasticsearch Service (#28481) (#28539)
* remove forward header and add extra steps for encoding

* add comment

* re-use forwarded header

* fix service param

* Make SDK the default auth option in UI

* use SDK code instead

* propagate err

* refactor flow

* fix prom service namespace

(cherry picked from commit 4d2b20f727)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-26 13:09:03 +01:00
Grot (@grafanabot)
1e027f5289 Drone: Upgrade build pipeline tool (#28533) (#28536)
* Drone: Upgrade grabpl

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 1571579c1d)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-26 12:16:41 +01:00
Grot (@grafanabot)
99613f86f8 Drone: Upgrade build-pipeline tool (#28520) (#28530)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit c3917e6525)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-26 09:49:10 +01:00
Grot (@grafanabot)
43a1cb25b9 Elasticsearch: Fix handling of errors when testing data source (#28498) (#28504)
#24999 changed the error response payload, but the test data source
was not adapted to this change and broke the feature of displaying
any errors to the user in the UI. This change should resolve this
problem.

Ref #24999
Ref #28481

(cherry picked from commit c8b7ccc66c)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-24 01:53:42 +02:00
Grot (@grafanabot)
648a149813 Plugins: Track plugin signing errors and expose them to the frontend (#28219) (#28505)
* first pass

* return list

* types and cleanup

* add to plugin page and add styles

* update comment

* update comment

* fix component path

* simplify error component

* simplify error struct

* fix tests

* don't export and fix string()

* update naming

* remove frontend

* introduce phantom loader

* track single error

* remove error from base

* remove unused struct

* remove unnecessary filter

* add errors endpoint

* Update set log to use id field

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>

* skip adding BE plugins

* remove errs from plugin + ds list

* remove unnecessary fields

* add signature state to panels

* remove unused code

* apply PR feedback

* update comment

* merge dto with model

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 1bee9f4168)

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
2020-10-23 17:54:34 +02:00
Grot (@grafanabot)
d8ed14f5a5 Cloud Monitoring: Fix help section for aliases (#28499) (#28509)
(cherry picked from commit 8327dd500f)

Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
2020-10-23 18:43:25 +03:00
Grot (@grafanabot)
8f5c9b192f Auth: Should redirect to login when anonymous enabled and URL with different org than anonymous specified (#28158) (#28503)
If anonymous access is enabled for an org and there are multiple
orgs. When requesting a page that requires user to be logged in
and orgId query string is set in the request url to an org not
equal the anonymous org, if the user is not logged in should
be redirected to the login page.

Fixes #26120

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
Co-authored-by: Sofia Papagiannaki <papagian@users.noreply.github.com>
(cherry picked from commit 3be82ecd4e)

Co-authored-by: Marcus Efraimsson <marcus.efraimsson@gmail.com>
2020-10-23 16:57:19 +02:00
Grot (@grafanabot)
0a184cb84f Drone: Don't build Windows installer for version branches (#28494) (#28495)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 090c683fea)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-23 12:30:11 +02:00
Grot (@grafanabot)
be4ef1dcd8 Drone: Add version branch pipeline (#28490) (#28492)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 07897c7e1e)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-23 10:37:26 +02:00
Grot (@grafanabot)
de86b51fb5 Explore: parse time range fix (#28467) (#28470)
* Explore: parse time range fix

* Remove commented out code

* Fix cases for string epoch and ISO strings

Co-authored-by: Andrej Ocenas <mr.ocenas@gmail.com>
(cherry picked from commit ce1f7908c8)

Co-authored-by: Zoltán Bedi <zoltan.bedi@gmail.com>
2020-10-22 17:02:58 +02:00
Grot (@grafanabot)
25653e15a6 Circle: Remove release pipeline (#28474) (#28477)
Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
(cherry picked from commit 77cbb4f0f9)

Co-authored-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-22 16:42:12 +02:00
227 changed files with 4537 additions and 2962 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,9 @@
load('scripts/pr.star', 'pr_pipelines')
load('scripts/master.star', 'master_pipelines')
load('scripts/release.star', 'release_pipelines', 'test_release_pipelines')
load('scripts/version.star', 'version_branch_pipelines')
def main(ctx):
edition = 'oss'
return pr_pipelines(edition=edition) + master_pipelines(edition=edition) + release_pipelines() + \
test_release_pipelines()
test_release_pipelines() + version_branch_pipelines()

1217
.drone.yml

File diff suppressed because it is too large Load Diff

27
.github/workflows/bump-version.yml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Bump version
on:
workflow_dispatch:
inputs:
version:
required: true
default: '7.x.x'
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions
uses: actions/checkout@v2
with:
repository: "grafana/grafana-github-actions"
path: ./actions
ref: main
- uses: actions/setup-node@v1
with:
node-version: '12'
- name: Install Actions
run: npm install --production --prefix ./actions
- name: Run bump version
uses: ./actions/bump-version
with:
token: ${{secrets.GH_BOT_ACCESS_TOKEN}}
metricsWriteAPIKey: ${{secrets.GRAFANA_MISC_STATS_API_KEY}}

View File

@@ -37,6 +37,7 @@ var (
libc string
pkgArch string
version string = "v1"
buildTags []string
// deb & rpm does not support semver so have to handle their version a little differently
linuxPackageVersion string = "v1"
linuxPackageIteration string = ""
@@ -59,11 +60,13 @@ func main() {
log.SetFlags(0)
var buildIdRaw string
var buildTagsRaw string
flag.StringVar(&goarch, "goarch", runtime.GOARCH, "GOARCH")
flag.StringVar(&goos, "goos", runtime.GOOS, "GOOS")
flag.StringVar(&gocc, "cc", "", "CC")
flag.StringVar(&libc, "libc", "", "LIBC")
flag.StringVar(&buildTagsRaw, "build-tags", "", "Sets custom build tags")
flag.BoolVar(&cgo, "cgo-enabled", cgo, "Enable cgo")
flag.StringVar(&pkgArch, "pkg-arch", "", "PKG ARCH")
flag.BoolVar(&race, "race", race, "Use race detector")
@@ -89,6 +92,10 @@ func main() {
return
}
if len(buildTagsRaw) > 0 {
buildTags = strings.Split(buildTagsRaw, ",")
}
log.Printf("Version: %s, Linux Version: %s, Package Iteration: %s\n", version, linuxPackageVersion, linuxPackageIteration)
if flag.NArg() == 0 {
@@ -105,16 +112,16 @@ func main() {
case "build-srv", "build-server":
clean()
doBuild("grafana-server", "./pkg/cmd/grafana-server", []string{})
doBuild("grafana-server", "./pkg/cmd/grafana-server", buildTags)
case "build-cli":
clean()
doBuild("grafana-cli", "./pkg/cmd/grafana-cli", []string{})
doBuild("grafana-cli", "./pkg/cmd/grafana-cli", buildTags)
case "build":
//clean()
for _, binary := range binaries {
doBuild(binary, "./pkg/cmd/"+binary, []string{})
doBuild(binary, "./pkg/cmd/"+binary, buildTags)
}
case "build-frontend":

View File

@@ -670,7 +670,7 @@ disable_total_stats = false
basic_auth_username =
basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# 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
@@ -705,6 +705,8 @@ sampler_type = const
# and indicates the initial sampling rate before the actual one
# is received from the mothership
sampler_param = 1
# sampling_server_url is the URL of a sampling manager providing a sampling strategy.
sampling_server_url =
# Whether or not to use Zipkin span propagation (x-b3- HTTP headers).
zipkin_propagation = false
# Setting this to true disables shared RPC spans.
@@ -768,6 +770,7 @@ enable_alpha = false
app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
allow_loading_unsigned_plugins =
marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]

View File

@@ -664,7 +664,7 @@
; basic_auth_username =
; basic_auth_password =
# Metrics environment info adds dimensions to the `grafana_environment_info` metric, which
# 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
@@ -697,6 +697,8 @@
# and indicates the initial sampling rate before the actual one
# is received from the mothership
;sampler_param = 1
# sampling_server_url is the URL of a sampling manager providing a sampling strategy.
;sampling_server_url =
# Whether or not to use Zipkin propagation (x-b3- HTTP headers).
;zipkin_propagation = false
# Setting this to true disables shared RPC spans.
@@ -756,6 +758,7 @@
;app_tls_skip_verify_insecure = false
# Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
;allow_loading_unsigned_plugins =
;marketplace_url = https://grafana.com/grafana/plugins/
#################################### Grafana Image Renderer Plugin ##########################
[plugin.grafana-image-renderer]

View File

@@ -1159,6 +1159,10 @@ This is the sampler configuration parameter. Depending on the value of `sampler_
May be set with the environment variable `JAEGER_SAMPLER_PARAM`.
### sampling_server_url
sampling_server_url is the URL of a sampling manager providing a sampling strategy.
### zipkin_propagation
Default value is `false`.
@@ -1336,6 +1340,10 @@ Set to `true` if you want to test alpha plugins that are not yet ready for gener
Enter a comma-separated list of plugin identifiers to identify plugins that are allowed to be loaded even if they lack a valid signature.
### marketplace_url
Custom install/learn more url for enterprise plugins. Defaults to https://grafana.com/grafana/plugins/.
<hr>
## [plugin.grafana-image-renderer]

View File

@@ -248,3 +248,19 @@ datasources:
logMessageField: message
logLevelField: fields.level
```
## Amazon Elasticsearch Service
AWS users using Amazon's Elasticsearch Service can use Grafana's Elasticsearch data source to visualize Elasticsearch data.
If you are using an AWS Identity and Access Management (IAM) policy to control access to your Amazon Elasticsearch Service domain, then you must use AWS Signature Version 4 (AWS SigV4) to sign all requests to that domain.
For more details on AWS SigV4, refer to the [AWS documentation](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html).
### AWS Signature Version 4 authentication
> **Note:** Only available in Grafana v7.3+.
In order to sign requests to your Amazon Elasticsearch Service domain, SigV4 can be enabled in the Grafana [configuration]({{< relref "../administration/configuration.md#sigv4_auth_enabled" >}}).
Once AWS SigV4 is enabled, it can be configured on the Elasticsearch data source configuration page. Refer to [Cloudwatch authentication]({{<relref "./cloudwatch.md#authentication" >}}) for more information about authentication options.
{{< docs-imagebox img="/img/docs/v73/elasticsearch-sigv4-config-editor.png" max-width="500px" class="docs-image--no-shadow" caption="SigV4 configuration for AWS Elasticsearch Service" >}}

View File

@@ -53,7 +53,7 @@ You can close the newly created query by clicking on the Close Split button.
> Share shortened link is only available in Grafana 7.3 and above.
The Share shortened link capability allows you to create smaller and simpler URLs of the format /goto/:uid instead of using longer URLs containing complex query parameters. You can create a shortened link by clicking on the **Share** option in Explore toolbar.
The Share shortened link capability allows you to create smaller and simpler URLs of the format /goto/:uid instead of using longer URLs containing complex query parameters. You can create a shortened link by clicking on the **Share** option in Explore toolbar. Please note that any shortened links that are never used will be automatically deleted after 7 days.
## Query history

View File

@@ -11,7 +11,7 @@ Overrides allow you to change the settings for one or more fields. Field options
For example, you could change the number of decimal places shown in all numeric fields or columns by changing the **Decimals** option for **Fields with type** that matches **Numeric**. For more information about options, refer to:
- [Standard field options]({{< relref "standard-field-options.md" >}}), which apply to all panel visualizations that allow transformations.
- [Table field options]({{< relref "table-field-options.md" >}}), which only apply to table panel visualizations.
- There can also be visualization specific field display options
## Add a field override
@@ -26,8 +26,6 @@ You can override as many field options as you want to.
- **Fields with type -** Allows you to select fields by type, such as string, numeric, and so on. Properties you add to a rule with this selector are applied to all fields that match the selected type.
1. Click **Add override property**.
1. Select the field option that you want to apply.
- [Standard field options]({{< relref "standard-field-options.md" >}}), which apply to all panel visualizations that allow transformations.
- [Table field options]({{< relref "table-field-options.md" >}}), which only apply to table panel visualizations.
1. Enter options by adding values in the fields. To return options to default values, delete the white text in the fields.
1. Continue to add overrides to this field by clicking **Add override property**, or you can click **Add override** and select a different field to add overrides to.
1. When finished, click **Save** to save all panel edits to the dashboard.

View File

@@ -37,11 +37,11 @@ Can do everything scoped to the organization. For example:
- Can view, add, and edit dashboards, panels, and alert rules in dashboards they have access to. This can be disabled on specific folders and dashboards.
- Can create, update, or delete playlists.
- Can access Explore.
- Can add, edit, or delete alert notification channels.
- Cannot add, edit, or delete data sources.
- Cannot add, edit, or delete alert notification channels.
- Cannot manage other organizations, users, and teams.
This role can be changed with the Grafana server setting [editors_can_admin]({{< relref "../administration/configuration.md#editors_can_admin" >}}). If you set this to `true`, then users with the Editor role can also administrate dashboards, folders and teams they create. This is especially useful for enabling self-organizing teams to administer their own dashboards.
This role can be changed with the Grafana server setting [editors_can_admin]({{< relref "../administration/configuration.md#editors_can_admin" >}}). If you set this to `true`, then users with the Editor role can also administrate dashboards, folders, and teams they create. This is especially useful for enabling self-organizing teams to administer their own dashboards.
## Viewer role

View File

@@ -15,16 +15,53 @@ weight = -17
This topic includes the release notes for the Grafana v7.3. For all details, read the full [CHANGELOG.md](https://github.com/grafana/grafana/blob/master/CHANGELOG.md).
## Highlights
Grafana 7.3 comes with a number of features and enhancements:
The main highlights are:
- [**Google Cloud Monitoring:** Out of the box dashboards]({{< relref "#cloud-monitoring-out-of-the-box-dashboards" >}})
- [**Shorten URL for dashboards and Explore**]({{< relref "#shorten-url-for-dashboards-and-explore" >}})
- [**Table improvements and new image cell mode**]({{< relref "#table-improvements-and-new-image-cell-mode" >}})
- [**New color scheme option**]({{< relref "#new-color-scheme-option" >}})
- [**SigV4 Authentication for Amazon Elasticsearch Service**]({{< relref "#sigv4-authentication-for-aws-users" >}})
#### Cloud monitoring out-of-the-box dashboards
## Table improvements and new image cell mode
The updated Cloud monitoring data source is shipped with pre-configured dashboards for five of the most popular GCP services:
The table has been updated with improved hover behavior for cells that have longer content than what fits the current column width. As you can see
in the animated gif below the cell will automatically expand to show you full content of the cell.
{{< figure src="/img/docs/v73/table_hover.gif" max-width="900px" caption="Table hover" >}}
Another new feature that can be seen in the image above is the new image cell display mode. If you have a field value that is an image URL or a base64 encoded image you can configure the table to display it as an image.
## New color scheme option
{{< figure src="/img/docs/v73/color_scheme_dropdown.png" max-width="450px" caption="Color scheme" class="pull-right" >}}
A new standard field [color scheme]({{< relref "../panels/field-options/standard-field-options.md#color-scheme" >}}) option has been added. This new option will provide a unified way for all new panels to specify how colors should be assigned.
* **Single color**: Specify a single color, useful in an override rule.
* **From thresholds**: Informs Grafana to take the color from the matching threshold.
* **Classic palette**: Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations.
* **Green-Yellow-Red (by value)**: This is a continuous color scheme where Grafana will interpolate a color based on the value being displayed and the field min & max values.
* **Blue-Yellow-Red (by value)**: Same as above but different colors.
* **Blues (by value)**: Same as above but color scheme go from panel background to blue.
<div class="clearfix"></div>
As you can see this adds new continuous color schemes where Grafana will interpolate colors. A great use of these new color schemes is the table panel where you can color the background and get a heatmap like effect.
{{< figure src="/img/docs/v73/table_color_scheme.png" max-width="900px" caption="table color scheme" >}}
Another thing to highlight is that all these new color schemes are theme aware and adapt to the current theme. For example here is how the new monochrome color scheme look like in the light theme:
{{< figure src="/img/docs/v73/table_color_scheme_mono_light.png" max-width="900px" caption="table color monochrome scheme" >}}
As this new option is a standard field option it works in every panel. Here is another example from the [Bar Gauge]({{< relref "../panels/visualizations/bar-gauge-panel.md" >}}) panel.
{{< figure src="/img/docs/v73/bar_gauge_gradient_color_scheme.png" max-width="900px" caption="bar gauge color scheme" >}}
## Google Cloud monitoring out-of-the-box dashboards
The updated Google Cloud monitoring data source is shipped with pre-configured dashboards for five of the most popular Google Cloud Platform (GCP) services:
- BigQuery
- Cloud Load Balancing
@@ -38,7 +75,13 @@ For more details, see the [Google Cloud Monitoring docs]({{<relref "../datasourc
## Shorten URL for dashboards and Explore
This is an amazing new feature that was created in cooperation with one of our community members. The new **share shortened link** capability allows you to create smaller and simpler URLs of the format `/goto/:uid` instead of using longer URLs that can contain complex query parameters. In Explore, you can create a shortened link by clicking on the share button in Explore toolbar. In the dashboards, a shortened url option is available through the share panel or dashboard button.
This is an amazing new feature that was created in cooperation with one of our community members. The new share shortened link capability allows you to create smaller and simpler URLs of the format `/goto/:uid` instead of using longer URLs that can contain complex query parameters. In Explore, you can create a shortened link by clicking on the share button in Explore toolbar. In the dashboards, a shortened url option is available through the share panel or dashboard button.
## SigV4 authentication for AWS users
You can now configure your Elasticsearch data source to access your Amazon Elasticsearch Service domain directly from Grafana.
For more details, refer to the [Elasticsearch docs]({{<relref "../datasources/elasticsearch/#aws-signature-version-4-authentication">}}).
## Grafana Enterprise features

View File

@@ -59,8 +59,7 @@ e2e.scenario({
e2e.components.DashboardLinks.dropDown()
.should('be.visible')
.click()
.wait('@tagsTemplatingSearch')
.wait('@tagsDemoSearch');
.wait('@tagsTemplatingSearch');
// verify all links, should have p2 value
verifyLinks('p2');

41
go.mod
View File

@@ -11,8 +11,7 @@ replace github.com/denisenkom/go-mssqldb => github.com/denisenkom/go-mssqldb v0.
replace k8s.io/client-go => k8s.io/client-go v0.18.8
require (
cloud.google.com/go v0.60.0 // indirect
cloud.google.com/go/storage v1.8.0
cloud.google.com/go/storage v1.10.0
github.com/BurntSushi/toml v0.3.1
github.com/VividCortex/mysqlerr v0.0.0-20170204212430-6c6b55f8796f
github.com/aws/aws-sdk-go v1.33.12
@@ -20,9 +19,8 @@ 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
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
github.com/facebookgo/inject v0.0.0-20180706035515-f23751cae28b
@@ -37,20 +35,20 @@ require (
github.com/go-sql-driver/mysql v1.5.0
github.com/go-stack/stack v1.8.0
github.com/gobwas/glob v0.2.3
github.com/golang/protobuf v1.4.2
github.com/google/go-cmp v0.5.0
github.com/golang/protobuf v1.4.3
github.com/google/go-cmp v0.5.2
github.com/gosimple/slug v1.4.2
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4
github.com/grafana/grafana-plugin-sdk-go v0.78.0
github.com/grafana/grafana-plugin-sdk-go v0.79.0
github.com/grafana/loki v1.6.0
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1
github.com/hashicorp/go-hclog v0.12.2
github.com/hashicorp/go-plugin v1.2.2
github.com/hashicorp/go-version v1.2.0
github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec
github.com/influxdata/influxdb-client-go/v2 v2.0.1
github.com/influxdata/influxdb-client-go/v2 v2.2.0
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
@@ -62,13 +60,13 @@ require (
github.com/opentracing/opentracing-go v1.2.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.7.1
github.com/prometheus/client_golang v1.8.0
github.com/prometheus/client_model v0.2.0
github.com/prometheus/common v0.10.0
github.com/prometheus/common v0.14.0
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
github.com/russellhaering/goxmldsig v1.1.0
github.com/smartystreets/goconvey v1.6.4
github.com/stretchr/testify v1.6.1
github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf
@@ -77,17 +75,22 @@ require (
github.com/uber/jaeger-client-go v2.25.0+incompatible
github.com/unknwon/com v1.0.1
github.com/urfave/cli/v2 v2.1.1
github.com/weaveworks/common v0.0.0-20201119133501-0619918236ec
github.com/xorcare/pointer v1.1.0
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/net v0.0.0-20200813134508-3edf25e44fcc
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d // indirect
golang.org/x/text v0.3.3 // indirect
google.golang.org/grpc v1.30.0
golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9
golang.org/x/net v0.0.0-20201022231255-08b38378de70
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e
golang.org/x/tools v0.0.0-20201023150057-2f4fa188d925 // indirect
google.golang.org/api v0.33.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 // indirect
google.golang.org/grpc v1.33.1
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect
gopkg.in/ini.v1 v1.51.0

126
go.sum
View File

@@ -15,13 +15,15 @@ cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.60.0 h1:R+tDlceO7Ss+zyvtsdhTxacDyZ1k99xwskQ4FT7ruoM=
cloud.google.com/go v0.60.0/go.mod h1:yw2G51M9IfRboUH61Us8GqCeF1PzPblB823Mn2q2eAU=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0 h1:Dg9iHVQfrhq82rUNu9ZxUDrJLaxFUe/HlCVaLyRruq8=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/bigtable v1.1.0/go.mod h1:B6ByKcIdYmhoyDzmOnQxyOhN6r05qnewYIxxG6L0/b4=
cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm+2xVAli2o=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
@@ -36,6 +38,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0 h1:86K1Gel7BQ9/WmNWn7dTKMvTLFzwtBe5FNqYbi9X35g=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
contrib.go.opencensus.io/exporter/ocagent v0.6.0/go.mod h1:zmKjrJcdo0aYcVS7bmEeSEBLPA9YJp5bjrofdU3pIXs=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@@ -226,6 +230,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=
@@ -244,9 +250,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dchest/uniuri v0.0.0-20160212164326-8902c56451e9/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
github.com/deepmap/oapi-codegen v1.3.6/go.mod h1:aBozjEveG+33xPiP55Iw/XbVkhtZHEGLq3nxlX0+hfU=
github.com/deepmap/oapi-codegen v1.3.11 h1:Nd3tDQfqgquLmCzyRONHzs5SJEwPPoQcFZxT8MKt1Hs=
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
github.com/deepmap/oapi-codegen v1.3.13 h1:9HKGCsdJqE4dnrQ8VerFS0/1ZOJPmAhN+g8xgp8y3K4=
github.com/deepmap/oapi-codegen v1.3.13/go.mod h1:WAmG5dWY8/PYHt4vKxlt90NsbHMAOCiteYKZMiIRfOo=
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec h1:NfhRXXFDPxcF5Cwo06DzeIaE7uuJtAUhsDwH3LNsjos=
github.com/denisenkom/go-mssqldb v0.0.0-20200620013148-b91950f658ec/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
@@ -308,6 +313,8 @@ github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5Kwzbycv
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/structtag v1.1.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fluent/fluent-bit-go v0.0.0-20190925192703-ea13c021720c/go.mod h1:WQX+afhrekY9rGK+WT4xvKSlzmia9gDoLYu4GGYGASQ=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@@ -319,7 +326,6 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
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=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -461,6 +467,7 @@ github.com/gocql/gocql v0.0.0-20200121121104-95d072f1b5bb/go.mod h1:DL0ekTmBSTdl
github.com/gocql/gocql v0.0.0-20200526081602-cd04bd7f22a7/go.mod h1:DL0ekTmBSTdlNF25Orwt/JMzqIq3EJ4MVa/J/uK64OY=
github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/gofrs/flock v0.7.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -471,6 +478,7 @@ github.com/gogo/protobuf v1.2.2-0.20190730201129-28a6bbf47e48/go.mod h1:SlYgWuQ5
github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/status v1.0.3 h1:WkVBY59mw7qUNTr/bLwO7J2vesJ0rQ2C3tMXrTd3w5M=
github.com/gogo/status v1.0.3/go.mod h1:SavQ51ycCLnc7dGyJxp8YAmudx8xqiVrRf+6IXRsugc=
github.com/golang-migrate/migrate/v4 v4.7.0/go.mod h1:Qvut3N4xKWjoH3sokBccML6WyHSnggXm/DvMMnTsQIc=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
@@ -490,8 +498,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -505,8 +513,9 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
@@ -524,8 +533,12 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
@@ -533,6 +546,8 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0 h1:pMen7vLs8nvgEYhywH3KDWJIJTeEr2ULsVWHWYHQyBs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190723021845-34ac40c74b70/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -547,6 +562,8 @@ github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.1.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
@@ -571,6 +588,7 @@ github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORR
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
@@ -580,8 +598,8 @@ github.com/gosimple/slug v1.4.2 h1:jDmprx3q/9Lfk4FkGZtvzDQ9Cj9eAmsjzeQGp24PeiQ=
github.com/gosimple/slug v1.4.2/go.mod h1:ER78kgg1Mv0NQGlXiDe57DpCyfbNywXXZ9mIorhxAf0=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4 h1:SPdxCL9BChFTlyi0Khv64vdCW4TMna8+sxL7+Chx+Ag=
github.com/grafana/grafana-plugin-model v0.0.0-20190930120109-1fc953a61fb4/go.mod h1:nc0XxBzjeGcrMltCDw269LoWF9S8ibhgxolCdA1R8To=
github.com/grafana/grafana-plugin-sdk-go v0.78.0 h1:w43X+b36goTvis4TAW5PzhkGuSJokgm3KKYPOYiENAc=
github.com/grafana/grafana-plugin-sdk-go v0.78.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
github.com/grafana/grafana-plugin-sdk-go v0.79.0 h1:7NVEIMlF8G9H7XUdLX9jH/g01FllE1GEBcFvzXZD+Kw=
github.com/grafana/grafana-plugin-sdk-go v0.79.0/go.mod h1:NvxLzGkVhnoBKwzkst6CFfpMFKwAdIUZ1q8ssuLeF60=
github.com/grafana/loki v1.6.0 h1:vHuFgfhW1iRMCm7/LgnZi02Ifvqj389vWhyfNJlHQTQ=
github.com/grafana/loki v1.6.0/go.mod h1:X+GvtCzAf2ok/xRLLvGB8kuWP1R+75nXnvjCEnenP0s=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
@@ -672,8 +690,8 @@ github.com/influxdata/go-syslog/v3 v3.0.1-0.20200510134747-836dce2cf6da/go.mod h
github.com/influxdata/influxdb v1.7.7/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
github.com/influxdata/influxdb v1.8.0/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ=
github.com/influxdata/influxdb v1.8.1/go.mod h1:SIzcnsjaHRFpmlxpJ4S3NT64qtEKYweNTUMb/vh0OMQ=
github.com/influxdata/influxdb-client-go/v2 v2.0.1 h1:vRla3taM+zkziP1NUGfN6Y6zJ9ZSSMg0fs/JhCGyX1s=
github.com/influxdata/influxdb-client-go/v2 v2.0.1/go.mod h1:eyFPc0lhFnNSpyCDb0ZkrB3Hbtqvn1K1JZmjo2BXqeo=
github.com/influxdata/influxdb-client-go/v2 v2.2.0 h1:2R/le0s/MZpHtc+ijuXKe2c4KGN14M85mWtGlmg6vec=
github.com/influxdata/influxdb-client-go/v2 v2.2.0/go.mod h1:fa/d1lAdUHxuc1jedx30ZfNG573oQTQmUni3N6pcW+0=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/influxdata/influxql v1.1.0/go.mod h1:KpVI7okXjK6PRi3Z5B+mtKZli+R1DnZgb3N+tzevNgo=
github.com/influxdata/line-protocol v0.0.0-20180522152040-32c6aa80de5e/go.mod h1:4kt73NQhadE3daL3WhR5EJ/J2ocX0PZzwxQ0gXJ7oFE=
@@ -695,11 +713,12 @@ github.com/jmespath/go-jmespath v0.3.0 h1:OS12ieG61fsCg5+qLJ+SsW9NicxNkg3b25OyT2
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
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=
@@ -745,6 +764,8 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6
github.com/knq/sysutil v0.0.0-20191005231841-15668db23d08/go.mod h1:dFWs1zEqDjFtnBXsd1vPOZaLsESovai349994nHx3e0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -791,6 +812,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=
@@ -915,6 +938,7 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM
github.com/opentracing-contrib/go-grpc v0.0.0-20180928155321-4b5a12d3ff02/go.mod h1:JNdpVEzCpXBgIiv4ds+TzhN1hrtxq6ClLrTlT9OQRSc=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing-contrib/go-stdlib v0.0.0-20190519235532-cf7a6c988dc9/go.mod h1:PLldrQSroqzH70Xl+1DQcGnefIbqsKR7UDaiux3zV+w=
github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w=
github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
@@ -969,8 +993,9 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.6.0/go.mod h1:ZLOG9ck3JLRdB5MgO8f+lLTe83AXG6ro35rLTxvnIl4=
github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw=
github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM=
github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
@@ -987,8 +1012,9 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.8.0/go.mod h1:PC/OgXc+UN7B4ALwvn1yzVZmVwvhXp5JsbBv6wSv6i0=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4=
github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/node_exporter v1.0.0-rc.0.0.20200428091818-01054558c289/go.mod h1:FGbBv5OPKjch+jNUJmEQpMZytIdyW0NdBtWFcfSKusc=
github.com/prometheus/procfs v0.0.0-20180612222113-7d6f385de8be/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -1001,8 +1027,9 @@ github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa
github.com/prometheus/procfs v0.0.6/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.0.11/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFBS8=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/prometheus v0.0.0-20180315085919-58e2a31db8de/go.mod h1:oAIUtOny2rjMX0OWN5vPR5/q/twIROJvdqnQKDdil/s=
github.com/prometheus/prometheus v0.0.0-20190818123050-43acd0e2e93f/go.mod h1:rMTlmxGCvukf2KMu3fClMDKLLoJ5hl61MhcJ7xKakf0=
github.com/prometheus/prometheus v1.8.2-0.20200107122003-4708915ac6ef/go.mod h1:7U90zPoLkWjEIQcy/rweQla82OCTUzxVHE51G3OhJbI=
@@ -1027,10 +1054,11 @@ github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
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=
@@ -1071,6 +1099,8 @@ github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.5.0/go.mod h1:+F7Ogzej0PZc/94MaYx/nvG9jOFMD2osvC3s+Squfpo=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
@@ -1105,7 +1135,6 @@ github.com/stretchr/testify v1.2.0/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -1127,6 +1156,7 @@ github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329 h1:VBsKFh4W1JEMz3
github.com/ua-parser/uap-go v0.0.0-20190826212731-daf92ba38329/go.mod h1:OBcG9bn7sHtXgarhUEb3OfCnNsgtGnkVf41ilSZ3K3E=
github.com/uber/jaeger-client-go v2.15.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.20.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.22.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.23.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.24.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
@@ -1151,6 +1181,9 @@ github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/weaveworks/common v0.0.0-20200206153930-760e36ae819a/go.mod h1:6enWAqfQBFrE8X/XdJwZr8IKgh1chStuFR0mjU/UOUw=
github.com/weaveworks/common v0.0.0-20200625145055-4b1847531bc9/go.mod h1:c98fKi5B9u8OsKGiWHLRKus6ToQ1Tubeow44ECO1uxY=
github.com/weaveworks/common v0.0.0-20201119133501-0619918236ec h1:5JmevdpzK10Z2ua0VDToj7Kg2+/t0FzdYBjsurYRE8k=
github.com/weaveworks/common v0.0.0-20201119133501-0619918236ec/go.mod h1:ykzWac1LtVfOxdCK+jD754at1Ws9dKCwFeUzkFBffPs=
github.com/weaveworks/promrus v1.2.0 h1:jOLf6pe6/vss4qGHjXmGz4oDJQA+AOCqEL3FvvZGz7M=
github.com/weaveworks/promrus v1.2.0/go.mod h1:SaE82+OJ91yqjrE1rsvBWVzNZKcHYFtMUyS1+Ogs/KA=
github.com/willf/bitset v1.1.3/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs=
@@ -1171,6 +1204,7 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zenazn/goji v0.9.1-0.20160507202103-64eb34159fe5/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
@@ -1198,6 +1232,8 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3 h1:8sGtKOrtQqkN1bp2AtX+misvLIlOmsEsNd+9NIcPEm8=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4 h1:LYy1Hy3MJdrCdMwwzxA/dRok4ejH+RwNGbuoD9fCjto=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
@@ -1242,6 +1278,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=
@@ -1330,8 +1368,10 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4=
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY=
golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1340,6 +1380,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43 h1:ld7aEMNHoBnnDAX15v1T6z31v8HwR2A9FYOuAhWqkwc=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1350,6 +1392,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1417,12 +1461,19 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc=
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/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=
@@ -1436,6 +1487,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjToGsyY4j24pTs2ScHnX7s=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1503,15 +1555,24 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200513201620-d5fe73897c97/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200603131246-cc40288be839/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200710042808-f1c4188a97a1/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200725200936-102e7d357031 h1:VtIxiVHWPhnny2ZTi4f9/2diZKqyLaq3FUTuud5+khA=
golang.org/x/tools v0.0.0-20200725200936-102e7d357031/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201023150057-2f4fa188d925 h1:iGGR3nU1TUd+WTF17QPTTShBEDG66IKsDIDKtC4EseY=
golang.org/x/tools v0.0.0-20201023150057-2f4fa188d925/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU=
@@ -1537,6 +1598,9 @@ google.golang.org/api v0.26.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0 h1:BaiDisFir8O4IJxvAabCGGkQ6yCJegNQqSVoYUNAnbk=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.33.0 h1:+gL0XvACeMIvpwLZ5rQZzLn5cwOsgg8dIcfJ2SYfBVw=
google.golang.org/api v0.33.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1547,6 +1611,8 @@ google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180608181217-32ee49c4dd80/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
@@ -1580,12 +1646,19 @@ google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200603110839-e855014d5736/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200626011028-ee7919e894b5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200710124503-20a17af7bd0e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200724131911-43cab4749ae7 h1:AWgNCmk2V5HZp9AiCDRBExX/b9I0Ey9F8STHDZlhCC4=
google.golang.org/genproto v0.0.0-20200724131911-43cab4749ae7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5 h1:YejJbGvoWsTXHab4OKNrzk27Dr7s4lPLnewbHue1+gM=
google.golang.org/genproto v0.0.0-20201022181438-0ff5f38871d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -1608,6 +1681,10 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1682,6 +1759,7 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
k8s.io/api v0.0.0-20190813020757-36bff7324fb7/go.mod h1:3Iy+myeAORNCLgjd/Xu9ebwN7Vh59Bw0vh9jhoX+V58=
k8s.io/api v0.0.0-20191115095533-47f6de673b26/go.mod h1:iA/8arsvelvo4IDqIhX4IbjTEKBGgvsf2OraTuRtLFU=

View File

@@ -1,6 +1,8 @@
{
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
"version": "7.3.0-beta.2"
"packages": [
"packages/*"
],
"version": "7.3.5"
}

View File

@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
"version": "7.3.0-beta2",
"version": "7.3.6",
"repository": "github:grafana/grafana",
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
@@ -46,7 +46,7 @@
},
"grafana": {
"whatsNewUrl": "https://grafana.com/docs/grafana/latest/guides/whats-new-in-v7-3/",
"releaseNotesUrl": "https://community.grafana.com/t/release-notes-v7-3-x/37993"
"releaseNotesUrl": "https://grafana.com/docs/grafana/latest/release-notes/"
},
"husky": {
"hooks": {

View File

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

View File

@@ -1,4 +1,4 @@
import { getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { getDecimalsForValue, getDisplayProcessor, getRawDisplayProcessor } from './displayProcessor';
import { DisplayProcessor, DisplayValue } from '../types/displayValue';
import { MappingType, ValueMapping } from '../types/valueMapping';
import { FieldConfig, FieldType, ThresholdsMode } from '../types';
@@ -16,9 +16,8 @@ function getDisplayProcessorFromConfig(config: FieldConfig) {
function assertSame(input: any, processors: DisplayProcessor[], match: DisplayValue) {
processors.forEach(processor => {
const value = processor(input);
expect(value.text).toEqual(match.text);
if (match.hasOwnProperty('numeric')) {
expect(value.numeric).toEqual(match.numeric);
for (const key of Object.keys(match)) {
expect((value as any)[key]).toEqual((match as any)[key]);
}
});
}
@@ -89,6 +88,27 @@ describe('Process simple display values', () => {
});
});
describe('Process null values', () => {
const processors = [
getDisplayProcessorFromConfig({
min: 0,
max: 100,
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [
{ value: -Infinity, color: '#000' },
{ value: 0, color: '#100' },
{ value: 100, color: '#200' },
],
},
}),
];
it('Null should get -Infinity (base) color', () => {
assertSame(null, processors, { text: '', numeric: NaN, color: '#000' });
});
});
describe('Format value', () => {
it('should return if value isNaN', () => {
const valueMappings: ValueMapping[] = [];
@@ -329,3 +349,21 @@ describe('getRawDisplayProcessor', () => {
expect(result).toEqual({ text: expected, numeric: null });
});
});
describe('getDecimalsForValue', () => {
it.each`
value | expected
${0} | ${0}
${13.37} | ${0}
${-13.37} | ${0}
${12679.3712345811212} | ${0}
${-12679.3712345811212} | ${0}
${0.3712345} | ${2}
${-0.37123458} | ${2}
${-0.04671994403853774} | ${3}
${0.04671994403853774} | ${3}
`('should return correct suggested decimal count', ({ value, expected }) => {
const result = getDecimalsForValue(value);
expect(result.decimals).toEqual(expected);
});
});

View File

@@ -115,7 +115,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
}
}
return { text, numeric, prefix, suffix, ...scaleFunc(0) };
return { text, numeric, prefix, suffix, ...scaleFunc(-Infinity) };
};
}
@@ -143,7 +143,7 @@ export function getDecimalsForValue(value: number, decimalOverride?: DecimalCoun
return { decimals: decimalOverride, scaledDecimals: null };
}
let dec = -Math.floor(Math.log(value) / Math.LN10) + 1;
let dec = -Math.floor(Math.log(Math.abs(value)) / Math.LN10) + 1;
const magn = Math.pow(10, -dec);
const norm = value / magn; // norm is between 1.0 and 10.0
let size;

View File

@@ -7,18 +7,18 @@ import {
setDynamicConfigValue,
setFieldConfigDefaults,
} from './fieldOverrides';
import { MutableDataFrame, toDataFrame } from '../dataframe';
import { MutableDataFrame, toDataFrame, ArrayDataFrame } from '../dataframe';
import {
DataFrame,
Field,
FieldColorModeId,
FieldConfig,
FieldConfigPropertyItem,
FieldConfigSource,
FieldType,
InterpolateFunction,
ThresholdsMode,
FieldColorModeId,
ScopedVars,
ThresholdsMode,
} from '../types';
import { locationUtil, Registry } from '../utils';
import { mockStandardProperties } from '../utils/tests/mockStandardProperties';
@@ -87,6 +87,54 @@ describe('Global MinMax', () => {
expect(minmax.min).toEqual(-20);
expect(minmax.max).toEqual(1234);
});
it('find global min max when all values are zero', () => {
const f0 = new ArrayDataFrame<{ title: string; value: number; value2: number | null }>([
{ title: 'AAA', value: 0, value2: 0 },
{ title: 'CCC', value: 0, value2: 0 },
]);
const minmax = findNumericFieldMinMax([f0]);
expect(minmax.min).toEqual(0);
expect(minmax.max).toEqual(0);
});
describe('when value is null', () => {
it('then global min max should be null', () => {
const frame = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1] },
{ name: 'Value', type: FieldType.number, values: [null] },
],
});
const { min, max } = findNumericFieldMinMax([frame]);
expect(min).toBe(null);
expect(max).toBe(null);
});
});
describe('when value values are zeo', () => {
it('then global min max should be correct', () => {
const frame = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2] },
{ name: 'Value', type: FieldType.number, values: [1, 2] },
],
});
const frame2 = toDataFrame({
fields: [
{ name: 'Time', type: FieldType.time, values: [1, 2] },
{ name: 'Value', type: FieldType.number, values: [0, 0] },
],
});
const { min, max } = findNumericFieldMinMax([frame, frame2]);
expect(min).toBe(0);
expect(max).toBe(2);
});
});
});
describe('applyFieldOverrides', () => {

View File

@@ -41,13 +41,13 @@ interface OverrideProps {
}
interface GlobalMinMax {
min: number;
max: number;
min?: number | null;
max?: number | null;
}
export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
let min = Number.MAX_VALUE;
let max = Number.MIN_VALUE;
let min: number | null = null;
let max: number | null = null;
const reducers = [ReducerID.min, ReducerID.max];
@@ -55,11 +55,15 @@ export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
for (const field of frame.fields) {
if (field.type === FieldType.number) {
const stats = reduceField({ field, reducers });
if (stats[ReducerID.min] < min) {
min = stats[ReducerID.min];
const statsMin = stats[ReducerID.min];
const statsMax = stats[ReducerID.max];
if (min === null || statsMin < min) {
min = statsMin;
}
if (stats[ReducerID.max] > max) {
max = stats[ReducerID.max];
if (max === null || statsMax > max) {
max = statsMax;
}
}
}

View File

@@ -18,7 +18,12 @@ export function getScaleCalculator(field: Field, theme: GrafanaTheme): ScaleCalc
const info = getMinMaxAndDelta(field);
return (value: number) => {
const percent = (value - info.min!) / info.delta;
let percent = 0;
if (value !== -Infinity) {
percent = (value - info.min!) / info.delta;
}
const threshold = getActiveThresholdForValue(field, value, percent);
return {

View File

@@ -12,6 +12,7 @@ export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
enable: boolean;
name: string;
iconColor: string;
hide?: boolean;
// Standard datasource query
target?: TQuery;

View File

@@ -55,6 +55,8 @@ export interface LicenseInfo {
expiry: number;
licenseUrl: string;
stateInfo: string;
hasValidLicense: boolean;
edition: string;
}
/**

View File

@@ -114,6 +114,7 @@ export interface DataSourcePluginMeta<T extends KeyValue = {}> extends PluginMet
queryOptions?: PluginMetaQueryOptions;
sort?: number;
streaming?: boolean;
unlicensed?: boolean;
}
interface PluginMetaQueryOptions {
@@ -599,6 +600,6 @@ export abstract class LanguageProvider {
* Returns startTask that resolves with a task list when main syntax is loaded.
* Task list consists of secondary promises that load more detailed language features.
*/
abstract start: () => Promise<any[]>;
abstract start: () => Promise<Array<Promise<any>>>;
startTask?: Promise<any[]>;
}

View File

@@ -2,12 +2,14 @@ import { ComponentClass } from 'react';
import { KeyValue } from './data';
import { LiveChannelSupport } from './live';
/** Describes plugins life cycle status */
export enum PluginState {
alpha = 'alpha', // Only included it `enable_alpha` is true
alpha = 'alpha', // Only included if `enable_alpha` config option is true
beta = 'beta', // Will show a warning banner
deprecated = 'deprecated', // Will continue to work -- but not show up in the options to add
}
/** Describes {@link https://grafana.com/docs/grafana/latest/plugins | type of plugin} */
export enum PluginType {
panel = 'panel',
datasource = 'datasource',
@@ -15,12 +17,26 @@ export enum PluginType {
renderer = 'renderer',
}
/** Describes status of {@link https://grafana.com/docs/grafana/latest/plugins/plugin-signatures/ | plugin signature} */
export enum PluginSignatureStatus {
internal = 'internal', // core plugin, no signature
valid = 'valid', // signed and accurate MANIFEST
invalid = 'invalid', // invalid signature
modified = 'modified', // valid signature, but content mismatch
unsigned = 'unsigned', // no MANIFEST file
missing = 'missing', // missing signature file
}
/** Describes error code returned from Grafana plugins API call */
export enum PluginErrorCode {
missingSignature = 'signatureMissing',
invalidSignature = 'signatureInvalid',
modifiedSignature = 'signatureModified',
}
/** Describes error returned from Grafana plugins API call */
export interface PluginError {
errorCode: PluginErrorCode;
pluginId: string;
}
export interface PluginMeta<T extends KeyValue = {}> {

View File

@@ -80,6 +80,10 @@ export type TraceData = {
warnings?: string[] | null;
};
export type TraceViewData = TraceData & {
spans: TraceSpanData[];
};
export type Trace = TraceData & {
duration: number;
endTime: number;

View File

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

View File

@@ -135,4 +135,14 @@ export const Pages = {
SoloPanel: {
url: (page: string) => `/d-solo/${page}`,
},
PluginsList: {
page: 'Plugins list page',
list: 'Plugins list',
listItem: 'Plugins list item',
signatureErrorNotice: 'Unsigned plugins notice',
},
PluginPage: {
page: 'Plugin page',
signatureInfo: 'Plugin signature info',
},
};

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/e2e",
"version": "7.3.0-beta.2",
"version": "7.3.5",
"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.3.0-beta.2",
"@grafana/e2e-selectors": "7.3.5",
"@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.3.0-beta.2",
"version": "7.3.5",
"description": "Grafana Runtime Library",
"keywords": [
"grafana",
@@ -22,8 +22,8 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@grafana/data": "7.3.0-beta.2",
"@grafana/ui": "7.3.0-beta.2",
"@grafana/data": "7.3.5",
"@grafana/ui": "7.3.5",
"systemjs": "0.20.19",
"systemjs-plugin-css": "0.1.37"
},

View File

@@ -62,6 +62,7 @@ export class GrafanaBootConfig implements GrafanaConfig {
rendererAvailable = false;
http2Enabled = false;
dateFormats?: SystemDateFormatSettings;
marketplaceUrl?: string;
constructor(options: GrafanaBootConfig) {
this.theme = options.bootData.user.lightTheme ? getTheme(GrafanaThemeType.Light) : getTheme(GrafanaThemeType.Dark);

View File

@@ -43,7 +43,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod +x /usr/local/bin/cc-test-reporter
curl -fL -o /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
curl -fL -o /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
apk add --no-cache git
# Install Mage

View File

@@ -44,7 +44,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod 755 /usr/local/bin/cc-test-reporter
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
chmod +x /usr/local/bin/grabpl
# Install Mage

View File

@@ -27,7 +27,7 @@ get_file "https://codeclimate.com/downloads/test-reporter/test-reporter-latest-l
"b4138199aa755ebfe171b57cc46910b13258ace5fbc4eaa099c42607cd0bff32"
chmod +x /usr/local/bin/cc-test-reporter
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.16/grabpl"
wget -O /usr/local/bin/grabpl "https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.20/grabpl"
chmod +x /usr/local/bin/grabpl
# Install Mage

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/toolkit",
"version": "7.3.0-beta.2",
"version": "7.3.5",
"description": "Grafana Toolkit",
"keywords": [
"grafana",

View File

@@ -97,6 +97,7 @@ const getCommonPlugins = (options: WebpackConfigurationOptions) => {
{ from: hasREADME ? 'README.md' : '../README.md', to: '.', force: true },
{ from: 'plugin.json', to: '.' },
{ from: '../LICENSE', to: '.' },
{ from: '../CHANGELOG.md', to: '.', force: true },
{ from: '**/*.json', to: '.' },
{ from: '**/*.svg', to: '.' },
{ from: '**/*.png', to: '.' },

View File

@@ -103,7 +103,7 @@ module.exports = ({ config, mode }) => {
minimize: isProductionBuild,
minimizer: isProductionBuild
? [
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco/ }),
new TerserPlugin({ cache: false, parallel: false, sourceMap: false, exclude: /monaco|bizcharts/ }),
new OptimizeCSSAssetsPlugin({}),
]
: [],

View File

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

View File

@@ -5,6 +5,7 @@ import { selectors } from '@grafana/e2e-selectors';
import { useTheme } from '../../themes';
import { Icon } from '../Icon/Icon';
import { IconName } from '../../types/icon';
import { getColorsFromSeverity } from '../../utils/colors';
export type AlertVariant = 'success' | 'warning' | 'error' | 'info';
@@ -76,21 +77,11 @@ export const Alert: FC<Props> = ({
};
const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean) => {
const { redBase, redShade, greenBase, greenShade, blue80, blue77, white } = theme.palette;
const backgrounds = {
error: css`
background: linear-gradient(90deg, ${redBase}, ${redShade});
`,
warning: css`
background: linear-gradient(90deg, ${redBase}, ${redShade});
`,
info: css`
background: linear-gradient(100deg, ${blue80}, ${blue77});
`,
success: css`
background: linear-gradient(100deg, ${greenBase}, ${greenShade});
`,
};
const { white } = theme.palette;
const severityColors = getColorsFromSeverity(severity, theme);
const background = css`
background: linear-gradient(90deg, ${severityColors[0]}, ${severityColors[0]});
`;
return {
container: css`
@@ -106,7 +97,7 @@ const getStyles = (theme: GrafanaTheme, severity: AlertVariant, outline: boolean
display: flex;
flex-direction: row;
align-items: center;
${backgrounds[severity]}
${background}
`,
icon: css`
padding: 0 ${theme.spacing.md} 0 0;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { HTMLAttributes } from 'react';
import { Icon } from '../Icon/Icon';
import { useTheme } from '../../themes/ThemeContext';
import { stylesFactory } from '../../themes/stylesFactory';
@@ -6,23 +6,23 @@ import { IconName } from '../../types';
import { Tooltip } from '../Tooltip/Tooltip';
import { getColorForTheme, GrafanaTheme } from '@grafana/data';
import tinycolor from 'tinycolor2';
import { css } from 'emotion';
import { HorizontalGroup } from '..';
import { css, cx } from 'emotion';
import { HorizontalGroup } from '../Layout/Layout';
export type BadgeColor = 'blue' | 'red' | 'green' | 'orange' | 'purple';
export interface BadgeProps {
export interface BadgeProps extends HTMLAttributes<HTMLDivElement> {
text: string;
color: BadgeColor;
icon?: IconName;
tooltip?: string;
}
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip }) => {
export const Badge = React.memo<BadgeProps>(({ icon, color, text, tooltip, className, ...otherProps }) => {
const theme = useTheme();
const styles = getStyles(theme, color);
const badge = (
<div className={styles.wrapper}>
<div className={cx(styles.wrapper, className)} {...otherProps}>
<HorizontalGroup align="center" spacing="xs">
{icon && <Icon name={icon} size="sm" />}
<span>{text}</span>

View File

@@ -63,6 +63,8 @@ export abstract class BigValueLayout {
fontSize: this.valueFontSize,
fontWeight: 500,
lineHeight: LINE_HEIGHT,
position: 'relative',
zIndex: 1,
};
switch (this.props.colorMode) {

View File

@@ -30,10 +30,12 @@ exports[`BigValue Render with basic options should render 1`] = `
<FormattedDisplayValue
style={
Object {
"color": "#202226",
"color": "#f7f8fa",
"fontSize": 230,
"fontWeight": 500,
"lineHeight": 1.2,
"position": "relative",
"zIndex": 1,
}
}
value={

View File

@@ -13,7 +13,7 @@ It is used in a `ConfigEditor` for data source plugins. You can find more exampl
### Example usage
```jsx
export const ConfigEditor = (props: Props) => {
const { options, onOptionsChange, config } = props;
const { options, onOptionsChange } = props;
return (
<>
<DataSourceHttpSettings

View File

@@ -1,11 +1,11 @@
import React from 'react';
import React, { useEffect } from 'react';
import { HttpSettingsProps } from './types';
import { SelectableValue } from '@grafana/data';
import { Button, InlineFormLabel, Input } from '..';
import Select from '../Forms/Legacy/Select/Select';
export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
const { dataSourceConfig } = props;
const { dataSourceConfig, onChange } = props;
const authProviderOptions = [
{ label: 'AWS SDK Default', value: 'default' },
@@ -42,6 +42,12 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
{ value: 'us-west-2', label: 'us-west-2' },
] as SelectableValue[];
// Apply some defaults on initial render
useEffect(() => {
const sigV4AuthType = dataSourceConfig.jsonData.sigV4AuthType || 'default';
onJsonDataChange('sigV4AuthType', sigV4AuthType);
}, []);
const onSecureJsonDataReset = (fieldName: string) => {
const state = {
...dataSourceConfig,
@@ -55,7 +61,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
},
};
props.onChange(state);
onChange(state);
};
const onSecureJsonDataChange = (fieldName: string, fieldValue: string) => {
@@ -67,7 +73,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
},
};
props.onChange(state);
onChange(state);
};
const onJsonDataChange = (fieldName: string, fieldValue: string) => {
@@ -79,7 +85,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
},
};
props.onChange(state);
onChange(state);
};
return (
@@ -100,7 +106,7 @@ export const SigV4AuthSettings: React.FC<HttpSettingsProps> = props => {
authProvider => authProvider.value === dataSourceConfig.jsonData.sigV4AuthType
)}
options={authProviderOptions}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || 'keys'}
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || ''}
onChange={option => {
onJsonDataChange('sigV4AuthType', option.value);
}}

View File

@@ -25,8 +25,6 @@ export interface Props extends Themeable {
className?: string;
}
const FONT_SCALE = 1;
export class Gauge extends PureComponent<Props> {
canvasElement: any;
@@ -95,13 +93,6 @@ export class Gauge extends PureComponent<Props> {
return formatted;
}
getFontScale(length: number): number {
if (length > 12) {
return FONT_SCALE - (length * 5) / 110;
}
return FONT_SCALE - (length * 5) / 101;
}
draw() {
const { field, showThresholdLabels, showThresholdMarkers, width, height, theme, value } = this.props;
@@ -112,7 +103,12 @@ export class Gauge extends PureComponent<Props> {
const gaugeWidth = Math.min(dimension / 5.5, 40) / gaugeWidthReduceRatio;
const thresholdMarkersWidth = gaugeWidth / 5;
const text = formattedValueToString(value);
const fontSize = calculateFontSize(text, dimension - gaugeWidth * 3, dimension, 1, 48);
// This not 100% accurate as I am unsure of flot's calculations here
const valueWidthBase = Math.min(width, dimension * 1.3) * 0.9;
// remove gauge & marker width (on left and right side)
// and 10px is some padding that flot adds to the outer canvas
const valueWidth = valueWidthBase - ((gaugeWidth + (showThresholdMarkers ? thresholdMarkersWidth : 0)) * 2 + 10);
const fontSize = calculateFontSize(text, valueWidth, dimension, 1, 48);
const thresholdLabelFontSize = fontSize / 2.5;
let min = field.min!;

View File

@@ -7,13 +7,22 @@ import { IconButton } from '../IconButton/IconButton';
import { HorizontalGroup } from '../Layout/Layout';
import panelArtDark from './panelArt_dark.svg';
import panelArtLight from './panelArt_light.svg';
import { AlertVariant } from '../Alert/Alert';
import { getColorsFromSeverity } from '../../utils/colors';
export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
children: React.ReactNode;
/** Title of the box */
title?: string | JSX.Element;
/** Url of the read more link */
url?: string;
/** Text of the read more link */
urlTitle?: string;
/** Indicates whether or not box should be rendered with Grafana branding background */
branded?: boolean;
/** Color variant of the box */
severity?: AlertVariant;
/** Call back to be performed when box is dismissed */
onDismiss?: () => void;
}
@@ -24,9 +33,9 @@ export interface InfoBoxProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
*/
export const InfoBox = React.memo(
React.forwardRef<HTMLDivElement, InfoBoxProps>(
({ title, className, children, branded, url, urlTitle, onDismiss, ...otherProps }, ref) => {
({ title, className, children, branded, url, urlTitle, onDismiss, severity = 'info', ...otherProps }, ref) => {
const theme = useTheme();
const styles = getInfoBoxStyles(theme);
const styles = getInfoBoxStyles(theme, severity);
const wrapperClassName = branded ? cx(styles.wrapperBranded, className) : cx(styles.wrapper, className);
return (
@@ -49,18 +58,15 @@ export const InfoBox = React.memo(
)
);
const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme) => ({
const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme, severity: AlertVariant) => ({
wrapper: css`
position: relative;
padding: ${theme.spacing.md};
background-color: ${theme.colors.bg2};
border-top: 3px solid ${theme.palette.blue80};
border-top: 3px solid ${getColorsFromSeverity(severity, theme)[0]};
margin-bottom: ${theme.spacing.md};
flex-grow: 1;
ul {
padding-left: ${theme.spacing.lg};
}
color: ${theme.colors.textSemiWeak};
code {
@include font-family-monospace();
@@ -109,5 +115,6 @@ const getInfoBoxStyles = stylesFactory((theme: GrafanaTheme) => ({
display: inline-block;
margin-top: ${theme.spacing.md};
font-size: ${theme.typography.size.sm};
color: ${theme.colors.textSemiWeak};
`,
}));

View File

@@ -20,7 +20,7 @@ export interface SelectCommonProps<T> {
filterOption?: (option: SelectableValue, searchQuery: string) => boolean;
/** Function for formatting the text that is displayed when creating a new value*/
formatCreateLabel?: (input: string) => string;
getOptionLabel?: (item: SelectableValue<T>) => string;
getOptionLabel?: (item: SelectableValue<T>) => React.ReactNode;
getOptionValue?: (item: SelectableValue<T>) => string;
inputValue?: string;
invalid?: boolean;

View File

@@ -49,7 +49,7 @@ export const DefaultCell: FC<TableCellProps> = props => {
{value}
</a>
)}
{showFilters && cell.value && <FilterActions {...props} />}
{showFilters && cell.value !== undefined && <FilterActions {...props} />}
</div>
);
};

View File

@@ -2,11 +2,13 @@ import React, { FC } from 'react';
import { TableCellProps } from './types';
export const ImageCell: FC<TableCellProps> = props => {
const { cell, tableStyles, cellProps } = props;
const { field, cell, tableStyles, cellProps } = props;
const displayValue = field.display!(cell.value);
return (
<div {...cellProps} className={tableStyles.cellContainer}>
<img src={cell.value} className={tableStyles.imageCell} />
<img src={displayValue.text} className={tableStyles.imageCell} />
</div>
);
};

View File

@@ -5,6 +5,7 @@ import {
getColumns,
getFilteredOptions,
getTextAlign,
rowToFieldValue,
sortOptions,
valuesToOptions,
} from './utils';
@@ -71,28 +72,88 @@ describe('Table utils', () => {
});
describe('filterByValue', () => {
it.each`
rows | id | filterValues | expected
${[]} | ${'0'} | ${[{ value: 'a' }]} | ${[]}
${[{ values: { 0: 'a' } }]} | ${'0'} | ${null} | ${[{ values: { 0: 'a' } }]}
${[{ values: { 0: 'a' } }]} | ${'0'} | ${undefined} | ${[{ values: { 0: 'a' } }]}
${[{ values: { 0: 'a' } }]} | ${'1'} | ${[{ value: 'b' }]} | ${[]}
${[{ values: { 0: 'a' } }]} | ${'0'} | ${[{ value: 'a' }]} | ${[{ values: { 0: 'a' } }]}
${[{ values: { 0: 'a' } }, { values: { 1: 'a' } }]} | ${'0'} | ${[{ value: 'a' }]} | ${[{ values: { 0: 'a' } }]}
${[{ values: { 0: 'a' } }, { values: { 0: 'b' } }, { values: { 0: 'c' } }]} | ${'0'} | ${[{ value: 'a' }, { value: 'b' }]} | ${[{ values: { 0: 'a' } }, { values: { 0: 'b' } }]}
`(
"when called with rows: '$rows.toString()', id: '$id' and filterValues: '$filterValues' then result should be '$expected'",
({ rows, id, filterValues, expected }) => {
expect(filterByValue(rows, id, filterValues)).toEqual(expected);
}
);
describe('happy path', () => {
const field: any = { values: new ArrayVector(['a', 'aa', 'ab', 'b', 'ba', 'bb', 'c']) };
const rows: any = [
{ index: 0, values: { 0: 'a' } },
{ index: 1, values: { 0: 'aa' } },
{ index: 2, values: { 0: 'ab' } },
{ index: 3, values: { 0: 'b' } },
{ index: 4, values: { 0: 'ba' } },
{ index: 5, values: { 0: 'bb' } },
{ index: 6, values: { 0: 'c' } },
];
const filterValues = [{ value: 'a' }, { value: 'b' }, { value: 'c' }];
const result = filterByValue(field)(rows, '0', filterValues);
expect(result).toEqual([
{ index: 0, values: { 0: 'a' } },
{ index: 3, values: { 0: 'b' } },
{ index: 6, values: { 0: 'c' } },
]);
});
describe('fast exit cases', () => {
describe('no rows', () => {
it('should return empty array', () => {
const field: any = { values: new ArrayVector(['a']) };
const rows: any = [];
const filterValues = [{ value: 'a' }];
const result = filterByValue(field)(rows, '', filterValues);
expect(result).toEqual([]);
});
});
describe('no filterValues', () => {
it('should return rows', () => {
const field: any = { values: new ArrayVector(['a']) };
const rows: any = [{}];
const filterValues = undefined;
const result = filterByValue(field)(rows, '', filterValues);
expect(result).toEqual([{}]);
});
});
describe('no field', () => {
it('should return rows', () => {
const field = undefined;
const rows: any = [{}];
const filterValues = [{ value: 'a' }];
const result = filterByValue(field)(rows, '', filterValues);
expect(result).toEqual([{}]);
});
});
describe('missing id in values', () => {
it('should return rows', () => {
const field: any = { values: new ArrayVector(['a', 'b', 'c']) };
const rows: any = [
{ index: 0, values: { 0: 'a' } },
{ index: 1, values: { 0: 'b' } },
{ index: 2, values: { 0: 'c' } },
];
const filterValues = [{ value: 'a' }, { value: 'b' }, { value: 'c' }];
const result = filterByValue(field)(rows, '1', filterValues);
expect(result).toEqual([]);
});
});
});
});
describe('calculateUniqueFieldValues', () => {
describe('when called without field', () => {
it('then it should return an empty object', () => {
const field = undefined;
const rows = [{ id: 0 }];
const rows = [{ index: 0 }];
const result = calculateUniqueFieldValues(rows, field);
@@ -142,15 +203,15 @@ describe('Table utils', () => {
text: `${value}.0`,
})),
};
const rows: any[] = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
const rows: any[] = [{ index: 0 }, { index: 1 }, { index: 2 }, { index: 3 }, { index: 4 }];
const result = calculateUniqueFieldValues(rows, field);
expect(field.display).toHaveBeenCalledTimes(5);
expect(result).toEqual({
'1.0': 1,
'2.0': 2,
'3.0': 3,
'1.0': '1.0',
'2.0': '2.0',
'3.0': '3.0',
});
});
});
@@ -163,7 +224,7 @@ describe('Table utils', () => {
name: 'value',
type: FieldType.number,
};
const rows: any[] = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
const rows: any[] = [{ index: 0 }, { index: 1 }, { index: 2 }, { index: 3 }, { index: 4 }];
const result = calculateUniqueFieldValues(rows, field);
@@ -182,7 +243,7 @@ describe('Table utils', () => {
name: 'value',
type: FieldType.number,
};
const rows: any[] = [{ id: 0 }, { id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }];
const rows: any[] = [{ index: 0 }, { index: 1 }, { index: 2 }, { index: 3 }, { index: 4 }];
const result = calculateUniqueFieldValues(rows, field);
@@ -196,6 +257,59 @@ describe('Table utils', () => {
});
});
describe('rowToFieldValue', () => {
describe('happy paths', () => {
describe('field without field display', () => {
const field: any = { values: new ArrayVector(['a', 'b', 'c']) };
const row = { index: 1 };
const result = rowToFieldValue(row, field);
expect(result).toEqual('b');
});
describe('field with display processor', () => {
const field: Field = {
config: {},
values: new ArrayVector([1, 2, 2, 1, 3, 5, 6]),
name: 'value',
type: FieldType.number,
display: jest.fn((value: any) => ({
numeric: 1,
percent: 0.01,
color: '',
title: `${value}.0`,
text: `${value}.0`,
})),
};
const row = { index: 4 };
const result = rowToFieldValue(row, field);
expect(result).toEqual('3.0');
});
});
describe('quick exist paths', () => {
describe('field is missing', () => {
const field = undefined;
const row = { index: 0 };
const result = rowToFieldValue(row, field);
expect(result).toEqual('');
});
describe('row is missing', () => {
const field: any = { values: new ArrayVector(['a', 'b', 'c']) };
const row = undefined;
const result = rowToFieldValue(row, field);
expect(result).toEqual('');
});
});
});
describe('valuesToOptions', () => {
describe('when called with a record object', () => {
it('then it should return sorted options from that object', () => {

View File

@@ -78,7 +78,7 @@ export function getColumns(data: DataFrame, availableWidth: number, columnMinWid
sortType: selectSortType(field.type),
width: fieldTableOptions.width,
minWidth: 50,
filter: memoizeOne(filterByValue),
filter: memoizeOne(filterByValue(field)),
justifyContent: getTextAlign(field),
});
}
@@ -116,23 +116,28 @@ function getCellComponent(displayMode: TableCellDisplayMode, field: Field) {
return DefaultCell;
}
export function filterByValue(rows: Row[], id: string, filterValues?: SelectableValue[]) {
if (rows.length === 0) {
return rows;
}
if (!filterValues) {
return rows;
}
return rows.filter(row => {
if (!row.values.hasOwnProperty(id)) {
return false;
export function filterByValue(field?: Field) {
return function(rows: Row[], id: string, filterValues?: SelectableValue[]) {
if (rows.length === 0) {
return rows;
}
const value = row.values[id];
return filterValues.find(filter => filter.value === value) !== undefined;
});
if (!filterValues) {
return rows;
}
if (!field) {
return rows;
}
return rows.filter(row => {
if (!row.values.hasOwnProperty(id)) {
return false;
}
const value = rowToFieldValue(row, field);
return filterValues.find(filter => filter.value === value) !== undefined;
});
};
}
export function calculateUniqueFieldValues(rows: any[], field?: Field) {
@@ -143,16 +148,25 @@ export function calculateUniqueFieldValues(rows: any[], field?: Field) {
const set: Record<string, any> = {};
for (let index = 0; index < rows.length; index++) {
const fieldIndex = parseInt(rows[index].id, 10);
const fieldValue = field.values.get(fieldIndex);
const displayValue = field.display ? field.display(fieldValue) : fieldValue;
const value = field.display ? formattedValueToString(displayValue) : displayValue;
set[value || '(Blanks)'] = fieldValue;
const value = rowToFieldValue(rows[index], field);
set[value || '(Blanks)'] = value;
}
return set;
}
export function rowToFieldValue(row: any, field?: Field): string {
if (!field || !row) {
return '';
}
const fieldValue = field.values.get(row.index);
const displayValue = field.display ? field.display(fieldValue) : fieldValue;
const value = field.display ? formattedValueToString(displayValue) : displayValue;
return value;
}
export function valuesToOptions(unique: Record<string, any>): SelectableValue[] {
return Object.keys(unique)
.reduce((all, key) => all.concat({ value: unique[key], label: key }), [] as SelectableValue[])

View File

@@ -191,4 +191,28 @@ describe('ThresholdsEditor', () => {
]);
});
});
describe('on load with invalid steps', () => {
it('should exclude invalid steps and render a proper list', () => {
const { instance } = setup({
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [
{ value: -Infinity, color: '#7EB26D', key: 1 },
{ value: 75, color: '#6ED0E0', key: 2 },
{ color: '#7EB26D', key: 3 } as any,
{ value: 78, color: '#EAB839', key: 4 },
{ value: null, color: '#7EB26D', key: 5 } as any,
{ value: null, color: '#7EB26D', key: 6 } as any,
],
},
});
expect(getCurrentThresholds(instance).steps).toEqual([
{ value: -Infinity, color: '#7EB26D' },
{ value: 75, color: '#6ED0E0' },
{ value: 78, color: '#EAB839' },
]);
});
});
});

View File

@@ -18,6 +18,7 @@ import { RadioButtonGroup } from '../Forms/RadioButtonGroup/RadioButtonGroup';
import { Button } from '../Button';
import { FullWidthButtonContainer } from '../Button/FullWidthButtonContainer';
import { Label } from '../Forms/Label';
import { isNumber } from 'lodash';
const modes: Array<SelectableValue<ThresholdsMode>> = [
{ value: ThresholdsMode.Absolute, label: 'Absolute', description: 'Pick thresholds based on the absolute values' },
@@ -38,6 +39,8 @@ interface State {
}
export class ThresholdsEditor extends PureComponent<Props, State> {
private latestThresholdInputRef: React.RefObject<HTMLInputElement>;
constructor(props: Props) {
super(props);
@@ -45,6 +48,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
steps[0].value = -Infinity;
this.state = { steps };
this.latestThresholdInputRef = React.createRef();
}
onAddThreshold = () => {
@@ -67,7 +71,12 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
const newThresholds = [...steps, add];
sortThresholds(newThresholds);
this.setState({ steps: newThresholds }, this.onChange);
this.setState({ steps: newThresholds }, () => {
if (this.latestThresholdInputRef.current) {
this.latestThresholdInputRef.current.focus();
}
this.onChange();
});
};
onRemoveThreshold = (threshold: ThresholdWithKey) => {
@@ -136,7 +145,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
});
};
renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles) {
renderInput(threshold: ThresholdWithKey, styles: ThresholdStyles, idx: number) {
const isPercent = this.props.thresholds.mode === ThresholdsMode.Percentage;
if (!isFinite(threshold.value)) {
@@ -167,6 +176,7 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
key={isPercent.toString()}
onChange={(event: ChangeEvent<HTMLInputElement>) => this.onChangeThresholdValue(event, threshold)}
value={threshold.value}
ref={idx === 0 ? this.latestThresholdInputRef : null}
onBlur={this.onBlur}
prefix={
<div className={styles.inputPrefix}>
@@ -208,13 +218,11 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
{steps
.slice(0)
.reverse()
.map(threshold => {
return (
<div className={styles.item} key={`${threshold.key}`}>
{this.renderInput(threshold, styles)}
</div>
);
})}
.map((threshold, idx) => (
<div className={styles.item} key={`${threshold.key}`}>
{this.renderInput(threshold, styles, idx)}
</div>
))}
</div>
<div>
@@ -242,13 +250,15 @@ function toThresholdsWithKey(steps?: Threshold[]): ThresholdWithKey[] {
steps = [{ value: -Infinity, color: 'green' }];
}
return steps.map(t => {
return {
color: t.color,
value: t.value === null ? -Infinity : t.value,
key: counter++,
};
});
return steps
.filter((t, i) => isNumber(t.value) || i === 0)
.map(t => {
return {
color: t.color,
value: t.value === null ? -Infinity : t.value,
key: counter++,
};
});
}
export function thresholdsWithoutKey(thresholds: ThresholdsConfig, steps: ThresholdWithKey[]): ThresholdsConfig {
@@ -279,7 +289,6 @@ const getStyles = stylesFactory(
wrapper: css`
display: flex;
flex-direction: column;
// margin-bottom: -${theme.spacing.formSpacingBase * 2}px;
`,
thresholds: css`
display: flex;

View File

@@ -6,6 +6,8 @@ import zip from 'lodash/zip';
import tinycolor from 'tinycolor2';
import lightTheme from '../themes/light';
import darkTheme from '../themes/dark';
import { GrafanaTheme } from '@grafana/data';
import { AlertVariant } from '../components/Alert/Alert';
export const PALETTE_ROWS = 4;
export const PALETTE_COLUMNS = 14;
@@ -97,7 +99,25 @@ function hslToHex(color: any) {
export function getTextColorForBackground(color: string) {
const b = tinycolor(color).getBrightness();
return b > 150 ? lightTheme.colors.textStrong : darkTheme.colors.textStrong;
return b > 180 ? lightTheme.colors.textStrong : darkTheme.colors.textStrong;
}
export let sortedColors = sortColorsByHue(colors);
/**
* Returns colors used for severity color coding. Use for single color retrievel(0 index) or gradient definition
* @internal
**/
export function getColorsFromSeverity(severity: AlertVariant, theme: GrafanaTheme): [string, string] {
switch (severity) {
case 'error':
case 'warning':
return [theme.palette.redBase, theme.palette.redShade];
case 'info':
return [theme.palette.blue80, theme.palette.blue77];
case 'success':
return [theme.palette.greenBase, theme.palette.greenShade];
default:
return [theme.palette.blue80, theme.palette.blue77];
}
}

View File

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

View File

@@ -2,11 +2,8 @@
exports[`<ListView> shallow tests matches a snapshot 1`] = `
<div
onScroll={[Function]}
style={
Object {
"height": "100%",
"overflowY": "auto",
"position": "relative",
}
}

View File

@@ -69,7 +69,7 @@ describe('<ListView>', () => {
itemsWrapperClassName: 'SomeClassName',
viewBuffer: 10,
viewBufferMin: 5,
windowScroller: false,
windowScroller: true,
};
describe('shallow tests', () => {
@@ -152,10 +152,6 @@ describe('<ListView>', () => {
instance = wrapper.instance();
});
it('getViewHeight() returns the viewHeight', () => {
expect(instance.getViewHeight()).toBe(clientHeight);
});
it('getBottomVisibleIndex() returns a number', () => {
const n = instance.getBottomVisibleIndex();
expect(Number.isNaN(n)).toBe(false);
@@ -233,9 +229,9 @@ describe('<ListView>', () => {
},
});
const hasChanged = instance._isViewChanged();
expect(hasChanged).toBe(true);
expect(spyFns.clientHeight).toHaveBeenCalled();
expect(spyFns.scrollTop).toHaveBeenCalled();
expect(hasChanged).toBe(true);
});
});
});

View File

@@ -67,7 +67,7 @@ type TListViewProps = {
itemsWrapperClassName?: string;
/**
* When adding new items to the DOM, this is the number of items to add above
* and below the current view. E.g. if list is 100 items and is srcolled
* and below the current view. E.g. if list is 100 items and is scrolled
* halfway down (so items [46, 55] are in view), then when a new range of
* items is rendered, it will render items `46 - viewBuffer` to
* `55 + viewBuffer`.
@@ -89,15 +89,20 @@ type TListViewProps = {
* - Ref:https://github.com/bvaughn/react-virtualized/blob/497e2a1942529560681d65a9ef9f5e9c9c9a49ba/docs/WindowScroller.md
*/
windowScroller?: boolean;
/**
* You need to pass in scrollElement when windowScroller is set to false.
* This element is responsible for tracking scrolling for lazy loading.
*/
scrollElement?: Element;
};
const DEFAULT_INITIAL_DRAW = 300;
const DEFAULT_INITIAL_DRAW = 100;
/**
* Virtualized list view component, for the most part, only renders the window
* of items that are in-view with some buffer before and after. Listens for
* scroll events and updates which items are rendered. See react-virtualized
* for a suite of components with similar, but generalized, functinality.
* for a suite of components with similar, but generalized, functionality.
* https://github.com/bvaughn/react-virtualized
*
* Note: Presently, ListView cannot be a PureComponent. This is because ListView
@@ -157,9 +162,9 @@ export default class ListView extends React.Component<TListViewProps> {
_windowScrollListenerAdded: boolean;
_htmlElm: HTMLElement;
/**
* HTMLElement holding the scroller.
* Element holding the scroller.
*/
_wrapperElm: HTMLElement | TNil;
_wrapperElm: Element | TNil;
/**
* HTMLElement holding the rendered items.
*/
@@ -202,6 +207,10 @@ export default class ListView extends React.Component<TListViewProps> {
}
window.addEventListener('scroll', this._onScroll);
this._windowScrollListenerAdded = true;
} else {
// The wrapper element should be the one that handles the scrolling. Once we are not using scroll-canvas we can remove this.
this._wrapperElm = this.props.scrollElement;
this._wrapperElm?.addEventListener('scroll', this._onScroll);
}
}
@@ -214,6 +223,8 @@ export default class ListView extends React.Component<TListViewProps> {
componentWillUnmount() {
if (this._windowScrollListenerAdded) {
window.removeEventListener('scroll', this._onScroll);
} else {
this._wrapperElm?.removeEventListener('scroll', this._onScroll);
}
}
@@ -308,8 +319,11 @@ export default class ListView extends React.Component<TListViewProps> {
};
_initWrapper = (elm: HTMLElement | TNil) => {
if (!this.props.windowScroller) {
return;
}
this._wrapperElm = elm;
if (!this.props.windowScroller && elm) {
if (elm) {
this._viewHeight = elm.clientHeight;
}
};
@@ -374,8 +388,8 @@ export default class ListView extends React.Component<TListViewProps> {
};
/**
* Get the height of the element at index `i`; first check the known heigths,
* fallbck to `.props.itemHeightGetter(...)`.
* Get the height of the element at index `i`; first check the known heights,
* fallback to `.props.itemHeightGetter(...)`.
*/
_getHeight = (i: number) => {
const key = this.props.getKeyFromIndex(i);

View File

@@ -82,6 +82,7 @@ type TVirtualizedTraceViewOwnProps = {
createSpanLink?: (
span: TraceSpan
) => { href: string; onClick?: (e: React.MouseEvent) => void; content: React.ReactNode };
scrollElement?: Element;
};
type VirtualizedTraceViewProps = TVirtualizedTraceViewOwnProps & TExtractUiFindFromStateReturn & TTraceTimeline;
@@ -445,6 +446,7 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
render() {
const styles = getStyles();
const { scrollElement } = this.props;
return (
<div>
<ListView
@@ -452,12 +454,13 @@ export class UnthemedVirtualizedTraceView extends React.Component<VirtualizedTra
dataLength={this.rowStates.length}
itemHeightGetter={this.getRowHeight}
itemRenderer={this.renderRow}
viewBuffer={300}
viewBufferMin={100}
viewBuffer={50}
viewBufferMin={50}
itemsWrapperClassName={styles.rowsWrapper}
getKeyFromIndex={this.getKeyFromIndex}
getIndexFromKey={this.getIndexFromKey}
windowScroller
windowScroller={false}
scrollElement={scrollElement}
/>
</div>
);

View File

@@ -102,6 +102,7 @@ type TProps = TExtractUiFindFromStateReturn & {
createSpanLink?: (
span: TraceSpan
) => { href: string; onClick?: (e: React.MouseEvent) => void; content: React.ReactNode };
scrollElement?: Element;
};
type State = {

View File

@@ -17,7 +17,7 @@ import _isEqual from 'lodash/isEqual';
// @ts-ignore
import { getTraceSpanIdsAsTree } from '../selectors/trace';
import { getConfigValue } from '../utils/config/get-config';
import { TraceKeyValuePair, TraceSpan, TraceSpanData, Trace, TraceData } from '@grafana/data';
import { TraceKeyValuePair, TraceSpan, Trace, TraceViewData } from '@grafana/data';
// @ts-ignore
import TreeNode from '../utils/TreeNode';
@@ -71,12 +71,11 @@ export function orderTags(spanTags: TraceKeyValuePair[], topPrefixes?: string[])
* NOTE: Mutates `data` - Transform the HTTP response data into the form the app
* generally requires.
*/
export default function transformTraceData(data: TraceData & { spans: TraceSpanData[] }): Trace | null {
let { traceID } = data;
if (!traceID) {
export default function transformTraceData(data: TraceViewData | undefined): Trace | null {
if (!data?.traceID) {
return null;
}
traceID = traceID.toLowerCase();
const traceID = data.traceID.toLowerCase();
let traceEndTime = 0;
let traceStartTime = Number.MAX_SAFE_INTEGER;

View File

@@ -256,6 +256,7 @@ func (hs *HTTPServer) registerRoutes() {
apiRoute.Get("/plugins/:pluginId/health", Wrap(hs.CheckHealth))
apiRoute.Any("/plugins/:pluginId/resources", hs.CallResource)
apiRoute.Any("/plugins/:pluginId/resources/*", hs.CallResource)
apiRoute.Any("/plugins/errors", Wrap(hs.GetPluginErrorsList))
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
pluginRoute.Get("/:pluginId/dashboards/", Wrap(GetPluginDashboards))

View File

@@ -173,6 +173,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"sort": getPanelSort(panel.Id),
"skipDataQuery": panel.SkipDataQuery,
"state": panel.State,
"signature": panel.Signature,
}
}
@@ -229,14 +230,17 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *models.ReqContext) (map[string]i
"isEnterprise": hs.License.HasValidLicense(),
},
"licenseInfo": map[string]interface{}{
"hasLicense": hs.License.HasLicense(),
"expiry": hs.License.Expiry(),
"stateInfo": hs.License.StateInfo(),
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
"hasLicense": hs.License.HasLicense(),
"hasValidLicense": hs.License.HasValidLicense(),
"expiry": hs.License.Expiry(),
"stateInfo": hs.License.StateInfo(),
"licenseUrl": hs.License.LicenseURL(c.SignedInUser),
"edition": hs.License.Edition(),
},
"featureToggles": hs.Cfg.FeatureToggles,
"rendererAvailable": hs.RenderService.IsAvailable(),
"http2Enabled": hs.Cfg.Protocol == setting.HTTP2Scheme,
"marketplaceUrl": hs.Cfg.MarketplaceURL,
}
return jsonObj, nil

View File

@@ -376,7 +376,7 @@ func (hs *HTTPServer) metricsEndpoint(ctx *macaron.Context) {
}
promhttp.
HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}).
HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{EnableOpenMetrics: true}).
ServeHTTP(ctx.Resp, ctx.Req.Request)
}

View File

@@ -3,7 +3,6 @@ package api
import (
"encoding/hex"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
@@ -115,7 +114,7 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
}
}
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
if redirectTo := c.GetCookie("redirect_to"); len(redirectTo) > 0 {
if err := hs.ValidateRedirectTo(redirectTo); err != nil {
// the user is already logged so instead of rendering the login page with error
// it should be redirected to the home page.
@@ -161,7 +160,7 @@ func (hs *HTTPServer) LoginAPIPing(c *models.ReqContext) Response {
}
func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Response {
action := "login"
authModule := ""
var user *models.User
var response *NormalResponse
@@ -170,13 +169,13 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Res
if err == nil && response.errMessage != "" {
err = errors.New(response.errMessage)
}
hs.SendLoginLog(&models.SendLoginLogCommand{
ReqContext: c,
LogAction: action,
User: user,
HTTPStatus: response.status,
Error: err,
})
hs.HooksService.RunLoginHook(&models.LoginInfo{
AuthModule: authModule,
User: user,
LoginUsername: cmd.User,
HTTPStatus: response.status,
Error: err,
}, c)
}()
if setting.DisableLoginForm {
@@ -192,9 +191,7 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Res
}
err := bus.Dispatch(authQuery)
if authQuery.AuthModule != "" {
action += fmt.Sprintf("-%s", authQuery.AuthModule)
}
authModule = authQuery.AuthModule
if err != nil {
response = Error(401, "Invalid username or password", err)
if err == login.ErrInvalidCredentials || err == login.ErrTooManyLoginAttempts || err == models.ErrUserNotFound {
@@ -224,7 +221,7 @@ func (hs *HTTPServer) LoginPost(c *models.ReqContext, cmd dtos.LoginCommand) Res
"message": "Logged in",
}
if redirectTo, _ := url.QueryUnescape(c.GetCookie("redirect_to")); len(redirectTo) > 0 {
if redirectTo := c.GetCookie("redirect_to"); len(redirectTo) > 0 {
if err := hs.ValidateRedirectTo(redirectTo); err == nil {
result["redirectUrl"] = redirectTo
} else {
@@ -316,11 +313,3 @@ func (hs *HTTPServer) RedirectResponseWithError(ctx *models.ReqContext, err erro
return Redirect(setting.AppSubUrl + "/login")
}
func (hs *HTTPServer) SendLoginLog(cmd *models.SendLoginLogCommand) {
if err := bus.Dispatch(cmd); err != nil {
if err != bus.ErrHandlerNotFound {
hs.log.Warn("Error while sending login log", "err", err)
}
}
}

View File

@@ -38,8 +38,8 @@ func GenStateString() (string, error) {
}
func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
loginInfo := LoginInformation{
Action: "login-oauth",
loginInfo := models.LoginInfo{
AuthModule: "oauth",
}
if setting.OAuthService == nil {
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
@@ -50,7 +50,7 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
}
name := ctx.Params(":name")
loginInfo.Action += fmt.Sprintf("-%s", name)
loginInfo.AuthModule = name
connect, ok := social.SocialMap[name]
if !ok {
hs.handleOAuthLoginError(ctx, loginInfo, LoginError{
@@ -172,8 +172,8 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
return
}
loginInfo.ExtUserInfo = buildExternalUserInfo(token, userInfo, name)
loginInfo.User, err = syncUser(ctx, loginInfo.ExtUserInfo, connect)
loginInfo.ExternalUser = *buildExternalUserInfo(token, userInfo, name)
loginInfo.User, err = syncUser(ctx, &loginInfo.ExternalUser, connect)
if err != nil {
hs.handleOAuthLoginErrorWithRedirect(ctx, loginInfo, err)
return
@@ -185,13 +185,8 @@ func (hs *HTTPServer) OAuthLogin(ctx *models.ReqContext) {
return
}
hs.SendLoginLog(&models.SendLoginLogCommand{
ReqContext: ctx,
LogAction: loginInfo.Action,
User: loginInfo.User,
ExternalUser: loginInfo.ExtUserInfo,
HTTPStatus: http.StatusOK,
})
loginInfo.HTTPStatus = http.StatusOK
hs.HooksService.RunLoginHook(&loginInfo, ctx)
metrics.MApiLoginOAuth.Inc()
if redirectTo, err := url.QueryUnescape(ctx.GetCookie("redirect_to")); err == nil && len(redirectTo) > 0 {
@@ -280,36 +275,21 @@ type LoginError struct {
Err error
}
type LoginInformation struct {
Action string
User *models.User
ExtUserInfo *models.ExternalUserInfo
}
func (hs *HTTPServer) handleOAuthLoginError(ctx *models.ReqContext, info LoginInformation, err LoginError) {
func (hs *HTTPServer) handleOAuthLoginError(ctx *models.ReqContext, info models.LoginInfo, err LoginError) {
ctx.Handle(err.HttpStatus, err.PublicMessage, err.Err)
logErr := err.Err
if logErr == nil {
logErr = errors.New(err.PublicMessage)
info.Error = err.Err
if info.Error == nil {
info.Error = errors.New(err.PublicMessage)
}
info.HTTPStatus = err.HttpStatus
hs.SendLoginLog(&models.SendLoginLogCommand{
ReqContext: ctx,
LogAction: info.Action,
HTTPStatus: err.HttpStatus,
Error: logErr,
})
hs.HooksService.RunLoginHook(&info, ctx)
}
func (hs *HTTPServer) handleOAuthLoginErrorWithRedirect(ctx *models.ReqContext, info LoginInformation, err error, v ...interface{}) {
func (hs *HTTPServer) handleOAuthLoginErrorWithRedirect(ctx *models.ReqContext, info models.LoginInfo, err error, v ...interface{}) {
hs.redirectWithError(ctx, err, v...)
hs.SendLoginLog(&models.SendLoginLogCommand{
ReqContext: ctx,
LogAction: info.Action,
User: info.User,
ExternalUser: info.ExtUserInfo,
Error: err,
})
info.Error = err
hs.HooksService.RunLoginHook(&info, ctx)
}

View File

@@ -1,7 +1,6 @@
package api
import (
"context"
"encoding/hex"
"errors"
"fmt"
@@ -18,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/hooks"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
@@ -318,6 +318,7 @@ func TestLoginPostRedirect(t *testing.T) {
hs := &HTTPServer{
log: &FakeLogger{},
Cfg: setting.NewCfg(),
HooksService: &hooks.HooksService{},
License: &licensing.OSSLicensingService{},
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
@@ -556,22 +557,23 @@ func setupAuthProxyLoginTest(enableLoginToken bool) *scenarioContext {
return sc
}
type loginLogTestReceiver struct {
cmd *models.SendLoginLogCommand
type loginHookTest struct {
info *models.LoginInfo
}
func (r *loginLogTestReceiver) SaveLoginLog(ctx context.Context, cmd *models.SendLoginLogCommand) error {
r.cmd = cmd
return nil
func (r *loginHookTest) LoginHook(loginInfo *models.LoginInfo, req *models.ReqContext) {
r.info = loginInfo
}
func TestLoginPostSendLoginLog(t *testing.T) {
func TestLoginPostRunLokingHook(t *testing.T) {
sc := setupScenarioContext("/login")
hookService := &hooks.HooksService{}
hs := &HTTPServer{
log: log.New("test"),
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
AuthTokenService: auth.NewFakeUserAuthTokenService(),
HooksService: hookService,
}
sc.defaultHandler = Wrap(func(w http.ResponseWriter, c *models.ReqContext) Response {
@@ -582,28 +584,26 @@ func TestLoginPostSendLoginLog(t *testing.T) {
return hs.LoginPost(c, cmd)
})
testReceiver := loginLogTestReceiver{}
bus.AddHandlerCtx("login-log-receiver", testReceiver.SaveLoginLog)
type sendLoginLogCase struct {
desc string
authUser *models.User
authModule string
authErr error
cmd models.SendLoginLogCommand
}
testHook := loginHookTest{}
hookService.AddLoginHook(testHook.LoginHook)
testUser := &models.User{
Id: 42,
Email: "",
}
testCases := []sendLoginLogCase{
testCases := []struct {
desc string
authUser *models.User
authModule string
authErr error
info models.LoginInfo
}{
{
desc: "invalid credentials",
authErr: login.ErrInvalidCredentials,
cmd: models.SendLoginLogCommand{
LogAction: "login",
info: models.LoginInfo{
AuthModule: "",
HTTPStatus: 401,
Error: login.ErrInvalidCredentials,
},
@@ -611,8 +611,8 @@ func TestLoginPostSendLoginLog(t *testing.T) {
{
desc: "user disabled",
authErr: login.ErrUserDisabled,
cmd: models.SendLoginLogCommand{
LogAction: "login",
info: models.LoginInfo{
AuthModule: "",
HTTPStatus: 401,
Error: login.ErrUserDisabled,
},
@@ -621,8 +621,8 @@ func TestLoginPostSendLoginLog(t *testing.T) {
desc: "valid Grafana user",
authUser: testUser,
authModule: "grafana",
cmd: models.SendLoginLogCommand{
LogAction: "login-grafana",
info: models.LoginInfo{
AuthModule: "grafana",
User: testUser,
HTTPStatus: 200,
},
@@ -631,8 +631,8 @@ func TestLoginPostSendLoginLog(t *testing.T) {
desc: "valid LDAP user",
authUser: testUser,
authModule: "ldap",
cmd: models.SendLoginLogCommand{
LogAction: "login-ldap",
info: models.LoginInfo{
AuthModule: "ldap",
User: testUser,
HTTPStatus: 200,
},
@@ -650,14 +650,15 @@ func TestLoginPostSendLoginLog(t *testing.T) {
sc.m.Post(sc.url, sc.defaultHandler)
sc.fakeReqNoAssertions("POST", sc.url).exec()
cmd := testReceiver.cmd
assert.Equal(t, c.cmd.LogAction, cmd.LogAction)
assert.Equal(t, c.cmd.HTTPStatus, cmd.HTTPStatus)
assert.Equal(t, c.cmd.Error, cmd.Error)
info := testHook.info
assert.Equal(t, c.info.AuthModule, info.AuthModule)
assert.Equal(t, "admin", info.LoginUsername)
assert.Equal(t, c.info.HTTPStatus, info.HTTPStatus)
assert.Equal(t, c.info.Error, info.Error)
if c.cmd.User != nil {
require.NotEmpty(t, cmd.User)
assert.Equal(t, c.cmd.User.Id, cmd.User.Id)
if c.info.User != nil {
require.NotEmpty(t, info.User)
assert.Equal(t, c.info.User.Id, info.User.Id)
}
})
}

View File

@@ -102,13 +102,8 @@ func (proxy *DataSourceProxy) HandleRequest() {
return
}
proxyErrorLogger := logger.New("userId", proxy.ctx.UserId, "orgId", proxy.ctx.OrgId, "uname", proxy.ctx.Login, "path", proxy.ctx.Req.URL.Path, "remote_addr", proxy.ctx.RemoteAddr(), "referer", proxy.ctx.Req.Referer())
reverseProxy := &httputil.ReverseProxy{
Director: proxy.getDirector(),
FlushInterval: time.Millisecond * 200,
ErrorLog: log.New(&logWrapper{logger: proxyErrorLogger}, "", 0),
}
proxyErrorLogger := logger.New("userId", proxy.ctx.UserId, "orgId", proxy.ctx.OrgId, "uname", proxy.ctx.Login,
"path", proxy.ctx.Req.URL.Path, "remote_addr", proxy.ctx.RemoteAddr(), "referer", proxy.ctx.Req.Referer())
transport, err := proxy.ds.GetHttpTransport()
if err != nil {
@@ -116,16 +111,43 @@ func (proxy *DataSourceProxy) HandleRequest() {
return
}
reverseProxy.Transport = &handleResponseTransport{
transport: transport,
reverseProxy := &httputil.ReverseProxy{
Director: proxy.director,
FlushInterval: time.Millisecond * 200,
ErrorLog: log.New(&logWrapper{logger: proxyErrorLogger}, "", 0),
Transport: &handleResponseTransport{
transport: transport,
},
ModifyResponse: func(resp *http.Response) error {
if resp.StatusCode == 401 {
// The data source rejected the request as unauthorized, convert to 400 (bad request)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return fmt.Errorf("failed to read data source response body: %w", err)
}
_ = resp.Body.Close()
proxyErrorLogger.Info("Authentication to data source failed", "body", string(body), "statusCode",
resp.StatusCode)
msg := "Authentication to data source failed"
*resp = http.Response{
StatusCode: 400,
Status: "Bad Request",
Body: ioutil.NopCloser(strings.NewReader(msg)),
ContentLength: int64(len(msg)),
}
}
return nil
},
}
proxy.logRequest()
span, ctx := opentracing.StartSpanFromContext(proxy.ctx.Req.Context(), "datasource reverse proxy")
defer span.Finish()
proxy.ctx.Req.Request = proxy.ctx.Req.WithContext(ctx)
defer span.Finish()
span.SetTag("datasource_id", proxy.ds.Id)
span.SetTag("datasource_type", proxy.ds.Type)
span.SetTag("user_id", proxy.ctx.SignedInUser.UserId)
@@ -152,68 +174,64 @@ func (proxy *DataSourceProxy) addTraceFromHeaderValue(span opentracing.Span, hea
}
}
func (proxy *DataSourceProxy) getDirector() func(req *http.Request) {
return func(req *http.Request) {
req.URL.Scheme = proxy.targetUrl.Scheme
req.URL.Host = proxy.targetUrl.Host
req.Host = proxy.targetUrl.Host
func (proxy *DataSourceProxy) director(req *http.Request) {
req.URL.Scheme = proxy.targetUrl.Scheme
req.URL.Host = proxy.targetUrl.Host
req.Host = proxy.targetUrl.Host
reqQueryVals := req.URL.Query()
reqQueryVals := req.URL.Query()
switch proxy.ds.Type {
case models.DS_INFLUXDB_08:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, "db/"+proxy.ds.Database+"/"+proxy.proxyPath)
reqQueryVals.Add("u", proxy.ds.User)
reqQueryVals.Add("p", proxy.ds.DecryptedPassword())
req.URL.RawQuery = reqQueryVals.Encode()
case models.DS_INFLUXDB:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, proxy.proxyPath)
req.URL.RawQuery = reqQueryVals.Encode()
if !proxy.ds.BasicAuth {
req.Header.Del("Authorization")
req.Header.Add("Authorization", util.GetBasicAuthHeader(proxy.ds.User, proxy.ds.DecryptedPassword()))
}
default:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, proxy.proxyPath)
switch proxy.ds.Type {
case models.DS_INFLUXDB_08:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, "db/"+proxy.ds.Database+"/"+proxy.proxyPath)
reqQueryVals.Add("u", proxy.ds.User)
reqQueryVals.Add("p", proxy.ds.DecryptedPassword())
req.URL.RawQuery = reqQueryVals.Encode()
case models.DS_INFLUXDB:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, proxy.proxyPath)
req.URL.RawQuery = reqQueryVals.Encode()
if !proxy.ds.BasicAuth {
req.Header.Set("Authorization", util.GetBasicAuthHeader(proxy.ds.User, proxy.ds.DecryptedPassword()))
}
default:
req.URL.Path = util.JoinURLFragments(proxy.targetUrl.Path, proxy.proxyPath)
}
if proxy.ds.BasicAuth {
req.Header.Del("Authorization")
req.Header.Add("Authorization", util.GetBasicAuthHeader(proxy.ds.BasicAuthUser, proxy.ds.DecryptedBasicAuthPassword()))
if proxy.ds.BasicAuth {
req.Header.Set("Authorization", util.GetBasicAuthHeader(proxy.ds.BasicAuthUser,
proxy.ds.DecryptedBasicAuthPassword()))
}
dsAuth := req.Header.Get("X-DS-Authorization")
if len(dsAuth) > 0 {
req.Header.Del("X-DS-Authorization")
req.Header.Set("Authorization", dsAuth)
}
applyUserHeader(proxy.cfg.SendUserHeader, req, proxy.ctx.SignedInUser)
keepCookieNames := []string{}
if proxy.ds.JsonData != nil {
if keepCookies := proxy.ds.JsonData.Get("keepCookies"); keepCookies != nil {
keepCookieNames = keepCookies.MustStringArray()
}
}
dsAuth := req.Header.Get("X-DS-Authorization")
if len(dsAuth) > 0 {
req.Header.Del("X-DS-Authorization")
req.Header.Del("Authorization")
req.Header.Add("Authorization", dsAuth)
}
proxyutil.ClearCookieHeader(req, keepCookieNames)
proxyutil.PrepareProxyRequest(req)
applyUserHeader(proxy.cfg.SendUserHeader, req, proxy.ctx.SignedInUser)
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
keepCookieNames := []string{}
if proxy.ds.JsonData != nil {
if keepCookies := proxy.ds.JsonData.Get("keepCookies"); keepCookies != nil {
keepCookieNames = keepCookies.MustStringArray()
}
}
// Clear Origin and Referer to avoir CORS issues
req.Header.Del("Origin")
req.Header.Del("Referer")
proxyutil.ClearCookieHeader(req, keepCookieNames)
proxyutil.PrepareProxyRequest(req)
if proxy.route != nil {
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
}
req.Header.Set("User-Agent", fmt.Sprintf("Grafana/%s", setting.BuildVersion))
// Clear Origin and Referer to avoir CORS issues
req.Header.Del("Origin")
req.Header.Del("Referer")
if proxy.route != nil {
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
}
if proxy.ds.JsonData != nil && proxy.ds.JsonData.Get("oauthPassThru").MustBool() {
addOAuthPassThruAuth(proxy.ctx, req)
}
if proxy.ds.JsonData != nil && proxy.ds.JsonData.Get("oauthPassThru").MustBool() {
addOAuthPassThruAuth(proxy.ctx, req)
}
}

View File

@@ -276,7 +276,7 @@ func TestDSRouteRule(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
Convey("Can translate request url and path", func() {
So(req.URL.Host, ShouldEqual, "graphite:8080")
@@ -303,7 +303,7 @@ func TestDSRouteRule(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
Convey("Should add db to url", func() {
So(req.URL.Path, ShouldEqual, "/db/site/")
@@ -330,7 +330,7 @@ func TestDSRouteRule(t *testing.T) {
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
req.Header.Set("Cookie", cookies)
proxy.getDirector()(&req)
proxy.director(&req)
Convey("Should clear all cookies", func() {
So(req.Header.Get("Cookie"), ShouldEqual, "")
@@ -357,7 +357,7 @@ func TestDSRouteRule(t *testing.T) {
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
req.Header.Set("Cookie", cookies)
proxy.getDirector()(&req)
proxy.director(&req)
Convey("Should keep named cookies", func() {
So(req.Header.Get("Cookie"), ShouldEqual, "JSESSION_ID=test")
@@ -379,7 +379,7 @@ func TestDSRouteRule(t *testing.T) {
req.Header.Add("X-Canary", "stillthere")
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
Convey("Should keep user request (including trailing slash)", func() {
So(req.URL.String(), ShouldEqual, "http://host/root/path/to/folder/")
@@ -436,7 +436,7 @@ func TestDSRouteRule(t *testing.T) {
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
Convey("Should have access token in header", func() {
So(req.Header.Get("Authorization"), ShouldEqual, fmt.Sprintf("%s %s", "Bearer", "testtoken"))
@@ -518,7 +518,7 @@ func TestDSRouteRule(t *testing.T) {
plugin := &plugins.DataSourcePlugin{}
ds := &models.DataSource{Url: backend.URL, Type: models.DS_GRAPHITE}
responseRecorder := &CloseNotifierResponseRecorder{
responseRecorder := &closeNotifierResponseRecorder{
ResponseRecorder: httptest.NewRecorder(),
}
defer responseRecorder.Close()
@@ -657,17 +657,17 @@ func TestNewDataSourceProxy_MSSQL(t *testing.T) {
}
}
type CloseNotifierResponseRecorder struct {
type closeNotifierResponseRecorder struct {
*httptest.ResponseRecorder
closeChan chan bool
}
func (r *CloseNotifierResponseRecorder) CloseNotify() <-chan bool {
func (r *closeNotifierResponseRecorder) CloseNotify() <-chan bool {
r.closeChan = make(chan bool)
return r.closeChan
}
func (r *CloseNotifierResponseRecorder) Close() {
func (r *closeNotifierResponseRecorder) Close() {
close(r.closeChan)
}
@@ -685,7 +685,7 @@ func getDatasourceProxiedRequest(ctx *models.ReqContext, cfg *setting.Cfg) *http
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
return req
}
@@ -796,7 +796,7 @@ func runDatasourceAuthTest(test *Test) {
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
So(err, ShouldBeNil)
proxy.getDirector()(req)
proxy.director(req)
test.checkReq(req)
}

View File

@@ -8,7 +8,6 @@ import (
"time"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
@@ -121,7 +120,7 @@ func (hs *HTTPServer) GetPluginList(c *models.ReqContext) Response {
listItem.DefaultNavUrl = setting.AppSubUrl + "/plugins/" + listItem.Id + "/"
}
// filter out disabled
// filter out disabled plugins
if enabledFilter == "1" && !listItem.Enabled {
continue
}
@@ -364,6 +363,10 @@ func (hs *HTTPServer) getCachedPluginSettings(pluginID string, user *models.Sign
return query.Result, nil
}
func (hs *HTTPServer) GetPluginErrorsList(c *models.ReqContext) Response {
return JSON(200, plugins.ScanningErrors())
}
func translatePluginRequestErrorToAPIError(err error) Response {
if errors.Is(err, backendplugin.ErrPluginNotRegistered) {
return Error(404, "Plugin not found", err)

View File

@@ -118,22 +118,24 @@ func (rr *routeRegister) Register(router Router) {
func (rr *routeRegister) route(pattern, method string, handlers ...macaron.Handler) {
h := make([]macaron.Handler, 0)
fullPattern := rr.prefix + pattern
for _, fn := range rr.namedMiddleware {
h = append(h, fn(pattern))
h = append(h, fn(fullPattern))
}
h = append(h, rr.subfixHandlers...)
h = append(h, handlers...)
for _, r := range rr.routes {
if r.pattern == rr.prefix+pattern && r.method == method {
if r.pattern == fullPattern && r.method == method {
panic("cannot add duplicate route")
}
}
rr.routes = append(rr.routes, route{
method: method,
pattern: rr.prefix + pattern,
pattern: fullPattern,
handlers: h,
})
}

View File

@@ -214,8 +214,13 @@ func TestNamedMiddlewareRouteRegister(t *testing.T) {
{method: "GET", pattern: "/user/admin/all", handlers: emptyHandlers(5)},
}
namedMiddlewares := map[string]bool{}
// Setup
rr := NewRouteRegister(emptyHandler)
rr := NewRouteRegister(func(name string) macaron.Handler {
namedMiddlewares[name] = true
return struct{ name string }{name: name}
})
rr.Delete("/admin", emptyHandler("1"))
rr.Get("/down", emptyHandler("1"), emptyHandler("2"))
@@ -247,6 +252,10 @@ func TestNamedMiddlewareRouteRegister(t *testing.T) {
t.Errorf("want %s got %v", testTable[i].pattern, fr.route[i].pattern)
}
if _, exist := namedMiddlewares[testTable[i].pattern]; !exist {
t.Errorf("could not find named route named %s", testTable[i].pattern)
}
if len(testTable[i].handlers) != len(fr.route[i].handlers) {
t.Errorf("want %d handlers got %d handlers \ntestcase: %v\nroute: %v\n",
len(testTable[i].handlers),

View File

@@ -57,6 +57,11 @@ func (hs *HTTPServer) redirectFromShortURL(c *models.ReqContext) {
return
}
// Failure to update LastSeenAt should still allow to redirect
if err := hs.ShortURLService.UpdateLastSeenAt(c.Req.Context(), shortURL); err != nil {
hs.log.Error("Failed to update short URL last seen at", "error", err)
}
hs.log.Debug("Redirecting short URL", "path", shortURL.Path)
c.Redirect(setting.ToAbsUrl(shortURL.Path), 302)
}

View File

@@ -19,6 +19,7 @@ import (
_ "github.com/russellhaering/goxmldsig"
_ "github.com/stretchr/testify/require"
_ "github.com/timberio/go-datemath"
_ "golang.org/x/time/rate"
_ "gopkg.in/square/go-jose.v2"
)

View File

@@ -25,6 +25,7 @@ type TracingService struct {
customTags map[string]string
samplerType string
samplerParam float64
samplingServerURL string
log log.Logger
closer io.Closer
zipkinPropagation bool
@@ -60,6 +61,7 @@ func (ts *TracingService) parseSettings() {
ts.samplerParam = section.Key("sampler_param").MustFloat64(1)
ts.zipkinPropagation = section.Key("zipkin_propagation").MustBool(false)
ts.disableSharedZipkinSpans = section.Key("disable_shared_zipkin_spans").MustBool(false)
ts.samplingServerURL = section.Key("sampling_server_url").MustString("")
}
func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) {
@@ -67,8 +69,9 @@ func (ts *TracingService) initJaegerCfg() (jaegercfg.Configuration, error) {
ServiceName: "grafana",
Disabled: !ts.enabled,
Sampler: &jaegercfg.SamplerConfig{
Type: ts.samplerType,
Param: ts.samplerParam,
Type: ts.samplerType,
Param: ts.samplerParam,
SamplingServerURL: ts.samplingServerURL,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: false,

View File

@@ -2,6 +2,7 @@ package middleware
import (
"net/url"
"regexp"
"strconv"
"strings"
@@ -53,18 +54,19 @@ func notAuthorized(c *models.ReqContext) {
redirectTo = setting.AppSubUrl + c.Req.RequestURI
}
// remove forceLogin query param if it exists
if parsed, err := url.ParseRequestURI(redirectTo); err == nil {
params := parsed.Query()
params.Del("forceLogin")
parsed.RawQuery = params.Encode()
WriteCookie(c.Resp, "redirect_to", url.QueryEscape(parsed.String()), 0, newCookieOptions)
} else {
c.Logger.Debug("Failed parsing request URI; redirect cookie will not be set", "redirectTo", redirectTo, "error", err)
}
// remove any forceLogin=true params
redirectTo = removeForceLoginParams(redirectTo)
WriteCookie(c.Resp, "redirect_to", url.QueryEscape(redirectTo), 0, newCookieOptions)
c.Redirect(setting.AppSubUrl + "/login")
}
var forceLoginParamsRegexp = regexp.MustCompile(`&?forceLogin=true`)
func removeForceLoginParams(str string) string {
return forceLoginParamsRegexp.ReplaceAllString(str, "")
}
func EnsureEditorOrViewerCanEdit(c *models.ReqContext) {
if !c.SignedInUser.HasRole(models.ROLE_EDITOR) && !setting.ViewersCanEdit {
accessForbidden(c)
@@ -94,6 +96,14 @@ func Auth(options *AuthOptions) macaron.Handler {
if err == nil {
forceLogin = forceLoginParam
}
if !forceLogin {
orgIDValue := c.Req.URL.Query().Get("orgId")
orgID, err := strconv.ParseInt(orgIDValue, 10, 64)
if err == nil && orgID > 0 && orgID != c.OrgId {
forceLogin = true
}
}
}
requireLogin := !c.AllowAnonymous || forceLogin
if !c.IsSignedIn && options.ReqSignedIn && requireLogin {

View File

@@ -1,9 +1,13 @@
package middleware
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/require"
. "github.com/smartystreets/goconvey/convey"
)
@@ -32,6 +36,60 @@ func TestMiddlewareAuth(t *testing.T) {
})
})
Convey("Anonymous auth enabled", func() {
origEnabled := setting.AnonymousEnabled
t.Cleanup(func() {
setting.AnonymousEnabled = origEnabled
})
origName := setting.AnonymousOrgName
t.Cleanup(func() {
setting.AnonymousOrgName = origName
})
setting.AnonymousEnabled = true
setting.AnonymousOrgName = "test"
bus.AddHandler("test", func(query *models.GetOrgByNameQuery) error {
query.Result = &models.Org{Id: 1, Name: "test"}
return nil
})
middlewareScenario(t, "ReqSignIn true and request with forceLogin in query string", func(sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure?forceLogin=true").exec()
Convey("Should redirect to login", func() {
So(sc.resp.Code, ShouldEqual, 302)
location, ok := sc.resp.Header()["Location"]
So(ok, ShouldBeTrue)
So(location[0], ShouldEqual, "/login")
})
})
middlewareScenario(t, "ReqSignIn true and request with same org provided in query string", func(sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure?orgId=1").exec()
Convey("Should not redirect to login", func() {
So(sc.resp.Code, ShouldEqual, 200)
})
})
middlewareScenario(t, "ReqSignIn true and request with different org provided in query string", func(sc *scenarioContext) {
sc.m.Get("/secure", reqSignIn, sc.defaultHandler)
sc.fakeReq("GET", "/secure?orgId=2").exec()
Convey("Should redirect to login", func() {
So(sc.resp.Code, ShouldEqual, 302)
location, ok := sc.resp.Header()["Location"]
So(ok, ShouldBeTrue)
So(location[0], ShouldEqual, "/login")
})
})
})
Convey("snapshot public mode or signed in", func() {
middlewareScenario(t, "Snapshot public mode disabled and unauthenticated request should return 401", func(sc *scenarioContext) {
sc.m.Get("/api/snapshot", SnapshotPublicModeOrSignedIn(), sc.defaultHandler)
@@ -48,3 +106,22 @@ func TestMiddlewareAuth(t *testing.T) {
})
})
}
func TestRemoveForceLoginparams(t *testing.T) {
tcs := []struct {
inp string
exp string
}{
{inp: "/?forceLogin=true", exp: "/?"},
{inp: "/d/dash/dash-title?ordId=1&forceLogin=true", exp: "/d/dash/dash-title?ordId=1"},
{inp: "/?kiosk&forceLogin=true", exp: "/?kiosk"},
{inp: "/d/dash/dash-title?ordId=1&kiosk&forceLogin=true", exp: "/d/dash/dash-title?ordId=1&kiosk"},
{inp: "/d/dash/dash-title?ordId=1&forceLogin=true&kiosk", exp: "/d/dash/dash-title?ordId=1&kiosk"},
{inp: "/d/dash/dash-title?forceLogin=true&kiosk", exp: "/d/dash/dash-title?&kiosk"},
}
for i, tc := range tcs {
t.Run(fmt.Sprintf("testcase %d", i), func(t *testing.T) {
require.Equal(t, tc.exp, removeForceLoginParams(tc.inp))
})
}
}

View File

@@ -22,6 +22,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
cw "github.com/weaveworks/common/middleware"
"gopkg.in/macaron.v1"
)
@@ -33,11 +34,11 @@ func Logger() macaron.Handler {
rw := res.(macaron.ResponseWriter)
c.Next()
timeTakenMs := time.Since(start) / time.Millisecond
timeTaken := time.Since(start) / time.Millisecond
if timer, ok := c.Data["perfmon.timer"]; ok {
timerTyped := timer.(prometheus.Summary)
timerTyped.Observe(float64(timeTakenMs))
timerTyped.Observe(float64(timeTaken))
}
status := rw.Status()
@@ -49,10 +50,25 @@ func Logger() macaron.Handler {
if ctx, ok := c.Data["ctx"]; ok {
ctxTyped := ctx.(*models.ReqContext)
if status == 500 {
ctxTyped.Logger.Error("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", int64(timeTakenMs), "size", rw.Size(), "referer", req.Referer())
logParams := []interface{}{
"method", req.Method,
"path", req.URL.Path,
"status", status,
"remote_addr", c.RemoteAddr(),
"time_ms", int64(timeTaken),
"size", rw.Size(),
"referer", req.Referer(),
}
traceID, exist := cw.ExtractTraceID(ctxTyped.Req.Request.Context())
if exist {
logParams = append(logParams, "traceID", traceID)
}
if status >= 500 {
ctxTyped.Logger.Error("Request Completed", logParams...)
} else {
ctxTyped.Logger.Info("Request Completed", "method", req.Method, "path", req.URL.Path, "status", status, "remote_addr", c.RemoteAddr(), "time_ms", int64(timeTakenMs), "size", rw.Size(), "referer", req.Referer())
ctxTyped.Logger.Info("Request Completed", logParams...)
}
}
}

View File

@@ -17,7 +17,7 @@ import (
func OrgRedirect() macaron.Handler {
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
orgIdValue := req.URL.Query().Get("orgId")
orgId, err := strconv.ParseInt(orgIdValue, 10, 32)
orgId, err := strconv.ParseInt(orgIdValue, 10, 64)
if err != nil || orgId == 0 {
return

View File

@@ -9,12 +9,17 @@ import (
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/setting"
"github.com/prometheus/client_golang/prometheus"
cw "github.com/weaveworks/common/middleware"
"gopkg.in/macaron.v1"
)
var (
httpRequestsInFlight prometheus.Gauge
httpRequestDurationHistogram *prometheus.HistogramVec
// DefBuckets are histogram buckets for the response time (in seconds)
// of a network service, including one that is responding very slowly.
defBuckets = []float64{.005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5}
)
func init() {
@@ -30,9 +35,9 @@ func init() {
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},
Buckets: defBuckets,
},
[]string{"handler"},
[]string{"handler", "status_code", "method"},
)
prometheus.MustRegister(httpRequestsInFlight, httpRequestDurationHistogram)
@@ -52,14 +57,26 @@ func RequestMetrics(cfg *setting.Cfg) func(handler string) macaron.Handler {
code := sanitizeCode(status)
method := sanitizeMethod(req.Method)
metrics.MHttpRequestTotal.WithLabelValues(handler, code, method).Inc()
duration := time.Since(now).Nanoseconds() / int64(time.Millisecond)
// enable histogram and disable summaries for http requests.
// enable histogram and disable summaries + counters for http requests.
if cfg.IsHTTPRequestHistogramEnabled() {
httpRequestDurationHistogram.WithLabelValues(handler).Observe(float64(duration))
// avoiding the sanitize functions for in the new instrumentation
// since they dont make much sense. We should remove them later.
histogram := httpRequestDurationHistogram.
WithLabelValues(handler, strconv.Itoa(rw.Status()), req.Method)
if traceID, ok := cw.ExtractSampledTraceID(c.Req.Context()); ok {
// Need to type-convert the Observer to an
// ExemplarObserver. This will always work for a
// HistogramVec.
histogram.(prometheus.ExemplarObserver).ObserveWithExemplar(
time.Since(now).Seconds(), prometheus.Labels{"traceID": traceID},
)
return
}
histogram.Observe(time.Since(now).Seconds())
} else {
duration := time.Since(now).Nanoseconds() / int64(time.Millisecond)
metrics.MHttpRequestTotal.WithLabelValues(handler, code, method).Inc()
metrics.MHttpRequestSummary.WithLabelValues(handler, code, method).Observe(float64(duration))
}

View File

@@ -15,6 +15,7 @@ var (
ErrAlertNotificationStateVersionConflict = errors.New("alert notification state update version conflict")
ErrAlertNotificationStateAlreadyExist = errors.New("alert notification state already exists")
ErrAlertNotificationFailedGenerateUniqueUid = errors.New("Failed to generate unique alert notification uid")
ErrAlertNotificationFailedTranslateUniqueID = errors.New("Failed to translate Notification Id to Uid")
)
type AlertNotificationStateType string

View File

@@ -123,6 +123,7 @@ var knownDatasourcePlugins = map[string]bool{
"grafana-influxdb-flux-datasource": true,
"doitintl-bigquery-datasource": true,
"grafana-azure-data-explorer-datasource": true,
"tempo": true,
}
func IsKnownDataSourcePlugin(dsType string) bool {

View File

@@ -193,13 +193,14 @@ func (ds *DataSource) sigV4Middleware(next http.RoundTripper) http.RoundTripper
return &SigV4Middleware{
Config: &Config{
AccessKey: decrypted["sigV4AccessKey"],
SecretKey: decrypted["sigV4SecretKey"],
Region: ds.JsonData.Get("sigV4Region").MustString(),
AssumeRoleARN: ds.JsonData.Get("sigV4AssumeRoleArn").MustString(),
AuthType: ds.JsonData.Get("sigV4AuthType").MustString(),
ExternalID: ds.JsonData.Get("sigV4ExternalId").MustString(),
Profile: ds.JsonData.Get("sigV4Profile").MustString(),
DatasourceType: ds.Type,
AccessKey: decrypted["sigV4AccessKey"],
SecretKey: decrypted["sigV4SecretKey"],
Region: ds.JsonData.Get("sigV4Region").MustString(),
AssumeRoleARN: ds.JsonData.Get("sigV4AssumeRoleArn").MustString(),
AuthType: ds.JsonData.Get("sigV4AuthType").MustString(),
ExternalID: ds.JsonData.Get("sigV4ExternalId").MustString(),
Profile: ds.JsonData.Get("sigV4Profile").MustString(),
},
Next: next,
}

View File

@@ -2,6 +2,7 @@ package models
import (
"errors"
"time"
)
var (
@@ -17,3 +18,9 @@ type ShortUrl struct {
CreatedAt int64
LastSeenAt int64
}
type DeleteShortUrlCommand struct {
OlderThan time.Time
NumDeleted int64
}

View File

@@ -5,17 +5,15 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/aws/defaults"
"github.com/aws/aws-sdk-go/aws/credentials"
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
"github.com/aws/aws-sdk-go/private/protocol/rest"
)
type AuthType string
@@ -26,6 +24,17 @@ const (
Credentials AuthType = "credentials"
)
// Host header is likely not necessary here
// (see https://github.com/golang/go/blob/cad6d1fef5147d31e94ee83934c8609d3ad150b7/src/net/http/request.go#L92)
// but adding for completeness
var permittedHeaders = map[string]struct{}{
"Host": {},
"Uber-Trace-Id": {},
"User-Agent": {},
"Accept": {},
"Accept-Encoding": {},
}
type SigV4Middleware struct {
Config *Config
Next http.RoundTripper
@@ -36,6 +45,8 @@ type Config struct {
Profile string
DatasourceType string
AccessKey string
SecretKey string
@@ -63,21 +74,45 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) {
return nil, err
}
if req.Body != nil {
// consume entire request body so that the signer can generate a hash from the contents
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
return signer.Sign(req, bytes.NewReader(body), "grafana", m.Config.Region, time.Now().UTC())
body, err := replaceBody(req)
if err != nil {
return nil, err
}
return signer.Sign(req, nil, "grafana", m.Config.Region, time.Now().UTC())
if strings.Contains(req.URL.RawPath, "%2C") {
req.URL.RawPath = rest.EscapePath(req.URL.RawPath, false)
}
stripHeaders(req)
return signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC())
}
func (m *SigV4Middleware) signer() (*v4.Signer, error) {
c, err := m.credentials()
if err != nil {
return nil, err
authType := AuthType(m.Config.AuthType)
var c *credentials.Credentials
switch authType {
case Keys:
c = credentials.NewStaticCredentials(m.Config.AccessKey, m.Config.SecretKey, "")
case Credentials:
c = credentials.NewSharedCredentials("", m.Config.Profile)
case Default:
// passing nil credentials will force AWS to allow a more complete credential chain vs the explicit default
s, err := session.NewSession(&aws.Config{
Region: aws.String(m.Config.Region),
})
if err != nil {
return nil, err
}
if m.Config.AssumeRoleARN != "" {
return v4.NewSigner(stscreds.NewCredentials(s, m.Config.AssumeRoleARN)), nil
}
return v4.NewSigner(s.Config.Credentials), nil
case "":
return nil, fmt.Errorf("invalid SigV4 auth type")
}
if m.Config.AssumeRoleARN != "" {
@@ -94,17 +129,33 @@ func (m *SigV4Middleware) signer() (*v4.Signer, error) {
return v4.NewSigner(c), nil
}
func (m *SigV4Middleware) credentials() (*credentials.Credentials, error) {
authType := AuthType(m.Config.AuthType)
switch authType {
case Default:
return defaults.CredChain(defaults.Config(), defaults.Handlers()), nil
case Keys:
return credentials.NewStaticCredentials(m.Config.AccessKey, m.Config.SecretKey, ""), nil
case Credentials:
return credentials.NewSharedCredentials("", m.Config.Profile), nil
func replaceBody(req *http.Request) ([]byte, error) {
if req.Body == nil {
return []byte{}, nil
}
payload, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
req.Body = ioutil.NopCloser(bytes.NewReader(payload))
return payload, nil
}
func awsServiceNamespace(dsType string) string {
switch dsType {
case DS_ES:
return "es"
case DS_PROMETHEUS:
return "aps"
default:
panic(fmt.Sprintf("Unsupported datasource %s", dsType))
}
}
func stripHeaders(req *http.Request) {
for h := range req.Header {
if _, exists := permittedHeaders[h]; !exists {
req.Header.Del(h)
}
}
return nil, fmt.Errorf("unrecognized authType: %s", authType)
}

View File

@@ -36,6 +36,15 @@ type ExternalUserInfo struct {
IsDisabled bool
}
type LoginInfo struct {
AuthModule string
User *User
ExternalUser ExternalUserInfo
LoginUsername string
HTTPStatus int
Error error
}
// ---------------------
// COMMANDS
@@ -65,15 +74,6 @@ type DeleteAuthInfoCommand struct {
UserAuth *UserAuth
}
type SendLoginLogCommand struct {
ReqContext *ReqContext
LogAction string
User *User
ExternalUser *ExternalUserInfo
HTTPStatus int
Error error
}
// ----------------------
// QUERIES

14
pkg/plugins/error.go Normal file
View File

@@ -0,0 +1,14 @@
package plugins
const (
signatureMissing ErrorCode = "signatureMissing"
signatureModified ErrorCode = "signatureModified"
signatureInvalid ErrorCode = "signatureInvalid"
)
type ErrorCode string
type PluginError struct {
ErrorCode `json:"errorCode"`
PluginID string `json:"pluginId,omitempty"`
}

View File

@@ -96,7 +96,7 @@ func (pb *PluginBase) registerPlugin(base *PluginBase) error {
}
if !strings.HasPrefix(base.PluginDir, setting.StaticRootPath) {
plog.Info("Registering plugin", "name", pb.Name)
plog.Info("Registering plugin", "id", pb.Id)
}
if len(pb.Dependencies.Plugins) == 0 {

View File

@@ -37,16 +37,21 @@ var (
GrafanaLatestVersion string
GrafanaHasUpdate bool
plog log.Logger
pluginScanningErrors map[string]*PluginError
)
type unsignedPluginConditionFunc = func(plugin *PluginBase) bool
type PluginScanner struct {
pluginPath string
errors []error
backendPluginManager backendplugin.Manager
cfg *setting.Cfg
requireSigned bool
log log.Logger
plugins map[string]*PluginBase
pluginPath string
errors []error
backendPluginManager backendplugin.Manager
cfg *setting.Cfg
requireSigned bool
log log.Logger
plugins map[string]*PluginBase
allowUnsignedPluginsCondition unsignedPluginConditionFunc
}
type PluginManager struct {
@@ -54,6 +59,10 @@ type PluginManager struct {
Cfg *setting.Cfg `inject:""`
log log.Logger
scanningErrors []error
// AllowUnsignedPluginsCondition changes the policy for allowing unsigned plugins. Signature validation only runs when plugins are starting
// and running plugins will not be terminated if they violate the new policy.
AllowUnsignedPluginsCondition unsignedPluginConditionFunc
}
func init() {
@@ -76,6 +85,7 @@ func (pm *PluginManager) Init() error {
"renderer": RendererPlugin{},
"transform": TransformPlugin{},
}
pluginScanningErrors = map[string]*PluginError{}
pm.log.Info("Starting plugin search")
@@ -185,12 +195,13 @@ func (pm *PluginManager) scanPluginPaths() error {
// scan a directory for plugins.
func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
scanner := &PluginScanner{
pluginPath: pluginDir,
backendPluginManager: pm.BackendPluginManager,
cfg: pm.Cfg,
requireSigned: requireSigned,
log: pm.log,
plugins: map[string]*PluginBase{},
pluginPath: pluginDir,
backendPluginManager: pm.BackendPluginManager,
cfg: pm.Cfg,
requireSigned: requireSigned,
log: pm.log,
plugins: map[string]*PluginBase{},
allowUnsignedPluginsCondition: pm.AllowUnsignedPluginsCondition,
}
// 1st pass: Scan plugins, also mapping plugins to their respective directories
@@ -229,9 +240,11 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
}
pm.log.Debug("Found plugin", "id", plugin.Id, "signature", plugin.Signature, "hasRoot", plugin.Root != nil)
if !scanner.validateSignature(plugin) {
pm.log.Debug("Not adding plugin since it lacks a valid signature", "id", plugin.Id,
"signature", plugin.Signature)
signingError := scanner.validateSignature(plugin)
if signingError != nil {
pm.log.Debug("Failed to validate plugin signature. Will skip loading", "id", plugin.Id,
"signature", plugin.Signature, "status", signingError.ErrorCode)
pluginScanningErrors[plugin.Id] = signingError
continue
}
@@ -242,8 +255,6 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
return fmt.Errorf("unknown plugin type %q", plugin.Type)
}
loader := reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
jsonFPath := filepath.Join(plugin.PluginDir, "plugin.json")
// External plugins need a module.js file for SystemJS to load
@@ -269,6 +280,8 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
jsonParser := json.NewDecoder(reader)
loader := reflect.New(reflect.TypeOf(pluginGoType)).Interface().(PluginLoader)
// Load the full plugin, and add it to manager
if err := loader.Load(jsonParser, plugin, scanner.backendPluginManager); err != nil {
if errors.Is(err, duplicatePluginError{}) {
@@ -276,10 +289,8 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
scanner.errors = append(scanner.errors, err)
continue
}
return err
}
pm.log.Debug("Successfully added plugin", "id", plugin.Id)
}
@@ -370,16 +381,10 @@ func (scanner *PluginScanner) IsBackendOnlyPlugin(pluginType string) bool {
}
// validateSignature validates a plugin's signature.
func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
// For the time being, we choose to only require back-end plugins to be signed
// NOTE: the state is calculated again when setting metadata on the object
if !plugin.Backend || !s.requireSigned {
return true
}
func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
if plugin.Signature == PluginSignatureValid {
s.log.Debug("Plugin has valid signature", "id", plugin.Id)
return true
return nil
}
if plugin.Root != nil {
@@ -393,7 +398,7 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
plugin.Signature = plugin.Root.Signature
if plugin.Signature == PluginSignatureValid {
s.log.Debug("Plugin has valid signature (inherited from root)", "id", plugin.Id)
return true
return nil
}
}
} else {
@@ -401,37 +406,70 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) bool {
"state", plugin.Signature)
}
// For the time being, we choose to only require back-end plugins to be signed
// NOTE: the state is calculated again when setting metadata on the object
if !plugin.Backend || !s.requireSigned {
return nil
}
switch plugin.Signature {
case PluginSignatureUnsigned:
allowUnsigned := false
for _, plug := range s.cfg.PluginsAllowUnsigned {
if plug == plugin.Id {
allowUnsigned = true
break
}
}
if setting.Env != setting.Dev && !allowUnsigned {
if allowed := s.allowUnsigned(plugin); !allowed {
s.log.Debug("Plugin is unsigned", "id", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q is unsigned", plugin.Id))
return false
return &PluginError{
ErrorCode: signatureMissing,
}
}
s.log.Warn("Running an unsigned backend plugin", "pluginID", plugin.Id, "pluginDir",
plugin.PluginDir)
return true
return nil
case PluginSignatureInvalid:
s.log.Debug("Plugin %q has an invalid signature", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q has an invalid signature", plugin.Id))
return false
return &PluginError{
ErrorCode: signatureInvalid,
}
case PluginSignatureModified:
s.log.Debug("Plugin %q has a modified signature", plugin.Id)
s.errors = append(s.errors, fmt.Errorf("plugin %q's signature has been modified", plugin.Id))
return false
return &PluginError{
ErrorCode: signatureModified,
}
default:
panic(fmt.Sprintf("Plugin %q has unrecognized plugin signature state %q", plugin.Id, plugin.Signature))
}
}
func (s *PluginScanner) allowUnsigned(plugin *PluginBase) bool {
if s.allowUnsignedPluginsCondition != nil {
return s.allowUnsignedPluginsCondition(plugin)
}
if setting.Env == setting.Dev {
return true
}
for _, plug := range s.cfg.PluginsAllowUnsigned {
if plug == plugin.Id {
return true
}
}
return false
}
func ScanningErrors() []PluginError {
scanningErrs := make([]PluginError, 0)
for id, e := range pluginScanningErrors {
scanningErrs = append(scanningErrs, PluginError{
ErrorCode: e.ErrorCode,
PluginID: id,
})
}
return scanningErrs
}
func GetPluginMarkdown(pluginId string, name string) ([]byte, error) {
plug, exists := Plugins[pluginId]
if !exists {

View File

@@ -273,7 +273,7 @@ func (s *Server) buildServiceGraph(services []*registry.Descriptor) error {
objs := []interface{}{
bus.GetBus(),
s.cfg,
routing.NewRouteRegister(middleware.RequestMetrics(s.cfg), middleware.RequestTracing),
routing.NewRouteRegister(middleware.RequestTracing, middleware.RequestMetrics(s.cfg)),
localcache.New(5*time.Minute, 10*time.Minute),
s,
}

View File

@@ -1,10 +1,13 @@
package conditions
import (
"errors"
"fmt"
"strings"
"time"
"github.com/grafana/grafana/pkg/tsdb/prometheus"
gocontext "context"
"github.com/grafana/grafana-plugin-sdk-go/data"
@@ -113,7 +116,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
}
if err := bus.Dispatch(getDsInfo); err != nil {
return nil, fmt.Errorf("Could not find datasource %v", err)
return nil, fmt.Errorf("could not find datasource: %w", err)
}
req := c.getRequestForAlertRule(getDsInfo.Result, timeRange, context.IsDebug)
@@ -158,11 +161,7 @@ func (c *QueryCondition) executeQuery(context *alerting.EvalContext, timeRange *
resp, err := c.HandleRequest(context.Ctx, getDsInfo.Result, req)
if err != nil {
if err == gocontext.DeadlineExceeded {
return nil, fmt.Errorf("Alert execution exceeded the timeout")
}
return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
return nil, toCustomError(err)
}
for _, v := range resp.Results {
@@ -334,7 +333,9 @@ func FrameToSeriesSlice(frame *data.Frame) (tsdb.TimeSeriesSlice, error) {
switch {
case field.Config != nil && field.Config.DisplayName != "":
ts.Name = field.Config.DisplayName
case field.Labels != nil:
case field.Config != nil && field.Config.DisplayNameFromDS != "":
ts.Name = field.Config.DisplayNameFromDS
case len(field.Labels) > 0:
ts.Tags = field.Labels.Copy()
// Tags are appended to the name so they are eventually included in EvalMatch's Metric property
// for display in notifications.
@@ -359,3 +360,18 @@ func FrameToSeriesSlice(frame *data.Frame) (tsdb.TimeSeriesSlice, error) {
return seriesSlice, nil
}
func toCustomError(err error) error {
// is context timeout
if errors.Is(err, gocontext.DeadlineExceeded) {
return fmt.Errorf("alert execution exceeded the timeout")
}
// is Prometheus error
if prometheus.IsAPIError(err) {
return prometheus.ConvertAPIError(err)
}
// generic fallback
return fmt.Errorf("tsdb.HandleRequest() error %v", err)
}

View File

@@ -296,6 +296,53 @@ func TestFrameToSeriesSlice(t *testing.T) {
},
Err: require.NoError,
},
{
name: "empty labels",
frame: data.NewFrame("",
data.NewField("Time", data.Labels{}, []time.Time{}),
data.NewField(`Values`, data.Labels{}, []float64{})),
seriesSlice: tsdb.TimeSeriesSlice{
&tsdb.TimeSeries{
Name: "Values",
Points: tsdb.TimeSeriesPoints{},
},
},
Err: require.NoError,
},
{
name: "display name from data source",
frame: data.NewFrame("",
data.NewField("Time", data.Labels{}, []time.Time{}),
data.NewField(`Values`, data.Labels{}, []*int64{}).SetConfig(&data.FieldConfig{
DisplayNameFromDS: "sloth",
})),
seriesSlice: tsdb.TimeSeriesSlice{
&tsdb.TimeSeries{
Name: "sloth",
Points: tsdb.TimeSeriesPoints{},
},
},
Err: require.NoError,
},
{
name: "prefer display name over data source display name",
frame: data.NewFrame("",
data.NewField("Time", data.Labels{}, []time.Time{}),
data.NewField(`Values`, data.Labels{}, []*int64{}).SetConfig(&data.FieldConfig{
DisplayName: "sloth #1",
DisplayNameFromDS: "sloth #2",
})),
seriesSlice: tsdb.TimeSeriesSlice{
&tsdb.TimeSeries{
Name: "sloth #1",
Points: tsdb.TimeSeriesPoints{},
},
},
Err: require.NoError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View File

@@ -74,7 +74,7 @@ func copyJSON(in json.Marshaler) (*simplejson.Json, error) {
return simplejson.NewJson(rawJSON)
}
func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json, validateAlertFunc func(*models.Alert) bool) ([]*models.Alert, error) {
func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json, validateAlertFunc func(*models.Alert) bool, logTranslationFailures bool) ([]*models.Alert, error) {
alerts := make([]*models.Alert, 0)
for _, panelObj := range jsonWithPanels.Get("panels").MustArray() {
@@ -84,7 +84,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
// check if the panel is collapsed
if collapsed && collapsedJSON.MustBool() {
// extract alerts from sub panels for collapsed panels
alertSlice, err := e.getAlertFromPanels(panel, validateAlertFunc)
alertSlice, err := e.getAlertFromPanels(panel, validateAlertFunc, logTranslationFailures)
if err != nil {
return nil, err
}
@@ -188,7 +188,7 @@ func (e *DashAlertExtractor) getAlertFromPanels(jsonWithPanels *simplejson.Json,
alert.Settings = jsonAlert
// validate
_, err = NewRuleFromDBAlert(alert)
_, err = NewRuleFromDBAlert(alert, logTranslationFailures)
if err != nil {
return nil, err
}
@@ -209,10 +209,10 @@ func validateAlertRule(alert *models.Alert) bool {
// GetAlerts extracts alerts from the dashboard json and does full validation on the alert json data.
func (e *DashAlertExtractor) GetAlerts() ([]*models.Alert, error) {
return e.extractAlerts(validateAlertRule)
return e.extractAlerts(validateAlertRule, true)
}
func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert) bool) ([]*models.Alert, error) {
func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert) bool, logTranslationFailures bool) ([]*models.Alert, error) {
dashboardJSON, err := copyJSON(e.Dash.Data)
if err != nil {
return nil, err
@@ -226,7 +226,7 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert
if len(rows) > 0 {
for _, rowObj := range rows {
row := simplejson.NewFromAny(rowObj)
a, err := e.getAlertFromPanels(row, validateFunc)
a, err := e.getAlertFromPanels(row, validateFunc, logTranslationFailures)
if err != nil {
return nil, err
}
@@ -234,7 +234,7 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert
alerts = append(alerts, a...)
}
} else {
a, err := e.getAlertFromPanels(dashboardJSON, validateFunc)
a, err := e.getAlertFromPanels(dashboardJSON, validateFunc, logTranslationFailures)
if err != nil {
return nil, err
}
@@ -249,6 +249,6 @@ func (e *DashAlertExtractor) extractAlerts(validateFunc func(alert *models.Alert
// ValidateAlerts validates alerts in the dashboard json but does not require a valid dashboard id
// in the first validation pass.
func (e *DashAlertExtractor) ValidateAlerts() error {
_, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 })
_, err := e.extractAlerts(func(alert *models.Alert) bool { return alert.OrgId != 0 && alert.PanelId != 0 }, false)
return err
}

View File

@@ -119,14 +119,14 @@ func (dd *DingDingNotifier) genBody(evalContext *alerting.EvalContext, messageUR
}
for i, match := range evalContext.EvalMatches {
message += fmt.Sprintf("\\n%2d. %s: %s", i+1, match.Metric, match.Value)
message += fmt.Sprintf("\n%2d. %s: %s", i+1, match.Metric, match.Value)
}
var bodyMsg map[string]interface{}
if dd.MsgType == "actionCard" {
// Embed the pic into the markdown directly because actionCard doesn't have a picUrl field
if dd.NeedsImage() && picURL != "" {
message = "![](" + picURL + ")\\n\\n" + message
message = "![](" + picURL + ")\n\n" + message
}
bodyMsg = map[string]interface{}{

View File

@@ -36,7 +36,7 @@ func (arr *defaultRuleReader) fetch() []*Rule {
res := make([]*Rule, 0)
for _, ruleDef := range cmd.Result {
if model, err := NewRuleFromDBAlert(ruleDef); err != nil {
if model, err := NewRuleFromDBAlert(ruleDef, false); err != nil {
arr.log.Error("Could not build alert model for rule", "ruleId", ruleDef.Id, "error", err)
} else {
res = append(res, model)

View File

@@ -113,7 +113,7 @@ func getTimeDurationStringToSeconds(str string) (int64, error) {
// NewRuleFromDBAlert maps a db version of
// alert to an in-memory version.
func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
func NewRuleFromDBAlert(ruleDef *models.Alert, logTranslationFailures bool) (*Rule, error) {
model := &Rule{}
model.ID = ruleDef.Id
model.OrgID = ruleDef.OrgId
@@ -140,7 +140,13 @@ func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
if id, err := jsonModel.Get("id").Int64(); err == nil {
uid, err := translateNotificationIDToUID(id, ruleDef.OrgId)
if err != nil {
logger.Error("Unable to translate notification id to uid", "error", err.Error(), "dashboardId", model.DashboardID, "alertId", model.ID, "panelId", model.PanelID, "notificationId", id)
if !errors.Is(err, models.ErrAlertNotificationFailedTranslateUniqueID) {
logger.Error("Failed to tranlate notification id to uid", "error", err.Error(), "dashboardId", model.DashboardID, "alert", model.Name, "panelId", model.PanelID, "notificationId", id)
}
if logTranslationFailures {
logger.Warn("Unable to translate notification id to uid", "dashboardId", model.DashboardID, "alert", model.Name, "panelId", model.PanelID, "notificationId", id)
}
} else {
model.Notifications = append(model.Notifications, uid)
}
@@ -176,7 +182,6 @@ func NewRuleFromDBAlert(ruleDef *models.Alert) (*Rule, error) {
func translateNotificationIDToUID(id int64, orgID int64) (string, error) {
notificationUID, err := getAlertNotificationUIDByIDAndOrgID(id, orgID)
if err != nil {
logger.Debug("Failed to translate Notification Id to Uid", "orgID", orgID, "Id", id)
return "", err
}

View File

@@ -100,7 +100,7 @@ func TestAlertRuleModel(t *testing.T) {
Settings: alertJSON,
}
alertRule, err := NewRuleFromDBAlert(alert)
alertRule, err := NewRuleFromDBAlert(alert, false)
So(err, ShouldBeNil)
So(len(alertRule.Conditions), ShouldEqual, 1)
@@ -142,7 +142,7 @@ func TestAlertRuleModel(t *testing.T) {
Settings: alertJSON,
}
alertRule, err := NewRuleFromDBAlert(alert)
alertRule, err := NewRuleFromDBAlert(alert, false)
Convey("swallows the error", func() {
So(err, ShouldBeNil)
So(alertRule.Notifications, ShouldNotContain, "999")
@@ -175,7 +175,7 @@ func TestAlertRuleModel(t *testing.T) {
Settings: alertJSON,
}
alertRule, err := NewRuleFromDBAlert(alert)
alertRule, err := NewRuleFromDBAlert(alert, false)
So(err, ShouldBeNil)
So(alertRule.Frequency, ShouldEqual, 60)
})
@@ -213,7 +213,7 @@ func TestAlertRuleModel(t *testing.T) {
Settings: alertJSON,
}
_, err := NewRuleFromDBAlert(alert)
_, err := NewRuleFromDBAlert(alert, false)
So(err, ShouldNotBeNil)
So(err.Error(), ShouldEqual, "alert validation error: Neither id nor uid is specified in 'notifications' block, type assertion to string failed AlertId: 1 PanelId: 1 DashboardId: 1")
})

Some files were not shown because too many files have changed in this diff Show More