Compare commits

..

9 Commits

Author SHA1 Message Date
Emil Tullstedt
e25cfc7fc2 Encryption: Fix securedata 2020-10-15 14:38:36 +02:00
Emil Tullstedt
0988471a16 Merge remote-tracking branch 'origin/master' into envelope 2020-10-15 14:29:48 +02:00
Emil Tullstedt
3c152dc5bd Encryption: Fix tests 2020-10-15 14:27:31 +02:00
Arve Knudsen
d9a156298b Drone: Fix enterprise release pipeline (#28289)
* Drone: Fixes

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

* Drone: Fixes

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
2020-10-15 14:06:52 +02:00
Mikhail Snetkov
e36925b55a Alerting: Append appSubUrl to back button on channel form (#28282) 2020-10-15 13:30:06 +02:00
Emil Tullstedt
92c1b1e4c6 Encryption: Remove new JSON structure 2020-10-14 16:16:05 +02:00
Emil Tullstedt
84669d2213 Encryption: Override Encrypt/Decrypt in util
Breaks every test that uses Encrypt or Decrypt
2020-10-14 15:53:49 +02:00
Emil Tullstedt
1ff6fa9424 Encryption: Improvements 2020-10-09 16:15:24 +02:00
Emil Tullstedt
2f4a097e53 Encryption: Start move to data keys 2020-10-08 17:06:38 +02:00
216 changed files with 2192 additions and 5012 deletions

View File

@@ -14,7 +14,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
@@ -27,21 +27,19 @@ steps:
DOCKERIZE_VERSION: 0.6.1
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -49,7 +47,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -64,7 +62,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -73,7 +71,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -82,7 +80,7 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --variants linux-x64,linux-x64-musl,osx64,win64 --no-pull-enterprise
depends_on:
@@ -91,7 +89,7 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise
depends_on:
@@ -99,7 +97,7 @@ steps:
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps
depends_on:
@@ -107,7 +105,7 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- . scripts/build/gpg-test-vars.sh && ./bin/grabpl package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise --variants linux-x64,linux-x64-musl,osx64,win64
depends_on:
@@ -120,7 +118,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -138,14 +136,14 @@ steps:
- end-to-end-tests-server
- name: build-storybook
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn storybook:build
depends_on:
- package
- name: build-frontend-docs
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./scripts/ci-reference-docs-lint.sh ci
depends_on:
@@ -162,7 +160,7 @@ steps:
- build-frontend-docs
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -178,7 +176,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -195,7 +193,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -272,7 +270,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
@@ -297,21 +295,19 @@ steps:
from_secret: drone_token
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -319,7 +315,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -334,7 +330,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -343,7 +339,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -352,7 +348,7 @@ steps:
- initialize
- name: publish-frontend-metrics
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./scripts/ci-frontend-metrics.sh | ./bin/grabpl publish-metrics $${GRAFANA_MISC_STATS_API_KEY}
environment:
@@ -363,7 +359,7 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise
depends_on:
@@ -372,7 +368,7 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --no-install-deps --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise
depends_on:
@@ -380,7 +376,7 @@ steps:
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin
environment:
@@ -391,7 +387,7 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl package --jobs 8 --edition oss --build-id ${DRONE_BUILD_NUMBER} --no-pull-enterprise --sign
environment:
@@ -415,7 +411,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -433,7 +429,7 @@ steps:
- end-to-end-tests-server
- name: build-storybook
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn storybook:build
depends_on:
@@ -452,7 +448,7 @@ steps:
- build-storybook
- name: build-frontend-docs
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./scripts/ci-reference-docs-lint.sh ci
depends_on:
@@ -469,7 +465,7 @@ steps:
- build-frontend-docs
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -499,7 +495,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -516,7 +512,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -532,7 +528,7 @@ steps:
- test-frontend
- name: release-next-npm-packages
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./node_modules/.bin/lerna bootstrap
- echo "//registry.npmjs.org/:_authToken=$${NPM_TOKEN}" >> ~/.npmrc
@@ -652,7 +648,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
@@ -727,12 +723,13 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl verify-version $${DRONE_TAG}
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- tar -C bin -xzvf dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- rm dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -741,21 +738,19 @@ steps:
DOCKERIZE_VERSION: 0.6.1
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -763,7 +758,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -778,7 +773,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -787,7 +782,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -796,9 +791,10 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl build-backend --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise $${DRONE_TAG}
environment:
GITHUB_TOKEN:
from_secret: github_token
@@ -808,15 +804,16 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition oss --no-pull-enterprise ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition oss --no-pull-enterprise $${DRONE_TAG}
depends_on:
- initialize
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin
environment:
@@ -827,9 +824,10 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl package --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl package --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign $${DRONE_TAG}
environment:
GITHUB_TOKEN:
from_secret: github_token
@@ -851,7 +849,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -869,7 +867,7 @@ steps:
- end-to-end-tests-server
- name: build-storybook
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn storybook:build
depends_on:
@@ -878,10 +876,11 @@ steps:
- name: publish-storybook
image: grafana/grafana-ci-deploy:1.2.6
commands:
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- printenv GCP_KEY | base64 -d > /tmp/gcpkey.json
- gcloud auth activate-service-account --key-file=/tmp/gcpkey.json
- gsutil -m rsync -d -r ./packages/grafana-ui/dist/storybook gs://grafana-storybook/latest
- gsutil -m rsync -d -r ./packages/grafana-ui/dist/storybook gs://grafana-storybook/${DRONE_TAG}
- gsutil -m rsync -d -r ./packages/grafana-ui/dist/storybook gs://grafana-storybook/$${DRONE_TAG}
environment:
GCP_KEY:
from_secret: gcp_key
@@ -889,7 +888,7 @@ steps:
- build-storybook
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -919,7 +918,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -936,7 +935,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -952,11 +951,12 @@ steps:
- test-frontend
- name: release-npm-packages
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./node_modules/.bin/lerna bootstrap
- echo "//registry.npmjs.org/:_authToken=$${NPM_TOKEN}" >> ~/.npmrc
- ./scripts/build/release-packages.sh ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./scripts/build/release-packages.sh $${DRONE_TAG}
environment:
NPM_TOKEN:
from_secret: npm_token
@@ -1029,13 +1029,14 @@ steps:
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
commands:
- $$env:DRONE_TAG=$$(.\grabpl.exe parse-tag-ref ${DRONE_TAG})
- $$gcpKey = $$env:GCP_KEY
- "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($$gcpKey)) > gcpkey.json"
- dos2unix gcpkey.json
- gcloud auth activate-service-account --key-file=gcpkey.json
- rm gcpkey.json
- cp C:\App\nssm-2.24.zip .
- .\grabpl.exe windows-installer --edition oss ${DRONE_TAG}
- .\grabpl.exe windows-installer --edition oss $$env:DRONE_TAG
- $$fname = ((Get-Childitem grafana*.msi -name) -split "`n")[0]
- gsutil cp $$fname gs://grafana-downloads/oss/release/
- gsutil cp "$$fname.sha256" gs://grafana-downloads/oss/release/
@@ -1071,28 +1072,30 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: clone
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
- chmod +x bin/grabpl
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- git clone "https://$${GITHUB_TOKEN}@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout ${DRONE_TAG}
- git checkout $${DRONE_TAG}
environment:
GITHUB_TOKEN:
from_secret: github_token
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- mv bin/grabpl /tmp/
- rmdir bin
- mv grafana-enterprise /tmp/
- /tmp/grabpl init-enterprise /tmp/grafana-enterprise ${DRONE_TAG}
- /tmp/grabpl init-enterprise /tmp/grafana-enterprise $${DRONE_TAG}
- mkdir bin
- mv /tmp/grabpl bin/
- ./bin/grabpl verify-version ${DRONE_TAG}
- ./bin/grabpl verify-version $${DRONE_TAG}
- curl -fLO https://github.com/jwilder/dockerize/releases/download/v$${DOCKERIZE_VERSION}/dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- tar -C bin -xzvf dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
- rm dockerize-linux-amd64-v$${DOCKERIZE_VERSION}.tar.gz
@@ -1103,21 +1106,19 @@ steps:
- clone
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -1125,7 +1126,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -1140,7 +1141,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -1149,7 +1150,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -1158,9 +1159,10 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl build-backend --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise $${DRONE_TAG}
environment:
GITHUB_TOKEN:
from_secret: github_token
@@ -1170,15 +1172,16 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition enterprise --no-pull-enterprise ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition enterprise --no-pull-enterprise $${DRONE_TAG}
depends_on:
- initialize
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition enterprise --no-install-deps --sign --signing-admin
environment:
@@ -1189,9 +1192,10 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl package --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl package --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign $${DRONE_TAG}
environment:
GITHUB_TOKEN:
from_secret: github_token
@@ -1213,7 +1217,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -1231,7 +1235,7 @@ steps:
- end-to-end-tests-server
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -1261,7 +1265,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -1278,7 +1282,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -1358,9 +1362,10 @@ steps:
commands:
- $$ProgressPreference = "SilentlyContinue"
- Invoke-WebRequest https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/windows/grabpl.exe -OutFile grabpl.exe
- $$env:DRONE_TAG=$$(.\grabpl.exe parse-tag-ref ${DRONE_TAG})
- git clone "https://$$env:GITHUB_TOKEN@github.com/grafana/grafana-enterprise.git"
- cd grafana-enterprise
- git checkout ${DRONE_TAG}
- git checkout $$env:DRONE_TAG
environment:
GITHUB_TOKEN:
from_secret: github_token
@@ -1380,13 +1385,14 @@ steps:
- name: build-windows-installer
image: grafana/ci-wix:0.1.1
commands:
- $$env:DRONE_TAG=$$(.\grabpl.exe parse-tag-ref ${DRONE_TAG})
- $$gcpKey = $$env:GCP_KEY
- "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($$gcpKey)) > gcpkey.json"
- dos2unix gcpkey.json
- gcloud auth activate-service-account --key-file=gcpkey.json
- rm gcpkey.json
- cp C:\App\nssm-2.24.zip .
- .\grabpl.exe windows-installer --edition enterprise ${DRONE_TAG}
- .\grabpl.exe windows-installer --edition enterprise $$env:DRONE_TAG
- $$fname = ((Get-Childitem grafana*.msi -name) -split "`n")[0]
- gsutil cp $$fname gs://grafana-downloads/enterprise/release/
- gsutil cp "$$fname.sha256" gs://grafana-downloads/enterprise/release/
@@ -1419,20 +1425,22 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
- chmod +x bin/grabpl
- ./bin/grabpl verify-version ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl verify-version $${DRONE_TAG}
environment:
DOCKERIZE_VERSION: 0.6.1
- name: publish-packages
image: grafana/grafana-ci-deploy:1.2.6
commands:
- ./bin/grabpl publish-packages --edition oss ${DRONE_TAG}
- ./bin/grabpl publish-packages --edition enterprise ${DRONE_TAG}
- export DRONE_TAG=$$(./bin/grabpl parse-tag-ref ${DRONE_COMMIT_REF})
- ./bin/grabpl publish-packages --edition oss $${DRONE_TAG}
- ./bin/grabpl publish-packages --edition enterprise $${DRONE_TAG}
environment:
GRAFANA_COM_API_KEY:
from_secret: grafana_api_key
@@ -1496,7 +1504,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
@@ -1510,21 +1518,19 @@ steps:
DOCKERIZE_VERSION: 0.6.1
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -1532,7 +1538,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -1547,7 +1553,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -1556,7 +1562,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -1565,7 +1571,7 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise v7.3.0-test
environment:
@@ -1577,7 +1583,7 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition oss --no-pull-enterprise v7.3.0-test
depends_on:
@@ -1585,7 +1591,7 @@ steps:
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition oss --no-install-deps --sign --signing-admin
environment:
@@ -1596,7 +1602,7 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl package --jobs 8 --edition oss --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign v7.3.0-test
environment:
@@ -1620,7 +1626,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -1638,7 +1644,7 @@ steps:
- end-to-end-tests-server
- name: build-storybook
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn storybook:build
depends_on:
@@ -1655,7 +1661,7 @@ steps:
- build-storybook
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -1679,7 +1685,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -1696,7 +1702,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -1712,7 +1718,7 @@ steps:
- test-frontend
- name: release-npm-packages
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./node_modules/.bin/lerna bootstrap
- echo "//registry.npmjs.org/:_authToken=$${NPM_TOKEN}" >> ~/.npmrc
@@ -1830,7 +1836,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: clone
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl
@@ -1843,7 +1849,7 @@ steps:
from_secret: github_token
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mv bin/grabpl /tmp/
- rmdir bin
@@ -1862,21 +1868,19 @@ steps:
- clone
- name: lint-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- golangci-lint run --config scripts/go/configs/.golangci.toml ./pkg/...
- revive -formatter stylish -config scripts/go/configs/revive.toml ./pkg/...
- ./scripts/revive-strict
- ./scripts/tidy-check.sh
- ./grafana-mixin/scripts/lint.sh
- ./grafana-mixin/scripts/build.sh
environment:
CGO_ENABLED: 1
depends_on:
- initialize
- name: codespell
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- "echo -e \"unknwon\nreferer\nerrorstring\neror\niam\" > words_to_ignore.txt"
- codespell -I words_to_ignore.txt docs/
@@ -1884,7 +1888,7 @@ steps:
- initialize
- name: shellcheck
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- curl -fLO http://storage.googleapis.com/grafana-downloads/ci-dependencies/shellcheck-v$${VERSION}.linux.x86_64.tar.xz
- echo $$CHKSUM shellcheck-v$${VERSION}.linux.x86_64.tar.xz | sha512sum --check --strict --status
@@ -1899,7 +1903,7 @@ steps:
- initialize
- name: test-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl test-backend
- ./bin/grabpl integration-tests
@@ -1908,7 +1912,7 @@ steps:
- lint-backend
- name: test-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- yarn run ci:test-frontend
environment:
@@ -1917,7 +1921,7 @@ steps:
- initialize
- name: build-backend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-backend --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise v7.3.0-test
environment:
@@ -1929,7 +1933,7 @@ steps:
- test-backend
- name: build-frontend
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-frontend --jobs 8 --github-token $${GITHUB_TOKEN} --no-install-deps --edition enterprise --no-pull-enterprise v7.3.0-test
depends_on:
@@ -1937,7 +1941,7 @@ steps:
- test-frontend
- name: build-plugins
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl build-plugins --jobs 8 --edition enterprise --no-install-deps --sign --signing-admin
environment:
@@ -1948,7 +1952,7 @@ steps:
- lint-backend
- name: package
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- ./bin/grabpl package --jobs 8 --edition enterprise --github-token $${GITHUB_TOKEN} --no-pull-enterprise --sign v7.3.0-test
environment:
@@ -1972,7 +1976,7 @@ steps:
- shellcheck
- name: end-to-end-tests-server
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
detach: true
commands:
- ./e2e/start-server
@@ -1990,7 +1994,7 @@ steps:
- end-to-end-tests-server
- name: copy-packages-for-docker
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- cp dist/*.tar.gz* packaging/docker/
depends_on:
@@ -2014,7 +2018,7 @@ steps:
- copy-packages-for-docker
- name: postgres-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq postgresql-client
@@ -2031,7 +2035,7 @@ steps:
- test-frontend
- name: mysql-integration-tests
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- apt-get update
- apt-get install -yq default-mysql-client
@@ -2172,7 +2176,7 @@ steps:
- echo $DRONE_RUNNER_NAME
- name: initialize
image: grafana/build-container:1.2.28
image: grafana/build-container:1.2.27
commands:
- mkdir -p bin
- curl -fL -o bin/grabpl https://grafana-downloads.storage.googleapis.com/grafana-build-pipeline/v0.5.18/grabpl

View File

@@ -1,73 +1,9 @@
# 7.3.0-beta1 (2020-10-15)
# 7.3.0-beta1 (2020-10-14)
### Breaking changes
- **CloudWatch**: The AWS CloudWatch data source's authentication scheme has changed. See the [upgrade notes](https://grafana.com/docs/grafana/latest/installation/upgrading/#upgrading-to-v73) for details and how this may affect you.
### Features / Enhancements
* **Alerting**: Add labels to name when converting data frame to series. [#28085](https://github.com/grafana/grafana/pull/28085), [@kylebrandt](https://github.com/kylebrandt)
* **Alerting**: Ensuring LINE Notify notifications are sent for all alert states. [#27639](https://github.com/grafana/grafana/pull/27639), [@haraldkubota](https://github.com/haraldkubota)
* **Auth**: Add SigV4 auth option to datasources. [#27552](https://github.com/grafana/grafana/pull/27552), [@wbrowne](https://github.com/wbrowne)
* **AzureMonitor**: Pass through null values instead of setting 0. [#28126](https://github.com/grafana/grafana/pull/28126), [@kylebrandt](https://github.com/kylebrandt)
* **Cloud Monitoring**: Out-of-the-box dashboards. [#27864](https://github.com/grafana/grafana/pull/27864), [@papagian](https://github.com/papagian)
* **CloudWatch**: Add support for AWS DirectConnect virtual interface metrics and add missing dimensions. [#28008](https://github.com/grafana/grafana/pull/28008), [@jgulick48](https://github.com/jgulick48)
* **CloudWatch**: Adding support for Amazon ElastiCache Redis metrics. [#28040](https://github.com/grafana/grafana/pull/28040), [@jgulick48](https://github.com/jgulick48)
* **CloudWatch**: Adding support for additional Amazon CloudFront metrics. [#28069](https://github.com/grafana/grafana/pull/28069), [@darrylsepeda](https://github.com/darrylsepeda)
* **CloudWatch**: Re-implement authentication. [#25548](https://github.com/grafana/grafana/pull/25548), [@aknuds1](https://github.com/aknuds1),[@patstrom](https://github.com/patstrom)
* **Dashboard**: Allow shortlink generation. [#27409](https://github.com/grafana/grafana/pull/27409), [@MisterSquishy](https://github.com/MisterSquishy)
* **Docker**: OpenShift compatability. [#27813](https://github.com/grafana/grafana/pull/27813), [@xlson](https://github.com/xlson)
* **Elasticsearch**: Support multiple pipeline aggregations for a query. [#27945](https://github.com/grafana/grafana/pull/27945), [@simianhacker](https://github.com/simianhacker)
* **Explore**: Allow shortlink generation. [#28222](https://github.com/grafana/grafana/pull/28222), [@ivanahuckova](https://github.com/ivanahuckova)
* **Explore**: Remove collapsing of visualisations. [#27026](https://github.com/grafana/grafana/pull/27026), [@ivanahuckova](https://github.com/ivanahuckova)
* **FieldColor**: Adds new standard color option for color. [#28039](https://github.com/grafana/grafana/pull/28039), [@torkelo](https://github.com/torkelo)
* **Gauge**: Improve text sizing and support non threshold color modes. [#28256](https://github.com/grafana/grafana/pull/28256), [@torkelo](https://github.com/torkelo)
* **NamedColors**: Named colors refactors. [#28235](https://github.com/grafana/grafana/pull/28235), [@torkelo](https://github.com/torkelo)
* **Panel Inspect**: Allow CSV download for Excel. [#27284](https://github.com/grafana/grafana/pull/27284), [@tomdaly](https://github.com/tomdaly)
* **Prometheus**: Add time range parameters to labels API. [#27548](https://github.com/grafana/grafana/pull/27548), [@kakkoyun](https://github.com/kakkoyun)
* **Snapshots**: Store dashboard data encrypted in the database. [#28129](https://github.com/grafana/grafana/pull/28129), [@wbrowne](https://github.com/wbrowne)
* **Table**: New cell hover behavior and image cell display mode. [#27669](https://github.com/grafana/grafana/pull/27669), [@torkelo](https://github.com/torkelo)
* **Timezones**: Include IANA timezone canonical name in TimeZoneInfo. [#27591](https://github.com/grafana/grafana/pull/27591), [@dprokop](https://github.com/dprokop)
* **Tracing**: Add Tempo data source. [#28204](https://github.com/grafana/grafana/pull/28204), [@aocenas](https://github.com/aocenas)
* **Transformations**: Add Concatenate fields transformer. [#28237](https://github.com/grafana/grafana/pull/28237), [@ryantxu](https://github.com/ryantxu)
* **Transformations**: improve the reduce transformer. [#27875](https://github.com/grafana/grafana/pull/27875), [@ryantxu](https://github.com/ryantxu)
* **Users**: Expire old user invites. [#27361](https://github.com/grafana/grafana/pull/27361), [@wbrowne](https://github.com/wbrowne)
* **Variables**: Adds loading state and indicators. [#27917](https://github.com/grafana/grafana/pull/27917), [@hugohaggmark](https://github.com/hugohaggmark)
* **Variables**: Adds support for key/value mapping in Custom variable. [#27829](https://github.com/grafana/grafana/pull/27829), [@sartaj10](https://github.com/sartaj10)
* **grafana/toolkit**: expose Jest maxWorkers arg for plugin test & build tasks. [#27724](https://github.com/grafana/grafana/pull/27724), [@domasx2](https://github.com/domasx2)
### Bug Fixes
* **Azure Analytics**: FormatAs Time series groups bool columns wrong. [#27713](https://github.com/grafana/grafana/issues/27713)
* **Azure**: Fixes cancellation of requests with different Azure sources. [#28180](https://github.com/grafana/grafana/pull/28180), [@hugohaggmark](https://github.com/hugohaggmark)
* **BackendSrv**: Reloads page instead of redirect on Unauthorized Error. [#28276](https://github.com/grafana/grafana/pull/28276), [@hugohaggmark](https://github.com/hugohaggmark)
* **Dashboard**: Do not allow users without edit permission to a folder to see new dashboard page. [#28249](https://github.com/grafana/grafana/pull/28249), [@torkelo](https://github.com/torkelo)
* **Dashboard**: Fixed issue accessing horizontal table scrollbar when placed at bottom of dashboard. [#28250](https://github.com/grafana/grafana/pull/28250), [@torkelo](https://github.com/torkelo)
* **DataProxy**: Add additional settings for dataproxy to help with network proxy timeouts. [#27841](https://github.com/grafana/grafana/pull/27841), [@kahinton](https://github.com/kahinton)
* **Database**: Adds new indices to alert_notification_state and alert_rule_tag tables. [#28166](https://github.com/grafana/grafana/pull/28166), [@KarineValenca](https://github.com/KarineValenca)
* **Explore**: Fix showing of Prometheus data in Query inspector. [#28128](https://github.com/grafana/grafana/pull/28128), [@ivanahuckova](https://github.com/ivanahuckova)
* **Explore**: Show results of Prometheus instant queries in formatted table. [#27767](https://github.com/grafana/grafana/pull/27767), [@ivanahuckova](https://github.com/ivanahuckova)
* **Graph**: Prevent legend from overflowing container. [#28254](https://github.com/grafana/grafana/pull/28254), [@jackw](https://github.com/jackw)
* **OAuth**: Fix token refresh failure when custom SSL settings are configured for OAuth provider. [#27523](https://github.com/grafana/grafana/pull/27523), [@billoley](https://github.com/billoley)
* **Plugins**: Let descendant plugins inherit their root's signature. [#27970](https://github.com/grafana/grafana/pull/27970), [@aknuds1](https://github.com/aknuds1)
* **Runtime**: Fix handling of short-lived background services. [#28025](https://github.com/grafana/grafana/pull/28025), [@ahlaw](https://github.com/ahlaw)
* **TemplateSrv**: Fix interpolating strings with object variables. [#28171](https://github.com/grafana/grafana/pull/28171), [@torkelo](https://github.com/torkelo)
* **Variables**: Fixes so constants set from url get completed state. [#28257](https://github.com/grafana/grafana/pull/28257), [@hugohaggmark](https://github.com/hugohaggmark)
* **Variables**: Prevent adhoc filters from crashing when they are not loaded properly. [#28226](https://github.com/grafana/grafana/pull/28226), [@mckn](https://github.com/mckn)
# 7.2.2 (2020-10-21)
### Features / Enhancements
**Caution:** Please do not use/enable the `database_metrics` feature flag. It will corrupt MySQL database tables. See [#28440](https://github.com/grafana/grafana/issues/28440) for more information.
~~**Instrumentation**: Add counters and histograms for database queries. [#28236](https://github.com/grafana/grafana/pull/28236), [@bergquist](https://github.com/bergquist)~~
- **Instrumentation**: Add histogram for request duration. [#28364](https://github.com/grafana/grafana/pull/28364), [@bergquist](https://github.com/bergquist)
- **Instrumentation**: Adds environment_info metric. [#28355](https://github.com/grafana/grafana/pull/28355), [@bergquist](https://github.com/bergquist)
### Bug Fixes
- **CloudWatch**: Fix custom metrics. [#28391](https://github.com/grafana/grafana/pull/28391), [@aknuds1](https://github.com/aknuds1)
# 7.2.1 (2020-10-08)
### Features / Enhancements

View File

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

View File

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

View File

@@ -8,6 +8,3 @@ Learn more about the backend architecture:
- Part 2: [Communication](communication.md)
- Part 3: [Database](database.md)
Learn more about the frontend architecture:
- Part 1: [Data requests](frontend-data-requests.md)

View File

@@ -1,41 +0,0 @@
# Data requests
[BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv) handles all outgoing HTTP requests from Grafana. This document explains the high-level concepts used by `BackendSrv`.
## Canceling requests
This section describes how canceling requests work in Grafana. While data sources can implement their own cancellation concept, we recommend that you use the method we describe here.
A data request can take a long time to finish. During the time between when a request starts and finishes, the user can change context. For example, the user may navigate away or issue the same request again.
If we wait for canceled requests to complete, it might create unnecessary load on data sources.
Grafana uses a concept called _request cancelation_ to cancel any ongoing request that Grafana doesn't need.
#### Before Grafana 7.2
Before Grafana can cancel any data request, it has to identify that request. Grafana identifies a request using the property `requestId` [passed as options](https://github.com/grafana/grafana/blob/master/docs/sources/packages_api/runtime/backendsrvrequest.md) when you use [BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv).
The cancellation logic is as follows:
- When an ongoing request discovers that an additional request with the same `requestId` has started, then Grafana will cancel the ongoing request.
- When an ongoing request discovers that the special "cancel all requests" `requestId` was sent, then Grafana will cancel the ongoing request.
#### After Grafana 7.2
Grafana 7.2 introduced an additional way of canceling requests using [RxJs](https://github.com/ReactiveX/rxjs). To support the new cancellation functionality, the data source needs to use the new `fetch` function in [BackendSrv](https://grafana.com/docs/grafana/latest/packages_api/runtime/backendsrv).
Migrating the core data sources to the new `fetch` function [is an ongoing process that you can read about in this issue.](https://github.com/grafana/grafana/issues/27222)
## Request queue
Depending on how the web browser implements the protocol for HTTP 1.1, it will limit the number of parallel requests, lets call this limit _max_parallel_browser_request_.
Unless you have configured Grafana to use HTTP2, the browser limits parallel data requests according to the browser's implementation. For more information on how to enable HTTP2, refer to [Configuration](https://grafana.com/docs/grafana/latest/administration/configuration/#protocol).
Because there is a _max_parallel_browser_request_ limit, if some of the requests take a long time, they will block later requests and make interacting with Grafana very slow.
#### Before Grafana 7.2
Not supported.
#### After Grafana 7.2
Grafana uses a _request queue_ to process all incoming data requests in order while reserving a free "spot" for any requests to the Grafana API.
Since the first implementation of the request queue doesn't take into account what browser the user uses, the _request queue_ limit for parallel data source requests is hard-coded to 5.
> **Note:** Grafana instances [configured with HTTP2 ](https://grafana.com/docs/grafana/latest/administration/configuration/#protocol) will have a hard coded limit of 1000.

View File

@@ -1,338 +0,0 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": "gdev-testdata",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-BlYlRd"
},
"custom": {
"align": "center",
"displayMode": "color-background",
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "blue",
"value": 20
},
{
"color": "orange",
"value": 60
},
{
"color": "red",
"value": 70
}
]
},
"unit": "degree"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Field"
},
"properties": [
{
"id": "custom.displayMode"
}
]
}
]
},
"gridPos": {
"h": 16,
"w": 19,
"x": 0,
"y": 0
},
"id": 4,
"options": {
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Last"
}
]
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 15,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Gradient color schemes",
"transformations": [
{
"id": "reduce",
"options": {
"reducers": ["max", "mean", "last", "min"]
}
},
{
"id": "organize",
"options": {
"excludeByName": {
"Field": false
},
"indexByName": {},
"renameByName": {}
}
}
],
"type": "table"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-blues"
},
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 20
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 26,
"w": 5,
"x": 19,
"y": 0
},
"id": 2,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": ["mean"],
"fields": "",
"values": false
},
"textMode": "value"
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"labels": "",
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 30,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Stats",
"type": "stat"
},
{
"datasource": "gdev-testdata",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"align": "center",
"displayMode": "color-background",
"filterable": false
},
"mappings": [],
"thresholds": {
"mode": "percentage",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "blue",
"value": 20
},
{
"color": "orange",
"value": 60
},
{
"color": "red",
"value": 70
}
]
},
"unit": "degree"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "Field"
},
"properties": [
{
"id": "custom.displayMode"
}
]
}
]
},
"gridPos": {
"h": 10,
"w": 19,
"x": 0,
"y": 16
},
"id": 5,
"options": {
"displayMode": "lcd",
"orientation": "auto",
"reduceOptions": {
"calcs": ["mean"],
"fields": "",
"values": false
},
"showUnfilled": true
},
"pluginVersion": "7.4.0-pre",
"targets": [
{
"alias": "",
"csvWave": {
"timeStep": 60,
"valuesCSV": "0,0,2,2,1,1"
},
"lines": 10,
"points": [],
"pulseWave": {
"offCount": 3,
"offValue": 1,
"onCount": 3,
"onValue": 2,
"timeStep": 60
},
"refId": "A",
"scenarioId": "random_walk",
"seriesCount": 15,
"stream": {
"bands": 1,
"noise": 2.2,
"speed": 250,
"spread": 3.5,
"type": "signal"
},
"stringInput": ""
}
],
"timeFrom": null,
"timeShift": null,
"title": "Bar Gauge LCD",
"transformations": [],
"type": "bargauge"
}
],
"schemaVersion": 26,
"style": "dark",
"tags": ["gdev", "demo"],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Gradient Color modes",
"uid": "inxsweKGz",
"version": 17
}

View File

@@ -399,7 +399,7 @@ The length of time that Grafana will wait for a successful TLS handshake with th
### expect_continue_timeout_seconds
The length of time that Grafana will wait for a datasources first response headers after fully writing the request headers, if the request has an “Expect: 100-continue” header. A value of `0` will result in the body being sent immediately. Default is `1` second. For more details check the [Transport.ExpectContinueTimeout](https://golang.org/pkg/net/http/#Transport.ExpectContinueTimeout) documentation.
The length of time that Grafana will wait for a datasources first response headers after fully writing the request headers, if the request has an “Expect: 100-continue” header. A value of `0` will result in the body being sent immediately. Default is `1` second. For more details check the [Transport.ExpectContinueTimeout](https://golang.org/pkg/net/http/#Transport.ExpectContinueTimeout) documentation.
### max_idle_connections
@@ -549,11 +549,9 @@ Number dashboard versions to keep (per dashboard). Default: `20`, Minimum: `1`.
> Only available in Grafana v6.7+.
This feature prevents users from setting the dashboard refresh interval to a lower value than a given interval value. The default interval value is 5 seconds.
This prevents users from setting the dashboard refresh interval of a lower than given interval. Per default this is 5 seconds.
The interval string is a possibly signed sequence of decimal numbers, followed by a unit suffix (ms, s, m, h, d), e.g. `30s` or `1m`.
As of Grafana v7.3, this also limits the refresh interval options in Explore.
### default_home_dashboard_path
Path to the default home dashboard. If this value is empty, then Grafana uses StaticRootPath + "dashboards/home.json"
@@ -625,7 +623,7 @@ Default is `false`.
### user_invite_max_lifetime_duration
The duration in time a user invitation remains valid before expiring.
The duration in time a user invitation remains valid before expiring.
This setting should be expressed as a duration. Examples: 6h (hours), 2d (days), 1w (week).
Default is `24h` (24 hours). The minimum supported duration is `15m` (15 minutes).
@@ -1075,15 +1073,6 @@ If both are set, then basic authentication is required to access the metrics end
<hr>
## [metrics.environment_info]
Adds dimensions to the `grafana_environment_info` metric, which can expose more information about the Grafana instance.
```
; exampleLabel1 = exampleValue1
; exampleLabel2 = exampleValue2
```
## [metrics.graphite]
Use these options if you want to send internal Grafana metrics to Graphite.

View File

@@ -14,7 +14,7 @@ weight = 500
SAML authentication integration allows your Grafana users to log in by using an external SAML 2.0 Identity Provider (IdP). To enable this, Grafana becomes a Service Provider (SP) in the authentication flow, interacting with the IdP to exchange user information.
The SAML single sign-on (SSO) standard is varied and flexible. Our implementation contains a subset of features needed to provide a smooth authentication experience into Grafana.
The SAML single-sign-on (SSO) standard is varied and flexible. Our implementation contains the subset of features needed to provide a smooth authentication experience into Grafana.
> Only available in Grafana Enterprise v6.3+. If you encounter any problems with our implementation, please don't hesitate to contact us.
@@ -45,14 +45,12 @@ The table below describes all SAML configuration options. Continue reading below
| ----------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------- | ------------- |
| `enabled` | No | Whether SAML authentication is allowed | `false` |
| `single_logout` | No | Whether SAML Single Logout enabled | `false` |
| `allow_idp_initiated` | No | Whether SAML IdP-initiated login is allowed | `false` |
| `certificate` or `certificate_path` | Yes | Base64-encoded string or Path for the SP X.509 certificate | |
| `private_key` or `private_key_path` | Yes | Base64-encoded string or Path for the SP private key | |
| `signature_algorithm` | No | Signature algorithm used for signing requests to the IdP. Supported values are rsa-sha1, rsa-sha256, rsa-sha512. | |
| `idp_metadata`, `idp_metadata_path`, or `idp_metadata_url` | Yes | Base64-encoded string, Path or URL for the IdP SAML metadata XML | |
| `max_issue_delay` | No | Duration, since the IdP issued a response and the SP is allowed to process it | `90s` |
| `metadata_valid_duration` | No | Duration, for how long the SP metadata is valid | `48h` |
| `relay_state` | No | Relay state for IdP-initiated login. Should match relay state configured in IdP | |
| `assertion_attribute_name` | No | Friendly name or name of the attribute within the SAML assertion to use as the user name | `displayName` |
| `assertion_attribute_login` | No | Friendly name or name of the attribute within the SAML assertion to use as the user login handle | `mail` |
| `assertion_attribute_email` | No | Friendly name or name of the attribute within the SAML assertion to use as the user email | `mail` |
@@ -83,9 +81,7 @@ You can only use one form of each configuration option. Using multiple forms, su
### Signature algorithm
> Only available in Grafana v7.3+
The SAML standard recommends using a digital signature for some types of messages, like authentication or logout requests. If the `signature_algorithm` option is configured, Grafana will put a digital signature into SAML requests. Supported signature types are `rsa-sha1`, `rsa-sha256`, `rsa-sha512`. This option should match your IdP configuration, otherwise, signature validation will fail. Grafana uses key and certificate configured with `private_key` and `certificate` options for signing SAML requests.
The SAML standard recommends using digital signature for some types of messages, like authentication or logout requests. If `signature_algorithm` option configured, Grafana will put digital signature into SAML requests. Supported signature types are `rsa-sha1`, `rsa-sha256`, `rsa-sha512`. This option should match your IdP configuration, otherwise, signature won't be validated by the IdP. Grafana uses key and certificate configured with `private_key` and `certificate` options for signing SAML requests.
### IdP metadata
@@ -117,19 +113,9 @@ The integration provides two key endpoints as part of Grafana:
- The `/saml/metadata` endpoint, which contains the SP metadata. You can either download and upload it manually, or youmake the IdP request it directly from the endpoint. Some providers name it Identifier or Entity ID.
- The `/saml/acs` endpoint, which is intended to receive the ACS (Assertion Customer Service) callback. Some providers name it SSO URL or Reply URL.
### IdP-initiated Single Sign-On (SSO)
### Single Logout
> Only available in Grafana v7.3+
By default, Grafana allows only service provider (SP) initiated logins (when the user logs in with SAML via Grafanas login page). If you want users to log in into Grafana directly from your identity provider (IdP), set the `allow_idp_initiated` configuration option to `true` and configure `relay_state` with the same value specified in the IdP configuration.
IdP-initiated SSO has some security risks, so make sure you understand the risks before enabling this feature. When using IdP-initiated SSO, Grafana receives unsolicited SAML requests and can't verify that login flow was started by the user. This makes it hard to detect whether SAML message has been stolen or replaced. Because of this, IdP-initiated SSO is vulnerable to login cross-site request forgery (CSRF) and man in the middle (MITM) attacks. We do not recommend using IdP-initiated SSO and keeping it disabled whenever possible.
### Single logout
> Only available in Grafana v7.3+
SAML's single logout feature allows users to log out from all applications associated with the current IdP session established via SAML SSO. If the `single_logout` option is set to `true` and a user logs out, Grafana requests IdP to end the user session which in turn triggers logout from all other applications the user is logged into using the same IdP session (applications should support single logout). Conversely, if another application connected to the same IdP logs out using single logout, Grafana receives a logout request from IdP and ends the user session.
Single Logout feature allows user to log out from all applications associated with current IdP session established via SAML SSO. If `single_logout` option set to `true` and user logs out, Grafana requests IdP to terminate user session. Then IdP triggers logout process for all other applications which user logged in with the same IdP session (application should support single logout). And conversely, if another application connected to the same IdP initiates single logout, Grafana gets logout request from IdP and terminates user session.
### Assertion mapping

View File

@@ -29,7 +29,7 @@ e2e.scenario({
e2e.components.DashboardLinks.link()
.should('be.visible')
.and(links => {
expect(links).to.have.length.greaterThan(13);
expect(links).to.have.length(13);
for (let index = 0; index < links.length; index++) {
expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`);

1
go.mod
View File

@@ -30,7 +30,6 @@ require (
github.com/facebookgo/structtag v0.0.0-20150214074306-217e25fb9691 // indirect
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
github.com/fatih/color v1.9.0
github.com/gchaincl/sqlhooks v1.3.0
github.com/go-macaron/binding v0.0.0-20190806013118-0b4f37bab25b
github.com/go-macaron/gzip v0.0.0-20160222043647-cad1c6580a07
github.com/go-macaron/session v0.0.0-20190805070824-1a3cdc6f5659

2
go.sum
View File

@@ -317,8 +317,6 @@ github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03D
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk=
github.com/gchaincl/sqlhooks v1.3.0 h1:yKPXxW9a5CjXaVf2HkQn6wn7TZARvbAOAelr3H8vK2Y=
github.com/gchaincl/sqlhooks v1.3.0/go.mod h1:9BypXnereMT0+Ys8WGWHqzgkkOfHIhyeUCqXC24ra34=
github.com/getkin/kin-openapi v0.2.0/go.mod h1:V1z9xl9oF5Wt7v32ne4FmiF1alpS4dM6mNzoywPOXlk=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=

View File

@@ -1,3 +0,0 @@
alerts.yaml
rules.yaml
dashboards_out

View File

@@ -1,13 +0,0 @@
all: fmt lint build clean
fmt:
./scripts/format.sh
lint:
./scripts/lint.sh
build:
./scripts/build.sh
clean:
rm -rf dashboards_out alerts.yaml rules.yaml

View File

@@ -1,28 +0,0 @@
# Grafana Mixin
_This is a work in progress. We aim for it to become a good role model for alerts
and dashboards eventually, but it is not quite there yet._
The Grafana Mixin is a set of configurable, reusable, and extensible alerts and
dashboards based on the metrics exported by Grafana. The mixin creates
recording and alerting rules for Prometheus and suitable dashboard descriptions
for Grafana.
To use them, you need to have `mixtool` and `jsonnetfmt` installed. If you
have a working Go development environment, it's easiest to run the following:
```bash
$ go get github.com/monitoring-mixins/mixtool/cmd/mixtool
$ go get github.com/google/go-jsonnet/cmd/jsonnetfmt
```
You can then build the Prometheus rules files `alerts.yaml` and
`rules.yaml` and a directory `dashboard_out` with the JSON dashboard files
for Grafana:
```bash
$ make build
```
For more advanced uses of mixins, see
https://github.com/monitoring-mixins/docs.

View File

@@ -1,14 +0,0 @@
groups:
- name: GrafanaAlerts
rules:
- alert: GrafanaRequestsFailing
for: 5m
expr: |
100 * namespace_job_handler_statuscode:http_request_total:rate5m{handler!~"/datasources/proxy/:id.*|/ds/query|/tsdb/query", statuscode=~"5.."}
/
namespace_job_handler_statuscode:http_request_total:rate5m{handler!~"/datasources/proxy/:id.*|/ds/query|/tsdb/query"}
> 0.5
labels:
severity: 'critical'
annotations:
message: "'{{ $labels.namespace }}' / '{{ $labels.job }}' / '{{ $labels.handler }}' is experiencing {{ $value | humanize }}% errors"

View File

@@ -1,528 +0,0 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": 35,
"iteration": 1602761142538,
"links": [],
"panels": [
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"noValue": "0",
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 0
},
"id": 6,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "grafana_alerting_result_total{job=~\"$job\", instance=~\"$instance\", state=\"alerting\"}",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Firing Alerts",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 0
},
"id": 8,
"options": {
"colorMode": "value",
"graphMode": "area",
"justifyMode": "auto",
"orientation": "auto",
"reduceOptions": {
"calcs": [
"mean"
],
"fields": "",
"values": false
}
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "sum(grafana_stat_totals_dashboard{job=~\"$job\", instance=~\"$instance\"})",
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Dashboards",
"type": "stat"
},
{
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {
"align": null
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 12,
"x": 12,
"y": 0
},
"id": 10,
"options": {
"showHeader": true
},
"pluginVersion": "7.0.4",
"targets": [
{
"expr": "grafana_build_info{job=~\"$job\", instance=~\"$instance\"}",
"instant": true,
"interval": "",
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Build Info",
"transformations": [
{
"id": "labelsToFields",
"options": {}
},
{
"id": "organize",
"options": {
"excludeByName": {
"Time": true,
"Value": true,
"branch": true,
"container": true,
"goversion": true,
"namespace": true,
"pod": true,
"revision": true
},
"indexByName": {
"Time": 7,
"Value": 11,
"branch": 4,
"container": 8,
"edition": 2,
"goversion": 6,
"instance": 1,
"job": 0,
"namespace": 9,
"pod": 10,
"revision": 5,
"version": 3
},
"renameByName": {}
}
}
],
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 5
},
"hiddenSeries": false,
"id": 2,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": true,
"steppedLine": false,
"targets": [
{
"expr": "sum by (statuscode) (irate(http_request_total{job=~\"$job\", instance=~\"$instance\"}[1m])) ",
"interval": "",
"legendFormat": "{{statuscode}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "RPS",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:157",
"format": "reqps",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:158",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "$datasource",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 5
},
"hiddenSeries": false,
"id": 4,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"nullPointMode": "null",
"options": {
"dataLinks": []
},
"percentage": false,
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "max(http_request_duration_milliseconds{job=~\"$job\", instance=~\"$instance\", quantile=\"0.99\"})",
"interval": "",
"legendFormat": "max-99th",
"refId": "A"
},
{
"expr": "max(http_request_duration_milliseconds{job=~\"$job\", instance=~\"$instance\", quantile=\"0.9\"})",
"interval": "",
"legendFormat": "max-90th",
"refId": "B"
},
{
"expr": "sum(irate(http_request_duration_milliseconds_sum{job=~\"$job\", instance=~\"$instance\"}[$__interval])) / sum(irate(http_request_duration_milliseconds_count{job=~\"$job\", instance=~\"$instance\"}[$__interval])) ",
"interval": "",
"legendFormat": "avg",
"refId": "C"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Request Latency",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:210",
"format": "ms",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"$$hashKey": "object:211",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"schemaVersion": 25,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "prometheus",
"value": "prometheus"
},
"hide": 0,
"includeAll": false,
"label": null,
"multi": false,
"name": "datasource",
"options": [],
"query": "prometheus",
"queryValue": "",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"type": "datasource"
},
{
"allValue": ".*",
"current": {
"selected": true,
"tags": [],
"text": "All",
"value": [
"$__all"
]
},
"datasource": "$datasource",
"definition": "label_values(grafana_build_info, job)",
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "job",
"options": [],
"query": "label_values(grafana_build_info, job)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"allValue": ".*",
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": "$datasource",
"definition": "label_values(grafana_build_info, instance)",
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "instance",
"options": [],
"query": "label_values(grafana_build_info, instance)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Grafana Overview",
"uid": "6be0s85Mk",
"version": 4
}

View File

@@ -1,15 +0,0 @@
{
grafanaDashboards: {
'grafana-overview.json': (import 'dashboards/grafana-overview.json'),
},
// Helper function to ensure that we don't override other rules, by forcing
// the patching of the groups list, and not the overall rules object.
local importRules(rules) = {
groups+: std.native('parseYaml')(rules)[0].groups,
},
prometheusRules+: importRules(importstr 'rules/rules.yaml'),
prometheusAlerts+: importRules(importstr 'alerts/alerts.yaml'),
}

View File

@@ -1,7 +0,0 @@
groups:
- name: grafana_rules
rules:
# Record error rate of http requests excluding dataproxy, /ds/query and /tsdb/query requests
- record: namespace_job_handler_statuscode:http_request_total:rate5m
expr: |
sum by (namespace, job, handler, statuscode) (rate(http_request_total[5m]))

View File

@@ -1,6 +0,0 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
mixtool generate all mixin.libsonnet

View File

@@ -1 +0,0 @@
JSONNET_FMT="jsonnetfmt -n 2 --max-blank-lines 2 --string-style s --comment-style s"

View File

@@ -1,9 +0,0 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
. scripts/common.sh
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
xargs -n 1 -- ${JSONNET_FMT} -i

View File

@@ -1,13 +0,0 @@
#!/bin/bash
set -eo pipefail
cd "$(dirname "$0")"/..
. scripts/common.sh
find . -name 'vendor' -prune -o -name '*.libsonnet' -print -o -name '*.jsonnet' -print | \
while read f; do \
${JSONNET_FMT} "$f" | diff -u "$f" -; \
done
mixtool lint mixin.libsonnet

View File

@@ -2,5 +2,5 @@
"npmClient": "yarn",
"useWorkspaces": true,
"packages": ["packages/*"],
"version": "7.3.0-beta.2"
"version": "7.3.0-pre.0"
}

View File

@@ -3,7 +3,7 @@
"license": "Apache-2.0",
"private": true,
"name": "grafana",
"version": "7.3.0-beta2",
"version": "7.3.0-pre",
"repository": "github:grafana/grafana",
"scripts": {
"api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js",
@@ -45,8 +45,8 @@
"ci:test-frontend": "yarn run prettier:check && yarn run packages:typecheck && yarn run typecheck && yarn run test"
},
"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"
"whatsNewUrl": "https://grafana.com/docs/grafana/latest/guides/whats-new-in-v7-2/",
"releaseNotesUrl": "https://community.grafana.com/t/release-notes-v7-2-x/36321"
},
"husky": {
"hooks": {
@@ -269,7 +269,6 @@
"react-loadable": "5.5.0",
"react-popper": "1.3.3",
"react-redux": "7.2.0",
"react-reverse-portal": "^2.0.1",
"react-sizeme": "2.6.12",
"react-split-pane": "0.1.89",
"react-transition-group": "4.3.0",

View File

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

View File

@@ -3,14 +3,13 @@ import _ from 'lodash';
// Types
import { Field, FieldType } from '../types/dataFrame';
import { GrafanaTheme } from '../types/theme';
import { GrafanaTheme, GrafanaThemeType } from '../types/theme';
import { DecimalCount, DecimalInfo, DisplayProcessor, DisplayValue } from '../types/displayValue';
import { getValueFormat } from '../valueFormats/valueFormats';
import { getMappedValue } from '../utils/valueMappings';
import { dateTime } from '../datetime';
import { KeyValue, TimeZone } from '../types';
import { getScaleCalculator } from './scale';
import { getTestTheme } from '../utils/testdata/testTheme';
interface DisplayProcessorOptions {
field: Partial<Field>;
@@ -42,7 +41,7 @@ export function getDisplayProcessor(options?: DisplayProcessorOptions): DisplayP
const config = field.config ?? {};
// Theme should be required or we need access to default theme instance from here
const theme = options.theme ?? getTestTheme();
const theme = options.theme ?? ({ type: GrafanaThemeType.Dark } as GrafanaTheme);
let unit = config.unit;
let hasDateUnit = unit && (timeFormats[unit] || unit.startsWith('time:'));

View File

@@ -1,5 +1,4 @@
import { Field, FieldColorModeId } from '../types';
import { getTestTheme } from '../utils/testdata/testTheme';
import { Field, GrafanaThemeType, GrafanaTheme, FieldColorModeId } from '../types';
import { fieldColorModeRegistry, FieldValueColorCalculator } from './fieldColor';
describe('fieldColorModeRegistry', () => {
@@ -10,7 +9,10 @@ describe('fieldColorModeRegistry', () => {
function getCalculator(options: GetCalcOptions): FieldValueColorCalculator {
const mode = fieldColorModeRegistry.get(options.mode);
return mode.getCalculator({ state: { seriesIndex: options.seriesIndex } } as Field, getTestTheme());
return mode.getCalculator(
{ state: { seriesIndex: options.seriesIndex } } as Field,
{ type: GrafanaThemeType.Dark } as GrafanaTheme
);
}
it('Schemes should interpolate', () => {

View File

@@ -54,74 +54,20 @@ export const fieldColorModeRegistry = new Registry<FieldColorMode>(() => {
// }),
new FieldColorSchemeMode({
id: FieldColorModeId.PaletteClassic,
name: 'Classic palette',
name: 'By series / Classic palette',
//description: 'Assigns color based on series or field index',
isContinuous: false,
isByValue: false,
colors: classicColors,
}),
new FieldColorSchemeMode({
id: 'continuous-GrYlRd',
name: 'Green-Yellow-Red',
name: 'By value / Green Yellow Red (gradient)',
//description: 'Interpolated colors based value, min and max',
isContinuous: true,
isByValue: true,
colors: ['green', 'yellow', 'red'],
}),
new FieldColorSchemeMode({
id: 'continuous-BlYlRd',
name: 'Blue-Yellow-Red',
isContinuous: true,
isByValue: true,
colors: ['dark-blue', 'super-light-yellow', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-YlRd',
name: 'Yellow-Red',
isContinuous: true,
isByValue: true,
colors: ['super-light-yellow', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-BlPu',
name: 'Blue-Purple',
isContinuous: true,
isByValue: true,
colors: ['blue', 'purple'],
}),
new FieldColorSchemeMode({
id: 'continuous-YlBl',
name: 'Yellow-Blue',
isContinuous: true,
isByValue: true,
colors: ['super-light-yellow', 'dark-blue'],
}),
new FieldColorSchemeMode({
id: 'continuous-blues',
name: 'Blues',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-blue'],
}),
new FieldColorSchemeMode({
id: 'continuous-reds',
name: 'Reds',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-red'],
}),
new FieldColorSchemeMode({
id: 'continuous-greens',
name: 'Greens',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-green'],
}),
new FieldColorSchemeMode({
id: 'continuous-purples',
name: 'Purples',
isContinuous: true,
isByValue: true,
colors: ['panel-bg', 'dark-purple'],
}),
];
});

View File

@@ -2,9 +2,9 @@ import merge from 'lodash/merge';
import { getFieldDisplayValues, GetFieldDisplayValuesOptions } from './fieldDisplay';
import { toDataFrame } from '../dataframe/processDataFrame';
import { ReducerID } from '../transformations/fieldReducer';
import { GrafanaTheme } from '../types/theme';
import { MappingType } from '../types';
import { standardFieldConfigEditorRegistry } from './standardFieldConfigEditorRegistry';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('FieldDisplay', () => {
beforeAll(() => {
@@ -241,7 +241,7 @@ function createDisplayOptions(extend: Partial<GetFieldDisplayValuesOptions> = {}
overrides: [],
defaults: {},
},
theme: getTestTheme(),
theme: {} as GrafanaTheme,
};
return merge<GetFieldDisplayValuesOptions, any>(options, extend);

View File

@@ -15,6 +15,7 @@ import {
FieldConfigPropertyItem,
FieldConfigSource,
FieldType,
GrafanaTheme,
InterpolateFunction,
ThresholdsMode,
FieldColorModeId,
@@ -27,7 +28,6 @@ import { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry';
import { getFieldDisplayName } from './fieldState';
import { ArrayVector } from '../vector';
import { getDisplayProcessor } from './displayProcessor';
import { getTestTheme } from '../utils/testdata/testTheme';
const property1: any = {
id: 'custom.property1', // Match field properties
@@ -136,7 +136,7 @@ describe('applyFieldOverrides', () => {
},
replaceVariables: (value: any) => value,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
theme: {} as GrafanaTheme,
fieldConfigRegistry: new FieldConfigOptionsRegistry(),
});
@@ -199,7 +199,7 @@ describe('applyFieldOverrides', () => {
fieldConfigRegistry: customFieldRegistry,
getDataSourceSettingsByUid: undefined as any,
replaceVariables: v => v,
theme: getTestTheme(),
theme: {} as GrafanaTheme,
})[0];
const outField = processed.fields[0];
@@ -216,7 +216,7 @@ describe('applyFieldOverrides', () => {
fieldConfig: src as FieldConfigSource, // defaults + overrides
replaceVariables: (undefined as any) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
theme: (undefined as any) as GrafanaTheme,
fieldConfigRegistry: customFieldRegistry,
})[0];
const valueColumn = data.fields[1];
@@ -244,7 +244,7 @@ describe('applyFieldOverrides', () => {
fieldConfig: src as FieldConfigSource, // defaults + overrides
replaceVariables: (undefined as any) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
theme: (undefined as any) as GrafanaTheme,
autoMinMax: true,
})[0];
const valueColumn = data.fields[1];
@@ -268,7 +268,7 @@ describe('applyFieldOverrides', () => {
return value;
}) as InterpolateFunction,
getDataSourceSettingsByUid: undefined as any,
theme: getTestTheme(),
theme: (undefined as any) as GrafanaTheme,
autoMinMax: true,
fieldConfigRegistry: customFieldRegistry,
})[0];
@@ -521,7 +521,7 @@ describe('getLinksSupplier', () => {
// this is used only for internal links so isn't needed here
() => ({} as any),
{
theme: getTestTheme(),
theme: {} as GrafanaTheme,
}
);
supplier({});
@@ -568,7 +568,7 @@ describe('getLinksSupplier', () => {
// We do not need to interpolate anything for this test
(value, vars, format) => value,
uid => ({ name: 'testDS' } as any),
{ theme: getTestTheme() }
{ theme: {} as GrafanaTheme }
);
const links = supplier({ valueRowIndex: 0 });
expect(links.length).toBe(1);

View File

@@ -2,7 +2,6 @@ import { getFieldDisplayValuesProxy } from './getFieldDisplayValuesProxy';
import { applyFieldOverrides } from './fieldOverrides';
import { toDataFrame } from '../dataframe';
import { GrafanaTheme } from '../types';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('getFieldDisplayValuesProxy', () => {
const data = applyFieldOverrides({
@@ -31,7 +30,7 @@ describe('getFieldDisplayValuesProxy', () => {
replaceVariables: (val: string) => val,
getDataSourceSettingsByUid: (val: string) => ({} as any),
timeZone: 'utc',
theme: getTestTheme(),
theme: {} as GrafanaTheme,
autoMinMax: true,
})[0];

View File

@@ -1,8 +1,7 @@
import { ThresholdsMode, Field, FieldType } from '../types';
import { ThresholdsMode, Field, FieldType, GrafanaThemeType, GrafanaTheme } from '../types';
import { sortThresholds } from './thresholds';
import { ArrayVector } from '../vector/ArrayVector';
import { getScaleCalculator } from './scale';
import { getTestTheme } from '../utils/testdata/testTheme';
describe('getScaleCalculator', () => {
it('should return percent, threshold and color', () => {
@@ -19,7 +18,7 @@ describe('getScaleCalculator', () => {
values: new ArrayVector([0, 50, 100]),
};
const calc = getScaleCalculator(field, getTestTheme());
const calc = getScaleCalculator(field, { type: GrafanaThemeType.Dark } as GrafanaTheme);
expect(calc(70)).toEqual({
percent: 0.7,
threshold: thresholds[1],

View File

@@ -2,7 +2,7 @@ import { map } from 'rxjs/operators';
import { DataTransformerID } from './ids';
import { DataTransformerInfo } from '../../types/transformations';
import { DataFrame, Field, TIME_SERIES_VALUE_FIELD_NAME } from '../../types/dataFrame';
import { DataFrame, Field } from '../../types/dataFrame';
import { ArrayVector } from '../../vector';
export enum ConcatenateFrameNameMode {
@@ -73,7 +73,7 @@ export function concatenateFields(data: DataFrame[], opts: ConcatenateTransforme
} else if (opts.frameNameMode === ConcatenateFrameNameMode.Label) {
copy.labels = { ...f.labels };
copy.labels[frameNameLabel] = frame.name;
} else if (!copy.name || copy.name === TIME_SERIES_VALUE_FIELD_NAME) {
} else if (!copy.name || copy.name === 'Value') {
copy.name = frame.name;
} else {
copy.name = `${frame.name} · ${f.name}`;

View File

@@ -1,3 +1,4 @@
import { SelectableValue } from './select';
import { Observable } from 'rxjs';
/**
@@ -16,7 +17,7 @@ export enum LiveChannelScope {
/**
* @alpha -- experimental
*/
export interface LiveChannelConfig<TMessage = any, TController = any> {
export interface LiveChannelConfig<TMessage = any> {
/**
* The path definition. either static, or it may contain variables identifed with {varname}
*/
@@ -27,6 +28,11 @@ export interface LiveChannelConfig<TMessage = any, TController = any> {
*/
description?: string;
/**
* When variables exist, this list will identify each one
*/
variables?: Array<SelectableValue<string>>;
/**
* The channel keeps track of who else is connected to the same channel
*/
@@ -40,9 +46,6 @@ export interface LiveChannelConfig<TMessage = any, TController = any> {
/** convert the raw stream message into a message that should be broadcast */
processMessage?: (msg: any) => TMessage;
/** some channels are managed by an explicit interface */
getController?: () => TController;
}
export enum LiveChannelConnectionState {

View File

@@ -27,12 +27,10 @@ export enum LogLevel {
unknown = 'unknown',
}
// Used for meta information such as common labels or returned log rows in logs view in Explore
export enum LogsMetaKind {
Number,
String,
LabelsMap,
Error,
}
export enum LogsSortOrder {

View File

@@ -9,7 +9,6 @@ import {
calculateStats,
getLogLevelFromKey,
sortLogsResult,
checkLogsError,
} from './logs';
describe('getLoglevel()', () => {
@@ -353,15 +352,3 @@ describe('sortLogsResult', () => {
});
});
});
describe('checkLogsError()', () => {
const log = ({
labels: {
__error__: 'Error Message',
foo: 'boo',
},
} as any) as LogRowModel;
test('should return correct error if error is present', () => {
expect(checkLogsError(log)).toStrictEqual({ hasError: true, errorMessage: 'Error Message' });
});
});

View File

@@ -210,16 +210,3 @@ export const sortLogsResult = (logsResult: LogsModel | null, sortOrder: LogsSort
export const sortLogRows = (logRows: LogRowModel[], sortOrder: LogsSortOrder) =>
sortOrder === LogsSortOrder.Ascending ? logRows.sort(sortInAscendingOrder) : logRows.sort(sortInDescendingOrder);
// Currently supports only error condition in Loki logs
export const checkLogsError = (logRow: LogRowModel): { hasError: boolean; errorMessage?: string } => {
if (logRow.labels.__error__) {
return {
hasError: true,
errorMessage: logRow.labels.__error__,
};
}
return {
hasError: false,
};
};

View File

@@ -34,8 +34,7 @@ export type Color =
| 'dark-purple'
| 'semi-dark-purple'
| 'light-purple'
| 'super-light-purple'
| 'panel-bg';
| 'super-light-purple';
type ThemeVariants = {
dark: string;
@@ -83,8 +82,6 @@ export function buildColorsMapForTheme(theme: GrafanaTheme): Record<Color, strin
}
}
colorsMap['panel-bg'] = theme.colors.panelBg;
return colorsMap;
}
@@ -121,25 +118,7 @@ export function getColorForTheme(color: string, theme: GrafanaTheme): string {
export function getColorFromHexRgbOrName(color: string, type?: GrafanaThemeType): string {
const themeType = type ?? GrafanaThemeType.Dark;
if (themeType === GrafanaThemeType.Dark) {
const darkTheme = ({
type: themeType,
colors: {
panelBg: '#141619',
},
} as unknown) as GrafanaTheme;
return getColorForTheme(color, darkTheme);
}
const lightTheme = ({
type: themeType,
colors: {
panelBg: '#000000',
},
} as unknown) as GrafanaTheme;
return getColorForTheme(color, lightTheme);
return getColorForTheme(color, ({ type: themeType } as unknown) as GrafanaTheme);
}
const buildNamedColorsPalette = () => {

View File

@@ -1,12 +0,0 @@
import { GrafanaTheme, GrafanaThemeType } from '../../types/theme';
export function getTestTheme(type: GrafanaThemeType = GrafanaThemeType.Dark): GrafanaTheme {
return ({
type,
isDark: type === GrafanaThemeType.Dark,
isLight: type === GrafanaThemeType.Light,
colors: {
panelBg: 'white',
},
} as unknown) as GrafanaTheme;
}

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,6 @@
export * from './services';
export * from './config';
export * from './types';
export * from './measurement';
export { loadPluginCss, SystemJS, PluginCssOptions } from './utils/plugin';
export { reportMetaAnalytics } from './utils/analytics';
export { DataSourceWithBackend, HealthCheckResult, HealthStatus } from './utils/DataSourceWithBackend';

View File

@@ -1,250 +0,0 @@
import { MeasurementCollector } from './collector';
import { MeasurementAction } from './types';
describe('MeasurementCollector', () => {
it('should collect values', () => {
const collector = new MeasurementCollector();
collector.addBatch({
measurements: [
{
name: 'test',
labels: { host: 'a' },
time: 100,
values: {
f0: 0,
f1: 1,
f2: 'hello',
},
},
{
name: 'test',
labels: { host: 'b' },
time: 101,
values: {
f0: 0,
f1: 1,
f2: 'hello',
},
config: {
f2: {
unit: 'mph',
},
},
},
{
name: 'test',
time: 102,
labels: { host: 'a' }, // should append to first value
values: {
// note the missing values for f0/1
f2: 'world',
},
},
],
});
const frames = collector.getData();
expect(frames.length).toEqual(2);
expect(frames[0]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
100,
102,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f0",
"type": "number",
"values": Array [
0,
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f1",
"type": "number",
"values": Array [
1,
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f2",
"type": "string",
"values": Array [
"hello",
"world",
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "a",
},
},
},
"name": "test",
"refId": undefined,
}
`);
expect(frames[1]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
101,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "b",
},
"name": "f0",
"type": "number",
"values": Array [
0,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "b",
},
"name": "f1",
"type": "number",
"values": Array [
1,
],
},
Object {
"config": Object {
"unit": "mph",
},
"labels": Object {
"host": "b",
},
"name": "f2",
"type": "string",
"values": Array [
"hello",
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "b",
},
},
},
"name": "test",
"refId": undefined,
}
`);
collector.addBatch({
action: MeasurementAction.Replace,
measurements: [
{
name: 'test',
time: 105,
labels: { host: 'a' },
values: {
f1: 10,
},
},
],
});
const frames2 = collector.getData();
expect(frames2.length).toEqual(2);
expect(frames2[0].length).toEqual(1); // not three!
expect(frames2[0]).toMatchInlineSnapshot(`
Object {
"fields": Array [
Object {
"config": Object {},
"labels": undefined,
"name": "time",
"type": "time",
"values": Array [
105,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f0",
"type": "number",
"values": Array [
null,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f1",
"type": "number",
"values": Array [
10,
],
},
Object {
"config": Object {},
"labels": Object {
"host": "a",
},
"name": "f2",
"type": "string",
"values": Array [
null,
],
},
],
"meta": Object {
"custom": Object {
"labels": Object {
"host": "a",
},
},
},
"name": "test",
"refId": undefined,
}
`);
collector.addBatch({
action: MeasurementAction.Clear,
measurements: [],
});
expect(collector.getData().length).toEqual(0);
});
});

View File

@@ -1,209 +0,0 @@
import {
CircularDataFrame,
Labels,
formatLabels,
FieldType,
DataFrame,
matchAllLabels,
parseLabels,
CircularVector,
ArrayVector,
} from '@grafana/data';
import { Measurement, MeasurementBatch, LiveMeasurements, MeasurementsQuery, MeasurementAction } from './types';
interface MeasurementCacheConfig {
append?: 'head' | 'tail';
capacity?: number;
}
/** This is a cache scoped to a the measurement name
*
* @alpha -- experimental
*/
export class MeasurementCache {
readonly frames: Record<string, CircularDataFrame> = {}; // key is the labels
constructor(public name: string, private config: MeasurementCacheConfig) {
if (!this.config) {
this.config = {
append: 'tail',
capacity: 600, // Default capacity 10min @ 1hz
};
}
}
getFrames(match?: Labels): DataFrame[] {
const frames = Object.values(this.frames);
if (!match) {
return frames;
}
return frames.filter(f => {
return matchAllLabels(match, f.meta?.custom?.labels);
});
}
addMeasurement(m: Measurement, action: MeasurementAction): DataFrame {
const key = m.labels ? formatLabels(m.labels) : '';
let frame = this.frames[key];
if (!frame) {
frame = new CircularDataFrame(this.config);
frame.name = this.name;
frame.addField({
name: 'time',
type: FieldType.time,
});
for (const [key, value] of Object.entries(m.values)) {
frame.addFieldFor(value, key).labels = m.labels;
}
frame.meta = {
custom: {
labels: m.labels,
},
};
this.frames[key] = frame;
}
// Clear existing values
if (action === MeasurementAction.Replace) {
for (const field of frame.fields) {
(field.values as ArrayVector).buffer.length = 0; // same buffer, but reset to empty length
}
}
// Add the timestamp
frame.values['time'].add(m.time || Date.now());
// Attach field config to the current fields
if (m.config) {
for (const [key, value] of Object.entries(m.config)) {
const f = frame.fields.find(f => f.name === key);
if (f) {
f.config = value;
}
}
}
// Append all values (a row)
for (const [key, value] of Object.entries(m.values)) {
let v = frame.values[key];
if (!v) {
const f = frame.addFieldFor(value, key);
f.labels = m.labels;
v = f.values;
}
v.add(value);
}
// Make sure all fields have the same length
frame.validate();
return frame;
}
}
/**
* @alpha -- experimental
*/
export class MeasurementCollector implements LiveMeasurements {
measurements = new Map<string, MeasurementCache>();
config: MeasurementCacheConfig = {
append: 'tail',
capacity: 600, // Default capacity 10min @ 1hz
};
//------------------------------------------------------
// Public
//------------------------------------------------------
getData(query?: MeasurementsQuery): DataFrame[] {
const { name, labels, fields } = query || {};
let data: DataFrame[] = [];
if (name) {
// for now we only match exact names
const m = this.measurements.get(name);
if (m) {
data = m.getFrames(labels);
}
} else {
for (const f of this.measurements.values()) {
data.push.apply(data, f.getFrames(labels));
}
}
if (fields && fields.length) {
let filtered: DataFrame[] = [];
for (const frame of data) {
const match = frame.fields.filter(f => fields.includes(f.name));
if (match.length > 0) {
filtered.push({ ...frame, fields: match }); // Copy the frame with fewer fields
}
}
}
return data;
}
getDistinctNames(): string[] {
return Object.keys(this.measurements);
}
getDistinctLabels(name: string): Labels[] {
const m = this.measurements.get(name);
if (m) {
return Object.keys(m.frames).map(k => parseLabels(k));
}
return [];
}
setCapacity(size: number) {
this.config.capacity = size;
// Now update all the circular buffers
for (const wrap of this.measurements.values()) {
for (const frame of Object.values(wrap.frames)) {
for (const field of frame.fields) {
(field.values as CircularVector).setCapacity(size);
}
}
}
}
getCapacity() {
return this.config.capacity!;
}
clear() {
this.measurements.clear();
}
//------------------------------------------------------
// Collector
//------------------------------------------------------
addBatch = (batch: MeasurementBatch) => {
let action = batch.action ?? MeasurementAction.Append;
if (action === MeasurementAction.Clear) {
this.measurements.clear();
action = MeasurementAction.Append;
}
// Change the local buffer size
if (batch.capacity && batch.capacity !== this.config.capacity) {
this.setCapacity(batch.capacity);
}
for (const measure of batch.measurements) {
const name = measure.name || '';
let m = this.measurements.get(name);
if (!m) {
m = new MeasurementCache(name, this.config);
this.measurements.set(name, m);
}
if (measure.values) {
m.addMeasurement(measure, action);
} else {
console.log('invalid measurement', measure);
}
}
return this;
};
}

View File

@@ -1,3 +0,0 @@
export * from './types';
export * from './collector';
export * from './query';

View File

@@ -1,77 +0,0 @@
import {
DataQueryResponse,
isLiveChannelMessageEvent,
isLiveChannelStatusEvent,
isValidLiveChannelAddress,
LiveChannelAddress,
LoadingState,
} from '@grafana/data';
import { LiveMeasurements, MeasurementsQuery } from './types';
import { getGrafanaLiveSrv } from '../services/live';
import { Observable, of } from 'rxjs';
import { map } from 'rxjs/operators';
/**
* @alpha -- experimental
*/
export function getLiveMeasurements(addr: LiveChannelAddress): LiveMeasurements | undefined {
if (!isValidLiveChannelAddress(addr)) {
return undefined;
}
const live = getGrafanaLiveSrv();
if (!live) {
return undefined;
}
const channel = live.getChannel<LiveMeasurements>(addr);
const getController = channel?.config?.getController;
return getController ? getController() : undefined;
}
/**
* When you know the stream will be managed measurements
*
* @alpha -- experimental
*/
export function getLiveMeasurementsObserver(
addr: LiveChannelAddress,
requestId: string,
query?: MeasurementsQuery
): Observable<DataQueryResponse> {
const rsp: DataQueryResponse = { data: [] };
if (!addr || !addr.path) {
return of(rsp); // Address not configured yet
}
const live = getGrafanaLiveSrv();
if (!live) {
// This will only happen with the feature flag is not enabled
rsp.error = { message: 'Grafana live is not initalized' };
return of(rsp);
}
rsp.key = requestId;
return live
.getChannel<LiveMeasurements>(addr)
.getStream()
.pipe(
map(evt => {
if (isLiveChannelMessageEvent(evt)) {
rsp.data = evt.message.getData(query);
if (!rsp.data.length) {
// ?? skip when data is empty ???
}
delete rsp.error;
rsp.state = LoadingState.Streaming;
} else if (isLiveChannelStatusEvent(evt)) {
if (evt.error != null) {
rsp.error = rsp.error;
rsp.state = LoadingState.Error;
}
}
return { ...rsp }; // send event on all status messages
})
);
}

View File

@@ -1,73 +0,0 @@
import { DataFrame, Labels, FieldConfig } from '@grafana/data';
/**
* the raw channel events are batches of Measurements
*
* @alpha -- experimental
*/
export interface Measurement {
name: string;
time?: number; // Missing will use the browser time
values: Record<string, any>;
config?: Record<string, FieldConfig>;
labels?: Labels;
}
/**
* @alpha -- experimental
*/
export enum MeasurementAction {
/** The measurements will be added to the client buffer */
Append = 'append',
/** The measurements will replace the client buffer */
Replace = 'replace',
/** All measurements will be removed from the client buffer before processing */
Clear = 'clear',
}
/**
* List of Measurements sent in a batch
*
* @alpha -- experimental
*/
export interface MeasurementBatch {
/**
* The default action is to append values to the client buffer
*/
action?: MeasurementAction;
/**
* List of measurements to process
*/
measurements: Measurement[];
/**
* This will set the capacity on the client buffer for everything
* in the measurement channel
*/
capacity?: number;
}
/**
* @alpha -- experimental
*/
export interface MeasurementsQuery {
name?: string;
labels?: Labels;
fields?: string[]; // only include the fields with these names
}
/**
* Channels that receive Measurements can collect them into frames
*
* @alpha -- experimental
*/
export interface LiveMeasurements {
getData(query?: MeasurementsQuery): DataFrame[];
getDistinctNames(): string[];
getDistinctLabels(name: string): Labels[];
setCapacity(size: number): void;
getCapacity(): number;
}

View File

@@ -6,9 +6,6 @@
*/
import { UrlQueryMap } from '@grafana/data';
/**
* @public
*/
export interface LocationUpdate {
/**
* Target path where you automatically wants to navigate the user.

View File

@@ -39,7 +39,7 @@ export const setGrafanaLiveSrv = (instance: GrafanaLiveSrv) => {
};
/**
* Used to retrieve the GrafanaLiveSrv that allows you to subscribe to
* Used to retrieve the {@link GrafanaLiveSrv} that allows you to subscribe to
* server side events and streams
*
* @alpha -- experimental

View File

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

View File

@@ -2,7 +2,7 @@
"author": "Grafana Labs",
"license": "Apache-2.0",
"name": "@grafana/ui",
"version": "7.3.0-beta.2",
"version": "7.3.0-pre.0",
"description": "Grafana Components Library",
"keywords": [
"grafana",
@@ -27,15 +27,15 @@
},
"dependencies": {
"@emotion/core": "^10.0.27",
"@grafana/data": "7.3.0-beta.2",
"@grafana/e2e-selectors": "7.3.0-beta.2",
"@grafana/data": "7.3.0-pre.0",
"@grafana/e2e-selectors": "7.3.0-pre.0",
"@grafana/slate-react": "0.22.9-grafana",
"@grafana/tsconfig": "^1.0.0-rc1",
"@iconscout/react-unicons": "1.1.4",
"@torkelo/react-select": "3.0.8",
"@types/hoist-non-react-statics": "3.3.1",
"@types/react-beautiful-dnd": "12.1.2",
"@types/react-color": "3.0.1",
"@types/hoist-non-react-statics": "3.3.1",
"@types/react-select": "3.0.8",
"@types/react-table": "7.0.12",
"@types/slate": "0.47.1",
@@ -43,9 +43,10 @@
"bizcharts": "^3.5.8",
"classnames": "2.2.6",
"d3": "5.15.0",
"emotion": "10.0.27",
"hoist-non-react-statics": "3.3.2",
"immutable": "3.8.2",
"emotion": "10.0.27",
"immutable": "3.8.2",
"jquery": "3.5.1",
"lodash": "4.17.19",
"moment": "2.24.0",

View File

@@ -9,7 +9,6 @@ import { calculateFontSize } from '../../utils/measureText';
// Types
import { BigValueColorMode, Props, BigValueJustifyMode, BigValueTextMode } from './BigValue';
import { getTextColorForBackground } from '../../utils';
const LINE_HEIGHT = 1.2;
const MAX_TITLE_SIZE = 30;
@@ -52,7 +51,7 @@ export abstract class BigValueLayout {
};
if (this.props.colorMode === BigValueColorMode.Background) {
styles.color = getTextColorForBackground(this.valueColor);
styles.color = 'white';
}
return styles;
@@ -70,7 +69,7 @@ export abstract class BigValueLayout {
styles.color = this.valueColor;
break;
case BigValueColorMode.Background:
styles.color = getTextColorForBackground(this.valueColor);
styles.color = 'white';
}
return styles;

View File

@@ -30,7 +30,7 @@ exports[`BigValue Render with basic options should render 1`] = `
<FormattedDisplayValue
style={
Object {
"color": "#202226",
"color": "white",
"fontSize": 230,
"fontWeight": 500,
"lineHeight": 1.2,

View File

@@ -45,12 +45,6 @@ describe('LogDetails', () => {
expect(wrapper.text().includes('key1label1key2label2')).toBe(true);
});
});
describe('when log row has error', () => {
it('should not render log level border', () => {
const wrapper = setup({ hasError: true }, undefined);
expect(wrapper.find({ 'aria-label': 'Log level' }).html()).not.toContain('logs-row__level');
});
});
describe('when row entry has parsable fields', () => {
it('should render heading ', () => {
const wrapper = setup(undefined, { entry: 'test=successful' });

View File

@@ -28,7 +28,6 @@ export interface Props extends Themeable {
showDuplicates: boolean;
getRows: () => LogRowModel[];
className?: string;
hasError?: boolean;
onMouseEnter?: () => void;
onMouseLeave?: () => void;
onClickFilterLabel?: (key: string, value: string) => void;
@@ -71,7 +70,6 @@ class UnThemedLogDetails extends PureComponent<Props> {
const {
row,
theme,
hasError,
onClickFilterOutLabel,
onClickFilterLabel,
getRows,
@@ -90,8 +88,6 @@ class UnThemedLogDetails extends PureComponent<Props> {
const labelsAvailable = Object.keys(labels).length > 0;
const fields = getAllFields(row, getFieldLinks);
const parsedFieldsAvailable = fields && fields.length > 0;
// If logs with error, we are not showing the level color
const levelClassName = cx(!hasError && [style.logsRowLevel, styles.logsRowLevelDetails]);
return (
<tr
@@ -100,7 +96,7 @@ class UnThemedLogDetails extends PureComponent<Props> {
onMouseLeave={onMouseLeave}
>
{showDuplicates && <td />}
<td className={levelClassName} aria-label="Log level" />
<td className={cx(style.logsRowLevel, styles.logsRowLevelDetails)} />
<td colSpan={4}>
<div className={style.logDetailsContainer}>
<table className={style.logDetailsTable}>

View File

@@ -8,10 +8,8 @@ import {
DataQueryResponse,
GrafanaTheme,
dateTimeFormat,
checkLogsError,
} from '@grafana/data';
import { Icon } from '../Icon/Icon';
import { Tooltip } from '../Tooltip/Tooltip';
import { cx, css } from 'emotion';
import {
@@ -74,10 +72,6 @@ const getStyles = stylesFactory((theme: GrafanaTheme) => {
label: hoverBackground;
background-color: ${bgColor};
`,
errorLogRow: css`
label: erroredLogRow;
color: ${theme.colors.textWeak};
`,
};
});
/**
@@ -166,27 +160,22 @@ class UnThemedLogRow extends PureComponent<Props, State> {
const { showDetails, showContext, hasHoverBackground } = this.state;
const style = getLogRowStyles(theme, row.logLevel);
const styles = getStyles(theme);
const { errorMessage, hasError } = checkLogsError(row);
const logRowBackground = cx(style.logsRow, {
[styles.hoverBackground]: hasHoverBackground,
[styles.errorLogRow]: hasError,
});
const hoverBackground = cx(style.logsRow, { [styles.hoverBackground]: hasHoverBackground });
return (
<>
<tr className={logRowBackground} onClick={this.toggleDetails}>
<tr
className={hoverBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
onClick={this.toggleDetails}
>
{showDuplicates && (
<td className={style.logsRowDuplicates}>
{row.duplicates && row.duplicates > 0 ? `${row.duplicates + 1}x` : null}
</td>
)}
<td className={cx({ [style.logsRowLevel]: !hasError })}>
{hasError && (
<Tooltip content={`Error: ${errorMessage}`} placement="right" theme="error">
<Icon className={style.logIconError} name="exclamation-triangle" size="sm" />
</Tooltip>
)}
</td>
<td className={style.logsRowLevel} />
{!allowDetails && (
<td title={showDetails ? 'Hide log details' : 'See log details'} className={style.logsRowToggleDetails}>
<Icon className={styles.topVerticalAlign} name={showDetails ? 'angle-down' : 'angle-right'} />
@@ -218,7 +207,7 @@ class UnThemedLogRow extends PureComponent<Props, State> {
</tr>
{this.state.showDetails && (
<LogDetails
className={logRowBackground}
className={hoverBackground}
onMouseEnter={this.addHoverBackground}
onMouseLeave={this.clearHoverBackground}
showDuplicates={showDuplicates}
@@ -229,7 +218,6 @@ class UnThemedLogRow extends PureComponent<Props, State> {
onClickHideParsedField={onClickHideParsedField}
getRows={getRows}
row={row}
hasError={hasError}
showParsedFields={showParsedFields}
/>
)}

View File

@@ -9,7 +9,6 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
let logColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const borderColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.gray2 }, theme.type);
const bgColor = selectThemeVariant({ light: theme.palette.gray5, dark: theme.palette.dark4 }, theme.type);
const hoverBgColor = selectThemeVariant({ light: theme.palette.gray7, dark: theme.palette.dark2 }, theme.type);
const context = css`
label: context;
visibility: hidden;
@@ -93,7 +92,7 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
}
&:hover {
background: ${hoverBgColor};
background: ${theme.colors.bodyBg};
}
`,
logsRowDuplicates: css`
@@ -117,10 +116,6 @@ export const getLogRowStyles = stylesFactory((theme: GrafanaTheme, logLevel?: Lo
background-color: ${logColor};
}
`,
logIconError: css`
color: ${theme.palette.red};
margin-left: -5px;
`,
logsRowToggleDetails: css`
label: logs-row-toggle-details__level;
position: relative;

View File

@@ -23,11 +23,9 @@ export const FieldColorEditor: React.FC<FieldConfigEditorProps<FieldColor | unde
const styles = useStyles(getStyles);
const options = fieldColorModeRegistry.list().map(mode => {
let suffix = mode.isByValue ? ' (by value)' : '';
return {
value: mode.id,
label: `${mode.name}${suffix}`,
label: mode.name,
description: mode.description,
isContinuous: mode.isContinuous,
isByValue: mode.isByValue,

View File

@@ -9,7 +9,6 @@ import memoizeOne from 'memoize-one';
import { GrafanaTheme } from '@grafana/data';
import { withTheme } from '../../themes';
// Default intervals used in the refresh picker component
export const defaultIntervals = ['5s', '10s', '30s', '1m', '5m', '15m', '30m', '1h', '2h', '1d'];
const getStyles = memoizeOne((theme: GrafanaTheme) => {

View File

@@ -131,21 +131,3 @@ export const CustomLabelField = () => {
</SegmentFrame>
);
};
export const HtmlAttributes = () => {
const [value, setValue] = useState<any>(options[0]);
return (
<SegmentFrame options={options}>
<Segment
data-testid="segment-test"
id="segment-id"
value={value}
options={groupedOptions}
onChange={({ value }) => {
setValue(value);
action('Segment value changed')(value);
}}
/>
</SegmentFrame>
);
};

View File

@@ -1,10 +1,10 @@
import React, { HTMLProps } from 'react';
import React from 'react';
import { cx } from 'emotion';
import _ from 'lodash';
import { SelectableValue } from '@grafana/data';
import { SegmentSelect, useExpandableLabel, SegmentProps } from './';
export interface SegmentSyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
export interface SegmentSyncProps<T> extends SegmentProps<T> {
value?: T | SelectableValue<T>;
onChange: (item: SelectableValue<T>) => void;
options: Array<SelectableValue<T>>;
@@ -18,7 +18,6 @@ export function Segment<T>({
className,
allowCustomValue,
placeholder,
...rest
}: React.PropsWithChildren<SegmentSyncProps<T>>) {
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
@@ -39,7 +38,6 @@ export function Segment<T>({
return (
<SegmentSelect
{...rest}
value={value && !_.isObject(value) ? { value } : value}
options={options}
width={width}

View File

@@ -123,21 +123,3 @@ export const CustomLabel = () => {
</SegmentFrame>
);
};
export const HtmlAttributes = () => {
const [value, setValue] = useState<any>(options[0]);
return (
<SegmentFrame loadOptions={() => loadOptions(options)}>
<SegmentAsync
data-testid="segment-async-test"
id="segment-async"
value={value}
loadOptions={() => loadOptions(options)}
onChange={item => {
setValue(item);
action('Segment value changed')(item.value);
}}
/>
</SegmentFrame>
);
};

View File

@@ -1,4 +1,4 @@
import React, { HTMLProps } from 'react';
import React from 'react';
import { cx } from 'emotion';
import _ from 'lodash';
import { SegmentSelect } from './SegmentSelect';
@@ -7,7 +7,7 @@ import { useExpandableLabel, SegmentProps } from '.';
import { useAsyncFn } from 'react-use';
import { AsyncState } from 'react-use/lib/useAsync';
export interface SegmentAsyncProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
export interface SegmentAsyncProps<T> extends SegmentProps<T> {
value?: T | SelectableValue<T>;
loadOptions: (query?: string) => Promise<Array<SelectableValue<T>>>;
onChange: (item: SelectableValue<T>) => void;
@@ -21,7 +21,6 @@ export function SegmentAsync<T>({
className,
allowCustomValue,
placeholder,
...rest
}: React.PropsWithChildren<SegmentAsyncProps<T>>) {
const [state, fetchOptions] = useAsyncFn(loadOptions, [loadOptions]);
const [Label, width, expanded, setExpanded] = useExpandableLabel(false);
@@ -44,7 +43,6 @@ export function SegmentAsync<T>({
return (
<SegmentSelect
{...rest}
value={value && !_.isObject(value) ? { value } : value}
options={state.value ?? []}
width={width}

View File

@@ -49,23 +49,6 @@ export const BasicInputWithPlaceholder = () => {
);
};
export const BasicInputWithHtmlAttributes = () => {
const [value, setValue] = useState('some text');
return (
<SegmentFrame>
<SegmentInput
data-testid="segment-input-test"
id="segment-input"
value={value}
onChange={text => {
setValue(text as string);
action('Segment value changed')(text);
}}
/>
</SegmentFrame>
);
};
const InputComponent = ({ initialValue }: any) => {
const [value, setValue] = useState(initialValue);
return (

View File

@@ -1,10 +1,10 @@
import React, { HTMLProps, useRef, useState } from 'react';
import React, { useRef, useState } from 'react';
import { cx, css } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { measureText } from '../../utils/measureText';
import { useExpandableLabel, SegmentProps } from '.';
export interface SegmentInputProps<T> extends SegmentProps<T>, Omit<HTMLProps<HTMLInputElement>, 'value' | 'onChange'> {
export interface SegmentInputProps<T> extends SegmentProps<T> {
value: string | number;
onChange: (text: string | number) => void;
autofocus?: boolean;
@@ -19,7 +19,6 @@ export function SegmentInput<T>({
className,
placeholder,
autofocus = false,
...rest
}: React.PropsWithChildren<SegmentInputProps<T>>) {
const ref = useRef<HTMLInputElement>(null);
const [value, setValue] = useState<number | string>(initialValue);
@@ -51,7 +50,6 @@ export function SegmentInput<T>({
return (
<input
{...rest}
ref={ref}
autoFocus
className={cx(`gf-form gf-form-input`, inputWidthStyle)}

View File

@@ -1,10 +1,10 @@
import React, { HTMLProps, useRef } from 'react';
import React, { useRef } from 'react';
import { css, cx } from 'emotion';
import useClickAway from 'react-use/lib/useClickAway';
import { SelectableValue } from '@grafana/data';
import { Select } from '../Forms/Legacy/Select/Select';
export interface Props<T> extends Omit<HTMLProps<HTMLDivElement>, 'value' | 'onChange'> {
export interface Props<T> {
value?: SelectableValue<T>;
options: Array<SelectableValue<T>>;
onChange: (item: SelectableValue<T>) => void;
@@ -22,7 +22,6 @@ export function SegmentSelect<T>({
width,
noOptionsMessage = '',
allowCustomValue = false,
...rest
}: React.PropsWithChildren<Props<T>>) {
const ref = useRef<HTMLDivElement>(null);
@@ -40,7 +39,7 @@ export function SegmentSelect<T>({
});
return (
<div {...rest} ref={ref}>
<div ref={ref}>
<Select
className={cx(
css`

View File

@@ -5,7 +5,6 @@ import { TableCellDisplayMode, TableCellProps } from './types';
import tinycolor from 'tinycolor2';
import { TableStyles } from './styles';
import { FilterActions } from './FilterActions';
import { getTextColorForBackground } from '../../utils';
export const DefaultCell: FC<TableCellProps> = props => {
const { field, cell, tableStyles, row, cellProps } = props;
@@ -66,12 +65,7 @@ function getCellStyle(tableStyles: TableStyles, field: Field, displayValue: Disp
.spin(5)
.toRgbString();
const textColor = getTextColorForBackground(displayValue.color!);
return tableStyles.buildCellContainerStyle(
textColor,
`linear-gradient(120deg, ${bgColor2}, ${displayValue.color})`
);
return tableStyles.buildCellContainerStyle('white', `linear-gradient(120deg, ${bgColor2}, ${displayValue.color})`);
}
return tableStyles.cellContainer;

View File

@@ -22,7 +22,7 @@ export { EmptySearchResult } from './EmptySearchResult/EmptySearchResult';
export { PieChart, PieChartType } from './PieChart/PieChart';
export { UnitPicker } from './UnitPicker/UnitPicker';
export { StatsPicker } from './StatsPicker/StatsPicker';
export { RefreshPicker, defaultIntervals } from './RefreshPicker/RefreshPicker';
export { RefreshPicker } from './RefreshPicker/RefreshPicker';
export { TimeRangePicker } from './TimePicker/TimeRangePicker';
export { TimeOfDayPicker } from './TimePicker/TimeOfDayPicker';
export { TimeZonePicker } from './TimePicker/TimeZonePicker';

View File

@@ -12,8 +12,6 @@ export type IconName =
| 'filter'
| 'angle-left'
| 'angle-right'
| 'angle-double-right'
| 'angle-double-down'
| 'pen'
| 'envelope'
| 'percentage'
@@ -130,8 +128,6 @@ export const getAvailableIcons = (): IconName[] => [
'filter',
'angle-left',
'angle-right',
'angle-double-right',
'angle-double-down',
'pen',
'envelope',
'percentage',

View File

@@ -4,8 +4,6 @@ import flattenDeep from 'lodash/flattenDeep';
import chunk from 'lodash/chunk';
import zip from 'lodash/zip';
import tinycolor from 'tinycolor2';
import lightTheme from '../themes/light';
import darkTheme from '../themes/dark';
export const PALETTE_ROWS = 4;
export const PALETTE_COLUMNS = 14;
@@ -95,9 +93,4 @@ function hslToHex(color: any) {
return tinycolor(color).toHexString();
}
export function getTextColorForBackground(color: string) {
const b = tinycolor(color).getBrightness();
return b > 150 ? lightTheme.colors.textStrong : darkTheme.colors.textStrong;
}
export let sortedColors = sortColorsByHue(colors);

View File

@@ -1,9 +1,6 @@
let canvas: HTMLCanvasElement | null = null;
const cache: Record<string, TextMetrics> = {};
/**
* @beta
*/
export function measureText(text: string, fontSize: number): TextMetrics {
const fontStyle = `${fontSize}px 'Roboto'`;
const cacheKey = text + fontStyle;
@@ -29,9 +26,6 @@ export function measureText(text: string, fontSize: number): TextMetrics {
return metrics;
}
/**
* @beta
*/
export function calculateFontSize(text: string, width: number, height: number, lineHeight: number, maxSize?: number) {
// calculate width in 14px
const textSize = measureText(text, 14);

View File

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

View File

@@ -15,7 +15,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { TimelineCollapser } from './TimelineCollapser';
import TimelineCollapser from './TimelineCollapser';
describe('<TimelineCollapser>', () => {
it('renders without exploding', () => {

View File

@@ -12,13 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { IconButton } from '@grafana/ui';
import { css } from 'emotion';
import React from 'react';
import { css } from 'emotion';
import cx from 'classnames';
import { UITooltip, UIIcon } from '../../uiElementsContext';
import { createStyle } from '../../Theme';
const getStyles = createStyle(() => {
return {
TraceTimelineViewer: css`
border-bottom: 1px solid #bbb;
`,
TimelineCollapser: css`
align-items: center;
display: flex;
@@ -26,6 +31,21 @@ const getStyles = createStyle(() => {
justify-content: center;
margin-right: 0.5rem;
`,
tooltipTitle: css`
white-space: pre;
`,
btn: css`
color: rgba(0, 0, 0, 0.5);
cursor: pointer;
margin-right: 0.3rem;
padding: 0.1rem;
&:hover {
color: rgba(0, 0, 0, 0.85);
}
`,
btnExpanded: css`
transform: rotate(90deg);
`,
};
});
@@ -36,27 +56,40 @@ type CollapserProps = {
onExpandAll: () => void;
};
export function TimelineCollapser(props: CollapserProps) {
const { onExpandAll, onExpandOne, onCollapseAll, onCollapseOne } = props;
function getTitle(value: string) {
const styles = getStyles();
return (
<div className={styles.TimelineCollapser} data-test-id="TimelineCollapser">
<IconButton tooltip="Expand +1" size="xl" tooltipPlacement="top" name="angle-down" onClick={onExpandOne} />
<IconButton tooltip="Collapse +1" size="xl" tooltipPlacement="top" name="angle-right" onClick={onCollapseOne} />
<IconButton
tooltip="Expand All"
size="xl"
tooltipPlacement="top"
name="angle-double-down"
onClick={onExpandAll}
/>
<IconButton
tooltip="Collapse All"
size="xl"
tooltipPlacement="top"
name="angle-double-right"
onClick={onCollapseAll}
/>
</div>
);
return <span className={styles.tooltipTitle}>{value}</span>;
}
export default class TimelineCollapser extends React.PureComponent<CollapserProps> {
containerRef: React.RefObject<HTMLDivElement>;
constructor(props: CollapserProps) {
super(props);
this.containerRef = React.createRef();
}
// TODO: Something less hacky than createElement to help TypeScript / AntD
getContainer = () => this.containerRef.current || document.createElement('div');
render() {
const { onExpandAll, onExpandOne, onCollapseAll, onCollapseOne } = this.props;
const styles = getStyles();
return (
<div className={styles.TimelineCollapser} ref={this.containerRef} data-test-id="TimelineCollapser">
<UITooltip title={getTitle('Expand +1')} getPopupContainer={this.getContainer}>
<UIIcon type="right" onClick={onExpandOne} className={cx(styles.btn, styles.btnExpanded)} />
</UITooltip>
<UITooltip title={getTitle('Collapse +1')} getPopupContainer={this.getContainer}>
<UIIcon type="right" onClick={onCollapseOne} className={styles.btn} />
</UITooltip>
<UITooltip title={getTitle('Expand All')} getPopupContainer={this.getContainer}>
<UIIcon type="double-right" onClick={onExpandAll} className={cx(styles.btn, styles.btnExpanded)} />
</UITooltip>
<UITooltip title={getTitle('Collapse All')} getPopupContainer={this.getContainer}>
<UIIcon type="double-right" onClick={onCollapseAll} className={styles.btn} />
</UITooltip>
</div>
);
}
}

View File

@@ -19,7 +19,7 @@ import TimelineHeaderRow from './TimelineHeaderRow';
import TimelineColumnResizer from './TimelineColumnResizer';
import TimelineViewingLayer from './TimelineViewingLayer';
import Ticks from '../Ticks';
import { TimelineCollapser } from './TimelineCollapser';
import TimelineCollapser from './TimelineCollapser';
describe('<TimelineHeaderRow>', () => {
let wrapper;

View File

@@ -16,7 +16,7 @@ import * as React from 'react';
import { css } from 'emotion';
import cx from 'classnames';
import { TimelineCollapser } from './TimelineCollapser';
import TimelineCollapser from './TimelineCollapser';
import TimelineColumnResizer from './TimelineColumnResizer';
import TimelineViewingLayer from './TimelineViewingLayer';
import Ticks from '../Ticks';

View File

@@ -21,7 +21,7 @@ type URLValidationError struct {
// Error returns the error message.
func (e URLValidationError) Error() string {
return fmt.Sprintf("validation of data source URL %q failed: %s", e.URL, e.Err.Error())
return fmt.Sprintf("Validation of data source URL %q failed: %s", e.URL, e.Err.Error())
}
// Unwrap returns the wrapped error.

View File

@@ -284,12 +284,12 @@ func tryGetEncryptedCookie(ctx *models.ReqContext, cookieName string) (string, b
return "", false
}
decryptedError, err := util.Decrypt(decoded, setting.SecretKey)
decryptedError, err := util.Decrypt(decoded)
return string(decryptedError), err == nil
}
func (hs *HTTPServer) trySetEncryptedCookie(ctx *models.ReqContext, cookieName string, value string, maxAge int) error {
encryptedError, err := util.Encrypt([]byte(value), setting.SecretKey)
encryptedError, err := util.Encrypt([]byte(value))
if err != nil {
return err
}

View File

@@ -109,7 +109,7 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
setting.OAuthAutoLogin = true
oauthError := errors.New("User not a member of one of the required organizations")
encryptedError, _ := util.Encrypt([]byte(oauthError.Error()), setting.SecretKey)
encryptedError, _ := util.Encrypt([]byte(oauthError.Error()))
expCookiePath := "/"
if len(setting.AppSubUrl) > 0 {
expCookiePath = setting.AppSubUrl

View File

@@ -75,7 +75,7 @@ func TestDSRouteRule(t *testing.T) {
}
setting.SecretKey = "password" //nolint:goconst
key, _ := util.Encrypt([]byte("123"), "password")
key, _ := util.Encrypt([]byte("123"))
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
@@ -189,7 +189,7 @@ func TestDSRouteRule(t *testing.T) {
}
setting.SecretKey = "password"
key, _ := util.Encrypt([]byte("123"), "password")
key, _ := util.Encrypt([]byte("123"))
ds := &models.DataSource{
JsonData: simplejson.NewFromAny(map[string]interface{}{
@@ -585,7 +585,7 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
plugin := plugins.DataSourcePlugin{}
_, err := NewDataSourceProxy(&ds, &plugin, &ctx, "api/method", &cfg)
require.Error(t, err)
assert.True(t, strings.HasPrefix(err.Error(), `validation of data source URL "://host/root" failed`))
assert.True(t, strings.HasPrefix(err.Error(), `Validation of data source URL "://host/root" failed`))
}
func TestNewDataSourceProxy_ProtocolLessURL(t *testing.T) {

View File

@@ -23,7 +23,7 @@ func TestPluginProxy(t *testing.T) {
setting.SecretKey = "password"
bus.AddHandler("test", func(query *models.GetPluginSettingByIdQuery) error {
key, err := util.Encrypt([]byte("123"), "password")
key, err := util.Encrypt([]byte("123"))
if err != nil {
return err
}

View File

@@ -2,7 +2,6 @@ package api
import (
"errors"
"fmt"
"path"
"strings"
@@ -28,7 +27,7 @@ func (hs *HTTPServer) createShortURL(c *models.ReqContext, cmd dtos.CreateShortU
return Error(500, "Failed to create short URL", err)
}
url := fmt.Sprintf("%s/goto/%s", strings.TrimSuffix(setting.AppUrl, "/"), shortURL.Uid)
url := path.Join(setting.AppUrl, "goto", shortURL.Uid)
c.Logger.Debug("Created short URL", "url", url)
dto := dtos.ShortURL{

View File

@@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
)
@@ -109,7 +108,7 @@ func updateRows(session *sqlstore.DBSession, rows []map[string][]byte, passwordF
}
func getUpdatedSecureJSONData(row map[string][]byte, passwordFieldName string) (map[string]interface{}, error) {
encryptedPassword, err := util.Encrypt(row[passwordFieldName], setting.SecretKey)
encryptedPassword, err := util.Encrypt(row[passwordFieldName])
if err != nil {
return nil, err
}

View File

@@ -1,16 +1,15 @@
package securedata
import (
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
type SecureData []byte
func Encrypt(data []byte) (SecureData, error) {
return util.Encrypt(data, setting.SecretKey)
return util.Encrypt(data)
}
func (s SecureData) Decrypt() ([]byte, error) {
return util.Decrypt(s, setting.SecretKey)
return util.Decrypt(s)
}

View File

@@ -2,7 +2,6 @@ package securejsondata
import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -14,7 +13,7 @@ type SecureJsonData map[string][]byte
// is true if the key exists and false if not.
func (s SecureJsonData) DecryptedValue(key string) (string, bool) {
if value, ok := s[key]; ok {
decryptedData, err := util.Decrypt(value, setting.SecretKey)
decryptedData, err := util.Decrypt(value)
if err != nil {
log.Fatalf(4, err.Error())
}
@@ -28,7 +27,7 @@ func (s SecureJsonData) DecryptedValue(key string) (string, bool) {
func (s SecureJsonData) Decrypt() map[string]string {
decrypted := make(map[string]string)
for key, data := range s {
decryptedData, err := util.Decrypt(data, setting.SecretKey)
decryptedData, err := util.Decrypt(data)
if err != nil {
log.Fatalf(4, err.Error())
}
@@ -38,11 +37,11 @@ func (s SecureJsonData) Decrypt() map[string]string {
return decrypted
}
// GetEncryptedJsonData returns map where all keys are encrypted.
// GetEncryptedJsonData returns map where all values are encrypted.
func GetEncryptedJsonData(sjd map[string]string) SecureJsonData {
encrypted := make(SecureJsonData)
for key, data := range sjd {
encryptedData, err := util.Encrypt([]byte(data), setting.SecretKey)
encryptedData, err := util.Encrypt([]byte(data))
if err != nil {
log.Fatalf(4, err.Error())
}

View File

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

View File

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

View File

@@ -139,7 +139,7 @@ type UpdatePluginDashboardError struct {
}
func (d UpdatePluginDashboardError) Error() string {
return "Dashboard belongs to plugin"
return "Dashboard belong to plugin"
}
const (

19
pkg/models/data_key.go Normal file
View File

@@ -0,0 +1,19 @@
package models
import (
"errors"
"time"
)
var (
ErrDataKeyNotFound = errors.New("data key not found")
)
type DataKey struct {
Active bool `json:"active"`
Name string `json:"name"`
Provider string `json:"provider"`
EncryptedData []byte `json:"-"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
}

View File

@@ -54,7 +54,7 @@ func TestDataSourceProxyCache(t *testing.T) {
json := simplejson.New()
json.Set("tlsAuthWithCACert", true)
tlsCaCert, err := util.Encrypt([]byte(caCert), "password")
tlsCaCert, err := util.Encrypt([]byte(caCert))
So(err, ShouldBeNil)
ds := DataSource{
Id: 1,
@@ -96,9 +96,9 @@ func TestDataSourceProxyCache(t *testing.T) {
json := simplejson.New()
json.Set("tlsAuth", true)
tlsClientCert, err := util.Encrypt([]byte(clientCert), "password")
tlsClientCert, err := util.Encrypt([]byte(clientCert))
So(err, ShouldBeNil)
tlsClientKey, err := util.Encrypt([]byte(clientKey), "password")
tlsClientKey, err := util.Encrypt([]byte(clientKey))
So(err, ShouldBeNil)
ds := DataSource{
@@ -130,7 +130,7 @@ func TestDataSourceProxyCache(t *testing.T) {
json := simplejson.New()
json.Set("tlsAuthWithCACert", true)
tlsCaCert, err := util.Encrypt([]byte(caCert), "password")
tlsCaCert, err := util.Encrypt([]byte(caCert))
So(err, ShouldBeNil)
ds := DataSource{
@@ -179,7 +179,7 @@ func TestDataSourceProxyCache(t *testing.T) {
json := simplejson.NewFromAny(map[string]interface{}{
"httpHeaderName1": "Authorization",
})
encryptedData, err := util.Encrypt([]byte(`Bearer xf5yhfkpsnmgo`), setting.SecretKey)
encryptedData, err := util.Encrypt([]byte(`Bearer xf5yhfkpsnmgo`))
if err != nil {
log.Fatal(err.Error())
}

View File

@@ -2,7 +2,7 @@ package models
import "github.com/centrifugal/centrifuge"
// ChannelPublisher writes data into a channel. Note that pemissions are not checked.
// ChannelPublisher writes data into a channel
type ChannelPublisher func(channel string, data []byte) error
// ChannelHandler defines the core channel behavior
@@ -17,10 +17,9 @@ type ChannelHandler interface {
OnPublish(c *centrifuge.Client, e centrifuge.PublishEvent) ([]byte, error)
}
// ChannelHandlerFactory should be implemented by all core features.
type ChannelHandlerFactory interface {
// GetHandlerForPath gets a ChannelHandler for a path.
// This is called fast and often -- it must be synchronized
// ChannelHandlerProvider -- this should be implemented by any core feature
type ChannelHandlerProvider interface {
// This is called fast and often -- it must be synchrnozed
GetHandlerForPath(path string) (ChannelHandler, error)
}

View File

@@ -1,50 +0,0 @@
package models
import "github.com/grafana/grafana-plugin-sdk-go/data"
// NOTE:
// this likely should go in the Plugin SDK since it will be useful from plugins
// Measurement is a single measurement value.
type Measurement struct {
// Name of the measurement.
Name string `json:"name,omitempty"`
// Time is the measurement time. Units are usually ms, but depends on the channel
Time int64 `json:"time,omitempty"`
// Values is the measurement's values. The value type is typically number or string.
Values map[string]interface{} `json:"values,omitempty"`
// Config is an optional list of field configs.
Config map[string]data.FieldConfig `json:"config,omitempty"`
// Labels are applied to all values.
Labels map[string]string `json:"labels,omitempty"`
}
// MeasurementAction defines what should happen when you send a list of measurements.
type MeasurementAction string
const (
// MeasurementActionAppend means new values should be added to a client buffer. This is the default action
MeasurementActionAppend MeasurementAction = "append"
// MeasurementActionReplace means new values should replace any existing values.
MeasurementActionReplace MeasurementAction = "replace"
// MeasurementActionClear means all existing values should be remoed before adding.
MeasurementActionClear MeasurementAction = "clear"
)
// MeasurementBatch is a collection of measurements all sent at once.
type MeasurementBatch struct {
// Action is the action in question, the default is append.
Action MeasurementAction `json:"action,omitempty"`
// Measurements is the array of measurements.
Measurements []Measurement `json:"measurements,omitempty"`
// Capacity is the suggested size of the client buffer
Capacity int64 `json:"capacity,omitempty"`
}

View File

@@ -2,15 +2,12 @@ package plugins
import (
"encoding/json"
"path/filepath"
"strings"
"github.com/gosimple/slug"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin/grpcplugin"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
)
type AppPluginCss struct {
@@ -24,8 +21,6 @@ type AppPlugin struct {
FoundChildPlugins []*PluginInclude `json:"-"`
Pinned bool `json:"-"`
Executable string `json:"executable,omitempty"`
}
// AppPluginRoute describes a plugin route that is defined in
@@ -72,15 +67,6 @@ func (app *AppPlugin) Load(decoder *json.Decoder, base *PluginBase, backendPlugi
return err
}
if app.Backend {
cmd := ComposePluginStartCommand(app.Executable)
fullpath := filepath.Join(app.PluginDir, cmd)
factory := grpcplugin.NewBackendPlugin(app.Id, fullpath, grpcplugin.PluginStartFuncs{})
if err := backendPluginManager.Register(app.Id, factory); err != nil {
return errutil.Wrapf(err, "failed to register backend plugin")
}
}
Apps[app.Id] = app
return nil
}

View File

@@ -35,25 +35,11 @@ const (
)
type PluginNotFoundError struct {
PluginID string
PluginId string
}
func (e PluginNotFoundError) Error() string {
return fmt.Sprintf("plugin with ID %q not found", e.PluginID)
}
type duplicatePluginError struct {
Plugin *PluginBase
ExistingPlugin *PluginBase
}
func (e duplicatePluginError) Error() string {
return fmt.Sprintf("plugin with ID %q already loaded from %q", e.Plugin.Id, e.ExistingPlugin.PluginDir)
}
func (e duplicatePluginError) Is(err error) bool {
_, ok := err.(duplicatePluginError)
return ok
return fmt.Sprintf("Plugin with id %s not found", e.PluginId)
}
// PluginLoader can load a plugin.
@@ -91,8 +77,8 @@ type PluginBase struct {
}
func (pb *PluginBase) registerPlugin(base *PluginBase) error {
if p, exists := Plugins[pb.Id]; exists {
return duplicatePluginError{Plugin: pb, ExistingPlugin: p}
if _, exists := Plugins[pb.Id]; exists {
return fmt.Errorf("Plugin with ID %q already exists", pb.Id)
}
if !strings.HasPrefix(base.PluginDir, setting.StaticRootPath) {

View File

@@ -271,12 +271,6 @@ func (pm *PluginManager) scan(pluginDir string, requireSigned bool) error {
// Load the full plugin, and add it to manager
if err := loader.Load(jsonParser, plugin, scanner.backendPluginManager); err != nil {
if errors.Is(err, duplicatePluginError{}) {
pm.log.Warn("Plugin is duplicate", "error", err)
scanner.errors = append(scanner.errors, err)
continue
}
return err
}

View File

@@ -2,7 +2,6 @@ package plugins
import (
"context"
"errors"
"fmt"
"path/filepath"
"testing"
@@ -164,23 +163,6 @@ func TestPluginManager_Init(t *testing.T) {
require.Empty(t, pm.scanningErrors)
assert.Equal(t, []string{"gel"}, fm.registeredPlugins)
})
t.Run("With nested plugin duplicating parent", func(t *testing.T) {
origPluginsPath := setting.PluginsPath
t.Cleanup(func() {
setting.PluginsPath = origPluginsPath
})
setting.PluginsPath = "testdata/duplicate-plugins"
pm := &PluginManager{
Cfg: &setting.Cfg{},
}
err := pm.Init()
require.NoError(t, err)
assert.Len(t, pm.scanningErrors, 1)
assert.True(t, errors.Is(pm.scanningErrors[0], duplicatePluginError{}))
})
}
func TestPluginManager_IsBackendOnlyPlugin(t *testing.T) {

View File

@@ -1,14 +0,0 @@
{
"type": "datasource",
"name": "Child",
"id": "test-app",
"info": {
"description": "Child plugin",
"author": {
"name": "Grafana Labs",
"url": "http://grafana.com"
},
"version": "1.0.0",
"updated": "2020-10-20"
}
}

View File

@@ -1,14 +0,0 @@
{
"type": "datasource",
"name": "Parent",
"id": "test-app",
"info": {
"description": "Parent plugin",
"author": {
"name": "Grafana Labs",
"url": "http://grafana.com"
},
"version": "1.0.0",
"updated": "2020-10-20"
}
}

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