Compare commits
101 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e261642f4 | ||
|
|
e1f213f4af | ||
|
|
68aead75de | ||
|
|
6b7518fff0 | ||
|
|
da9757b3d3 | ||
|
|
f25d63954b | ||
|
|
11f305f88a | ||
|
|
3795ad5af0 | ||
|
|
f5d8063da2 | ||
|
|
751f32c752 | ||
|
|
8e4fb74e8f | ||
|
|
13c8669ac7 | ||
|
|
e9553ddd72 | ||
|
|
7922b400ef | ||
|
|
3ed851151f | ||
|
|
76d3a7a1e6 | ||
|
|
2ec763a0d4 | ||
|
|
240417f82f | ||
|
|
d95ac6c85c | ||
|
|
1e6c14a750 | ||
|
|
f511cf6727 | ||
|
|
9b70e04cdf | ||
|
|
95a9fad671 | ||
|
|
e6327b0536 | ||
|
|
df367b0634 | ||
|
|
2069630cb3 | ||
|
|
a578cdf18c | ||
|
|
81e18c85bf | ||
|
|
d7208411cc | ||
|
|
14c494085e | ||
|
|
3a50fc8db0 | ||
|
|
7472e37cf9 | ||
|
|
be6425d461 | ||
|
|
f34fecbca0 | ||
|
|
509174df31 | ||
|
|
6ada37b445 | ||
|
|
6d79790397 | ||
|
|
089c636acf | ||
|
|
fcd66eef29 | ||
|
|
cffb1cd98d | ||
|
|
881a595651 | ||
|
|
004104717c | ||
|
|
b345e28a2d | ||
|
|
fa1db4dc12 | ||
|
|
9a3fbb8782 | ||
|
|
7292e1508e | ||
|
|
2489dc4d3a | ||
|
|
ca8de25f0c | ||
|
|
ff7c462600 | ||
|
|
41bbafe979 | ||
|
|
37e4a19ea8 | ||
|
|
f04131c9c6 | ||
|
|
7e6c34fc20 | ||
|
|
0e5da44bc1 | ||
|
|
fc44872ca2 | ||
|
|
c88fc42fc6 | ||
|
|
d05c3462da | ||
|
|
8db22cef96 | ||
|
|
edfe914cb0 | ||
|
|
4cf6c916c7 | ||
|
|
697e4f7037 | ||
|
|
ca4fdc46fd | ||
|
|
e086a96161 | ||
|
|
945573eb93 | ||
|
|
b8ad4eaab3 | ||
|
|
f034cbef50 | ||
|
|
23be6e3898 | ||
|
|
7798f01cc9 | ||
|
|
38b96278c8 | ||
|
|
f142752ad6 | ||
|
|
1cfe644d51 | ||
|
|
d8ac457ebc | ||
|
|
3d4ff87721 | ||
|
|
0c5524786b | ||
|
|
c0adf1022b | ||
|
|
706cc59d1d | ||
|
|
f9f3b9d953 | ||
|
|
1b14c6b8db | ||
|
|
d8674013cb | ||
|
|
fc86862533 | ||
|
|
3636749a43 | ||
|
|
f30f6d8a35 | ||
|
|
202b9e5ca9 | ||
|
|
c9a6e09235 | ||
|
|
c03fbac5db | ||
|
|
de19ba97cb | ||
|
|
e5be54b0f0 | ||
|
|
70bf01f4f9 | ||
|
|
0ee8427386 | ||
|
|
7995ab999f | ||
|
|
b51303b83d | ||
|
|
c14ee2d245 | ||
|
|
d394be2555 | ||
|
|
f523500df2 | ||
|
|
d55ca82f0b | ||
|
|
7f9990c889 | ||
|
|
6a2d9224f1 | ||
|
|
88ae4c0e64 | ||
|
|
f11bfe95da | ||
|
|
49dd35f8d9 | ||
|
|
557639a458 |
707
.drone.yml
707
.drone.yml
File diff suppressed because it is too large
Load Diff
27
.github/workflows/bump-version.yml
vendored
Normal file
27
.github/workflows/bump-version.yml
vendored
Normal 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}}
|
||||
13
build.go
13
build.go
@@ -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":
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
40
go.mod
40
go.mod
@@ -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.6-0.20201227203850-bca570abb2ce
|
||||
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.4
|
||||
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,21 @@ 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
|
||||
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
|
||||
|
||||
136
go.sum
136
go.sum
@@ -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=
|
||||
@@ -224,8 +228,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
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.6-0.20201227203850-bca570abb2ce h1:pAuTpLhCqC20s2RLhUirfw606jReW+8z2U5EvG+0S7E=
|
||||
github.com/crewjam/saml v0.4.6-0.20201227203850-bca570abb2ce/go.mod h1:/gCaeLf13J8/621RNZ6TaExji/8xCWcn6UmdJ57wURQ=
|
||||
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 +248,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 +311,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 +324,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 +465,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 +476,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 +496,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 +511,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 +531,14 @@ 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-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=
|
||||
github.com/google/go-cmp v0.5.4/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,8 @@ 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 +1096,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 +1132,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 +1153,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 +1178,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 +1201,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 +1229,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=
|
||||
@@ -1240,8 +1273,8 @@ golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200422194213-44a606286825/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
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 +1363,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 +1375,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 +1387,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 +1456,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 +1482,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 +1550,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-20201226215659-b1c90890d22a h1:pdfjQ7VswBeGam3EpuEJ4e8EAb7JgaubV570LO/SIQM=
|
||||
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a/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 +1593,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 +1606,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 +1641,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 +1676,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=
|
||||
@@ -1674,6 +1746,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1682,6 +1755,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=
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"],
|
||||
"version": "7.3.1"
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"version": "7.3.5"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"name": "grafana",
|
||||
"version": "7.3.1",
|
||||
"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": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/data",
|
||||
"version": "7.3.1",
|
||||
"version": "7.3.5",
|
||||
"description": "Grafana Data Library",
|
||||
"keywords": [
|
||||
"typescript"
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
setDynamicConfigValue,
|
||||
setFieldConfigDefaults,
|
||||
} from './fieldOverrides';
|
||||
import { MutableDataFrame, toDataFrame } from '../dataframe';
|
||||
import { MutableDataFrame, toDataFrame, ArrayDataFrame } from '../dataframe';
|
||||
import {
|
||||
DataFrame,
|
||||
Field,
|
||||
@@ -88,6 +88,17 @@ describe('Global MinMax', () => {
|
||||
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({
|
||||
@@ -98,8 +109,30 @@ describe('Global MinMax', () => {
|
||||
});
|
||||
const { min, max } = findNumericFieldMinMax([frame]);
|
||||
|
||||
expect(min).toBeNull();
|
||||
expect(max).toBeNull();
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -58,19 +58,11 @@ export function findNumericFieldMinMax(data: DataFrame[]): GlobalMinMax {
|
||||
const statsMin = stats[ReducerID.min];
|
||||
const statsMax = stats[ReducerID.max];
|
||||
|
||||
if (!statsMin) {
|
||||
if (min === null || statsMin < min) {
|
||||
min = statsMin;
|
||||
}
|
||||
|
||||
if (!statsMax) {
|
||||
max = statsMax;
|
||||
}
|
||||
|
||||
if (statsMin && statsMin < min) {
|
||||
min = statsMin;
|
||||
}
|
||||
|
||||
if (statsMax && statsMax > max) {
|
||||
if (max === null || statsMax > max) {
|
||||
max = statsMax;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -141,7 +141,12 @@ describe('Series to rows', () => {
|
||||
name: 'A',
|
||||
fields: [
|
||||
{ name: 'Time', type: FieldType.time, values: [100, 150, 200], config: { displayName: 'Random time' } },
|
||||
{ name: 'Temp', type: FieldType.number, values: [1, 4, 5], config: { displayName: 'Temp' } },
|
||||
{
|
||||
name: 'Temp',
|
||||
type: FieldType.number,
|
||||
values: [1, 4, 5],
|
||||
config: { displayName: 'Temp', displayNameFromDS: 'dsName' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ const copyFieldStructure = (field: Field, name: string): Field => {
|
||||
name: name,
|
||||
values: new ArrayVector(),
|
||||
config: {
|
||||
...omit(field.config, 'displayName'),
|
||||
...omit(field.config, ['displayName', 'displayNameFromDS']),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
|
||||
enable: boolean;
|
||||
name: string;
|
||||
iconColor: string;
|
||||
hide?: boolean;
|
||||
|
||||
// Standard datasource query
|
||||
target?: TQuery;
|
||||
|
||||
@@ -55,6 +55,8 @@ export interface LicenseInfo {
|
||||
expiry: number;
|
||||
licenseUrl: string;
|
||||
stateInfo: string;
|
||||
hasValidLicense: boolean;
|
||||
edition: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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[]>;
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export enum PluginType {
|
||||
renderer = 'renderer',
|
||||
}
|
||||
|
||||
/** Describes status of {@link https://grafana.com/docs/grafana/latest/plugins/plugin-signature-verification/ | plugin signature} */
|
||||
/** 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
|
||||
|
||||
@@ -80,6 +80,10 @@ export type TraceData = {
|
||||
warnings?: string[] | null;
|
||||
};
|
||||
|
||||
export type TraceViewData = TraceData & {
|
||||
spans: TraceSpanData[];
|
||||
};
|
||||
|
||||
export type Trace = TraceData & {
|
||||
duration: number;
|
||||
endTime: number;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e-selectors",
|
||||
"version": "7.3.1",
|
||||
"version": "7.3.5",
|
||||
"description": "Grafana End-to-End Test Selectors Library",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/e2e",
|
||||
"version": "7.3.1",
|
||||
"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.1",
|
||||
"@grafana/e2e-selectors": "7.3.5",
|
||||
"@grafana/tsconfig": "^1.0.0-rc1",
|
||||
"@mochajs/json-file-reporter": "^1.2.0",
|
||||
"blink-diff": "1.0.13",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/runtime",
|
||||
"version": "7.3.1",
|
||||
"version": "7.3.5",
|
||||
"description": "Grafana Runtime Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -22,8 +22,8 @@
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/data": "7.3.1",
|
||||
"@grafana/ui": "7.3.1",
|
||||
"@grafana/data": "7.3.5",
|
||||
"@grafana/ui": "7.3.5",
|
||||
"systemjs": "0.20.19",
|
||||
"systemjs-plugin-css": "0.1.37"
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/toolkit",
|
||||
"version": "7.3.1",
|
||||
"version": "7.3.5",
|
||||
"description": "Grafana Toolkit",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
|
||||
@@ -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: '.' },
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"author": "Grafana Labs",
|
||||
"license": "Apache-2.0",
|
||||
"name": "@grafana/ui",
|
||||
"version": "7.3.1",
|
||||
"version": "7.3.5",
|
||||
"description": "Grafana Components Library",
|
||||
"keywords": [
|
||||
"grafana",
|
||||
@@ -27,8 +27,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/core": "^10.0.27",
|
||||
"@grafana/data": "7.3.1",
|
||||
"@grafana/e2e-selectors": "7.3.1",
|
||||
"@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",
|
||||
|
||||
@@ -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 || authProviderOptions[0]}
|
||||
defaultValue={dataSourceConfig.jsonData.sigV4AuthType || ''}
|
||||
onChange={option => {
|
||||
onJsonDataChange('sigV4AuthType', option.value);
|
||||
}}
|
||||
|
||||
@@ -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!;
|
||||
|
||||
@@ -49,7 +49,7 @@ export const DefaultCell: FC<TableCellProps> = props => {
|
||||
{value}
|
||||
</a>
|
||||
)}
|
||||
{showFilters && cell.value && <FilterActions {...props} />}
|
||||
{showFilters && cell.value !== undefined && <FilterActions {...props} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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[])
|
||||
|
||||
@@ -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' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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' },
|
||||
@@ -249,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 {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jaegertracing/jaeger-ui-components",
|
||||
"version": "7.3.1",
|
||||
"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.1",
|
||||
"@grafana/ui": "7.3.1",
|
||||
"@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",
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
exports[`<ListView> shallow tests matches a snapshot 1`] = `
|
||||
<div
|
||||
onScroll={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"height": "100%",
|
||||
"overflowY": "auto",
|
||||
"position": "relative",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -102,6 +102,7 @@ type TProps = TExtractUiFindFromStateReturn & {
|
||||
createSpanLink?: (
|
||||
span: TraceSpan
|
||||
) => { href: string; onClick?: (e: React.MouseEvent) => void; content: React.ReactNode };
|
||||
scrollElement?: Element;
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -230,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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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,14 +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,
|
||||
hs.HooksService.RunLoginHook(&models.LoginInfo{
|
||||
AuthModule: authModule,
|
||||
User: user,
|
||||
LoginUsername: cmd.User,
|
||||
HTTPStatus: response.status,
|
||||
Error: err,
|
||||
})
|
||||
}, c)
|
||||
}()
|
||||
|
||||
if setting.DisableLoginForm {
|
||||
@@ -193,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 {
|
||||
@@ -225,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 {
|
||||
@@ -317,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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,15 +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, "admin", cmd.LoginUsername)
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,11 +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"
|
||||
)
|
||||
@@ -104,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))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"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/defaults"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
v4 "github.com/aws/aws-sdk-go/aws/signer/v4"
|
||||
"github.com/aws/aws-sdk-go/private/protocol/rest"
|
||||
@@ -25,6 +24,22 @@ 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": {},
|
||||
"Content-Type": {},
|
||||
"Content-Length": {},
|
||||
"securitytenant": {},
|
||||
"sgtenant": {},
|
||||
"kbn-xsrf": {},
|
||||
}
|
||||
|
||||
type SigV4Middleware struct {
|
||||
Config *Config
|
||||
Next http.RoundTripper
|
||||
@@ -73,26 +88,36 @@ func (m *SigV4Middleware) signRequest(req *http.Request) (http.Header, error) {
|
||||
req.URL.RawPath = rest.EscapePath(req.URL.RawPath, false)
|
||||
}
|
||||
|
||||
// if X-Forwarded-For header is present, omit during signing step as it breaks AWS request verification
|
||||
forwardHeader := req.Header.Get("X-Forwarded-For")
|
||||
if forwardHeader != "" {
|
||||
req.Header.Del("X-Forwarded-For")
|
||||
|
||||
header, err := signer.Sign(req, bytes.NewReader(body), awsServiceNamespace(m.Config.DatasourceType), m.Config.Region, time.Now().UTC())
|
||||
|
||||
// reset pre-existing X-Forwarded-For header value
|
||||
req.Header.Set("X-Forwarded-For", forwardHeader)
|
||||
|
||||
return header, err
|
||||
}
|
||||
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 != "" {
|
||||
@@ -109,21 +134,6 @@ 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
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unrecognized authType: %s", authType)
|
||||
}
|
||||
|
||||
func replaceBody(req *http.Request) ([]byte, error) {
|
||||
if req.Body == nil {
|
||||
return []byte{}, nil
|
||||
@@ -146,3 +156,11 @@ func awsServiceNamespace(dsType string) string {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,16 +74,6 @@ type DeleteAuthInfoCommand struct {
|
||||
UserAuth *UserAuth
|
||||
}
|
||||
|
||||
type SendLoginLogCommand struct {
|
||||
ReqContext *ReqContext
|
||||
LogAction string
|
||||
User *User
|
||||
ExternalUser *ExternalUserInfo
|
||||
LoginUsername string
|
||||
HTTPStatus int
|
||||
Error error
|
||||
}
|
||||
|
||||
// ----------------------
|
||||
// QUERIES
|
||||
|
||||
|
||||
@@ -41,14 +41,17 @@ var (
|
||||
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 {
|
||||
@@ -56,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() {
|
||||
@@ -188,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
|
||||
@@ -406,21 +414,13 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
|
||||
|
||||
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 &PluginError{
|
||||
ErrorCode: signatureMissing,
|
||||
}
|
||||
}
|
||||
|
||||
s.log.Warn("Running an unsigned backend plugin", "pluginID", plugin.Id, "pluginDir",
|
||||
plugin.PluginDir)
|
||||
return nil
|
||||
@@ -441,6 +441,24 @@ func (s *PluginScanner) validateSignature(plugin *PluginBase) *PluginError {
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 = "\\n\\n" + message
|
||||
message = "\n\n" + message
|
||||
}
|
||||
|
||||
bodyMsg = map[string]interface{}{
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/shorturls"
|
||||
|
||||
"github.com/grafana/grafana/pkg/bus"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/serverlock"
|
||||
@@ -20,6 +22,7 @@ type CleanUpService struct {
|
||||
log log.Logger
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
ServerLockService *serverlock.ServerLockService `inject:""`
|
||||
ShortURLService *shorturls.ShortURLService `inject:""`
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -46,6 +49,7 @@ func (srv *CleanUpService) Run(ctx context.Context) error {
|
||||
srv.deleteExpiredDashboardVersions()
|
||||
srv.cleanUpOldAnnotations(ctxWithTimeout)
|
||||
srv.expireOldUserInvites()
|
||||
srv.deleteStaleShortURLs()
|
||||
err := srv.ServerLockService.LockAndExecute(ctx, "delete old login attempts",
|
||||
time.Minute*10, func() {
|
||||
srv.deleteOldLoginAttempts()
|
||||
@@ -151,3 +155,14 @@ func (srv *CleanUpService) expireOldUserInvites() {
|
||||
srv.log.Debug("Expired user invites", "rows affected", cmd.NumExpired)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *CleanUpService) deleteStaleShortURLs() {
|
||||
cmd := models.DeleteShortUrlCommand{
|
||||
OlderThan: time.Now().Add(-time.Hour * 24 * 7),
|
||||
}
|
||||
if err := srv.ShortURLService.DeleteStaleShortURLs(context.Background(), &cmd); err != nil {
|
||||
srv.log.Error("Problem deleting stale short urls", "error", err.Error())
|
||||
} else {
|
||||
srv.log.Debug("Deleted short urls", "rows affected", cmd.NumDeleted)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,11 @@ import (
|
||||
|
||||
type IndexDataHook func(indexData *dtos.IndexViewData, req *models.ReqContext)
|
||||
|
||||
type LoginHook func(loginInfo *models.LoginInfo, req *models.ReqContext)
|
||||
|
||||
type HooksService struct {
|
||||
indexDataHooks []IndexDataHook
|
||||
loginHooks []LoginHook
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -29,3 +32,13 @@ func (srv *HooksService) RunIndexDataHooks(indexData *dtos.IndexViewData, req *m
|
||||
hook(indexData, req)
|
||||
}
|
||||
}
|
||||
|
||||
func (srv *HooksService) AddLoginHook(hook LoginHook) {
|
||||
srv.loginHooks = append(srv.loginHooks, hook)
|
||||
}
|
||||
|
||||
func (srv *HooksService) RunLoginHook(loginInfo *models.LoginInfo, req *models.ReqContext) {
|
||||
for _, hook := range srv.loginHooks {
|
||||
hook(loginInfo, req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
openSource = "Open Source"
|
||||
)
|
||||
|
||||
type OSSLicensingService struct {
|
||||
Cfg *setting.Cfg `inject:""`
|
||||
HooksService *hooks.HooksService `inject:""`
|
||||
@@ -21,7 +25,7 @@ func (*OSSLicensingService) Expiry() int64 {
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) Edition() string {
|
||||
return "Open Source"
|
||||
return openSource
|
||||
}
|
||||
|
||||
func (*OSSLicensingService) StateInfo() string {
|
||||
|
||||
@@ -48,6 +48,10 @@ func (ns *NotificationService) sendWebRequestSync(ctx context.Context, webhook *
|
||||
webhook.HttpMethod = http.MethodPost
|
||||
}
|
||||
|
||||
if webhook.HttpMethod != http.MethodPost && webhook.HttpMethod != http.MethodPut {
|
||||
return fmt.Errorf("webhook only supports HTTP methods PUT or POST")
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(webhook.HttpMethod, webhook.Url, bytes.NewReader([]byte(webhook.Body)))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -47,7 +47,6 @@ func (ap *PluginProvisioner) apply(cfg *pluginsAsConfig) error {
|
||||
}
|
||||
} else {
|
||||
app.PluginVersion = query.Result.PluginVersion
|
||||
app.Pinned = query.Result.Pinned
|
||||
}
|
||||
|
||||
ap.log.Info("Updating app from configuration ", "type", app.PluginID, "enabled", app.Enabled)
|
||||
|
||||
@@ -2,7 +2,7 @@ package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -115,25 +115,25 @@ func (ps *provisioningServiceImpl) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (ps *provisioningServiceImpl) ProvisionDatasources() error {
|
||||
datasourcePath := path.Join(ps.Cfg.ProvisioningPath, "datasources")
|
||||
datasourcePath := filepath.Join(ps.Cfg.ProvisioningPath, "datasources")
|
||||
err := ps.provisionDatasources(datasourcePath)
|
||||
return errutil.Wrap("Datasource provisioning error", err)
|
||||
}
|
||||
|
||||
func (ps *provisioningServiceImpl) ProvisionPlugins() error {
|
||||
appPath := path.Join(ps.Cfg.ProvisioningPath, "plugins")
|
||||
appPath := filepath.Join(ps.Cfg.ProvisioningPath, "plugins")
|
||||
err := ps.provisionPlugins(appPath)
|
||||
return errutil.Wrap("app provisioning error", err)
|
||||
}
|
||||
|
||||
func (ps *provisioningServiceImpl) ProvisionNotifications() error {
|
||||
alertNotificationsPath := path.Join(ps.Cfg.ProvisioningPath, "notifiers")
|
||||
alertNotificationsPath := filepath.Join(ps.Cfg.ProvisioningPath, "notifiers")
|
||||
err := ps.provisionNotifiers(alertNotificationsPath)
|
||||
return errutil.Wrap("Alert notification provisioning error", err)
|
||||
}
|
||||
|
||||
func (ps *provisioningServiceImpl) ProvisionDashboards() error {
|
||||
dashboardPath := path.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||
dashboardPath := filepath.Join(ps.Cfg.ProvisioningPath, "dashboards")
|
||||
dashProvisioner, err := ps.newDashboardProvisioner(dashboardPath)
|
||||
if err != nil {
|
||||
return errutil.Wrap("Failed to create provisioner", err)
|
||||
|
||||
@@ -76,3 +76,16 @@ func (s ShortURLService) CreateShortURL(ctx context.Context, user *models.Signed
|
||||
|
||||
return &shortURL, nil
|
||||
}
|
||||
|
||||
func (s ShortURLService) DeleteStaleShortURLs(ctx context.Context, cmd *models.DeleteShortUrlCommand) error {
|
||||
return s.SQLStore.WithTransactionalDbSession(ctx, func(session *sqlstore.DBSession) error {
|
||||
var rawSql = "DELETE FROM short_url WHERE created_at <= ? AND (last_seen_at IS NULL OR last_seen_at = 0)"
|
||||
|
||||
if result, err := session.Exec(rawSql, cmd.OlderThan.Unix()); err != nil {
|
||||
return err
|
||||
} else if cmd.NumDeleted, err = result.RowsAffected(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,6 +47,31 @@ func TestShortURLService(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expectedTime.Unix(), updatedShortURL.LastSeenAt)
|
||||
})
|
||||
|
||||
t.Run("and stale short urls can be deleted", func(t *testing.T) {
|
||||
staleShortURL, err := service.CreateShortURL(context.Background(), user, refPath)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, staleShortURL)
|
||||
require.NotEmpty(t, staleShortURL.Uid)
|
||||
require.Equal(t, int64(0), staleShortURL.LastSeenAt)
|
||||
|
||||
cmd := models.DeleteShortUrlCommand{OlderThan: time.Unix(staleShortURL.CreatedAt, 0)}
|
||||
err = service.DeleteStaleShortURLs(context.Background(), &cmd)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(1), cmd.NumDeleted)
|
||||
|
||||
t.Run("and previously accessed short urls will still exist", func(t *testing.T) {
|
||||
updatedShortURL, err := service.GetShortURLByUID(context.Background(), user, existingShortURL.Uid)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, updatedShortURL)
|
||||
})
|
||||
|
||||
t.Run("and no action when no stale short urls exist", func(t *testing.T) {
|
||||
cmd := models.DeleteShortUrlCommand{OlderThan: time.Unix(existingShortURL.CreatedAt, 0)}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, int64(0), cmd.NumDeleted)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("User cannot look up nonexistent short URLs", func(t *testing.T) {
|
||||
|
||||
@@ -374,16 +374,16 @@ func deleteDashboard(cmd *models.DeleteDashboardCommand, sess *DBSession) error
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id = ?",
|
||||
"DELETE FROM annotation WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id = ?",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id = ?",
|
||||
}
|
||||
|
||||
if dashboard.IsFolder {
|
||||
deletes = append(deletes, "DELETE FROM dashboard_provisioning WHERE dashboard_id in (select id from dashboard where folder_id = ?)")
|
||||
deletes = append(deletes, "DELETE FROM dashboard WHERE folder_id = ?")
|
||||
|
||||
dashIds := []struct {
|
||||
Id int64
|
||||
}{}
|
||||
err := sess.SQL("select id from dashboard where folder_id = ?", dashboard.Id).Find(&dashIds)
|
||||
err := sess.SQL("SELECT id FROM dashboard WHERE folder_id = ?", dashboard.Id).Find(&dashIds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -393,6 +393,23 @@ func deleteDashboard(cmd *models.DeleteDashboardCommand, sess *DBSession) error
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(dashIds) > 0 {
|
||||
childrenDeletes := []string{
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM star WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_version WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM annotation WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_provisioning WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id IN (SELECT id FROM dashboard WHERE org_id = ? AND folder_id = ?)",
|
||||
}
|
||||
for _, sql := range childrenDeletes {
|
||||
_, err := sess.Exec(sql, dashboard.OrgId, dashboard.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := deleteAlertDefinition(dashboard.Id, sess); err != nil {
|
||||
@@ -401,7 +418,6 @@ func deleteDashboard(cmd *models.DeleteDashboardCommand, sess *DBSession) error
|
||||
|
||||
for _, sql := range deletes {
|
||||
_, err := sess.Exec(sql, dashboard.Id)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -201,6 +201,14 @@ func TestDashboardDataAccess(t *testing.T) {
|
||||
So(query.Result.Updated.IsZero(), ShouldBeFalse)
|
||||
})
|
||||
|
||||
Convey("Should be able to delete empty folder", func() {
|
||||
emptyFolder := insertTestDashboard("2 test dash folder", 1, 0, true, "prod", "webapp")
|
||||
|
||||
deleteCmd := &models.DeleteDashboardCommand{Id: emptyFolder.Id}
|
||||
err := DeleteDashboard(deleteCmd)
|
||||
So(err, ShouldBeNil)
|
||||
})
|
||||
|
||||
Convey("Should be able to delete a dashboard folder and its children", func() {
|
||||
deleteCmd := &models.DeleteDashboardCommand{Id: savedFolder.Id}
|
||||
err := DeleteDashboard(deleteCmd)
|
||||
|
||||
@@ -46,4 +46,7 @@ INSERT INTO dashboard_acl
|
||||
`
|
||||
|
||||
mg.AddMigration("save default acl rules in dashboard_acl table", NewRawSqlMigration(rawSQL))
|
||||
|
||||
mg.AddMigration("delete acl rules for deleted dashboards and folders", NewRawSqlMigration(
|
||||
"DELETE FROM dashboard_acl WHERE dashboard_id NOT IN (SELECT id FROM dashboard) AND dashboard_id != -1"))
|
||||
}
|
||||
|
||||
@@ -219,4 +219,10 @@ func addDashboardMigration(mg *Migrator) {
|
||||
Cols: []string{"title"},
|
||||
Type: IndexType,
|
||||
}))
|
||||
|
||||
mg.AddMigration("delete tags for deleted dashboards", NewRawSqlMigration(
|
||||
"DELETE FROM dashboard_tag WHERE dashboard_id NOT IN (SELECT id FROM dashboard)"))
|
||||
|
||||
mg.AddMigration("delete stars for deleted dashboards", NewRawSqlMigration(
|
||||
"DELETE FROM star WHERE dashboard_id NOT IN (SELECT id FROM dashboard)"))
|
||||
}
|
||||
|
||||
@@ -68,4 +68,7 @@ func addDashboardSnapshotMigrations(mg *Migrator) {
|
||||
mg.AddMigration("Add encrypted dashboard json column", NewAddColumnMigration(snapshotV5, &Column{
|
||||
Name: "dashboard_encrypted", Type: DB_Blob, Nullable: true,
|
||||
}))
|
||||
|
||||
mg.AddMigration("Change dashboard_encrypted column to MEDIUMBLOB", NewRawSqlMigration("").
|
||||
Mysql("ALTER TABLE dashboard_snapshot MODIFY dashboard_encrypted MEDIUMBLOB;"))
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
@@ -68,16 +69,18 @@ func TestTempUserCommandsAndQueries(t *testing.T) {
|
||||
})
|
||||
|
||||
Convey("Should be able expire temp user", func() {
|
||||
cmd2 := models.ExpireTempUsersCommand{OlderThan: timeNow()}
|
||||
createdAt := time.Unix(cmd.Result.Created, 0)
|
||||
cmd2 := models.ExpireTempUsersCommand{OlderThan: createdAt.Add(1 * time.Second)}
|
||||
err := ExpireOldUserInvites(&cmd2)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd2.NumExpired, ShouldEqual, 1)
|
||||
So(cmd2.NumExpired, ShouldEqual, int64(1))
|
||||
|
||||
Convey("Should do nothing when no temp users to expire", func() {
|
||||
cmd2 = models.ExpireTempUsersCommand{OlderThan: timeNow()}
|
||||
createdAt := time.Unix(cmd.Result.Created, 0)
|
||||
cmd2 := models.ExpireTempUsersCommand{OlderThan: createdAt.Add(1 * time.Second)}
|
||||
err := ExpireOldUserInvites(&cmd2)
|
||||
So(err, ShouldBeNil)
|
||||
So(cmd2.NumExpired, ShouldEqual, 0)
|
||||
So(cmd2.NumExpired, ShouldEqual, int64(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -273,6 +273,7 @@ type Cfg struct {
|
||||
PluginsAppsSkipVerifyTLS bool
|
||||
PluginSettings PluginSettings
|
||||
PluginsAllowUnsigned []string
|
||||
MarketplaceURL string
|
||||
DisableSanitizeHtml bool
|
||||
EnterpriseLicensePath string
|
||||
|
||||
@@ -796,6 +797,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
|
||||
plug = strings.TrimSpace(plug)
|
||||
cfg.PluginsAllowUnsigned = append(cfg.PluginsAllowUnsigned, plug)
|
||||
}
|
||||
cfg.MarketplaceURL = pluginsSection.Key("marketplace_url").MustString("https://grafana.com/grafana/plugins/")
|
||||
cfg.Protocol = Protocol
|
||||
|
||||
// Read and populate feature toggles list
|
||||
|
||||
@@ -421,7 +421,7 @@ func toGrafanaUnit(unit string) string {
|
||||
return "cps"
|
||||
case "Percent":
|
||||
return "percent"
|
||||
case "Milliseconds":
|
||||
case "MilliSeconds":
|
||||
return "ms"
|
||||
case "Seconds":
|
||||
return "s"
|
||||
|
||||
@@ -88,12 +88,11 @@ func buildSearchExpression(query *cloudWatchQuery, stat string) string {
|
||||
}
|
||||
|
||||
if query.MatchExact {
|
||||
schema := query.Namespace
|
||||
schema := fmt.Sprintf("%q", query.Namespace)
|
||||
if len(dimensionNames) > 0 {
|
||||
sort.Strings(dimensionNames)
|
||||
schema += fmt.Sprintf(",%s", join(dimensionNames, ",", `"`, `"`))
|
||||
}
|
||||
|
||||
return fmt.Sprintf("REMOVE_EMPTY(SEARCH('{%s} %s', '%s', %s))", schema, searchTerm, stat, strconv.Itoa(query.Period))
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func TestMetricDataQueryBuilder_buildSearchExpression(t *testing.T) {
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{AWS/EC2,"LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
})
|
||||
|
||||
t.Run("Query has three dimension values for two given dimension keys", func(t *testing.T) {
|
||||
@@ -40,7 +40,7 @@ func TestMetricDataQueryBuilder_buildSearchExpression(t *testing.T) {
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{AWS/EC2,"InstanceId","LoadBalancer"} MetricName="CPUUtilization" "InstanceId"=("i-123" OR "i-456" OR "i-789") "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "InstanceId"=("i-123" OR "i-456" OR "i-789") "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
})
|
||||
|
||||
t.Run("No OR operator was added if a star was used for dimension value", func(t *testing.T) {
|
||||
@@ -72,7 +72,7 @@ func TestMetricDataQueryBuilder_buildSearchExpression(t *testing.T) {
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{AWS/EC2,"LoadBalancer"} MetricName="CPUUtilization"', 'Average', 300))`, res)
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization"', 'Average', 300))`, res)
|
||||
})
|
||||
|
||||
t.Run("Query has three dimension values for two given dimension keys, and one value is a star", func(t *testing.T) {
|
||||
@@ -89,7 +89,7 @@ func TestMetricDataQueryBuilder_buildSearchExpression(t *testing.T) {
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{AWS/EC2,"InstanceId","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
})
|
||||
|
||||
t.Run("Query has a dimension key with a space", func(t *testing.T) {
|
||||
@@ -105,7 +105,24 @@ func TestMetricDataQueryBuilder_buildSearchExpression(t *testing.T) {
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{AWS/Kafka,"Cluster Name"} MetricName="CpuUser" "Cluster Name"="dev-cluster"', 'Average', 300))`, res)
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/Kafka","Cluster Name"} MetricName="CpuUser" "Cluster Name"="dev-cluster"', 'Average', 300))`, res)
|
||||
})
|
||||
|
||||
t.Run("Query has a custom namespace contains spaces", func(t *testing.T) {
|
||||
query := &cloudWatchQuery{
|
||||
Namespace: "Test-API Cache by Minute",
|
||||
MetricName: "CpuUser",
|
||||
Dimensions: map[string][]string{
|
||||
"LoadBalancer": {"lb1", "lb2", "lb3"},
|
||||
"InstanceId": {"i-123", "*", "i-789"},
|
||||
},
|
||||
Period: 300,
|
||||
Expression: "",
|
||||
MatchExact: matchExact,
|
||||
}
|
||||
|
||||
res := buildSearchExpression(query, "Average")
|
||||
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"Test-API Cache by Minute","InstanceId","LoadBalancer"} MetricName="CpuUser" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -105,12 +105,11 @@ func parseMetricResults(results map[string]*cloudwatch.MetricDataResult, labels
|
||||
}
|
||||
}
|
||||
|
||||
timeField := data.NewField("timestamp", nil, []*time.Time{})
|
||||
timeField.SetConfig(&data.FieldConfig{DisplayName: "Time"})
|
||||
timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, []*time.Time{})
|
||||
valueField := data.NewField(data.TimeSeriesValueFieldName, tags, []*float64{})
|
||||
|
||||
frameName := formatAlias(query, query.Stats, tags, label)
|
||||
valueField := data.NewField("value", tags, []*float64{})
|
||||
valueField.SetConfig(&data.FieldConfig{DisplayName: frameName})
|
||||
valueField.SetConfig(&data.FieldConfig{DisplayNameFromDS: frameName})
|
||||
|
||||
emptyFrame := data.Frame{
|
||||
Name: frameName,
|
||||
@@ -160,12 +159,11 @@ func parseMetricResults(results map[string]*cloudwatch.MetricDataResult, labels
|
||||
points = append(points, val)
|
||||
}
|
||||
|
||||
timeField := data.NewField("timestamp", nil, timestamps)
|
||||
timeField.SetConfig(&data.FieldConfig{DisplayName: "Time"})
|
||||
timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps)
|
||||
valueField := data.NewField(data.TimeSeriesValueFieldName, tags, points)
|
||||
|
||||
frameName := formatAlias(query, query.Stats, tags, label)
|
||||
valueField := data.NewField("value", tags, points)
|
||||
valueField.SetConfig(&data.FieldConfig{DisplayName: frameName})
|
||||
valueField.SetConfig(&data.FieldConfig{DisplayNameFromDS: frameName})
|
||||
|
||||
frame := data.Frame{
|
||||
Name: frameName,
|
||||
|
||||
@@ -310,10 +310,13 @@ func TestCloudWatchResponseParser(t *testing.T) {
|
||||
frame := frames[0]
|
||||
assert.False(t, partialData)
|
||||
assert.Equal(t, "AWS/ApplicationELB_TargetResponseTime_Average", frame.Name)
|
||||
assert.Equal(t, "Time", frame.Fields[0].Name)
|
||||
assert.Equal(t, "lb", frame.Fields[1].Labels["LoadBalancer"])
|
||||
assert.Equal(t, 10.0, *frame.Fields[1].At(0).(*float64))
|
||||
assert.Equal(t, 20.0, *frame.Fields[1].At(1).(*float64))
|
||||
assert.Nil(t, frame.Fields[1].At(2))
|
||||
assert.Equal(t, 30.0, *frame.Fields[1].At(3).(*float64))
|
||||
assert.Equal(t, "Value", frame.Fields[1].Name)
|
||||
assert.Equal(t, "", frame.Fields[1].Config.DisplayName)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -209,9 +209,10 @@ func (fb *frameBuilder) Append(record *query.FluxRecord) error {
|
||||
// set the labels
|
||||
labels := make(map[string]string)
|
||||
for _, name := range fb.labels {
|
||||
val, ok := record.ValueByKey(name).(string)
|
||||
if ok {
|
||||
labels[name] = val
|
||||
val := record.ValueByKey(name)
|
||||
str := fmt.Sprintf("%v", val)
|
||||
if val != nil && str != "" {
|
||||
labels[name] = str
|
||||
}
|
||||
}
|
||||
fb.active.Fields[1].Labels = labels
|
||||
|
||||
@@ -73,141 +73,129 @@ func verifyGoldenResponse(t *testing.T, name string) *backend.DataResponse {
|
||||
}
|
||||
|
||||
func TestExecuteSimple(t *testing.T) {
|
||||
t.Run("Simple Test", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "simple")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
require.Contains(t, dr.Frames[0].Name, "test")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 2)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
dr := verifyGoldenResponse(t, "simple")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
require.Contains(t, dr.Frames[0].Name, "test")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 2)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
})
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
}
|
||||
|
||||
func TestExecuteSingle(t *testing.T) {
|
||||
t.Run("Single value", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "single")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
})
|
||||
dr := verifyGoldenResponse(t, "single")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
}
|
||||
|
||||
func TestExecuteMultiple(t *testing.T) {
|
||||
t.Run("Multiple Test", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "multiple")
|
||||
require.Len(t, dr.Frames, 4)
|
||||
require.Contains(t, dr.Frames[0].Name, "test")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 2)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
dr := verifyGoldenResponse(t, "multiple")
|
||||
require.Len(t, dr.Frames, 3)
|
||||
require.Contains(t, dr.Frames[0].Name, "test")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 2)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
})
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
}
|
||||
|
||||
func TestExecuteGrouping(t *testing.T) {
|
||||
t.Run("Grouping Test", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "grouping")
|
||||
require.Len(t, dr.Frames, 3)
|
||||
require.Contains(t, dr.Frames[0].Name, "system")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 1)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
dr := verifyGoldenResponse(t, "grouping")
|
||||
require.Len(t, dr.Frames, 3)
|
||||
require.Contains(t, dr.Frames[0].Name, "system")
|
||||
require.Len(t, dr.Frames[0].Fields[1].Labels, 1)
|
||||
require.Equal(t, "Time", dr.Frames[0].Fields[0].Name)
|
||||
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
})
|
||||
st, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(st)
|
||||
fmt.Println("----------------------")
|
||||
}
|
||||
|
||||
func TestAggregateGrouping(t *testing.T) {
|
||||
t.Run("Grouping Test", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "aggregate")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
dr := verifyGoldenResponse(t, "aggregate")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
|
||||
str, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(str)
|
||||
str, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(str)
|
||||
|
||||
// `Name:
|
||||
// Dimensions: 2 Fields by 3 Rows
|
||||
// +-------------------------------+--------------------------+
|
||||
// | Name: Time | Name: |
|
||||
// | Labels: | Labels: host=hostname.ru |
|
||||
// | Type: []time.Time | Type: []*float64 |
|
||||
// +-------------------------------+--------------------------+
|
||||
// | 2020-06-05 12:06:00 +0000 UTC | 8.291 |
|
||||
// | 2020-06-05 12:07:00 +0000 UTC | 0.534 |
|
||||
// | 2020-06-05 12:08:00 +0000 UTC | 0.667 |
|
||||
// +-------------------------------+--------------------------+
|
||||
// `
|
||||
// `Name:
|
||||
// Dimensions: 2 Fields by 3 Rows
|
||||
// +-------------------------------+--------------------------+
|
||||
// | Name: Time | Name: |
|
||||
// | Labels: | Labels: host=hostname.ru |
|
||||
// | Type: []time.Time | Type: []*float64 |
|
||||
// +-------------------------------+--------------------------+
|
||||
// | 2020-06-05 12:06:00 +0000 UTC | 8.291 |
|
||||
// | 2020-06-05 12:07:00 +0000 UTC | 0.534 |
|
||||
// | 2020-06-05 12:08:00 +0000 UTC | 0.667 |
|
||||
// +-------------------------------+--------------------------+
|
||||
// `
|
||||
|
||||
expectedFrame := data.NewFrame("",
|
||||
data.NewField("Time", nil, []time.Time{
|
||||
time.Date(2020, 6, 5, 12, 6, 0, 0, time.UTC),
|
||||
time.Date(2020, 6, 5, 12, 7, 0, 0, time.UTC),
|
||||
time.Date(2020, 6, 5, 12, 8, 0, 0, time.UTC),
|
||||
}),
|
||||
data.NewField("", map[string]string{"host": "hostname.ru"}, []*float64{
|
||||
pointer.Float64(8.291),
|
||||
pointer.Float64(0.534),
|
||||
pointer.Float64(0.667),
|
||||
}),
|
||||
)
|
||||
expectedFrame.Meta = &data.FrameMeta{}
|
||||
expectedFrame := data.NewFrame("",
|
||||
data.NewField("Time", nil, []time.Time{
|
||||
time.Date(2020, 6, 5, 12, 6, 0, 0, time.UTC),
|
||||
time.Date(2020, 6, 5, 12, 7, 0, 0, time.UTC),
|
||||
time.Date(2020, 6, 5, 12, 8, 0, 0, time.UTC),
|
||||
}),
|
||||
data.NewField("", map[string]string{"host": "hostname.ru"}, []*float64{
|
||||
pointer.Float64(8.291),
|
||||
pointer.Float64(0.534),
|
||||
pointer.Float64(0.667),
|
||||
}),
|
||||
)
|
||||
expectedFrame.Meta = &data.FrameMeta{}
|
||||
|
||||
diff := cmp.Diff(expectedFrame, dr.Frames[0], data.FrameTestCompareOptions()...)
|
||||
assert.Empty(t, diff)
|
||||
})
|
||||
diff := cmp.Diff(expectedFrame, dr.Frames[0], data.FrameTestCompareOptions()...)
|
||||
assert.Empty(t, diff)
|
||||
}
|
||||
|
||||
func TestNonStandardTimeColumn(t *testing.T) {
|
||||
t.Run("Time Column", func(t *testing.T) {
|
||||
dr := verifyGoldenResponse(t, "non_standard_time_column")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
dr := verifyGoldenResponse(t, "non_standard_time_column")
|
||||
require.Len(t, dr.Frames, 1)
|
||||
|
||||
str, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(str)
|
||||
str, err := dr.Frames[0].StringTable(-1, -1)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(str)
|
||||
|
||||
// Dimensions: 2 Fields by 1 Rows
|
||||
// +-----------------------------------------+------------------+
|
||||
// | Name: _start_water | Name: |
|
||||
// | Labels: | Labels: st=1 |
|
||||
// | Type: []time.Time | Type: []*float64 |
|
||||
// +-----------------------------------------+------------------+
|
||||
// | 2020-06-28 17:50:13.012584046 +0000 UTC | 156.304 |
|
||||
// +-----------------------------------------+------------------+
|
||||
// Dimensions: 2 Fields by 1 Rows
|
||||
// +-----------------------------------------+------------------+
|
||||
// | Name: _start_water | Name: |
|
||||
// | Labels: | Labels: st=1 |
|
||||
// | Type: []time.Time | Type: []*float64 |
|
||||
// +-----------------------------------------+------------------+
|
||||
// | 2020-06-28 17:50:13.012584046 +0000 UTC | 156.304 |
|
||||
// +-----------------------------------------+------------------+
|
||||
|
||||
expectedFrame := data.NewFrame("",
|
||||
data.NewField("_start_water", nil, []time.Time{
|
||||
time.Date(2020, 6, 28, 17, 50, 13, 12584046, time.UTC),
|
||||
}),
|
||||
data.NewField("", map[string]string{"st": "1"}, []*float64{
|
||||
pointer.Float64(156.304),
|
||||
}),
|
||||
)
|
||||
expectedFrame.Meta = &data.FrameMeta{}
|
||||
expectedFrame := data.NewFrame("",
|
||||
data.NewField("_start_water", nil, []time.Time{
|
||||
time.Date(2020, 6, 28, 17, 50, 13, 12584046, time.UTC),
|
||||
}),
|
||||
data.NewField("", map[string]string{"st": "1"}, []*float64{
|
||||
pointer.Float64(156.304),
|
||||
}),
|
||||
)
|
||||
expectedFrame.Meta = &data.FrameMeta{}
|
||||
|
||||
diff := cmp.Diff(expectedFrame, dr.Frames[0], data.FrameTestCompareOptions()...)
|
||||
assert.Empty(t, diff)
|
||||
})
|
||||
diff := cmp.Diff(expectedFrame, dr.Frames[0], data.FrameTestCompareOptions()...)
|
||||
assert.Empty(t, diff)
|
||||
}
|
||||
|
||||
func TestBuckets(t *testing.T) {
|
||||
t.Run("Buckes", func(t *testing.T) {
|
||||
verifyGoldenResponse(t, "buckets")
|
||||
})
|
||||
verifyGoldenResponse(t, "buckets")
|
||||
}
|
||||
|
||||
func TestBooleanGrouping(t *testing.T) {
|
||||
verifyGoldenResponse(t, "boolean")
|
||||
}
|
||||
|
||||
func TestGoldenFiles(t *testing.T) {
|
||||
t.Run("Renamed", func(t *testing.T) {
|
||||
verifyGoldenResponse(t, "renamed")
|
||||
})
|
||||
verifyGoldenResponse(t, "renamed")
|
||||
}
|
||||
|
||||
func TestRealQuery(t *testing.T) {
|
||||
|
||||
9
pkg/tsdb/influxdb/flux/testdata/boolean.csv
vendored
Normal file
9
pkg/tsdb/influxdb/flux/testdata/boolean.csv
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
#group,false,false,false,false,true,true,false,false,false,true
|
||||
#datatype,string,long,string,string,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,long,boolean,string
|
||||
#default,_result,,,,,,,,,
|
||||
,result,table,_field,_measurement,_start,_stop,_time,_value,dead,train
|
||||
,,0,file_size,ingress,2020-10-28T00:07:53.819772368Z,2020-11-11T00:07:53.819772368Z,2020-11-09T17:27:00Z,3339,true,350117
|
||||
,,1,file_size,ingress,2020-10-28T00:07:53.819772368Z,2020-11-11T00:07:53.819772368Z,2020-11-10T12:29:00Z,3666,true,350125
|
||||
,,2,file_size,ingress,2020-10-28T00:07:53.819772368Z,2020-11-11T00:07:53.819772368Z,2020-11-09T19:01:00Z,3570,true,350236
|
||||
,,3,file_size,ingress,2020-10-28T00:07:53.819772368Z,2020-11-11T00:07:53.819772368Z,2020-11-09T07:13:00Z,2772,true,350410
|
||||
|
||||
|
58
pkg/tsdb/influxdb/flux/testdata/boolean.golden.txt
vendored
Normal file
58
pkg/tsdb/influxdb/flux/testdata/boolean.golden.txt
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
🌟 This was machine generated. Do not edit. 🌟
|
||||
|
||||
Frame[0] {}
|
||||
Name: ingress
|
||||
Dimensions: 2 Fields by 1 Rows
|
||||
+-------------------------------+---------------------------------+
|
||||
| Name: Time | Name: file_size |
|
||||
| Labels: | Labels: dead=true, train=350117 |
|
||||
| Type: []time.Time | Type: []*int64 |
|
||||
+-------------------------------+---------------------------------+
|
||||
| 2020-11-09 17:27:00 +0000 UTC | 3339 |
|
||||
+-------------------------------+---------------------------------+
|
||||
|
||||
|
||||
|
||||
Frame[1]
|
||||
Name: ingress
|
||||
Dimensions: 2 Fields by 1 Rows
|
||||
+-------------------------------+---------------------------------+
|
||||
| Name: Time | Name: file_size |
|
||||
| Labels: | Labels: dead=true, train=350125 |
|
||||
| Type: []time.Time | Type: []*int64 |
|
||||
+-------------------------------+---------------------------------+
|
||||
| 2020-11-10 12:29:00 +0000 UTC | 3666 |
|
||||
+-------------------------------+---------------------------------+
|
||||
|
||||
|
||||
|
||||
Frame[2]
|
||||
Name: ingress
|
||||
Dimensions: 2 Fields by 1 Rows
|
||||
+-------------------------------+---------------------------------+
|
||||
| Name: Time | Name: file_size |
|
||||
| Labels: | Labels: dead=true, train=350236 |
|
||||
| Type: []time.Time | Type: []*int64 |
|
||||
+-------------------------------+---------------------------------+
|
||||
| 2020-11-09 19:01:00 +0000 UTC | 3570 |
|
||||
+-------------------------------+---------------------------------+
|
||||
|
||||
|
||||
|
||||
Frame[3]
|
||||
Name: ingress
|
||||
Dimensions: 2 Fields by 1 Rows
|
||||
+-------------------------------+---------------------------------+
|
||||
| Name: Time | Name: file_size |
|
||||
| Labels: | Labels: dead=true, train=350410 |
|
||||
| Type: []time.Time | Type: []*int64 |
|
||||
+-------------------------------+---------------------------------+
|
||||
| 2020-11-09 07:13:00 +0000 UTC | 2772 |
|
||||
+-------------------------------+---------------------------------+
|
||||
|
||||
|
||||
====== TEST DATA RESPONSE (arrow base64) ======
|
||||
FRAME=QVJST1cxAAD/////+AEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAHgAAAADAAAAUAAAACgAAAAEAAAAmP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAAC4/v//CAAAABAAAAAHAAAAaW5ncmVzcwAEAAAAbmFtZQAAAADc/v//CAAAAAwAAAACAAAAe30AAAQAAABtZXRhAAAAAAIAAADgAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAACEAAAAjAAAAAAAAgGQAAAAAgAAADAAAAAEAAAAQP///wgAAAAUAAAACQAAAGZpbGVfc2l6ZQAAAAQAAABuYW1lAAAAAGj///8IAAAALAAAACAAAAB7ImRlYWQiOiJ0cnVlIiwidHJhaW4iOiIzNTAxMTcifQAAAAAGAAAAbGFiZWxzAAAAAAAACAAMAAgABwAIAAAAAAAAAUAAAAAJAAAAZmlsZV9zaXplABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAP////+4AAAAFAAAAAAAAAAMABYAFAATAAwABAAMAAAAEAAAAAAAAAAUAAAAAAAAAwMACgAYAAwACAAEAAoAAAAUAAAAWAAAAAEAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAAAAAAIAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAADo6c795kUWCw0AAAAAAAAQAAAADAAUABIADAAIAAQADAAAABAAAAAsAAAAPAAAAAAAAwABAAAACAIAAAAAAADAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAAB4AAAAAwAAAFAAAAAoAAAABAAAAJj+//8IAAAADAAAAAAAAAAAAAAABQAAAHJlZklkAAAAuP7//wgAAAAQAAAABwAAAGluZ3Jlc3MABAAAAG5hbWUAAAAA3P7//wgAAAAMAAAAAgAAAHt9AAAEAAAAbWV0YQAAAAACAAAA4AAAABgAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAhAAAAIwAAAAAAAIBkAAAAAIAAAAwAAAABAAAAED///8IAAAAFAAAAAkAAABmaWxlX3NpemUAAAAEAAAAbmFtZQAAAABo////CAAAACwAAAAgAAAAeyJkZWFkIjoidHJ1ZSIsInRyYWluIjoiMzUwMTE3In0AAAAABgAAAGxhYmVscwAAAAAAAAgADAAIAAcACAAAAAAAAAFAAAAACQAAAGZpbGVfc2l6ZQASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEQAAABMAAAAAAAACkwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAQAAAABAAAAFRpbWUAAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAwAEAAAAVGltZQAAAAAoAgAAQVJST1cx
|
||||
FRAME=QVJST1cxAAD/////2AEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAAC4/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAANj+//8IAAAAEAAAAAcAAABpbmdyZXNzAAQAAABuYW1lAAAAAAIAAADgAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAACEAAAAjAAAAAAAAgGQAAAAAgAAADAAAAAEAAAAQP///wgAAAAUAAAACQAAAGZpbGVfc2l6ZQAAAAQAAABuYW1lAAAAAGj///8IAAAALAAAACAAAAB7ImRlYWQiOiJ0cnVlIiwidHJhaW4iOiIzNTAxMjUifQAAAAAGAAAAbGFiZWxzAAAAAAAACAAMAAgABwAIAAAAAAAAAUAAAAAJAAAAZmlsZV9zaXplABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAAAAAAD/////uAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAABAAAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAFgAAAABAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAeCxdTyVGFlIOAAAAAAAAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADgAAAAAAAMAAQAAAOgBAAAAAAAAwAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABUAAAAAgAAACgAAAAEAAAAuP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADY/v//CAAAABAAAAAHAAAAaW5ncmVzcwAEAAAAbmFtZQAAAAACAAAA4AAAABgAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAhAAAAIwAAAAAAAIBkAAAAAIAAAAwAAAABAAAAED///8IAAAAFAAAAAkAAABmaWxlX3NpemUAAAAEAAAAbmFtZQAAAABo////CAAAACwAAAAgAAAAeyJkZWFkIjoidHJ1ZSIsInRyYWluIjoiMzUwMTI1In0AAAAABgAAAGxhYmVscwAAAAAAAAgADAAIAAcACAAAAAAAAAFAAAAACQAAAGZpbGVfc2l6ZQASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEQAAABMAAAAAAAACkwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAQAAAABAAAAFRpbWUAAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAwAEAAAAVGltZQAAAAAAAgAAQVJST1cx
|
||||
FRAME=QVJST1cxAAD/////2AEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAAC4/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAANj+//8IAAAAEAAAAAcAAABpbmdyZXNzAAQAAABuYW1lAAAAAAIAAADgAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAACEAAAAjAAAAAAAAgGQAAAAAgAAADAAAAAEAAAAQP///wgAAAAUAAAACQAAAGZpbGVfc2l6ZQAAAAQAAABuYW1lAAAAAGj///8IAAAALAAAACAAAAB7ImRlYWQiOiJ0cnVlIiwidHJhaW4iOiIzNTAyMzYifQAAAAAGAAAAbGFiZWxzAAAAAAAACAAMAAgABwAIAAAAAAAAAUAAAAAJAAAAZmlsZV9zaXplABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAAAAAAD/////uAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAABAAAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAFgAAAABAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAOBz5HuxFFvINAAAAAAAAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADgAAAAAAAMAAQAAAOgBAAAAAAAAwAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABUAAAAAgAAACgAAAAEAAAAuP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADY/v//CAAAABAAAAAHAAAAaW5ncmVzcwAEAAAAbmFtZQAAAAACAAAA4AAAABgAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAhAAAAIwAAAAAAAIBkAAAAAIAAAAwAAAABAAAAED///8IAAAAFAAAAAkAAABmaWxlX3NpemUAAAAEAAAAbmFtZQAAAABo////CAAAACwAAAAgAAAAeyJkZWFkIjoidHJ1ZSIsInRyYWluIjoiMzUwMjM2In0AAAAABgAAAGxhYmVscwAAAAAAAAgADAAIAAcACAAAAAAAAAFAAAAACQAAAGZpbGVfc2l6ZQASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEQAAABMAAAAAAAACkwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAQAAAABAAAAFRpbWUAAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAwAEAAAAVGltZQAAAAAAAgAAQVJST1cx
|
||||
FRAME=QVJST1cxAAD/////2AEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAAC4/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAANj+//8IAAAAEAAAAAcAAABpbmdyZXNzAAQAAABuYW1lAAAAAAIAAADgAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAACEAAAAjAAAAAAAAgGQAAAAAgAAADAAAAAEAAAAQP///wgAAAAUAAAACQAAAGZpbGVfc2l6ZQAAAAQAAABuYW1lAAAAAGj///8IAAAALAAAACAAAAB7ImRlYWQiOiJ0cnVlIiwidHJhaW4iOiIzNTA0MTAifQAAAAAGAAAAbGFiZWxzAAAAAAAACAAMAAgABwAIAAAAAAAAAUAAAAAJAAAAZmlsZV9zaXplABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAAAAAAD/////uAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAABAAAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAFgAAAABAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAAAAAACAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAA2MxTfMVFFtQKAAAAAAAAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADgAAAAAAAMAAQAAAOgBAAAAAAAAwAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABUAAAAAgAAACgAAAAEAAAAuP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADY/v//CAAAABAAAAAHAAAAaW5ncmVzcwAEAAAAbmFtZQAAAAACAAAA4AAAABgAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAhAAAAIwAAAAAAAIBkAAAAAIAAAAwAAAABAAAAED///8IAAAAFAAAAAkAAABmaWxlX3NpemUAAAAEAAAAbmFtZQAAAABo////CAAAACwAAAAgAAAAeyJkZWFkIjoidHJ1ZSIsInRyYWluIjoiMzUwNDEwIn0AAAAABgAAAGxhYmVscwAAAAAAAAgADAAIAAcACAAAAAAAAAFAAAAACQAAAGZpbGVfc2l6ZQASABgAFAAAABMADAAAAAgABAASAAAAFAAAAEQAAABMAAAAAAAACkwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAQAAAABAAAAFRpbWUAAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAwAEAAAAVGltZQAAAAAAAgAAQVJST1cx
|
||||
6
pkg/tsdb/influxdb/flux/testdata/multiple.csv
vendored
6
pkg/tsdb/influxdb/flux/testdata/multiple.csv
vendored
@@ -10,12 +10,6 @@
|
||||
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
|
||||
,,1,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T10:34:08.135814545Z,4,i,test,1,adsfasdf
|
||||
,,1,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.850214724Z,-1,i,test,1,adsfasdf
|
||||
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,bool,string,string,string,string
|
||||
#group,false,false,true,true,false,false,true,true,true,true
|
||||
#default,_result,,,,,,,,,
|
||||
,result,table,_start,_stop,_time,_value,_field,_measurement,a,b
|
||||
,,2,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.62797864Z,false,f,test,0,adsfasdf
|
||||
,,2,2020-02-17T22:19:49.747562847Z,2020-02-18T22:19:49.747562847Z,2020-02-18T22:08:44.969100374Z,true,f,test,0,adsfasdf
|
||||
#datatype,string,long,dateTime:RFC3339Nano,dateTime:RFC3339Nano,dateTime:RFC3339Nano,unsignedLong,string,string,string,string
|
||||
#group,false,false,true,true,false,false,true,true,true,true
|
||||
#default,_result,,,,,,,,,
|
||||
|
||||
|
@@ -32,20 +32,6 @@ Frame[2]
|
||||
Name: test
|
||||
Dimensions: 2 Fields by 2 Rows
|
||||
+-----------------------------------------+-------------------------+
|
||||
| Name: Time | Name: f |
|
||||
| Labels: | Labels: a=0, b=adsfasdf |
|
||||
| Type: []time.Time | Type: []*bool |
|
||||
+-----------------------------------------+-------------------------+
|
||||
| 2020-02-18 22:08:44.62797864 +0000 UTC | false |
|
||||
| 2020-02-18 22:08:44.969100374 +0000 UTC | true |
|
||||
+-----------------------------------------+-------------------------+
|
||||
|
||||
|
||||
|
||||
Frame[3]
|
||||
Name: test
|
||||
Dimensions: 2 Fields by 2 Rows
|
||||
+-----------------------------------------+-------------------------+
|
||||
| Name: _start | Name: i |
|
||||
| Labels: | Labels: a=0, b=adsfasdf |
|
||||
| Type: []time.Time | Type: []*uint64 |
|
||||
@@ -58,5 +44,4 @@ Dimensions: 2 Fields by 2 Rows
|
||||
====== TEST DATA RESPONSE (arrow base64) ======
|
||||
FRAME=QVJST1cxAAD/////2AEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAHgAAAADAAAAUAAAACgAAAAEAAAAvP7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADc/v//CAAAABAAAAAEAAAAdGVzdAAAAAAEAAAAbmFtZQAAAAAA////CAAAAAwAAAACAAAAe30AAAQAAABtZXRhAAAAAAIAAAC8AAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAB0AAAAdAAAAAAAAwF0AAAAAgAAACgAAAAEAAAAZP///wgAAAAMAAAAAQAAAGYAAAAEAAAAbmFtZQAAAACE////CAAAACQAAAAYAAAAeyJhIjoiMSIsImIiOiJhZHNmYXNkZiJ9AAAAAAYAAABsYWJlbHMAAAAAAACO////AAACAAEAAABmABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAAAAAAD/////uAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAACAAAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAFgAAAACAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAACRnfi9q3j0FUR3uluTnvQVZmZmZmZm9j9mZmZmZmYaQBAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA4AAAAAAADAAEAAADoAQAAAAAAAMAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAeAAAAAMAAABQAAAAKAAAAAQAAAC8/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAANz+//8IAAAAEAAAAAQAAAB0ZXN0AAAAAAQAAABuYW1lAAAAAAD///8IAAAADAAAAAIAAAB7fQAABAAAAG1ldGEAAAAAAgAAALwAAAAYAAAAAAASABgAFAATABIADAAAAAgABAASAAAAFAAAAHQAAAB0AAAAAAADAXQAAAACAAAAKAAAAAQAAABk////CAAAAAwAAAABAAAAZgAAAAQAAABuYW1lAAAAAIT///8IAAAAJAAAABgAAAB7ImEiOiIxIiwiYiI6ImFkc2Zhc2RmIn0AAAAABgAAAGxhYmVscwAAAAAAAI7///8AAAIAAQAAAGYAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAATAAAAAAAAApMAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAQAAABUaW1lAAAAAAQAAABuYW1lAAAAAAAAAAAAAAYACAAGAAYAAAAAAAMABAAAAFRpbWUAAAAAAAIAAEFSUk9XMQ==
|
||||
FRAME=QVJST1cxAAD/////wAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAADQ/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAPD+//8IAAAAEAAAAAQAAAB0ZXN0AAAAAAQAAABuYW1lAAAAAAIAAADIAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAB0AAAAfAAAAAAAAgGAAAAAAgAAACgAAAAEAAAAWP///wgAAAAMAAAAAQAAAGkAAAAEAAAAbmFtZQAAAAB4////CAAAACQAAAAYAAAAeyJhIjoiMSIsImIiOiJhZHNmYXNkZiJ9AAAAAAYAAABsYWJlbHMAAAAAAAAIAAwACAAHAAgAAAAAAAABQAAAAAEAAABpABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAAAAAAD/////uAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAACAAAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAFgAAAACAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAACAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAACRnfi9q3j0FUR3uluTnvQVBAAAAAAAAAD//////////xAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA4AAAAAAADAAEAAADQAQAAAAAAAMAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAVAAAAAIAAAAoAAAABAAAAND+//8IAAAADAAAAAAAAAAAAAAABQAAAHJlZklkAAAA8P7//wgAAAAQAAAABAAAAHRlc3QAAAAABAAAAG5hbWUAAAAAAgAAAMgAAAAYAAAAAAASABgAFAATABIADAAAAAgABAASAAAAFAAAAHQAAAB8AAAAAAACAYAAAAACAAAAKAAAAAQAAABY////CAAAAAwAAAABAAAAaQAAAAQAAABuYW1lAAAAAHj///8IAAAAJAAAABgAAAB7ImEiOiIxIiwiYiI6ImFkc2Zhc2RmIn0AAAAABgAAAGxhYmVscwAAAAAAAAgADAAIAAcACAAAAAAAAAFAAAAAAQAAAGkAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAATAAAAAAAAApMAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAQAAABUaW1lAAAAAAQAAABuYW1lAAAAAAAAAAAAAAYACAAGAAYAAAAAAAMABAAAAFRpbWUAAAAA6AEAAEFSUk9XMQ==
|
||||
FRAME=QVJST1cxAAD/////sAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAADc/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAPz+//8IAAAAEAAAAAQAAAB0ZXN0AAAAAAQAAABuYW1lAAAAAAIAAAC8AAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAB0AAAAeAAAAAAABgF0AAAAAgAAACgAAAAEAAAAZP///wgAAAAMAAAAAQAAAGYAAAAEAAAAbmFtZQAAAACE////CAAAACQAAAAYAAAAeyJhIjoiMCIsImIiOiJhZHNmYXNkZiJ9AAAAAAYAAABsYWJlbHMAAAAAAAAEAAQABAAAAAEAAABmABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAP////+4AAAAFAAAAAAAAAAMABYAFAATAAwABAAMAAAAGAAAAAAAAAAUAAAAAAAAAwMACgAYAAwACAAEAAoAAAAUAAAAWAAAAAIAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAgAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAJBpe06TnvQVVoTQYpOe9BUCAAAAAAAAABAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA8AAAAAAADAAEAAADAAQAAAAAAAMAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAADc/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAPz+//8IAAAAEAAAAAQAAAB0ZXN0AAAAAAQAAABuYW1lAAAAAAIAAAC8AAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAB0AAAAeAAAAAAABgF0AAAAAgAAACgAAAAEAAAAZP///wgAAAAMAAAAAQAAAGYAAAAEAAAAbmFtZQAAAACE////CAAAACQAAAAYAAAAeyJhIjoiMCIsImIiOiJhZHNmYXNkZiJ9AAAAAAYAAABsYWJlbHMAAAAAAAAEAAQABAAAAAEAAABmABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAVGltZQAAAAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAQAAABUaW1lAAAAAOABAABBUlJPVzE=
|
||||
FRAME=QVJST1cxAAD/////uAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFQAAAACAAAAKAAAAAQAAADU/v//CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAPT+//8IAAAAEAAAAAQAAAB0ZXN0AAAAAAQAAABuYW1lAAAAAAIAAADEAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAB0AAAAfAAAAAAAAgF8AAAAAgAAACgAAAAEAAAAXP///wgAAAAMAAAAAQAAAGkAAAAEAAAAbmFtZQAAAAB8////CAAAACQAAAAYAAAAeyJhIjoiMCIsImIiOiJhZHNmYXNkZiJ9AAAAAAYAAABsYWJlbHMAAAAAAAAAAAYACAAEAAYAAABAAAAAAQAAAGkAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAATAAAAAAAAApMAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAYAAABfc3RhcnQAAAQAAABuYW1lAAAAAAAAAAAAAAYACAAGAAYAAAAAAAMABgAAAF9zdGFydAAA/////7gAAAAUAAAAAAAAAAwAFgAUABMADAAEAAwAAAAgAAAAAAAAABQAAAAAAAADAwAKABgADAAIAAQACgAAABQAAABYAAAAAgAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAAAAAAAgAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAX9ljmZlQ9BVf2WOZmVD0FQAAAAAAAAAAAgAAAAAAAAAQAAAADAAUABIADAAIAAQADAAAABAAAAAsAAAAPAAAAAAAAwABAAAAyAEAAAAAAADAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAwAAAAIAAQACgAAAAgAAABUAAAAAgAAACgAAAAEAAAA1P7//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAAD0/v//CAAAABAAAAAEAAAAdGVzdAAAAAAEAAAAbmFtZQAAAAACAAAAxAAAABgAAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAdAAAAHwAAAAAAAIBfAAAAAIAAAAoAAAABAAAAFz///8IAAAADAAAAAEAAABpAAAABAAAAG5hbWUAAAAAfP///wgAAAAkAAAAGAAAAHsiYSI6IjAiLCJiIjoiYWRzZmFzZGYifQAAAAAGAAAAbGFiZWxzAAAAAAAAAAAGAAgABAAGAAAAQAAAAAEAAABpABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEwAAAAAAAAKTAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAGAAAAX3N0YXJ0AAAEAAAAbmFtZQAAAAAAAAAAAAAGAAgABgAGAAAAAAADAAYAAABfc3RhcnQAAOgBAABBUlJPVzE=
|
||||
|
||||
@@ -2,6 +2,7 @@ package prometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/tsdb"
|
||||
api "github.com/prometheus/client_golang/api"
|
||||
"github.com/prometheus/client_golang/api"
|
||||
apiv1 "github.com/prometheus/client_golang/api/prometheus/v1"
|
||||
"github.com/prometheus/common/model"
|
||||
)
|
||||
@@ -216,3 +217,18 @@ func parseResponse(value model.Value, query *PrometheusQuery) (*tsdb.QueryResult
|
||||
|
||||
return queryRes, nil
|
||||
}
|
||||
|
||||
func IsAPIError(err error) bool {
|
||||
// Have to use errors.As to compare Prometheus errors, since errors.Is won't work due to Prometheus
|
||||
// errors being pointers and errors.Is ends up comparing them by pointer address
|
||||
var e *apiv1.Error
|
||||
return errors.As(err, &e)
|
||||
}
|
||||
|
||||
func ConvertAPIError(err error) error {
|
||||
var e *apiv1.Error
|
||||
if errors.As(err, &e) {
|
||||
return fmt.Errorf("%s: %s", e.Msg, e.Detail)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user