mirror of
https://github.com/grafana/grafana.git
synced 2026-01-10 14:07:49 +08:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df5de8219b | ||
|
|
3b64a0b431 | ||
|
|
5130b3c704 | ||
|
|
1c3628f3c6 | ||
|
|
add798f9be | ||
|
|
2966c2b41f | ||
|
|
8c70b0b4eb | ||
|
|
a844040faf | ||
|
|
93605ed4ee | ||
|
|
15d4d230cb | ||
|
|
cd3523db68 | ||
|
|
566460f4fe | ||
|
|
b582b64026 | ||
|
|
5ba62c974d | ||
|
|
83d11f0a57 | ||
|
|
207a7c81c3 | ||
|
|
5d5fa03cf6 | ||
|
|
7ff801727c | ||
|
|
6211301d09 | ||
|
|
a3ac4c50c9 | ||
|
|
e0f1535806 | ||
|
|
4d9705cb04 | ||
|
|
87e16f2266 | ||
|
|
60990ea070 | ||
|
|
38b5ba191c | ||
|
|
302e95684d | ||
|
|
9c3ae92722 | ||
|
|
0e2194e4b8 | ||
|
|
31c9ac9e3e | ||
|
|
527925ece5 | ||
|
|
bdeda62175 | ||
|
|
0944b9eab8 | ||
|
|
b60d5d502c | ||
|
|
7c8d3f7e51 | ||
|
|
0f159d1541 | ||
|
|
99f4aabbc5 | ||
|
|
1a3525781f | ||
|
|
2a87c627ad | ||
|
|
ee5cc5a271 | ||
|
|
ccd7b6ce7e | ||
|
|
1fdeca1015 | ||
|
|
6be2d4128f | ||
|
|
1194671ac0 | ||
|
|
bd602fd6d9 | ||
|
|
f1c1311961 | ||
|
|
ea7eb59bac | ||
|
|
0b985cfd8d | ||
|
|
7d65c5c2a6 | ||
|
|
df07305465 | ||
|
|
f25d2fe7e2 | ||
|
|
0417533b61 | ||
|
|
6d4a443aaf |
@@ -7,6 +7,7 @@ exclude_unchanged = true
|
||||
follow_symlink = true
|
||||
include_dir = ["apps", "conf", "devenv/dev-dashboards", "pkg", "public/views"]
|
||||
include_ext = ["go", "ini", "toml", "html", "json"]
|
||||
pre_cmd = ["make gen-go"]
|
||||
stop_on_error = true
|
||||
send_interrupt = true
|
||||
kill_delay = 500
|
||||
|
||||
@@ -35,7 +35,7 @@ module.exports = [
|
||||
'data/',
|
||||
'deployment_tools_config.json',
|
||||
'devenv',
|
||||
'e2e-playwright/test-plugins',
|
||||
'e2e/test-plugins',
|
||||
'e2e/tmp',
|
||||
'packages/grafana-ui/src/components/Icon/iconBundle.ts',
|
||||
'pkg',
|
||||
|
||||
@@ -476,7 +476,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "2"]
|
||||
],
|
||||
"packages/grafana-prometheus/src/querybuilder/shared/types.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"packages/grafana-prometheus/src/resource_clients.test.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
@@ -1280,6 +1281,9 @@ exports[`better eslint`] = {
|
||||
"public/app/features/alerting/unified/components/rule-editor/AnnotationKeyInput.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-editor/AnnotationsStep.tsx:5381": [
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-editor/CloudEvaluationBehavior.tsx:5381": [
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "0"],
|
||||
[0, 0, 0, "Add noMargin prop to Field components to remove built-in margins. Use layout components like Stack or Grid with the gap prop instead for consistent spacing.", "1"]
|
||||
@@ -2333,7 +2337,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/features/geo/utils/frameVectorSource.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/features/inspector/InspectDataOptions.tsx:5381": [
|
||||
[0, 0, 0, "\'HorizontalGroup\' import from \'@grafana/ui\' is restricted from being used by a pattern. Use Stack component instead.", "0"],
|
||||
@@ -2780,6 +2785,10 @@ exports[`better eslint`] = {
|
||||
"public/app/features/transformers/editors/CalculateFieldTransformerEditor/WindowOptionsEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/features/transformers/editors/CalculateFieldTransformerEditor/index.ts:5381": [
|
||||
[0, 0, 0, "Do not re-export imported variable (\`CalculateFieldTransformerEditor\`)", "0"],
|
||||
[0, 0, 0, "Do not re-export imported variable (\`calculateFieldTransformRegistryItem\`)", "1"]
|
||||
],
|
||||
"public/app/features/transformers/editors/ConvertFieldTypeTransformerEditor.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
@@ -3453,7 +3462,8 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./trace\`)", "0"]
|
||||
],
|
||||
"public/app/plugins/datasource/jaeger/datasource.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/datasource/loki/LanguageProvider.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
@@ -3756,6 +3766,9 @@ exports[`better eslint`] = {
|
||||
"public/app/plugins/panel/geomap/types.ts:5381": [
|
||||
[0, 0, 0, "Do not re-export imported variable (\`./panelcfg.gen\`)", "0"]
|
||||
],
|
||||
"public/app/plugins/panel/geomap/utils/layers.ts:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
"public/app/plugins/panel/geomap/utils/tooltip.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
|
||||
@@ -42,7 +42,7 @@ require (
|
||||
github.com/yalue/merged_fs v1.3.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
golang.org/x/tools v0.33.0 // indirect
|
||||
|
||||
@@ -89,8 +89,8 @@ golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
|
||||
@@ -27,7 +27,7 @@ require (
|
||||
github.com/tetratelabs/wazero v1.6.0 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/net v0.40.0 // indirect
|
||||
golang.org/x/oauth2 v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.27.0 // indirect
|
||||
golang.org/x/sync v0.14.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.25.0 // indirect
|
||||
|
||||
@@ -57,8 +57,8 @@ golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
|
||||
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
|
||||
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
|
||||
95
.drone.yml
95
.drone.yml
@@ -25,7 +25,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build verify-drone
|
||||
@@ -75,7 +75,7 @@ steps:
|
||||
- go install github.com/bazelbuild/buildtools/buildifier@latest
|
||||
- buildifier --lint=warn -mode=check -r .
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: lint-starlark
|
||||
trigger:
|
||||
event:
|
||||
@@ -140,7 +140,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -150,7 +150,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -159,7 +159,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -193,7 +193,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
@@ -210,7 +210,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-package
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -289,7 +289,7 @@ steps:
|
||||
- commands:
|
||||
- npx wait-on@7.0.1 http://$HOST:$PORT
|
||||
- yarn playwright install --with-deps chromium
|
||||
- yarn e2e:playwright --grep @plugins
|
||||
- yarn e2e:playwright
|
||||
depends_on:
|
||||
- grafana-server
|
||||
- build-test-plugins
|
||||
@@ -472,7 +472,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-cue
|
||||
trigger:
|
||||
event:
|
||||
@@ -542,7 +542,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-cue
|
||||
trigger:
|
||||
branch: main
|
||||
@@ -609,7 +609,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -619,7 +619,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -628,7 +628,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -661,7 +661,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --version
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --uninstall 'qemu-*'
|
||||
- docker run --privileged --rm tonistiigi/binfmt:qemu-v7.0.0-28 --install all
|
||||
@@ -678,7 +678,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-package
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -761,7 +761,7 @@ steps:
|
||||
- commands:
|
||||
- npx wait-on@7.0.1 http://$HOST:$PORT
|
||||
- yarn playwright install --with-deps chromium
|
||||
- yarn e2e:playwright --grep @plugins
|
||||
- yarn e2e:playwright
|
||||
depends_on:
|
||||
- grafana-server
|
||||
- build-test-plugins
|
||||
@@ -1156,7 +1156,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
@@ -1286,7 +1286,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
@@ -1427,7 +1427,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket
|
||||
@@ -1519,7 +1519,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1619,7 +1619,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- depends_on:
|
||||
- compile-build-cmd
|
||||
@@ -1716,7 +1716,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build publish grafana-com --edition oss ${DRONE_TAG}
|
||||
@@ -1757,7 +1757,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_main.sh
|
||||
@@ -1790,7 +1790,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -1831,13 +1831,30 @@ platform:
|
||||
os: linux
|
||||
services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $(/usr/bin/github-app-external-token) > /github-app/token
|
||||
environment:
|
||||
GITHUB_APP_ID:
|
||||
from_secret: github-app-app-id
|
||||
GITHUB_APP_INSTALLATION_ID:
|
||||
from_secret: github-app-installation-id
|
||||
GITHUB_APP_PRIVATE_KEY:
|
||||
from_secret: github-app-private-key
|
||||
failure: ignore
|
||||
image: us-docker.pkg.dev/grafanalabs-global/docker-deployment-tools-prod/github-app-secret-writer:2024-11-05-v11688112090.1-83920c59
|
||||
name: github-app-generate-token
|
||||
volumes:
|
||||
- name: github-app
|
||||
path: /github-app
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh
|
||||
depends_on:
|
||||
- github-app-generate-token
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
@@ -1867,7 +1884,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -1889,6 +1906,10 @@ volumes:
|
||||
- host:
|
||||
path: /var/run/docker.sock
|
||||
name: docker
|
||||
- name: github-app
|
||||
path: /github-app
|
||||
- name: github-app
|
||||
temp: {}
|
||||
---
|
||||
clone:
|
||||
retries: 3
|
||||
@@ -1953,7 +1974,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_tag_grafana.sh
|
||||
@@ -1986,7 +2007,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -2060,7 +2081,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_build_nightly_grafana.sh
|
||||
@@ -2093,7 +2114,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -2204,7 +2225,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GRAFANA_DIR=$$(pwd)
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- ./pkg/build/daggerbuild/scripts/drone_publish_nightly_grafana.sh
|
||||
@@ -2239,7 +2260,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-publish
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -2333,7 +2354,7 @@ steps:
|
||||
- commands:
|
||||
- wget -qO- https://github.com/dagger/dagger/releases/download/v0.18.8/dagger_v0.18.8_linux_amd64.tar.gz
|
||||
| tar zx -C /bin
|
||||
- apk add docker
|
||||
- apk add docker bash
|
||||
- export GITHUB_TOKEN=$(cat /github-app/token)
|
||||
- dagger run --silent go run ./pkg/build/cmd artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
|
||||
--enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --build-id=$${DRONE_BUILD_NUMBER}
|
||||
@@ -2369,7 +2390,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.4-alpine
|
||||
image: golang:1.24.6-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -2711,7 +2732,7 @@ steps:
|
||||
- commands:
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.4-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.6-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.16.0-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0
|
||||
@@ -2739,7 +2760,7 @@ steps:
|
||||
- commands:
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.4-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.6-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22.16.0-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0
|
||||
@@ -2986,6 +3007,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: d20f1d6e2e8347701f82114ad352f53db57dc95b5b3831941fa93d063a92b9d8
|
||||
hmac: e7227aeb1bbea13606266ce540b5f0e0a63f05f56a3eb072954d54527dcc5a11
|
||||
|
||||
...
|
||||
|
||||
22
.github/CODEOWNERS
vendored
22
.github/CODEOWNERS
vendored
@@ -80,7 +80,6 @@
|
||||
/apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad
|
||||
/apps/folder/ @grafana/grafana-app-platform-squad
|
||||
/apps/playlist/ @grafana/grafana-app-platform-squad
|
||||
/apps/secret/ @grafana/grafana-operator-experience-squad
|
||||
/apps/investigations/ @fcjack @matryer @svennergr
|
||||
/apps/advisor/ @grafana/plugins-platform-backend
|
||||
/apps/iam/ @grafana/access-squad
|
||||
@@ -88,8 +87,8 @@
|
||||
/pkg/apis/ @grafana/grafana-app-platform-squad
|
||||
/pkg/apis/query @grafana/grafana-datasources-core-services
|
||||
/pkg/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend
|
||||
/pkg/apis/secret @grafana/grafana-operator-experience-squad
|
||||
/pkg/bus/ @grafana/grafana-search-and-storage
|
||||
/pkg/clientauth/ @grafana/grafana-app-platform-squad
|
||||
/pkg/cmd/ @grafana/grafana-backend-group
|
||||
/pkg/cmd/grafana-cli/commands/install_command.go @grafana/plugins-platform-backend
|
||||
/pkg/cmd/grafana-cli/commands/install_command_test.go @grafana/plugins-platform-backend
|
||||
@@ -150,7 +149,6 @@
|
||||
/pkg/services/hooks/ @grafana/grafana-backend-group
|
||||
/pkg/services/kmsproviders/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/licensing/ @grafana/grafana-operator-experience-squad
|
||||
/pkg/services/mtdsclient/ @grafana/grafana-datasources-core-services
|
||||
/pkg/services/navtree/ @grafana/grafana-backend-group
|
||||
/pkg/services/notifications/ @grafana/grafana-backend-group
|
||||
/pkg/services/org/ @grafana/grafana-backend-group
|
||||
@@ -179,6 +177,7 @@
|
||||
/pkg/setting/ @grafana/grafana-backend-services-squad
|
||||
/pkg/tests/ @grafana/grafana-backend-services-squad
|
||||
/pkg/tests/apis/ @grafana/grafana-app-platform-squad
|
||||
/pkg/tests/apis/query @grafana/grafana-datasources-core-services
|
||||
/pkg/tests/apis/alerting @grafana/grafana-app-platform-squad @grafana/alerting-backend
|
||||
/pkg/tests/api/correlations/ @grafana/datapro
|
||||
/pkg/tsdb/grafanads/ @grafana/grafana-backend-group
|
||||
@@ -306,7 +305,6 @@
|
||||
/devenv/jsonnet/ @grafana/dataviz-squad
|
||||
/devenv/local_cdn/ @grafana/frontend-ops
|
||||
/devenv/local-npm/ @grafana/frontend-ops
|
||||
/devenv/frontend-service/ @grafana/grafana-frontend-platform
|
||||
/devenv/setup.sh @grafana/grafana-backend-services-squad
|
||||
/devenv/plugins.yaml @grafana/plugins-platform-frontend
|
||||
|
||||
@@ -402,10 +400,8 @@
|
||||
/public/app/core/internationalization/ @grafana/grafana-frontend-platform
|
||||
/e2e/ @grafana/grafana-frontend-platform
|
||||
/e2e/cloud-plugins-suite/ @grafana/partner-datasources
|
||||
/e2e-playwright/ @grafana/grafana-frontend-platform
|
||||
/e2e-playwright/plugin-e2e/ @grafana/oss-big-tent @grafana/partner-datasources
|
||||
/e2e-playwright/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
|
||||
/e2e-playwright/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend
|
||||
/e2e/plugin-e2e/plugin-e2e-api-tests/ @grafana/plugins-platform-frontend
|
||||
/e2e/test-plugins/grafana-extensionstest-app/ @grafana/plugins-platform-frontend
|
||||
|
||||
# Packages
|
||||
/packages/ @grafana/grafana-frontend-platform @grafana/plugins-platform-frontend
|
||||
@@ -481,7 +477,6 @@
|
||||
/cypress.config.js @grafana/grafana-frontend-platform
|
||||
/.levignore.js @grafana/plugins-platform-frontend
|
||||
playwright.config.ts @grafana/plugins-platform-frontend
|
||||
playwright.storybook.config.ts @grafana/grafana-frontend-platform
|
||||
|
||||
# public folder
|
||||
/public/app/api/ @grafana/grafana-frontend-platform
|
||||
@@ -628,7 +623,6 @@ playwright.storybook.config.ts @grafana/grafana-frontend-platform
|
||||
/scripts/cli/ @grafana/grafana-frontend-platform
|
||||
/scripts/clean-git-or-error.sh @grafana/grafana-as-code
|
||||
/scripts/grafana-server/ @grafana/grafana-frontend-platform
|
||||
/scripts/check-frontend-dev.sh @grafana/grafana-frontend-platform
|
||||
/scripts/helpers/ @grafana/grafana-developer-enablement-squad
|
||||
/scripts/import_many_dashboards.sh @torkelo
|
||||
/scripts/mixin-check.sh @bergquist
|
||||
@@ -753,7 +747,6 @@ embed.go @grafana/grafana-as-code
|
||||
/pkg/kinds/ @grafana/grafana-as-code
|
||||
/pkg/registry/ @grafana/grafana-as-code
|
||||
/pkg/registry/apis/ @grafana/grafana-app-platform-squad
|
||||
/pkg/registry/apis/folders @grafana/grafana-search-and-storage
|
||||
/pkg/registry/apis/query @grafana/grafana-datasources-core-services
|
||||
/pkg/registry/apis/secret @grafana/grafana-operator-experience-squad
|
||||
/pkg/registry/apis/userstorage @grafana/grafana-app-platform-squad @grafana/plugins-platform-backend
|
||||
@@ -777,7 +770,6 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/pr-checks.json @tolzhabayev
|
||||
/.github/pr-commands.json @tolzhabayev
|
||||
/.github/renovate.json5 @grafana/frontend-ops
|
||||
/.github/actions/check-jobs/action.yml @grafana/grafana-frontend-platform
|
||||
/.github/actions/setup-enterprise/action.yml @grafana/grafana-backend-group
|
||||
/.github/actions/setup-grafana-bench/ @Proximyst
|
||||
/.github/actions/build-package @grafana/grafana-developer-enablement-squad
|
||||
@@ -804,7 +796,6 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/commands.yml @torkelo
|
||||
/.github/workflows/community-release.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/detect-breaking-changes-* @grafana/plugins-platform-frontend
|
||||
/.github/workflows/detect-plugin-extension-changes.yml @grafana/plugins-platform-frontend
|
||||
/.github/workflows/documentation-ci.yml @grafana/docs-tooling
|
||||
/.github/workflows/deploy-pr-preview.yml @grafana/docs-tooling
|
||||
/.github/workflows/feature-toggles-ci.yml @grafana/docs-tooling
|
||||
@@ -826,7 +817,6 @@ embed.go @grafana/grafana-as-code
|
||||
/.github/workflows/scripts/json-file-to-job-output.js @grafana/plugins-platform-frontend
|
||||
/.github/workflows/stale.yml @grafana/grafana-developer-enablement-squad
|
||||
/.github/workflows/storybook-verification.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/storybook-verification-playwright.yml @grafana/grafana-frontend-platform
|
||||
/.github/workflows/update-make-docs.yml @grafana/docs-tooling
|
||||
/.github/workflows/scripts/kinds/verify-kinds.go @grafana/platform-monitoring
|
||||
/.github/workflows/scripts/create-security-branch/create-security-branch.sh @grafana/grafana-developer-enablement-squad
|
||||
@@ -886,7 +876,3 @@ embed.go @grafana/grafana-as-code
|
||||
/conf/provisioning/datasources/ @grafana/plugins-platform-backend
|
||||
/conf/provisioning/plugins/ @grafana/plugins-platform-backend
|
||||
/conf/provisioning/sample/ @grafana/grafana-git-ui-sync-team
|
||||
|
||||
# Security
|
||||
/relyance.yaml @grafana/security-team
|
||||
/.github/workflows/relyance-scan.yml @grafana/security-team
|
||||
|
||||
48
.github/actions/check-jobs/action.yml
vendored
48
.github/actions/check-jobs/action.yml
vendored
@@ -1,48 +0,0 @@
|
||||
name: Check jobs results
|
||||
description: Checks if any jobs have failed and exits with error if failures are found. Use to check the results of matrix test runs.
|
||||
inputs:
|
||||
needs:
|
||||
description: JSON string containing the needs context from the workflow
|
||||
required: true
|
||||
failure-message:
|
||||
description: Custom message to display when failures are found
|
||||
required: false
|
||||
default: "One or more jobs have failed"
|
||||
success-message:
|
||||
description: Custom message to display when all jobs pass
|
||||
required: false
|
||||
default: "All jobs passed successfully"
|
||||
outputs:
|
||||
any-failed:
|
||||
description: Whether any jobs failed
|
||||
value: ${{ steps.check-jobs.outputs.any-failed }}
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Check test suites
|
||||
id: check-jobs
|
||||
shell: bash
|
||||
env:
|
||||
NEEDS: ${{ inputs.needs }}
|
||||
FAILURE_MSG: ${{ inputs.failure-message }}
|
||||
SUCCESS_MSG: ${{ inputs.success-message }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Print the needs context, debugging
|
||||
echo "$NEEDS" | jq
|
||||
|
||||
# Extract failures
|
||||
FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')"
|
||||
|
||||
# Check if there are any failures
|
||||
if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then
|
||||
echo "❌ $FAILURE_MSG"
|
||||
echo "Failed suites:"
|
||||
echo "$FAILURES" | jq -r 'to_entries[] | "- \(.key): \(.value)"'
|
||||
echo "any-failed=true" >> "$GITHUB_OUTPUT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ $SUCCESS_MSG"
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -8,11 +8,11 @@ updates:
|
||||
directories:
|
||||
- "/"
|
||||
- "/apps/playlist"
|
||||
- "/apps/secret"
|
||||
- "/apps/investigations"
|
||||
- "/pkg/aggregator"
|
||||
- "/pkg/apimachinery"
|
||||
- "/pkg/apis/folder"
|
||||
- "/pkg/apis/secret"
|
||||
- "/pkg/apiserver"
|
||||
- "/pkg/build"
|
||||
- "/pkg/build/wire"
|
||||
|
||||
2
.github/license_finder.yaml
vendored
2
.github/license_finder.yaml
vendored
@@ -99,7 +99,7 @@
|
||||
:versions: []
|
||||
:when: 2025-05-03 13:10:00.000000000 Z
|
||||
- - :license
|
||||
- github.com/grafana/grafana/apps/secret
|
||||
- github.com/grafana/grafana/pkg/apis/secret
|
||||
- unknown
|
||||
- :who: Carl Bergquist
|
||||
:why: repository is owned by Grafana Labs
|
||||
|
||||
2
.github/workflows/add-to-whats-new.yml
vendored
2
.github/workflows/add-to-whats-new.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943 # v2.9.3
|
||||
- uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 # v2.9.1
|
||||
with:
|
||||
message: |
|
||||
Since you've added the `Add to what's new` label, consider drafting a [What's new note](https://admin.grafana.com/content-admin/#/collections/whats-new/new) for this feature.
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
with:
|
||||
credentials_json: '${{ env.PLUGINS_GOOGLE_CREDENTIALS }}'
|
||||
- name: 'Set up Cloud SDK'
|
||||
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
|
||||
uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a'
|
||||
- name: Setup nodejs environment
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
||||
@@ -165,7 +165,7 @@ jobs:
|
||||
project_id: 'grafanalabs-global'
|
||||
|
||||
- name: 'Set up Cloud SDK'
|
||||
uses: 'google-github-actions/setup-gcloud@77e7a554d41e2ee56fc945c52dfd3f33d12def9a'
|
||||
uses: 'google-github-actions/setup-gcloud@6189d56e4096ee891640bb02ac264be376592d6a'
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
with:
|
||||
version: '>= 363.0.0'
|
||||
@@ -271,7 +271,7 @@ jobs:
|
||||
# Comment on the PR
|
||||
- name: Comment on PR
|
||||
if: steps.levitate-run.outputs.exit_code == 1
|
||||
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728
|
||||
with:
|
||||
header: levitate-breaking-change-comment
|
||||
number: ${{ github.event.pull_request.number }}
|
||||
@@ -288,7 +288,7 @@ jobs:
|
||||
# Remove comment from the PR (no more breaking changes)
|
||||
- name: Remove comment from PR
|
||||
if: steps.levitate-run.outputs.exit_code == 0
|
||||
uses: marocchino/sticky-pull-request-comment@d2ad0de260ae8b0235ce059e63f2949ba9e05943
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728
|
||||
with:
|
||||
header: levitate-breaking-change-comment
|
||||
number: ${{ github.event.pull_request.number }}
|
||||
|
||||
@@ -1,156 +0,0 @@
|
||||
---
|
||||
name: Detect Plugin Extension Changes
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- 'public/**'
|
||||
|
||||
env:
|
||||
# Space-separated list of keywords referring to plugin extensions
|
||||
PLUGIN_EXTENSION_KEYWORDS: "usePluginLinks, usePluginComponent, usePluginComponents, usePluginFunctions, PluginExtensionPoints"
|
||||
|
||||
jobs:
|
||||
detect-plugin-extension-changes:
|
||||
name: Detect Plugin Extension Changes
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check for plugin extension changes
|
||||
id: check-changes
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { execSync } = require('child_process');
|
||||
const fs = require('fs');
|
||||
|
||||
// Plugin extension keywords from environment
|
||||
const keywords = process.env.PLUGIN_EXTENSION_KEYWORDS.split(',');
|
||||
const baseSha = '${{ github.event.pull_request.base.sha }}';
|
||||
const headSha = '${{ github.event.pull_request.head.sha }}';
|
||||
|
||||
console.log('Checking for plugin extension changes...');
|
||||
console.log('Keywords:', keywords);
|
||||
|
||||
// Get changed files in packages/ and public/ directories
|
||||
let changedFiles = [];
|
||||
const diffOutput = execSync(
|
||||
`git diff --name-only ${baseSha}...${headSha} -- packages/ public/`,
|
||||
{ encoding: 'utf8' }
|
||||
).trim();
|
||||
|
||||
if (diffOutput) {
|
||||
changedFiles = diffOutput.split('\n').filter(file => {
|
||||
// Validate file path and ensure it's in target directories
|
||||
return file.match(/^(packages\/|public\/)/) &&
|
||||
file.match(/^[a-zA-Z0-9._/-]+$/) &&
|
||||
fs.existsSync(file);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Changed files to check:', changedFiles);
|
||||
|
||||
// Check each file for plugin extension keywords
|
||||
const filesWithChanges = new Set();
|
||||
let hasPluginExtensionChanges = false;
|
||||
|
||||
for (const file of changedFiles) {
|
||||
try {
|
||||
// Get the diff for this specific file
|
||||
const fileDiff = execSync(
|
||||
`git diff ${baseSha}...${headSha} -- "${file}"`,
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
|
||||
// Check if any keywords are in the diff
|
||||
for (const keyword of keywords) {
|
||||
if (fileDiff.includes(keyword)) {
|
||||
console.log(`Found ${keyword} in ${file}`);
|
||||
filesWithChanges.add(file);
|
||||
hasPluginExtensionChanges = true;
|
||||
break; // Found at least one keyword, move to next file
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error checking file ${file}:`, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Set outputs
|
||||
const filesArray = Array.from(filesWithChanges);
|
||||
const formattedFiles = filesArray.length > 0
|
||||
? '`' + filesArray.join('`\\n- `') + '`'
|
||||
: '';
|
||||
|
||||
core.setOutput('plugin_extension_changes', hasPluginExtensionChanges.toString());
|
||||
core.setOutput('formatted_changed_files', formattedFiles);
|
||||
|
||||
if (hasPluginExtensionChanges) {
|
||||
console.log('The following files have changes that may affect plugin extensions:');
|
||||
console.log(filesArray);
|
||||
} else {
|
||||
console.log('No changes detected in core Grafana extensions or extension points.');
|
||||
}
|
||||
|
||||
- name: Send Slack Message via Payload
|
||||
id: slack
|
||||
if: steps.check-changes.outputs.plugin_extension_changes == 'true'
|
||||
uses: grafana/shared-workflows/actions/send-slack-message@0941e3408fa4789fec9062c44a2a9e1832146ba6 #v2.0.1
|
||||
with:
|
||||
method: chat.postMessage
|
||||
payload-templated: true
|
||||
payload: |
|
||||
{
|
||||
"channel": "C031SLFH6G0",
|
||||
"text": "Plugin Extension changes in core Grafana *PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }} :information_source:",
|
||||
"icon_emoji": ":grot:",
|
||||
"username": "Plugin Extension Bot",
|
||||
"blocks": [
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*Plugin Extensions:* possible changes to extension points in core Grafana."
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*PR:* <${{ github.event.pull_request.html_url }}|#${{ github.event.pull_request.number }}>\n*Job:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Job>"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*File(s) with changes:*\n- ${{ steps.check-changes.outputs.formatted_changed_files }}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "section",
|
||||
"text": {
|
||||
"type": "mrkdwn",
|
||||
"text": "*What to do?*\nMake sure that:\n- All extension point ids start with `grafana/`\n- All extension point ids are exposed via <https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/types/pluginExtensions.ts#L183|the `PluginExtensionPoints` enum in grafana-data>\n- Core Grafana is not registering extensions to extension points offered by plugins"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
5
.github/workflows/frontend-lint.yml
vendored
5
.github/workflows/frontend-lint.yml
vendored
@@ -16,7 +16,6 @@ jobs:
|
||||
contents: read
|
||||
outputs:
|
||||
changed: ${{ steps.detect-changes.outputs.frontend }}
|
||||
prettier: ${{ steps.detect-changes.outputs.frontend == 'true' || steps.detect-changes.outputs.docs == 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
@@ -35,7 +34,7 @@ jobs:
|
||||
id-token: write
|
||||
# Run this workflow only for PRs from forks; if it gets merged into `main` or `release-*`,
|
||||
# the `lint-frontend-prettier-enterprise` workflow will run instead
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.prettier == 'true'
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true && needs.detect-changes.outputs.changed == 'true'
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -56,7 +55,7 @@ jobs:
|
||||
contents: read
|
||||
id-token: write
|
||||
# Run this workflow for non-PR events (like pushes to `main` or `release-*`) OR for internal PRs (PRs not from forks)
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.prettier == 'true'
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false && needs.detect-changes.outputs.changed == 'true'
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
191
.github/workflows/pr-e2e-tests.yml
vendored
191
.github/workflows/pr-e2e-tests.yml
vendored
@@ -7,17 +7,12 @@ on:
|
||||
- main
|
||||
- release-*.*.*
|
||||
|
||||
# TODO: re-enable this before merging
|
||||
# concurrency:
|
||||
# group: ${{ github.workflow }}-${{ github.ref }}
|
||||
# cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
|
||||
|
||||
permissions: {}
|
||||
|
||||
env:
|
||||
ACTIONS_STEP_DEBUG: true
|
||||
RUNNER_DEBUG: 1
|
||||
|
||||
jobs:
|
||||
detect-changes:
|
||||
name: Detect whether code changed
|
||||
@@ -49,56 +44,22 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
path: ./grafana
|
||||
persist-credentials: false
|
||||
|
||||
# TODO: add a cleanup workflow to remove the cache when the PR is closed
|
||||
# https://github.com/actions/cache/blob/main/tips-and-workarounds.md#force-deletion-of-caches-overriding-default-cache-eviction-policy
|
||||
# TODO: maybe we could just use the cache to store the build, instead of uploading as an artifact?
|
||||
- uses: actions/cache@v4
|
||||
id: cache
|
||||
with:
|
||||
key: "build-grafana-${{ runner.os }}-${{ hashFiles('yarn.lock', 'public/*', 'packages/*', 'pkg/**/*.go', '**/go.mod', '**/go.sum', '!**_test.go', '!**.test.ts', '!**.test.tsx') }}"
|
||||
path: |
|
||||
build-dir
|
||||
|
||||
# If no cache hit, build Grafana
|
||||
- name: Build Grafana
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
- uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
with:
|
||||
verb: run
|
||||
args: go run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}" > out.txt
|
||||
- name: Cat built artifact
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: cat out.txt
|
||||
- name: Move built artifact
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p build-dir
|
||||
mv "$(cat out.txt)" build-dir/grafana.tar.gz
|
||||
|
||||
# If cache hit, validate the artifact is present
|
||||
- name: Validate artifact
|
||||
if: steps.cache.outputs.cache-hit == 'true'
|
||||
run: |
|
||||
if [ ! -f build-dir/grafana.tar.gz ]; then
|
||||
echo "Error: build-dir/grafana.tar.gz not found in cache"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Set artifact name
|
||||
run: echo "artifact=grafana-server-${{github.run_number}}" >> "$GITHUB_OUTPUT"
|
||||
args: go -C grafana run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}/grafana" > out.txt
|
||||
- run: mv "$(cat out.txt)" grafana.tar.gz
|
||||
- run: echo "artifact=grafana-e2e-${{github.run_number}}" >> "$GITHUB_OUTPUT"
|
||||
id: artifact
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4
|
||||
id: upload
|
||||
with:
|
||||
retention-days: 1
|
||||
name: ${{ steps.artifact.outputs.artifact }}
|
||||
path: build-dir/grafana.tar.gz
|
||||
path: grafana.tar.gz
|
||||
|
||||
# TODO: we won't need this when we only have playwright
|
||||
build-e2e-runner:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.changed == 'true'
|
||||
@@ -198,119 +159,6 @@ jobs:
|
||||
path: videos
|
||||
retention-days: 1
|
||||
|
||||
run-playwright-tests:
|
||||
needs:
|
||||
- build-grafana
|
||||
name: Playwright E2E tests (${{ matrix.shard }}/${{ matrix.shardTotal }})
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
shard: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||
shardTotal: [8]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: ${{ needs.build-grafana.outputs.artifact }}
|
||||
- name: Run E2E tests
|
||||
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
|
||||
with:
|
||||
verb: run
|
||||
args: go run ./pkg/build/e2e-playwright --package=grafana.tar.gz --shard=${{ matrix.shard }}/${{ matrix.shardTotal }} --blob-dir=./blob-report
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: success() || failure()
|
||||
with:
|
||||
name: playwright-blob-${{ github.run_number }}-${{ matrix.shard }}
|
||||
path: ./blob-report
|
||||
retention-days: 1
|
||||
|
||||
required-playwright-tests:
|
||||
needs:
|
||||
- run-playwright-tests
|
||||
- build-grafana
|
||||
if: ${{ !cancelled() }}
|
||||
name: All Playwright tests complete
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: blobs
|
||||
pattern: playwright-blob-*
|
||||
merge-multiple: true
|
||||
|
||||
- name: Check blob reports
|
||||
run: |
|
||||
if [ ! "$(ls -A ./blobs)" ]; then
|
||||
echo "Error: No blob reports found in ./blobs directory"
|
||||
echo "Did the Playwright tests run at all?"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found blob reports in ./blobs:"
|
||||
ls -lah ./blobs
|
||||
|
||||
- name: Merge into HTML Report
|
||||
run: npx playwright merge-reports --reporter html ./blobs
|
||||
|
||||
- name: Merge into JSON Report
|
||||
env:
|
||||
PLAYWRIGHT_JSON_OUTPUT_NAME: /tmp/playwright-results.json
|
||||
run: npx playwright merge-reports --reporter=json ./blobs
|
||||
|
||||
- name: Bench report
|
||||
run: |
|
||||
docker run --rm \
|
||||
--volume="/tmp/playwright-results.json:/home/bench/tests/playwright-results.json" \
|
||||
us-docker.pkg.dev/grafanalabs-global/docker-grafana-bench-prod/grafana-bench:v0.5.1 report \
|
||||
--grafana-url "http://localhost:3000" \
|
||||
--grafana-version "CI- ${{ github.sha }}" \
|
||||
--test-suite-name "FrontendCore" \
|
||||
--report-input playwright \
|
||||
--report-output log \
|
||||
--log-level DEBUG \
|
||||
/home/bench/tests/playwright-results.json
|
||||
|
||||
- name: Upload HTML report
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-html-${{ github.run_number }}
|
||||
path: playwright-report
|
||||
retention-days: 7
|
||||
|
||||
- name: Check test suites
|
||||
id: check-jobs
|
||||
uses: ./.github/actions/check-jobs
|
||||
continue-on-error: true # Failure will be reported on Show test results step
|
||||
with:
|
||||
needs: ${{ toJson(needs) }}
|
||||
failure-message: "One or more E2E test suites have failed"
|
||||
success-message: "All E2E test suites completed successfully"
|
||||
|
||||
- name: Show test results
|
||||
env:
|
||||
FAILED: ${{ steps.check-jobs.outputs.any-failed }}
|
||||
# sed removes the leading `../../src/` from the paths
|
||||
run: |
|
||||
npx playwright merge-reports --reporter list ./all-blob-reports | sed 's|\(\.\./\)\{1,\}src/|/|g'
|
||||
if [ "$FAILED" = "true" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
run-a11y-test:
|
||||
needs:
|
||||
- build-grafana
|
||||
@@ -345,7 +193,6 @@ jobs:
|
||||
required-e2e-tests:
|
||||
needs:
|
||||
- run-e2e-tests
|
||||
- build-grafana
|
||||
# a11y test is not listed on purpose: it is not an important E2E test.
|
||||
# It is also totally fine to fail right now.
|
||||
# always() is the best function here.
|
||||
@@ -356,13 +203,13 @@ jobs:
|
||||
name: All E2E tests complete
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Check test suites
|
||||
uses: ./.github/actions/check-jobs
|
||||
with:
|
||||
needs: ${{ toJson(needs) }}
|
||||
failure-message: "One or more E2E test suites have failed"
|
||||
success-message: "All E2E test suites completed successfully"
|
||||
env:
|
||||
NEEDS: ${{ toJson(needs) }}
|
||||
run: |
|
||||
FAILURES="$(echo "$NEEDS" | jq 'with_entries(select(.value.result == "failure")) | map_values(.result)')"
|
||||
echo "$FAILURES"
|
||||
if [ "$(echo "$FAILURES" | jq '. | length')" != "0" ]; then
|
||||
exit 1
|
||||
fi
|
||||
echo "All OK!"
|
||||
|
||||
18
.github/workflows/pr-frontend-unit-tests.yml
vendored
18
.github/workflows/pr-frontend-unit-tests.yml
vendored
@@ -90,23 +90,6 @@ jobs:
|
||||
TEST_SHARD: ${{ matrix.chunk }}
|
||||
TEST_SHARD_TOTAL: 8
|
||||
|
||||
frontend-decoupled-plugin-tests:
|
||||
needs: detect-changes
|
||||
if: needs.detect-changes.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
name: "Decoupled plugin tests"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
cache-dependency-path: 'yarn.lock'
|
||||
- run: yarn install --immutable --check-cache
|
||||
- run: yarn run plugin:test:ci
|
||||
|
||||
# This is the job that is actually required by rulesets.
|
||||
# We need to require EITHER the OSS or the Enterprise job to pass.
|
||||
# However, if one is skipped, GitHub won't flat-map the shards,
|
||||
@@ -115,7 +98,6 @@ jobs:
|
||||
needs:
|
||||
- frontend-unit-tests
|
||||
- frontend-unit-tests-enterprise
|
||||
- frontend-decoupled-plugin-tests
|
||||
# always() is the best function here.
|
||||
# success() || failure() will skip this function if any need is also skipped.
|
||||
# That means conditional test suites will fail the entire requirement check.
|
||||
|
||||
69
.github/workflows/pr-go-workspace-check.yml
vendored
69
.github/workflows/pr-go-workspace-check.yml
vendored
@@ -45,72 +45,3 @@ jobs:
|
||||
fi
|
||||
- name: Ensure Dockerfile contains submodule COPY commands
|
||||
run: ./scripts/go-workspace/validate-dockerfile.sh
|
||||
|
||||
check-wire:
|
||||
name: Check Wire Changes
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Set go version
|
||||
uses: actions/setup-go@19bb51245e9c80abacb2e91cc42b33fa478b8639
|
||||
with:
|
||||
cache: false
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Setup Enterprise
|
||||
if: github.event.pull_request.head.repo.fork == false
|
||||
uses: ./.github/actions/setup-enterprise
|
||||
|
||||
- name: Calculate generated wire checksums
|
||||
id: pre_gen_oss
|
||||
run: |
|
||||
OSS_WIRE_CHECKSUM="$(sha256sum pkg/server/wire_gen.go | cut -d' ' -f1)"
|
||||
echo "wire_checksum=$OSS_WIRE_CHECKSUM" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Calculate generated enterprise wire checksums
|
||||
id: pre_gen_enterprise
|
||||
if: github.event.pull_request.head.repo.fork == false
|
||||
run: |
|
||||
ENTERPRISE_WIRE_CHECKSUM="$(sha256sum pkg/server/enterprise_wire_gen.go | cut -d' ' -f1)"
|
||||
echo "wire_checksum=$ENTERPRISE_WIRE_CHECKSUM" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Generate Go files
|
||||
run: make gen-go
|
||||
|
||||
- name: Check for generated file changes
|
||||
run: |
|
||||
OSS_WIRE_CHANGED=false
|
||||
CURRENT_OSS_WIRE_CHECKSUM=$(sha256sum pkg/server/wire_gen.go | cut -d' ' -f1)
|
||||
if [ "$CURRENT_OSS_WIRE_CHECKSUM" != "${{ steps.pre_gen_oss.outputs.wire_checksum }}" ]; then
|
||||
OSS_WIRE_CHANGED=true
|
||||
echo "Uncomitted changes detected in pkg/server/wire_gen.go"
|
||||
fi
|
||||
|
||||
ENTERPRISE_WIRE_CHANGED=false
|
||||
if [ -f "pkg/server/enterprise_wire_gen.go" ]; then
|
||||
CURRENT_ENTERPRISE_WIRE_CHECKSUM=$(sha256sum pkg/server/enterprise_wire_gen.go | cut -d' ' -f1)
|
||||
if [ "$CURRENT_ENTERPRISE_WIRE_CHECKSUM" != "${{ steps.pre_gen_enterprise.outputs.wire_checksum }}" ]; then
|
||||
ENTERPRISE_WIRE_CHANGED=true
|
||||
echo "Uncomitted changes detected in pkg/server/enterprise_wire_gen.go"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$OSS_WIRE_CHANGED" = "false" ] && [ "$ENTERPRISE_WIRE_CHANGED" = "false" ]; then
|
||||
echo "No changes in generated Go files"
|
||||
else
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "false" ]]; then
|
||||
echo "> !!! Please link Enterprise and run 'make gen-go', then commit the changes to both repositories."
|
||||
else
|
||||
echo "> !!! Please run 'make gen-go' and commit the changes."
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
5
.github/workflows/publish-artifact.yml
vendored
5
.github/workflows/publish-artifact.yml
vendored
@@ -48,7 +48,7 @@ jobs:
|
||||
path: ./artifact
|
||||
- name: Log in to GCS
|
||||
id: login-to-gcs
|
||||
uses: grafana/shared-workflows/actions/login-to-gcs@main
|
||||
uses: grafana/shared-workflows/actions/login-to-gcs@login-to-gcs/v0.2.1
|
||||
with:
|
||||
environment: ${{ inputs.environment }}
|
||||
service_account: ${{ inputs.service-account }}
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
find ./artifact -mindepth 2 -maxdepth 2 -exec cp -r {} out/ \;
|
||||
ls -al out
|
||||
- name: Upload artifacts
|
||||
uses: grafana/shared-workflows/actions/push-to-gcs@main
|
||||
uses: grafana/shared-workflows/actions/push-to-gcs@push-to-gcs-v0.2.0
|
||||
with:
|
||||
bucket: ${{ inputs.bucket }}
|
||||
environment: ${{ inputs.environment }}
|
||||
@@ -66,4 +66,3 @@ jobs:
|
||||
path: out
|
||||
bucket_path: ${{ inputs.bucket-path }}
|
||||
service_account: ${{ inputs.service-account }}
|
||||
gzip: false
|
||||
|
||||
26
.github/workflows/release-build.yml
vendored
26
.github/workflows/release-build.yml
vendored
@@ -1,6 +1,16 @@
|
||||
name: Build Release Packages
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
source-event:
|
||||
description: If this workflow was triggered by another workflow, this value should be set to the GITHUB_EVENT_NAME of that source workflow.
|
||||
type: string
|
||||
required: false
|
||||
default: workflow_dispatch
|
||||
schedule:
|
||||
# Every weeknight at midnight
|
||||
# "Scheduled workflows will only run on the default branch." (docs.github.com)
|
||||
- cron: "0 0 * * 1-5"
|
||||
push:
|
||||
branches:
|
||||
- release-*.*.*
|
||||
@@ -96,8 +106,8 @@ jobs:
|
||||
with:
|
||||
github-token: ${{ steps.generate_token.outputs.token }}
|
||||
script: |
|
||||
const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT} = process.env;
|
||||
|
||||
const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT, GITHUB_EVENT_NAME} = process.env;
|
||||
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'grafana',
|
||||
repo: 'grafana-enterprise',
|
||||
@@ -108,6 +118,7 @@ jobs:
|
||||
"build-id": String(BUILD_ID),
|
||||
"bucket": BUCKET,
|
||||
"grafana-commit": GRAFANA_COMMIT,
|
||||
"source-event": GITHUB_EVENT_NAME,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -128,22 +139,31 @@ jobs:
|
||||
include:
|
||||
- name: linux-amd64
|
||||
artifacts: targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook
|
||||
verify: true
|
||||
- name: linux-arm64
|
||||
artifacts: targz:grafana:linux/arm64,deb:grafana:linux/arm64,rpm:grafana:linux/arm64,docker:grafana:linux/arm64,docker:grafana:linux/arm64:ubuntu
|
||||
verify: false
|
||||
- name: linux-s390x
|
||||
artifacts: targz:grafana:linux/s390x,deb:grafana:linux/s390x,rpm:grafana:linux/s390x,docker:grafana:linux/s390x,docker:grafana:linux/s390x:ubuntu
|
||||
verify: true
|
||||
- name: linux-armv7
|
||||
artifacts: targz:grafana:linux/arm/v7,deb:grafana:linux/arm/v7,docker:grafana:linux/arm/v7,docker:grafana:linux/arm/v7:ubuntu
|
||||
verify: true
|
||||
- name: linux-armv6
|
||||
artifacts: targz:grafana:linux/arm/v6,deb:grafana:linux/arm/v6
|
||||
verify: true
|
||||
- name: windows-amd64
|
||||
artifacts: targz:grafana:windows/amd64,zip:grafana:windows/amd64,msi:grafana:windows/amd64
|
||||
verify: true
|
||||
- name: windows-arm64
|
||||
artifacts: targz:grafana:windows/arm64,zip:grafana:windows/arm64
|
||||
verify: true
|
||||
- name: darwin-amd64
|
||||
artifacts: targz:grafana:darwin/amd64
|
||||
verify: true
|
||||
- name: darwin-arm64
|
||||
artifacts: targz:grafana:darwin/arm64
|
||||
verify: true
|
||||
steps:
|
||||
- uses: grafana/shared-workflows/actions/dockerhub-login@main
|
||||
- uses: actions/checkout@v4
|
||||
@@ -162,7 +182,7 @@ jobs:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: ${{ needs.setup.outputs.version }}
|
||||
output: artifacts-${{ matrix.name }}.txt
|
||||
verify: true
|
||||
verify: ${{ matrix.verify }}
|
||||
build-id: ${{ github.run_id }}
|
||||
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
|
||||
with:
|
||||
|
||||
2
.github/workflows/release-pr.yml
vendored
2
.github/workflows/release-pr.yml
vendored
@@ -208,7 +208,7 @@ jobs:
|
||||
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
|
||||
run: |
|
||||
git add package.json lerna.json yarn.lock packages public
|
||||
test -e e2e-playwright/test-plugins && git add e2e-playwright/test-plugins
|
||||
test -e e2e/test-plugins && git add e2e/test-plugins
|
||||
git commit -m "Update version to $VERSION"
|
||||
|
||||
- name: Git push
|
||||
|
||||
33
.github/workflows/relyance-scan.yml
vendored
33
.github/workflows/relyance-scan.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Relyance Compliance Inspection
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *' # Run daily at 00:00 UTC
|
||||
workflow_dispatch: # Allow for manual trigger
|
||||
|
||||
jobs:
|
||||
relyance-compliance-inspector:
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write # Needed for Vault access
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Get API key
|
||||
id: vault-secrets
|
||||
uses: grafana/shared-workflows/actions/get-vault-secrets@5d7e361bc7e0a183cde8afe9899fb7b596d2659b # get-vault-secrets-v1.2.0
|
||||
with:
|
||||
repo_secrets: |
|
||||
API_KEY=relyance:API_KEY
|
||||
|
||||
- name: Run Relyance scan
|
||||
env:
|
||||
API_KEY: "${{ fromJSON(steps.vault-secrets.outputs.secrets).API_KEY }}"
|
||||
run: |
|
||||
docker pull gcr.io/relyance-ext/compliance_inspector:release && \
|
||||
docker run --rm -v ${{ github.workspace }}:/repo --env "API_KEY=${{ env.API_KEY }}" gcr.io/relyance-ext/compliance_inspector:release
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
tools/
|
||||
public/
|
||||
conf/
|
||||
e2e-playwright/test-plugins/
|
||||
e2e/test-plugins/
|
||||
devenv/
|
||||
key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }}
|
||||
# only rebuild grafana if search files have changed ( or dependencies )
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
tools/
|
||||
public/
|
||||
conf/
|
||||
e2e-playwright/test-plugins/
|
||||
e2e/test-plugins/
|
||||
devenv/
|
||||
key: ${{ runner.os }}-grafana-${{ hashFiles('go.mod', 'package-lock.json', 'Makefile', 'pkg/storage/**/*.go', 'public/app/features/search/**/*.ts', 'public/app/features/search/**/*.tsx') }}
|
||||
- name: Set the step name
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
name: Verify Storybook (Playwright)
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/grafana-ui/**'
|
||||
- 'e2e-playwright/storybook/**'
|
||||
- '!docs/**'
|
||||
- '!*.md'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'packages/grafana-ui/**'
|
||||
- 'e2e-playwright/storybook/**'
|
||||
- '!docs/**'
|
||||
- '!*.md'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
verify-storybook:
|
||||
name: Verify Storybook (Playwright)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
|
||||
- name: Install Playwright browsers
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: Run Storybook and E2E tests
|
||||
run: yarn e2e:playwright:storybook
|
||||
12
.github/workflows/trufflehog.yml
vendored
12
.github/workflows/trufflehog.yml
vendored
@@ -21,17 +21,15 @@ jobs:
|
||||
contents: read # clone the repo
|
||||
|
||||
steps:
|
||||
- name: Calculate fetch depth
|
||||
id: fetch_depth
|
||||
shell: bash
|
||||
run: echo "fetch_depth=$(( ${{ github.event.pull_request.commits }} + 2 ))" >> "$GITHUB_OUTPUT"
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: ${{ steps.fetch_depth.outputs.fetch_depth }}
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: Trufflehog
|
||||
uses: trufflesecurity/trufflehog@eafb8c5f6a06175141c27f17bcc17941853d0047 # v3.90.0
|
||||
uses: trufflesecurity/trufflehog@90190deac64289cb10bb694894be8db9ead8790b # v3.88.29
|
||||
with:
|
||||
base: ${{ github.event.pull_request.base.sha }}
|
||||
head: ${{ github.event.pull_request.head.sha }}
|
||||
extra_args: --results=verified
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -237,4 +237,4 @@ public/app/plugins/**/dist/
|
||||
# Mock service worker used for fake API responses in frontend development
|
||||
public/mockServiceWorker.js
|
||||
|
||||
/e2e-playwright/test-plugins/*/dist
|
||||
/e2e/test-plugins/*/dist
|
||||
|
||||
@@ -83,14 +83,6 @@ linters:
|
||||
deny:
|
||||
- pkg: github.com/grafana/grafana/pkg
|
||||
desc: apps/playlist is not allowed to import grafana core
|
||||
apps-secret:
|
||||
list-mode: lax
|
||||
files:
|
||||
- ./apps/secret/*
|
||||
- ./apps/secret/**/*
|
||||
deny:
|
||||
- pkg: github.com/grafana/grafana/pkg
|
||||
desc: apps/secret is not allowed to import grafana core
|
||||
coreplugins:
|
||||
files:
|
||||
- '**/pkg/tsdb/grafana-pyroscope-datasource/*'
|
||||
|
||||
213
CHANGELOG.md
213
CHANGELOG.md
@@ -1,63 +1,180 @@
|
||||
<!-- 12.0.2+security-01 START -->
|
||||
<!-- 12.1.0 START -->
|
||||
|
||||
# 12.0.2+security-01 (2025-07-17)
|
||||
# 12.1.0 (2025-07-23)
|
||||
|
||||
### Features and enhancements
|
||||
|
||||
- **Profiles:** Stop passing response headers for Grafana-Pyroscope and parca datasources [#106730](https://github.com/grafana/grafana/pull/106730), [@simonswine](https://github.com/simonswine)
|
||||
- **Access:** Disable role none option if advanced access control is not enabled [#107378](https://github.com/grafana/grafana/pull/107378), [@Jguer](https://github.com/Jguer)
|
||||
- **Alerting:** Add OAuth2 Support for Webhook Receiver [#106302](https://github.com/grafana/grafana/pull/106302), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Add ability to import rules to GMA from Prometheus YAML [#105807](https://github.com/grafana/grafana/pull/105807), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add details to the payload when tracking import to GMA [#106404](https://github.com/grafana/grafana/pull/106404), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add export folder action to the new list view [#106256](https://github.com/grafana/grafana/pull/106256), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add filters for health and contact point in Prometheus Rules api [#106580](https://github.com/grafana/grafana/pull/106580), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Add loading spinner for loading groups state [#106289](https://github.com/grafana/grafana/pull/106289), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add need more info for import ui datasource field [#106364](https://github.com/grafana/grafana/pull/106364), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Add provenance to Prometheus API [#106596](https://github.com/grafana/grafana/pull/106596), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Add provenance to remote-ruler extension response (Enterprise)
|
||||
- **Alerting:** Add simplified routing metadata to the details tab [#106403](https://github.com/grafana/grafana/pull/106403), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Alerting:** Add state history backend to write ALERTS metric [#104361](https://github.com/grafana/grafana/pull/104361), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Add support for Redis Sentinel for Alerting HA [#106322](https://github.com/grafana/grafana/pull/106322), [@vstpme](https://github.com/vstpme)
|
||||
- **Alerting:** Allow disabling recording rules write for a data source in the UI [#106664](https://github.com/grafana/grafana/pull/106664), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Correctly persist FiredAt in SyncRuleStatePersister [#106658](https://github.com/grafana/grafana/pull/106658), [@fayzal-g](https://github.com/fayzal-g)
|
||||
- **Alerting:** Ensure errors cleared when Alerting after error [#105246](https://github.com/grafana/grafana/pull/105246), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Evaluate all imported from Prometheus rules sequentially [#106295](https://github.com/grafana/grafana/pull/106295), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Extensible Settings module [#107831](https://github.com/grafana/grafana/pull/107831), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Filter out rules managed by integrations and add an info alert [#106602](https://github.com/grafana/grafana/pull/106602), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Filter out synthetic datasource-managed rules when importing to GMA [#106358](https://github.com/grafana/grafana/pull/106358), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** List V2 - Add labels popup [#107193](https://github.com/grafana/grafana/pull/107193), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** List V2 - Grouped view filters [#106400](https://github.com/grafana/grafana/pull/106400), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** List V2 - Use backend filters for GMA rules [#106897](https://github.com/grafana/grafana/pull/106897), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Make paginated rules endpoint strongly consistent (Enterprise)
|
||||
- **Alerting:** Optimize out unnecessary permission check for rule groups (Enterprise)
|
||||
- **Alerting:** Optimize prometheus api permission checks [#106299](https://github.com/grafana/grafana/pull/106299), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Optimize prometheus api permission checks (Enterprise)
|
||||
- **Alerting:** Persist alert instance FiredAt field [#105927](https://github.com/grafana/grafana/pull/105927), [@fayzal-g](https://github.com/fayzal-g)
|
||||
- **Alerting:** Remove ruler from alert list view2 [#106778](https://github.com/grafana/grafana/pull/106778), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Resend alerts for states that are missing in the eval results [#105965](https://github.com/grafana/grafana/pull/105965), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Send notifications immediately on Error|NoData -> Normal transitions [#106421](https://github.com/grafana/grafana/pull/106421), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Support PDC in Grafana-managed recording rules [#106677](https://github.com/grafana/grafana/pull/106677), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Use default_datasource_uid as the default target for recording rules in UI [#106415](https://github.com/grafana/grafana/pull/106415), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Annotations:** Use dashboard uids instead of dashboard ids [#106676](https://github.com/grafana/grafana/pull/106676), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **App Platform Provisioning:** Add experimental nanogit mode for Git Sync [#106763](https://github.com/grafana/grafana/pull/106763), [@MissingRoberto](https://github.com/MissingRoberto)
|
||||
- **Auth:** Add Azure/Entra workload identity support [#104807](https://github.com/grafana/grafana/pull/104807), [@mehighlow](https://github.com/mehighlow)
|
||||
- **Auth:** Enable improved session handling by default for OAuth and SAML [#107442](https://github.com/grafana/grafana/pull/107442), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Auth:** Enable ssoSettingsLDAP by default [#106310](https://github.com/grafana/grafana/pull/106310), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Auth:** Remove api key endpoints [#106019](https://github.com/grafana/grafana/pull/106019), [@dmihai](https://github.com/dmihai)
|
||||
- **Auth:** Remove code for authenticating API keys [#105998](https://github.com/grafana/grafana/pull/105998), [@dmihai](https://github.com/dmihai)
|
||||
- **Azure:** Support scope selection in Resource Graph queries [#105835](https://github.com/grafana/grafana/pull/105835), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Betterer:** Only allow singleton Storage use [#105310](https://github.com/grafana/grafana/pull/105310), [@tskarhed](https://github.com/tskarhed)
|
||||
- **Caching:** Remove memcached reconnect_interval setting (Enterprise)
|
||||
- **Chore:** Update k8s.io to v0.33.1 [#105307](https://github.com/grafana/grafana/pull/105307), [@ryantxu](https://github.com/ryantxu)
|
||||
- **Cloud Monitoring:** Add support for service account impersonation [#107022](https://github.com/grafana/grafana/pull/107022), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **CloudMigrations:** Add Mute Timings as dependency for Notification Policies [#106751](https://github.com/grafana/grafana/pull/106751), [@macabu](https://github.com/macabu)
|
||||
- **CloudWatch:** Backport aws-sdk-go-v2 update from external plugin [#107136](https://github.com/grafana/grafana/pull/107136), [@njvrzm](https://github.com/njvrzm)
|
||||
- **CloudWatch:** Improve instance attribute variable query editor [#105206](https://github.com/grafana/grafana/pull/105206), [@iwysiu](https://github.com/iwysiu)
|
||||
- **Cloudwatch:** Add missing AWS regions [#106304](https://github.com/grafana/grafana/pull/106304), [@chriscerie](https://github.com/chriscerie)
|
||||
- **Dashboard Provisioning:** Reduce db load [#106114](https://github.com/grafana/grafana/pull/106114), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Dashboard:** Add Alert icon in library panels [#107723](https://github.com/grafana/grafana/pull/107723), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Add server-configurable quick ranges for the time picker [#102254](https://github.com/grafana/grafana/pull/102254), [@chodges15](https://github.com/chodges15)
|
||||
- **Dashboard:** Formatting Currency - add new custom 'financial' currency format without abbreviations [#106604](https://github.com/grafana/grafana/pull/106604), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Library Panels - Add ability to search by folder name [#106997](https://github.com/grafana/grafana/pull/106997), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Schema V2 - Auto-transform V2 dashboards in V1Resource export mode [#105997](https://github.com/grafana/grafana/pull/105997), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Datasources:** Migrate to new sigv4 middleware (Enterprise)
|
||||
- **Datasources:** Update grafana-aws-sdk for new sigv4 middleware and aws-sdk-go v1 removal [#107522](https://github.com/grafana/grafana/pull/107522), [@njvrzm](https://github.com/njvrzm)
|
||||
- **DatePicker:** Add cursor not-allowed style and hover background color [#106451](https://github.com/grafana/grafana/pull/106451), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Dependencies:** Bump Go to v1.24.4 [#106533](https://github.com/grafana/grafana/pull/106533), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/go-viper/mapstructure/v2 from 2.2.1 to 2.3.0 [#107379](https://github.com/grafana/grafana/pull/107379), [@macabu](https://github.com/macabu)
|
||||
- **Dependencies:** Bump github.com/openfga/openfga to v1.8.13 to address CVE-2025-48371 [#106064](https://github.com/grafana/grafana/pull/106064), [@macabu](https://github.com/macabu)
|
||||
- **ElasticSearch:** Remove frontend response parsing [#104148](https://github.com/grafana/grafana/pull/104148), [@nojaf](https://github.com/nojaf)
|
||||
- **Geomap:** Add HiDPI support to CARTO basemap (#81195) [#106211](https://github.com/grafana/grafana/pull/106211), [@cledwynl](https://github.com/cledwynl)
|
||||
- **Git Sync UI:** Delete Provisioned Dashboard Flow [#106593](https://github.com/grafana/grafana/pull/106593), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Grafana/data:** Extract fuzzy search core [#107110](https://github.com/grafana/grafana/pull/107110), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **I18n:** Update eslint rule to catch some untranslated object properties [#105072](https://github.com/grafana/grafana/pull/105072), [@tomratcliffe](https://github.com/tomratcliffe)
|
||||
- **InfluxDB:** Add an optional time range filter for tag queries in the query panel autocompleteInflux tag filter [#107195](https://github.com/grafana/grafana/pull/107195), [@NikolayTsvetkov](https://github.com/NikolayTsvetkov)
|
||||
- **LBAC for data sources:** Adds team filtering for lbac rules (Enterprise)
|
||||
- **Library Panels:** Mark library panel RBAC as GA & enable by default [#106833](https://github.com/grafana/grafana/pull/106833), [@kaydelaney](https://github.com/kaydelaney)
|
||||
- **Library Panels:** Modify connection api endpoint to be compatible with unified storage [#107088](https://github.com/grafana/grafana/pull/107088), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Library elements:** Remove ability to set as "library variable" [#106594](https://github.com/grafana/grafana/pull/106594), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Library panels:** Remove `libraryPanelRBAC` feature flag, and enable rbac by default [#107222](https://github.com/grafana/grafana/pull/107222), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Loki:** Remove experimental lokiQuerySplittingConfig [#107298](https://github.com/grafana/grafana/pull/107298), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Remove experimental predefined operations [#107289](https://github.com/grafana/grafana/pull/107289), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **OAuth:** Add access token as third source for user info extraction [#107636](https://github.com/grafana/grafana/pull/107636), [@Jguer](https://github.com/Jguer)
|
||||
- **Plugin Extensions:** Expose PluginMeta generic in usePluginContext [#107577](https://github.com/grafana/grafana/pull/107577), [@MattIPv4](https://github.com/MattIPv4)
|
||||
- **Postgres:** Switch the datasource plugin from lib/pq to pgx [#103961](https://github.com/grafana/grafana/pull/103961), [@zoltanbedi](https://github.com/zoltanbedi)
|
||||
- **Preferences:** Use dashboard uid for the home dashboard [#106666](https://github.com/grafana/grafana/pull/106666), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Profiles:** Stop passing response headers for Grafana-Pyroscope and parca datasources [#106577](https://github.com/grafana/grafana/pull/106577), [@simonswine](https://github.com/simonswine)
|
||||
- **Prometheus:** Deprecation message for Azure auth [#106490](https://github.com/grafana/grafana/pull/106490), [@bossinc](https://github.com/bossinc)
|
||||
- **Prometheus:** Facilitate tree shaking with exports and bundler mode [#105575](https://github.com/grafana/grafana/pull/105575), [@NWRichmond](https://github.com/NWRichmond)
|
||||
- **Prometheus:** Migrate remaining selectors to data-testid [#106564](https://github.com/grafana/grafana/pull/106564), [@idastambuk](https://github.com/idastambuk)
|
||||
- **ProvisionedFolder:** Delete folder drawer [#107089](https://github.com/grafana/grafana/pull/107089), [@ywzheng1](https://github.com/ywzheng1)
|
||||
- **Provisioning:** Add pure git repository type [#106815](https://github.com/grafana/grafana/pull/106815), [@MissingRoberto](https://github.com/MissingRoberto)
|
||||
- **Querying:** Pass dashboard and panel title as headers [#107032](https://github.com/grafana/grafana/pull/107032), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Remote Alertmanager:** Send SMTP config [#106337](https://github.com/grafana/grafana/pull/106337), [@santihernandezc](https://github.com/santihernandezc)
|
||||
- **Restore dashboards:** Add filters and search [#106994](https://github.com/grafana/grafana/pull/106994), [@Clarity-89](https://github.com/Clarity-89)
|
||||
- **SCIM:** Ignore unsupported fields in user PATCH requests (Enterprise)
|
||||
- **SCIM:** Implement operation for adding an externalId value to a team (Enterprise)
|
||||
- **SCIM:** Implement the add members operation in group PATCH requests (Enterprise)
|
||||
- **SCIM:** Implement the remove members operation in group PATCH requests (Enterprise)
|
||||
- **SCIM:** Update externalId field in group PATCH request (Enterprise)
|
||||
- **SQL Expressions:** Always convert on type first [#106083](https://github.com/grafana/grafana/pull/106083), [@kylebrandt](https://github.com/kylebrandt)
|
||||
- **Select:** Set min width for the current selected item when width=auto [#106131](https://github.com/grafana/grafana/pull/106131), [@tskarhed](https://github.com/tskarhed)
|
||||
- **StateTimeline:** Display false and empty string values [#107059](https://github.com/grafana/grafana/pull/107059), [@jesdavpet](https://github.com/jesdavpet)
|
||||
- **StateTimeline:** Support `NaN` and `null` value mappings [#105638](https://github.com/grafana/grafana/pull/105638), [@fastfrwrd](https://github.com/fastfrwrd)
|
||||
- **Storage:** Take `migration_locking` setting into account [#105938](https://github.com/grafana/grafana/pull/105938), [@JohnnyQQQQ](https://github.com/JohnnyQQQQ)
|
||||
- **TableNG:** Refactor to better take advantage of react-data-grid [#103755](https://github.com/grafana/grafana/pull/103755), [@leeoniya](https://github.com/leeoniya)
|
||||
- **Tables:** Pills for Table Cells [#107485](https://github.com/grafana/grafana/pull/107485), [@timlevett](https://github.com/timlevett)
|
||||
- **Teams:** Add support for updating externalId field [#106406](https://github.com/grafana/grafana/pull/106406), [@dmihai](https://github.com/dmihai)
|
||||
- **Tempo:** Enable native histograms for Tempo service graph [#105989](https://github.com/grafana/grafana/pull/105989), [@bohandley](https://github.com/bohandley)
|
||||
- **TimeRangePicker:** Highlight range on hover [#106616](https://github.com/grafana/grafana/pull/106616), [@joshhunt](https://github.com/joshhunt)
|
||||
- **TraceView:** Resource attributes links extension point [#104680](https://github.com/grafana/grafana/pull/104680), [@edvard-falkskar](https://github.com/edvard-falkskar)
|
||||
- **Transformations:** Add "Auto" mode to Organize Fields [#103055](https://github.com/grafana/grafana/pull/103055), [@gelicia](https://github.com/gelicia)
|
||||
- **Transformations:** GA the Regression transformation [#106074](https://github.com/grafana/grafana/pull/106074), [@gelicia](https://github.com/gelicia)
|
||||
- **Unified storage:** Respect GF_DATABASE_URL override [#105331](https://github.com/grafana/grafana/pull/105331), [@pstibrany](https://github.com/pstibrany)
|
||||
- **VQB:** Add selected columns to GROUP BY dropdown (#106349) [#106391](https://github.com/grafana/grafana/pull/106391), [@Shubham19032004](https://github.com/Shubham19032004)
|
||||
- **VQB:** Allow custom table names in TableSelector [#106420](https://github.com/grafana/grafana/pull/106420), [@Victorthedev](https://github.com/Victorthedev)
|
||||
- **XYChart:** Add support for x=time [#106459](https://github.com/grafana/grafana/pull/106459), [@leeoniya](https://github.com/leeoniya)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **FlameGraph:** Fix bug for function names that conflict with JavaScript object prototype properties [#106625](https://github.com/grafana/grafana/pull/106625), [@simonswine](https://github.com/simonswine)
|
||||
- **Security:** Fixed CVE-2025-6023
|
||||
- **Security:** Fixed CVE-2025-6197
|
||||
- **Alerting:** Fix $value type when single data source is queried [#106080](https://github.com/grafana/grafana/pull/106080), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Fix ImportToGMARules flaky test [#106495](https://github.com/grafana/grafana/pull/106495), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix RefIds not being shown when creating or editing Grafana-managed recording rule [#106840](https://github.com/grafana/grafana/pull/106840), [@soniaAguilarPeiron](https://github.com/soniaAguilarPeiron)
|
||||
- **Alerting:** Fix contact points tab visibility when user can only create [#106735](https://github.com/grafana/grafana/pull/106735), [@JacobsonMT](https://github.com/JacobsonMT)
|
||||
- **Alerting:** Fix eval time unit in list view [#106488](https://github.com/grafana/grafana/pull/106488), [@ebuildy](https://github.com/ebuildy)
|
||||
- **Alerting:** Fix group interval override when adding new rules [#107324](https://github.com/grafana/grafana/pull/107324), [@konrad147](https://github.com/konrad147)
|
||||
- **Alerting:** Fix group-level labels and query_offset in the import API [#106379](https://github.com/grafana/grafana/pull/106379), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Fix notification policy conflicts originating from provenance mismatch [#107343](https://github.com/grafana/grafana/pull/107343), [@moustafab](https://github.com/moustafab)
|
||||
- **Alerting:** Fix resolved notifications for same-label Error to Normal transitions [#106210](https://github.com/grafana/grafana/pull/106210), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
- **Alerting:** Hide labels section if we only have private labels [#105996](https://github.com/grafana/grafana/pull/105996), [@gillesdemey](https://github.com/gillesdemey)
|
||||
- **Annotations:** Remove prometheus from legacy runner [#106737](https://github.com/grafana/grafana/pull/106737), [@scottlepp](https://github.com/scottlepp)
|
||||
- **Azure:** Fix Application Insights metadata requests [#105614](https://github.com/grafana/grafana/pull/105614), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix duplicated trace links [#105698](https://github.com/grafana/grafana/pull/105698), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix legend formatting [#106504](https://github.com/grafana/grafana/pull/106504), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Azure:** Fix resource name determination in template variable queries [#105705](https://github.com/grafana/grafana/pull/105705), [@aangelisc](https://github.com/aangelisc)
|
||||
- **BarChart/StateTimeline:** Use noValue setting for error message when data is empty [#107147](https://github.com/grafana/grafana/pull/107147), [@fastfrwrd](https://github.com/fastfrwrd)
|
||||
- **CloudWatch:** Fix http client handling + assume role bug [#107893](https://github.com/grafana/grafana/pull/107893), [@njvrzm](https://github.com/njvrzm)
|
||||
- **CloudWatch:** Fix proxy transport issue [#107807](https://github.com/grafana/grafana/pull/107807), [@njvrzm](https://github.com/njvrzm)
|
||||
- **Dashboard:** FF `dashboardNewLayouts` Fix library panels non-editable when multiple added [#107052](https://github.com/grafana/grafana/pull/107052), [@axelavargas](https://github.com/axelavargas)
|
||||
- **Dashboard:** Fix cache validation to prevent stale cache [#105918](https://github.com/grafana/grafana/pull/105918), [@yashschandra](https://github.com/yashschandra)
|
||||
- **Dashboard:** Fixes issue with dashboard links that include all variables [#106356](https://github.com/grafana/grafana/pull/106356), [@torkelo](https://github.com/torkelo)
|
||||
- **Dashboards:** Fix history list for dashboard uids that end in `-` [#107073](https://github.com/grafana/grafana/pull/107073), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Drilldown:** Fix js crash when using http [#105646](https://github.com/grafana/grafana/pull/105646), [@chu121su12](https://github.com/chu121su12)
|
||||
- **Fix:** Increase login_attempt.ip_address column length for IPv6 support [#107035](https://github.com/grafana/grafana/pull/107035), [@Jguer](https://github.com/Jguer)
|
||||
- **FlameGraph:** Fix bug for function names that conflict with JavaScript object prototype properties [#106338](https://github.com/grafana/grafana/pull/106338), [@simonswine](https://github.com/simonswine)
|
||||
- **Folders:** Correctly resolve nested folder breadcrumbs [#106344](https://github.com/grafana/grafana/pull/106344), [@IevaVasiljeva](https://github.com/IevaVasiljeva)
|
||||
- **GrafanaUI:** Fix Combobox ignoring loading prop [#105584](https://github.com/grafana/grafana/pull/105584), [@ValeraS](https://github.com/ValeraS)
|
||||
- **Graphite:** Fix annotation queries [#106553](https://github.com/grafana/grafana/pull/106553), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix date mutation [#107414](https://github.com/grafana/grafana/pull/107414), [@aangelisc](https://github.com/aangelisc)
|
||||
- **Graphite:** Fix nested variable interpolation for repeated rows [#106976](https://github.com/grafana/grafana/pull/106976), [@aangelisc](https://github.com/aangelisc)
|
||||
- **K8s:** Dashboards /apis: Fix library element connections [#106734](https://github.com/grafana/grafana/pull/106734), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Loki:** Fix health check message [#107170](https://github.com/grafana/grafana/pull/107170), [@wooffie](https://github.com/wooffie)
|
||||
- **Loki:** Fix issue where step parameter using a template variable was marked as invalid [#106541](https://github.com/grafana/grafana/pull/106541), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **Loki:** Fix label browser not sorted after selection of a label [#107394](https://github.com/grafana/grafana/pull/107394), [@paulojmdias](https://github.com/paulojmdias)
|
||||
- **Org:** Fix org deletion [#106193](https://github.com/grafana/grafana/pull/106193), [@stephaniehingtgen](https://github.com/stephaniehingtgen)
|
||||
- **Plugins:** Fix and encode invalid gRPC header values [#107339](https://github.com/grafana/grafana/pull/107339), [@ivanahuckova](https://github.com/ivanahuckova)
|
||||
- **PostgreSQL:** Fix error on panel when toggling sqlDatasourceDatabaseSelection feature [#106965](https://github.com/grafana/grafana/pull/106965), [@HasithDeAlwis](https://github.com/HasithDeAlwis)
|
||||
- **Profiles:** Fix for passing the response headers [#106293](https://github.com/grafana/grafana/pull/106293), [@simonswine](https://github.com/simonswine)
|
||||
- **Reporting:** Stop sending reports with Never schedule on creation (Enterprise)
|
||||
- **SCIM:** Fix PUT request for deactivating a user (Enterprise)
|
||||
- **SCIM:** Fix the removal of all members in group PUT requests (Enterprise)
|
||||
- **SCIM:** Fix user patch operation (Enterprise)
|
||||
- **Security:** Add fix for CVE-2025-3580 [#105976](https://github.com/grafana/grafana/pull/105976), [@baldm0mma](https://github.com/baldm0mma)
|
||||
- **Security:** Fixes for CVE-2025-6197 and CVE-2025-6023 [#108333](https://github.com/grafana/grafana/pull/108333), [@mgyongyosi](https://github.com/mgyongyosi)
|
||||
- **Settings:** Fix reencryption and rollback of encrypted values in setting table (Enterprise)
|
||||
- **Tempo:** Fix showing dangling edges in NodeGraph [#107245](https://github.com/grafana/grafana/pull/107245), [@ifrost](https://github.com/ifrost)
|
||||
- **ToolTip:** Fix flexbox bug with tooltip when `maxWidth` is set manually [#107145](https://github.com/grafana/grafana/pull/107145), [@jdmarshall](https://github.com/jdmarshall)
|
||||
- **URLParams:** Stringify true values as key=true always (fixes issues with variables with true value) [#106440](https://github.com/grafana/grafana/pull/106440), [@torkelo](https://github.com/torkelo)
|
||||
|
||||
<!-- 12.0.2+security-01 END -->
|
||||
<!-- 11.6.3+security-01 START -->
|
||||
### Breaking changes
|
||||
|
||||
# 11.6.3+security-01 (2025-07-17)
|
||||
- **Alerting:** Enable recording rules by default [#105603](https://github.com/grafana/grafana/pull/105603), [@alexander-akhmetov](https://github.com/alexander-akhmetov)
|
||||
|
||||
### Features and enhancements
|
||||
### Plugin development fixes & changes
|
||||
|
||||
- **Profiles:** Stop passing response headers for Grafana-Pyroscope and parca datasources [#106729](https://github.com/grafana/grafana/pull/106729), [@simonswine](https://github.com/simonswine)
|
||||
- **Carousel:** Always center image [#106468](https://github.com/grafana/grafana/pull/106468), [@ashharrison90](https://github.com/ashharrison90)
|
||||
- **Drawer:** Include divider and close button when passing a custom title element [#106896](https://github.com/grafana/grafana/pull/106896), [@ashharrison90](https://github.com/ashharrison90)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **FlameGraph:** Fix bug for function names that conflict with JavaScript object prototype properties [#106624](https://github.com/grafana/grafana/pull/106624), [@simonswine](https://github.com/simonswine)
|
||||
- **Security:** Fixed CVE-2025-6023
|
||||
- **Security:** Fixed CVE-2025-6197
|
||||
|
||||
<!-- 11.6.3+security-01 END -->
|
||||
<!-- 11.5.6+security-01 START -->
|
||||
|
||||
# 11.5.6+security-01 (2025-07-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-6023
|
||||
- **Security:** Fixed CVE-2025-6197
|
||||
|
||||
<!-- 11.5.6+security-01 END -->
|
||||
<!-- 11.4.6+security-01 START -->
|
||||
|
||||
# 11.4.6+security-01 (2025-07-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-6023
|
||||
- **Security:** Fixed CVE-2025-6197
|
||||
|
||||
<!-- 11.4.6+security-01 END -->
|
||||
<!-- 11.3.8+security-01 START -->
|
||||
|
||||
# 11.3.8+security-01 (2025-07-17)
|
||||
|
||||
### Bug fixes
|
||||
|
||||
- **Security:** Fixed CVE-2025-6023
|
||||
- **Security:** Fixed CVE-2025-6197
|
||||
|
||||
<!-- 11.3.8+security-01 END -->
|
||||
<!-- 12.1.0 END -->
|
||||
<!-- 12.0.2 START -->
|
||||
|
||||
# 12.0.2 (2025-06-17)
|
||||
|
||||
@@ -1,56 +1,29 @@
|
||||
# Contribute to Grafana
|
||||
# Contributing to Grafana
|
||||
|
||||
Thank you for your interest in contributing to Grafana! We welcome all people who want to contribute in a healthy and constructive manner within our community. To help us create a safe and positive community experience for all, we require all participants to adhere to the [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
This document is a guide to help you through the process of making technical contributions to Grafana.
|
||||
This document is a guide to help you through the process of contributing to Grafana.
|
||||
|
||||
Whether you're a new contributer or a seasoned veteran we hope these resources help you connect with the community:
|
||||
## Become a contributor
|
||||
|
||||
Interact and be heard:
|
||||
You can contribute to Grafana in several ways. Here are some examples:
|
||||
|
||||
- Forums: Do you have a problem, question, or curiosity? Visit our [forums](https://gra.fan/fromgithubtoforums) for a reservoir of knowledge- submit your own questions and answers!
|
||||
- Meetups: Craving in-person connections without the long journeys? [Join your local Grafana & Friends meetup group](https://gra.fan/githubtomeetup)!
|
||||
- Community Slack: Eager for real-time connections with fellow users? Begin a conversation on [Slack](https://gra.fan/githubtoslack).
|
||||
Learn:
|
||||
- YouTube: From getting started to exploring newer projects like Pyroscope and Beyla, the [Grafana YouTube channel](https://gra.fan/githubtoyoutube) has what you need to get started!
|
||||
- Meetups: Join a [group near you](https://gra.fan/githubtomeetup) to learn from local experts and ask questions in real time.
|
||||
Share your story:
|
||||
- Meetups and blogs: We’d love to feature your OSS Grafana Labs use case or story at an upcoming Grafana & Friends meetup or on the Grafana blog! Submit your idea [here](https://gra.fan/githubtocca) and we’ll connect with you on next steps if accepted.
|
||||
|
||||
## Make technical contributions
|
||||
|
||||
We welcome your technical contributions! Here are some examples:
|
||||
|
||||
- Contribute to the Grafana codebase- check out these [help-wanted issues](<(https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)>)
|
||||
- Develop community [plugins](https://grafana.com/developers/plugin-tools)
|
||||
- Report [bugs](https://github.com/grafana/grafana/issues/new?template=0-bug-report.yaml)
|
||||
- [Triage issues](https://github.com/grafana/grafana/blob/4414b92e93440cc9ed0f281989ee71dc16216a15/contribute/triage-issues.md)
|
||||
- Report [security vulnerabilities](https://github.com/grafana/grafana/security/policy)
|
||||
- Submit a [feature request](https://github.com/grafana/grafana/issues/new?template=1-feature_requests.md)
|
||||
- Write [technical documentation](https://github.com/grafana/grafana/blob/4414b92e93440cc9ed0f281989ee71dc16216a15/contribute/documentation/README.md)
|
||||
- Contribute to the Grafana codebase.
|
||||
- Report and triage bugs.
|
||||
- Develop community plugins and dashboards.
|
||||
- Write technical documentation and blog posts, for users and contributors.
|
||||
- Organize meetups and user groups in your local area.
|
||||
- Help others by answering questions about Grafana.
|
||||
|
||||
**Please note:** We do not currently accept contributions for translations. Please do not submit pull requests translating grafana.json files - they will be rejected. We do accept contributions to mark up phrases for translation. See [Internationalization](contribute/internationalization.md).
|
||||
|
||||
### Your first contribution
|
||||
|
||||
Unsure where to begin contributing to Grafana? Start by browsing issues labeled `beginner friendly` or `help wanted`.
|
||||
|
||||
- [Beginner-friendly](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) issues are generally straightforward to complete.
|
||||
- [Help wanted](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issues are problems we would like the community to help us with regardless of complexity.
|
||||
|
||||
If you're looking to make a code change, see how to set up your environment for [local development](contribute/developer-guide.md).
|
||||
|
||||
When you're ready to contribute, it's time to [create a pull request](/contribute/create-pull-request.md).
|
||||
|
||||
### Develop a plugin
|
||||
|
||||
Developing a Grafana plugin is a fantastic way to share your unique ideas with the community, extend the platform’s capabilities, and make a real impact on how people visualize and understand their data. Check out our guide to creating [plugins](https://grafana.com/developers/plugin-tools)
|
||||
For more ways to contribute, check out the [Open Source Guides](https://opensource.guide/how-to-contribute/).
|
||||
|
||||
### Report bugs
|
||||
|
||||
Before submitting a new issue, try to make sure someone hasn't already reported the problem. Look through the [existing issues](https://github.com/grafana/grafana/issues) for similar issues.
|
||||
|
||||
Report a bug by submitting a [bug report](https://github.com/grafana/grafana/issues/new?template=0-bug-report.yaml). Make sure that you provide as much information as possible on how to reproduce the bug.
|
||||
Report a bug by submitting a [bug report](https://github.com/grafana/grafana/issues/new?labels=type%3A+bug&template=1-bug_report.md). Make sure that you provide as much information as possible on how to reproduce the bug.
|
||||
|
||||
Follow the issue template and add additional information that will help us replicate the problem.
|
||||
|
||||
@@ -65,25 +38,42 @@ For a dashboard related issues:
|
||||
|
||||
For authentication and alerting Grafana server logs are useful.
|
||||
|
||||
#### Security issues
|
||||
|
||||
If you believe you've found a security vulnerability, please read our [security policy](https://github.com/grafana/grafana/security/policy) for more details.
|
||||
|
||||
### Suggest enhancements
|
||||
|
||||
If you have an idea of how to improve Grafana, submit a [feature request](https://github.com/grafana/grafana/issues/new?assignees=&labels=type%2Ffeature-request&projects=&template=1-feature_requests.md).
|
||||
|
||||
We want to make Grafana accessible to even more people. Submit an [accessibility issue](https://github.com/grafana/grafana/issues/new?labels=type%3A+accessibility&template=3-accessibility.md) to help us understand what we can improve.
|
||||
|
||||
### Write documentation
|
||||
|
||||
To edit or write technical content, refer to [Contribute to our documentation](/contribute/documentation/README.md). We welcome your expertise and input as our body of technical content grows.
|
||||
|
||||
### Triage issues
|
||||
|
||||
If you don't have the knowledge or time to code, consider helping with _issue triage_. The community will thank you for saving them time by spending some of yours.
|
||||
|
||||
Read more about the ways you can [Triage issues](/contribute/triage-issues.md).
|
||||
|
||||
#### Security issues
|
||||
### Answering questions
|
||||
|
||||
If you believe you've found a security vulnerability, please read our [security policy](https://github.com/grafana/grafana/security/policy) for more details on reporting.
|
||||
If you have a question and you can't find the answer in the [documentation](https://grafana.com/docs/), the next step is to ask it on the [community site](https://community.grafana.com/).
|
||||
|
||||
### Suggest enhancements
|
||||
It's important to us to help these users, and we'd love your help. Sign up to our [community site](https://community.grafana.com/), and start helping other Grafana users by answering their questions.
|
||||
|
||||
If you have an idea of how to improve Grafana, submit a [feature request](https://github.com/grafana/grafana/issues/new?template=1-feature_requests.md).
|
||||
### Your first contribution
|
||||
|
||||
We want to make Grafana accessible to even more people. Submit an [accessibility issue](https://github.com/grafana/grafana/issues/new?template=2-accessibility.md) to help us understand what we can improve.
|
||||
Unsure where to begin contributing to Grafana? Start by browsing issues labeled `beginner friendly` or `help wanted`.
|
||||
|
||||
### Write documentation
|
||||
- [Beginner-friendly](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22beginner+friendly%22) issues are generally straightforward to complete.
|
||||
- [Help wanted](https://github.com/grafana/grafana/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) issues are problems we would like the community to help us with regardless of complexity.
|
||||
|
||||
To edit or write technical content, refer to [Contribute to our documentation](/contribute/documentation/README.md). We welcome your expertise and input as our body of technical content grows.
|
||||
If you're looking to make a code change, see how to set up your environment for [local development](contribute/developer-guide.md).
|
||||
|
||||
When you're ready to contribute, it's time to [create a pull request](/contribute/create-pull-request.md).
|
||||
|
||||
#### Contributor License Agreement (CLA)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ ARG JS_SRC=js-builder
|
||||
# By using FROM instructions we can delegate dependency updates to dependabot
|
||||
FROM alpine:3.21.3 AS alpine-base
|
||||
FROM ubuntu:22.04 AS ubuntu-base
|
||||
FROM golang:1.24.4-alpine AS go-builder-base
|
||||
FROM golang:1.24.6-alpine AS go-builder-base
|
||||
FROM --platform=${JS_PLATFORM} node:22-alpine AS js-builder-base
|
||||
|
||||
# Javascript build stage
|
||||
@@ -69,9 +69,9 @@ COPY go.* ./
|
||||
COPY .bingo .bingo
|
||||
COPY .citools .citools
|
||||
|
||||
# Copy go dependencies first
|
||||
# If updating this, please also update devenv/frontend-service/backend.dockerfile
|
||||
# Include vendored dependencies
|
||||
COPY pkg/util/xorm pkg/util/xorm
|
||||
COPY pkg/apis/secret pkg/apis/secret
|
||||
COPY pkg/apiserver pkg/apiserver
|
||||
COPY pkg/apimachinery pkg/apimachinery
|
||||
COPY pkg/build pkg/build
|
||||
@@ -83,7 +83,6 @@ COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
|
||||
COPY pkg/semconv pkg/semconv
|
||||
COPY pkg/aggregator pkg/aggregator
|
||||
COPY apps/playlist apps/playlist
|
||||
COPY apps/secret apps/secret
|
||||
COPY apps/investigations apps/investigations
|
||||
COPY apps/advisor apps/advisor
|
||||
COPY apps/dashboard apps/dashboard
|
||||
|
||||
16
Makefile
16
Makefile
@@ -9,7 +9,7 @@ include .bingo/Variables.mk
|
||||
include .citools/Variables.mk
|
||||
|
||||
GO = go
|
||||
GO_VERSION = 1.24.4
|
||||
GO_VERSION = 1.24.6
|
||||
GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh)
|
||||
GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh)
|
||||
SH_FILES ?= $(shell find ./scripts -name *.sh)
|
||||
@@ -279,18 +279,6 @@ run-frontend: deps-js ## Fetch js dependencies and watch frontend for rebuild
|
||||
run-air: ## [Experimental] Build and run backend, and watch for changes. See .air.toml for configuration. Check https://github.com/air-verse/air for installation instructions.
|
||||
air -c .air.toml
|
||||
|
||||
.PHONY: frontend-service-check
|
||||
frontend-service-check:
|
||||
./devenv/frontend-service/local-init.sh
|
||||
|
||||
.PHONY: frontend-service-up
|
||||
frontend-service-up: frontend-service-check
|
||||
tilt up -f devenv/frontend-service/Tiltfile
|
||||
|
||||
.PHONY: frontend-service-down
|
||||
frontend-service-down: frontend-service-check
|
||||
tilt down -f devenv/frontend-service/Tiltfile
|
||||
|
||||
##@ Testing
|
||||
|
||||
.PHONY: test-go
|
||||
@@ -483,7 +471,7 @@ protobuf: ## Compile protobuf definitions
|
||||
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.5
|
||||
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.4.0
|
||||
buf generate pkg/plugins/backendplugin/pluginextensionv2 --template pkg/plugins/backendplugin/pluginextensionv2/buf.gen.yaml
|
||||
buf generate apps/secret/decrypt/v1beta1 --template apps/secret/decrypt/v1beta1/buf.gen.yaml
|
||||
buf generate pkg/apis/secret/v0alpha1/decrypt --template pkg/apis/secret/v0alpha1/decrypt/buf.gen.yaml
|
||||
buf generate pkg/storage/unified/proto --template pkg/storage/unified/proto/buf.gen.yaml
|
||||
buf generate pkg/services/authz/proto/v1 --template pkg/services/authz/proto/v1/buf.gen.yaml
|
||||
buf generate pkg/services/ngalert/store/proto/v1 --template pkg/services/ngalert/store/proto/v1/buf.gen.yaml
|
||||
|
||||
@@ -1,22 +1,3 @@
|
||||
APP_SDK_VERSION := v0.39.2
|
||||
APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION)
|
||||
APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk
|
||||
|
||||
.PHONY: install-app-sdk
|
||||
install-app-sdk: $(APP_SDK_BIN) ## Install the Grafana App SDK
|
||||
|
||||
$(APP_SDK_BIN):
|
||||
@echo "Installing Grafana App SDK version $(APP_SDK_VERSION)"
|
||||
@mkdir -p $(APP_SDK_DIR)
|
||||
# The only way to install specific versions of binaries using `go install`
|
||||
# is by setting GOBIN to the directory you want to install the binary to.
|
||||
GOBIN=$(APP_SDK_DIR) go install github.com/grafana/grafana-app-sdk/cmd/grafana-app-sdk@$(APP_SDK_VERSION)
|
||||
@touch $@
|
||||
|
||||
.PHONY: update-app-sdk
|
||||
update-app-sdk: ## Update the Grafana App SDK dependency in go.mod
|
||||
go get github.com/grafana/grafana-app-sdk@$(APP_SDK_VERSION)
|
||||
|
||||
.PHONY: generate
|
||||
generate: ## Run Grafana App SDK code generation
|
||||
@$(APP_SDK_BIN) generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none
|
||||
generate:
|
||||
@grafana-app-sdk generate -g ./pkg/apis --grouping=group --postprocess --defencoding=none
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module github.com/grafana/grafana/apps/advisor
|
||||
|
||||
go 1.24.4
|
||||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.39.2
|
||||
k8s.io/apimachinery v0.33.2
|
||||
github.com/grafana/grafana-app-sdk v0.39.0
|
||||
k8s.io/apimachinery v0.33.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff
|
||||
)
|
||||
@@ -33,7 +33,7 @@ require (
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.1 // indirect
|
||||
github.com/grafana/grafana-app-sdk/logging v0.38.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -79,9 +79,9 @@ require (
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.33.2 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.2 // indirect
|
||||
k8s.io/client-go v0.33.2 // indirect
|
||||
k8s.io/api v0.33.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.1 // indirect
|
||||
k8s.io/client-go v0.33.1 // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
|
||||
@@ -63,14 +63,11 @@ github.com/grafana/grafana-app-sdk v0.30.0/go.mod h1:jhfqNIovb+Mes2vdMf9iMCWQkp1
|
||||
github.com/grafana/grafana-app-sdk v0.31.0/go.mod h1:Xw00NL7qpRLo5r3Gn48Bl1Xn2n4eUDI5pYf/wMufKWs=
|
||||
github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI=
|
||||
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
|
||||
github.com/grafana/grafana-app-sdk v0.39.2 h1:ymfr+1318t+JC9U2OYrzVpGmNG/aJONUmFFu/G98Xh8=
|
||||
github.com/grafana/grafana-app-sdk v0.39.2/go.mod h1:t0m6q561lpoHQCixS9LUHFUhUzDClzNtm7BH60gHVSY=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.29.0 h1:mgbXaAf33aFwqwGVeaX30l8rkeAJH0iACgX5Rn6YkN4=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.29.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.30.0/go.mod h1:xy6ZyVXl50Z3DBDLybvBPphbykPhuVNed/VNmen9DQM=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.35.0/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
|
||||
github.com/grafana/grafana-app-sdk/logging v0.39.1/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 h1:VNqngBF40hVlDloBruUehVYC3ArSgIyScOAyMRqBxRg=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1/go.mod h1:RBRO7fro65R6tjKzYgLAFo0t1QEXY1Dp+i/bvpRiqiQ=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
@@ -305,25 +302,21 @@ k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
||||
k8s.io/api v0.33.1/go.mod h1:87esjTn9DRSRTD4fWMXamiXxJhpOIREjWOSjsW1kEHw=
|
||||
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
|
||||
k8s.io/apiextensions-apiserver v0.32.0 h1:S0Xlqt51qzzqjKPxfgX1xh4HBZE+p8KKBq+k2SWNOE0=
|
||||
k8s.io/apiextensions-apiserver v0.32.0/go.mod h1:86hblMvN5yxMvZrZFX2OhIHAuFIMJIZ19bTvzkP+Fmw=
|
||||
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
|
||||
k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss=
|
||||
k8s.io/apiextensions-apiserver v0.33.1/go.mod h1:uNQ52z1A1Gu75QSa+pFK5bcXc4hq7lpOXbweZgi4dqA=
|
||||
k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8=
|
||||
k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg=
|
||||
k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apimachinery v0.33.1/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8=
|
||||
k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY=
|
||||
k8s.io/client-go v0.33.1/go.mod h1:JAsUrl1ArO7uRVFWfcj6kOomSlCv+JpvIsp6usAGefA=
|
||||
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
|
||||
@@ -24,8 +24,5 @@ type CheckMetadata struct {
|
||||
|
||||
// NewCheckMetadata creates a new CheckMetadata object.
|
||||
func NewCheckMetadata() *CheckMetadata {
|
||||
return &CheckMetadata{
|
||||
Finalizers: []string{},
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
return &CheckMetadata{}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,8 @@ import (
|
||||
type Check struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
|
||||
// Spec is the spec of the Check
|
||||
Spec CheckSpec `json:"spec" yaml:"spec"`
|
||||
|
||||
Status CheckStatus `json:"status" yaml:"status"`
|
||||
Spec CheckSpec `json:"spec" yaml:"spec"`
|
||||
CheckStatus CheckStatus `json:"status" yaml:"status"`
|
||||
}
|
||||
|
||||
func (o *Check) GetSpec() any {
|
||||
@@ -40,14 +37,14 @@ func (o *Check) SetSpec(spec any) error {
|
||||
|
||||
func (o *Check) GetSubresources() map[string]any {
|
||||
return map[string]any{
|
||||
"status": o.Status,
|
||||
"status": o.CheckStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Check) GetSubresource(name string) (any, bool) {
|
||||
switch name {
|
||||
case "status":
|
||||
return o.Status, true
|
||||
return o.CheckStatus, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
@@ -60,7 +57,7 @@ func (o *Check) SetSubresource(name string, value any) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set status type %#v, not of type CheckStatus", value)
|
||||
}
|
||||
o.Status = cast
|
||||
o.CheckStatus = cast
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("subresource '%s' does not exist", name)
|
||||
@@ -222,20 +219,6 @@ func (o *Check) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *Check) DeepCopy() *Check {
|
||||
cpy := &Check{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *Check) DeepCopyInto(dst *Check) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
|
||||
o.Spec.DeepCopyInto(&dst.Spec)
|
||||
o.Status.DeepCopyInto(&dst.Status)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.Object = &Check{}
|
||||
|
||||
@@ -279,41 +262,5 @@ func (o *CheckList) SetItems(items []resource.Object) {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *CheckList) DeepCopy() *CheckList {
|
||||
cpy := &CheckList{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *CheckList) DeepCopyInto(dst *CheckList) {
|
||||
resource.CopyObjectInto(dst, o)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.ListObject = &CheckList{}
|
||||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
// DeepCopy creates a full deep copy of Spec
|
||||
func (s *CheckSpec) DeepCopy() *CheckSpec {
|
||||
cpy := &CheckSpec{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies Spec into another Spec object
|
||||
func (s *CheckSpec) DeepCopyInto(dst *CheckSpec) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
|
||||
// DeepCopy creates a full deep copy of CheckStatus
|
||||
func (s *CheckStatus) DeepCopy() *CheckStatus {
|
||||
cpy := &CheckStatus{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies CheckStatus into another CheckStatus object
|
||||
func (s *CheckStatus) DeepCopyInto(dst *CheckStatus) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
|
||||
@@ -3,18 +3,16 @@
|
||||
package v0alpha1
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type CheckReport struct {
|
||||
// Number of elements analyzed
|
||||
Count int64 `json:"count"`
|
||||
// List of failures
|
||||
Failures []CheckReportFailure `json:"failures"`
|
||||
type CheckErrorLink struct {
|
||||
// URL to a page with more information about the error
|
||||
Url string `json:"url"`
|
||||
// Human readable error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewCheckReport creates a new CheckReport object.
|
||||
func NewCheckReport() *CheckReport {
|
||||
return &CheckReport{
|
||||
Failures: []CheckReportFailure{},
|
||||
}
|
||||
// NewCheckErrorLink creates a new CheckErrorLink object.
|
||||
func NewCheckErrorLink() *CheckErrorLink {
|
||||
return &CheckErrorLink{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
@@ -35,22 +33,7 @@ type CheckReportFailure struct {
|
||||
|
||||
// NewCheckReportFailure creates a new CheckReportFailure object.
|
||||
func NewCheckReportFailure() *CheckReportFailure {
|
||||
return &CheckReportFailure{
|
||||
Links: []CheckErrorLink{},
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type CheckErrorLink struct {
|
||||
// URL to a page with more information about the error
|
||||
Url string `json:"url"`
|
||||
// Human readable error message
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// NewCheckErrorLink creates a new CheckErrorLink object.
|
||||
func NewCheckErrorLink() *CheckErrorLink {
|
||||
return &CheckErrorLink{}
|
||||
return &CheckReportFailure{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
@@ -73,7 +56,7 @@ func NewCheckstatusOperatorState() *CheckstatusOperatorState {
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type CheckStatus struct {
|
||||
Report CheckReport `json:"report"`
|
||||
Report CheckV0alpha1StatusReport `json:"report"`
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]CheckstatusOperatorState `json:"operatorStates,omitempty"`
|
||||
@@ -84,7 +67,7 @@ type CheckStatus struct {
|
||||
// NewCheckStatus creates a new CheckStatus object.
|
||||
func NewCheckStatus() *CheckStatus {
|
||||
return &CheckStatus{
|
||||
Report: *NewCheckReport(),
|
||||
Report: *NewCheckV0alpha1StatusReport(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,3 +87,16 @@ const (
|
||||
CheckStatusOperatorStateStateInProgress CheckStatusOperatorStateState = "in_progress"
|
||||
CheckStatusOperatorStateStateFailed CheckStatusOperatorStateState = "failed"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type CheckV0alpha1StatusReport struct {
|
||||
// Number of elements analyzed
|
||||
Count int64 `json:"count"`
|
||||
// List of failures
|
||||
Failures []CheckReportFailure `json:"failures"`
|
||||
}
|
||||
|
||||
// NewCheckV0alpha1StatusReport creates a new CheckV0alpha1StatusReport object.
|
||||
func NewCheckV0alpha1StatusReport() *CheckV0alpha1StatusReport {
|
||||
return &CheckV0alpha1StatusReport{}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,5 @@ type CheckTypeMetadata struct {
|
||||
|
||||
// NewCheckTypeMetadata creates a new CheckTypeMetadata object.
|
||||
func NewCheckTypeMetadata() *CheckTypeMetadata {
|
||||
return &CheckTypeMetadata{
|
||||
Finalizers: []string{},
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
return &CheckTypeMetadata{}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,8 @@ import (
|
||||
type CheckType struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
|
||||
// Spec is the spec of the CheckType
|
||||
Spec CheckTypeSpec `json:"spec" yaml:"spec"`
|
||||
|
||||
Status CheckTypeStatus `json:"status" yaml:"status"`
|
||||
Spec CheckTypeSpec `json:"spec" yaml:"spec"`
|
||||
CheckTypeStatus CheckTypeStatus `json:"status" yaml:"status"`
|
||||
}
|
||||
|
||||
func (o *CheckType) GetSpec() any {
|
||||
@@ -40,14 +37,14 @@ func (o *CheckType) SetSpec(spec any) error {
|
||||
|
||||
func (o *CheckType) GetSubresources() map[string]any {
|
||||
return map[string]any{
|
||||
"status": o.Status,
|
||||
"status": o.CheckTypeStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *CheckType) GetSubresource(name string) (any, bool) {
|
||||
switch name {
|
||||
case "status":
|
||||
return o.Status, true
|
||||
return o.CheckTypeStatus, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
@@ -60,7 +57,7 @@ func (o *CheckType) SetSubresource(name string, value any) error {
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set status type %#v, not of type CheckTypeStatus", value)
|
||||
}
|
||||
o.Status = cast
|
||||
o.CheckTypeStatus = cast
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("subresource '%s' does not exist", name)
|
||||
@@ -222,20 +219,6 @@ func (o *CheckType) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *CheckType) DeepCopy() *CheckType {
|
||||
cpy := &CheckType{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *CheckType) DeepCopyInto(dst *CheckType) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
|
||||
o.Spec.DeepCopyInto(&dst.Spec)
|
||||
o.Status.DeepCopyInto(&dst.Status)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.Object = &CheckType{}
|
||||
|
||||
@@ -279,41 +262,5 @@ func (o *CheckTypeList) SetItems(items []resource.Object) {
|
||||
}
|
||||
}
|
||||
|
||||
func (o *CheckTypeList) DeepCopy() *CheckTypeList {
|
||||
cpy := &CheckTypeList{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *CheckTypeList) DeepCopyInto(dst *CheckTypeList) {
|
||||
resource.CopyObjectInto(dst, o)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.ListObject = &CheckTypeList{}
|
||||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
// DeepCopy creates a full deep copy of Spec
|
||||
func (s *CheckTypeSpec) DeepCopy() *CheckTypeSpec {
|
||||
cpy := &CheckTypeSpec{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies Spec into another Spec object
|
||||
func (s *CheckTypeSpec) DeepCopyInto(dst *CheckTypeSpec) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
|
||||
// DeepCopy creates a full deep copy of CheckTypeStatus
|
||||
func (s *CheckTypeStatus) DeepCopy() *CheckTypeStatus {
|
||||
cpy := &CheckTypeStatus{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies CheckTypeStatus into another CheckTypeStatus object
|
||||
func (s *CheckTypeStatus) DeepCopyInto(dst *CheckTypeStatus) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,5 @@ type CheckTypeSpec struct {
|
||||
|
||||
// NewCheckTypeSpec creates a new CheckTypeSpec object.
|
||||
func NewCheckTypeSpec() *CheckTypeSpec {
|
||||
return &CheckTypeSpec{
|
||||
Steps: []CheckTypeStep{},
|
||||
}
|
||||
return &CheckTypeSpec{}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,16 @@ package v0alpha1
|
||||
import "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
const (
|
||||
// APIGroup is the API group used by all kinds in this package
|
||||
APIGroup = "advisor.grafana.app"
|
||||
// APIVersion is the API version used by all kinds in this package
|
||||
APIVersion = "v0alpha1"
|
||||
// Group is the API group used by all kinds in this package
|
||||
Group = "advisor.grafana.app"
|
||||
// Version is the API version used by all kinds in this package
|
||||
Version = "v0alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
|
||||
GroupVersion = schema.GroupVersion{
|
||||
Group: APIGroup,
|
||||
Version: APIVersion,
|
||||
Group: Group,
|
||||
Version: Version,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -15,7 +15,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.Check": schema_pkg_apis_advisor_v0alpha1_Check(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckErrorLink": schema_pkg_apis_advisor_v0alpha1_CheckErrorLink(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckList": schema_pkg_apis_advisor_v0alpha1_CheckList(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReport": schema_pkg_apis_advisor_v0alpha1_CheckReport(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure": schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec": schema_pkg_apis_advisor_v0alpha1_CheckSpec(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckStatus": schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref),
|
||||
@@ -25,6 +24,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStatus": schema_pkg_apis_advisor_v0alpha1_CheckTypeStatus(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeStep": schema_pkg_apis_advisor_v0alpha1_CheckTypeStep(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypestatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport": schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref),
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState": schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref),
|
||||
}
|
||||
}
|
||||
@@ -57,9 +57,8 @@ func schema_pkg_apis_advisor_v0alpha1_Check(ref common.ReferenceCallback) common
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Spec is the spec of the Check",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec"),
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckSpec"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
@@ -154,43 +153,6 @@ func schema_pkg_apis_advisor_v0alpha1_CheckList(ref common.ReferenceCallback) co
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_advisor_v0alpha1_CheckReport(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"count": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Number of elements analyzed",
|
||||
Default: 0,
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"failures": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "List of failures",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"count", "failures"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_advisor_v0alpha1_CheckReportFailure(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -296,7 +258,7 @@ func schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref common.ReferenceCallback)
|
||||
"report": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReport"),
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport"),
|
||||
},
|
||||
},
|
||||
"operatorStates": {
|
||||
@@ -334,7 +296,7 @@ func schema_pkg_apis_advisor_v0alpha1_CheckStatus(ref common.ReferenceCallback)
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReport", "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState"},
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckV0alpha1StatusReport", "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckstatusOperatorState"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,9 +328,8 @@ func schema_pkg_apis_advisor_v0alpha1_CheckType(ref common.ReferenceCallback) co
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Spec is the spec of the CheckType",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec"),
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckTypeSpec"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
@@ -605,6 +566,43 @@ func schema_pkg_apis_advisor_v0alpha1_CheckTypestatusOperatorState(ref common.Re
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_advisor_v0alpha1_CheckV0alpha1StatusReport(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"count": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Number of elements analyzed",
|
||||
Default: 0,
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"failures": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "List of failures",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"count", "failures"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1.CheckReportFailure"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_advisor_v0alpha1_CheckstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -7,19 +7,15 @@ package apis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
|
||||
v0alpha1 "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"itemID":{"description":"ID of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"moreInfo":{"description":"More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.","type":"string"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","itemID","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object"}}`)
|
||||
rawSchemaCheckv0alpha1 = []byte(`{"spec":{"properties":{"data":{"additionalProperties":{"type":"string"},"description":"Generic data input that a check can receive","type":"object"}},"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"},"report":{"properties":{"count":{"description":"Number of elements analyzed","type":"integer"},"failures":{"description":"List of failures","items":{"properties":{"item":{"description":"Human readable identifier of the item that failed","type":"string"},"itemID":{"description":"ID of the item that failed","type":"string"},"links":{"description":"Links to actions that can be taken to resolve the failure","items":{"properties":{"message":{"description":"Human readable error message","type":"string"},"url":{"description":"URL to a page with more information about the error","type":"string"}},"required":["url","message"],"type":"object"},"type":"array"},"moreInfo":{"description":"More information about the failure, not meant to be displayed to the user. Used for LLM suggestions.","type":"string"},"severity":{"description":"Severity of the failure","enum":["high","low"],"type":"string"},"stepID":{"description":"Step ID that the failure is associated with","type":"string"}},"required":["severity","stepID","item","itemID","links"],"type":"object"},"type":"array"}},"required":["count","failures"],"type":"object"}},"required":["report"],"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaCheckv0alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaCheckv0alpha1, &versionSchemaCheckv0alpha1)
|
||||
rawSchemaCheckTypev0alpha1 = []byte(`{"spec":{"properties":{"name":{"type":"string"},"steps":{"items":{"properties":{"description":{"type":"string"},"resolution":{"type":"string"},"stepID":{"type":"string"},"title":{"type":"string"}},"required":["title","description","stepID","resolution"],"type":"object"},"type":"array"}},"required":["name","steps"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object"}}`)
|
||||
rawSchemaCheckTypev0alpha1 = []byte(`{"spec":{"properties":{"name":{"type":"string"},"steps":{"items":{"properties":{"description":{"type":"string"},"resolution":{"type":"string"},"stepID":{"type":"string"},"title":{"type":"string"}},"required":["title","description","stepID","resolution"],"type":"object"},"type":"array"}},"required":["name","steps"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaCheckTypev0alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaCheckTypev0alpha1, &versionSchemaCheckTypev0alpha1)
|
||||
)
|
||||
@@ -62,6 +58,12 @@ var appManifestData = app.ManifestData{
|
||||
},
|
||||
}
|
||||
|
||||
func jsonToMap(j string) map[string]any {
|
||||
m := make(map[string]any)
|
||||
json.Unmarshal([]byte(j), &j)
|
||||
return m
|
||||
}
|
||||
|
||||
func LocalManifest() app.Manifest {
|
||||
return app.NewEmbeddedManifest(appManifestData)
|
||||
}
|
||||
@@ -69,15 +71,3 @@ func LocalManifest() app.Manifest {
|
||||
func RemoteManifest() app.Manifest {
|
||||
return app.NewAPIServerManifest("advisor")
|
||||
}
|
||||
|
||||
var kindVersionToGoType = map[string]resource.Kind{
|
||||
"Check/v0alpha1": v0alpha1.CheckKind(),
|
||||
"CheckType/v0alpha1": v0alpha1.CheckTypeKind(),
|
||||
}
|
||||
|
||||
// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists.
|
||||
// If there is no association for the provided Kind and Version, exists will return false.
|
||||
func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exists bool) {
|
||||
goType, exists = kindVersionToGoType[fmt.Sprintf("%s/%s", kind, version)]
|
||||
return goType, exists
|
||||
}
|
||||
|
||||
@@ -106,25 +106,3 @@ func SetStatusAnnotation(ctx context.Context, client resource.Client, obj resour
|
||||
}},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func SetAnnotations(ctx context.Context, client resource.Client, obj resource.Object, annotations map[string]string) error {
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
}},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func SetStatus(ctx context.Context, client resource.Client, obj resource.Object, status any) error {
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status",
|
||||
Value: status,
|
||||
}},
|
||||
}, resource.PatchOptions{
|
||||
Subresource: "status",
|
||||
}, obj)
|
||||
}
|
||||
|
||||
@@ -78,21 +78,28 @@ func processCheck(ctx context.Context, log logging.Logger, client resource.Clien
|
||||
return fmt.Errorf("error running steps: %w", err)
|
||||
}
|
||||
|
||||
report := &advisorv0alpha1.CheckReport{
|
||||
report := &advisorv0alpha1.CheckV0alpha1StatusReport{
|
||||
Failures: failures,
|
||||
Count: int64(len(items)),
|
||||
}
|
||||
c.Status.Report = *report
|
||||
err = checks.SetStatus(ctx, client, obj, c.Status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Set the status annotation to processed and annotate the steps ignored
|
||||
annotations := checks.AddAnnotations(ctx, obj, map[string]string{
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
checks.IgnoreStepsAnnotationList: checkType.GetAnnotations()[checks.IgnoreStepsAnnotationList],
|
||||
})
|
||||
return checks.SetAnnotations(ctx, client, obj, annotations)
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{
|
||||
{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: *report,
|
||||
}, {
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
},
|
||||
},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func processCheckRetry(ctx context.Context, log logging.Logger, client resource.Client, typesClient resource.Client, obj resource.Object, check checks.Check) error {
|
||||
@@ -150,7 +157,7 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
|
||||
}
|
||||
}
|
||||
// Pull failures from the report for the items to retry
|
||||
c.Status.Report.Failures = slices.DeleteFunc(c.Status.Report.Failures, func(f advisorv0alpha1.CheckReportFailure) bool {
|
||||
c.CheckStatus.Report.Failures = slices.DeleteFunc(c.CheckStatus.Report.Failures, func(f advisorv0alpha1.CheckReportFailure) bool {
|
||||
if f.ItemID == itemToRetry {
|
||||
for _, newFailure := range failures {
|
||||
if newFailure.StepID == f.StepID {
|
||||
@@ -164,13 +171,19 @@ func processCheckRetry(ctx context.Context, log logging.Logger, client resource.
|
||||
// Failure not in the list of items to retry, keep it
|
||||
return false
|
||||
})
|
||||
err = checks.SetStatus(ctx, client, obj, c.Status)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Delete the retry annotation to mark the check as processed
|
||||
annotations := checks.DeleteAnnotations(ctx, obj, []string{checks.RetryAnnotation})
|
||||
return checks.SetAnnotations(ctx, client, obj, annotations)
|
||||
return client.PatchInto(ctx, obj.GetStaticMetadata().Identifier(), resource.PatchRequest{
|
||||
Operations: []resource.PatchOperation{{
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/status/report",
|
||||
Value: c.CheckStatus.Report,
|
||||
}, {
|
||||
Operation: resource.PatchOpAdd,
|
||||
Path: "/metadata/annotations",
|
||||
Value: annotations,
|
||||
}},
|
||||
}, resource.PatchOptions{}, obj)
|
||||
}
|
||||
|
||||
func runStepsInParallel(ctx context.Context, log logging.Logger, spec *advisorv0alpha1.CheckSpec, steps []checks.Step, items []any) ([]advisorv0alpha1.CheckReportFailure, error) {
|
||||
|
||||
@@ -95,9 +95,9 @@ func TestProcessMultipleCheckItems(t *testing.T) {
|
||||
err = processCheck(ctx, logging.DefaultLogger, client, typesClient, obj, check)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
r := client.values[0].(advisorv0alpha1.CheckStatus)
|
||||
assert.Equal(t, r.Report.Count, int64(100))
|
||||
assert.Len(t, r.Report.Failures, 50)
|
||||
r := client.lastValue.(advisorv0alpha1.CheckV0alpha1StatusReport)
|
||||
assert.Equal(t, r.Count, int64(100))
|
||||
assert.Len(t, r.Failures, 50)
|
||||
}
|
||||
|
||||
func TestProcessCheck_AlreadyProcessed(t *testing.T) {
|
||||
@@ -231,7 +231,7 @@ func TestProcessCheckRetry_SkipMissingItem(t *testing.T) {
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
obj.Status.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
{
|
||||
ItemID: "item",
|
||||
StepID: "step",
|
||||
@@ -254,7 +254,7 @@ func TestProcessCheckRetry_SkipMissingItem(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation])
|
||||
assert.Empty(t, obj.Status.Report.Failures)
|
||||
assert.Empty(t, obj.CheckStatus.Report.Failures)
|
||||
}
|
||||
|
||||
func TestProcessCheckRetry_Success(t *testing.T) {
|
||||
@@ -263,7 +263,7 @@ func TestProcessCheckRetry_Success(t *testing.T) {
|
||||
checks.RetryAnnotation: "item",
|
||||
checks.StatusAnnotation: checks.StatusAnnotationProcessed,
|
||||
})
|
||||
obj.Status.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
obj.CheckStatus.Report.Failures = []advisorv0alpha1.CheckReportFailure{
|
||||
{
|
||||
ItemID: "item",
|
||||
StepID: "step",
|
||||
@@ -286,16 +286,16 @@ func TestProcessCheckRetry_Success(t *testing.T) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, checks.StatusAnnotationProcessed, obj.GetAnnotations()[checks.StatusAnnotation])
|
||||
assert.Empty(t, obj.GetAnnotations()[checks.RetryAnnotation])
|
||||
assert.Empty(t, obj.Status.Report.Failures)
|
||||
assert.Empty(t, obj.CheckStatus.Report.Failures)
|
||||
}
|
||||
|
||||
type mockClient struct {
|
||||
resource.Client
|
||||
values []any
|
||||
lastValue any
|
||||
}
|
||||
|
||||
func (m *mockClient) PatchInto(ctx context.Context, id resource.Identifier, req resource.PatchRequest, opts resource.PatchOptions, obj resource.Object) error {
|
||||
m.values = append(m.values, req.Operations[0].Value)
|
||||
m.lastValue = req.Operations[0].Value
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/alerting/notifications
|
||||
|
||||
go 1.24.4
|
||||
go 1.24.6
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.39.2
|
||||
@@ -89,9 +89,9 @@ require (
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
|
||||
@@ -317,12 +317,12 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaReceiver = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &Receiver{}, &ReceiverList{}, resource.WithKind("Receiver"),
|
||||
resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
|
||||
resource.WithPlural("receivers"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
|
||||
FieldSelector: "spec.title",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*Receiver)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaTemplateGroup = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TemplateGroup{}, &TemplateGroupList{}, resource.WithKind("TemplateGroup"),
|
||||
resource.WithPlural("templategroups"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
|
||||
resource.WithPlural("templategroups"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
|
||||
FieldSelector: "spec.title",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*TemplateGroup)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaTimeInterval = resource.NewSimpleSchema("notifications.alerting.grafana.app", "v0alpha1", &TimeInterval{}, &TimeIntervalList{}, resource.WithKind("TimeInterval"),
|
||||
resource.WithPlural("timeintervals"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{{
|
||||
resource.WithPlural("timeintervals"), resource.WithScope(resource.NamespacedScope), resource.WithSelectableFields([]resource.SelectableField{resource.SelectableField{
|
||||
FieldSelector: "spec.name",
|
||||
FieldValueFunc: func(o resource.Object) (string, error) {
|
||||
cast, ok := o.(*TimeInterval)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
APP_SDK_VERSION := v0.39.2
|
||||
APP_SDK_VERSION := v0.39.0
|
||||
APP_SDK_DIR := $(shell go env GOPATH)/bin/app-sdk-$(APP_SDK_VERSION)
|
||||
APP_SDK_BIN := $(APP_SDK_DIR)/grafana-app-sdk
|
||||
|
||||
@@ -60,10 +60,3 @@ post-generate-cleanup: ## Clean up the generated code
|
||||
@echo "// To sync changes, run: make generate in apps/dashboard" >> ./pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
|
||||
@echo "" >> ./pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
|
||||
@cat ./kinds/v2alpha1/dashboard_spec.cue >> ./pkg/apis/dashboard/v2alpha1/dashboard_spec.cue
|
||||
|
||||
# Copy dashboard/v2alpha2 spec so we can use it for schema validation
|
||||
@echo "// This file is managed by grafana-app-sdk - DO NOT EDIT MANUALLY" > ./pkg/apis/dashboard/v2alpha2/dashboard_spec.cue
|
||||
@echo "// Source: apps/dashboard/kinds/v2alpha2/dashboard_spec.cue" >> ./pkg/apis/dashboard/v2alpha2/dashboard_spec.cue
|
||||
@echo "// To sync changes, run: make generate in apps/dashboard" >> ./pkg/apis/dashboard/v2alpha2/dashboard_spec.cue
|
||||
@echo "" >> ./pkg/apis/dashboard/v2alpha2/dashboard_spec.cue
|
||||
@cat ./kinds/v2alpha2/dashboard_spec.cue >> ./pkg/apis/dashboard/v2alpha2/dashboard_spec.cue
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/dashboard
|
||||
|
||||
go 1.24.4
|
||||
go 1.24.6
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.11.1
|
||||
@@ -116,8 +116,8 @@ require (
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
|
||||
|
||||
@@ -357,10 +357,10 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS
|
||||
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237 h1:Kog3KlB4xevJlAcbbbzPfRG0+X9fdoGM+UBRKVz6Wr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250519155744-55703ea1f237/go.mod h1:ezi0AVyMKDWy5xAncvjLWH7UcLBB5n7y2fQ8MzjJcto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
|
||||
@@ -3,8 +3,7 @@ package kinds
|
||||
import (
|
||||
v0 "github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1"
|
||||
v1 "github.com/grafana/grafana/sdkkinds/dashboard/v1beta1"
|
||||
v2alpha1 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1"
|
||||
v2alpha2 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha2"
|
||||
v2 "github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1"
|
||||
)
|
||||
|
||||
// Status is the shared status of all dashboard versions.
|
||||
@@ -64,13 +63,7 @@ dashboard: {
|
||||
}
|
||||
"v2alpha1": {
|
||||
schema: {
|
||||
spec: v2alpha1.DashboardSpec
|
||||
status: DashboardStatus
|
||||
}
|
||||
}
|
||||
"v2alpha2": {
|
||||
schema: {
|
||||
spec: v2alpha2.DashboardSpec
|
||||
spec: v2.DashboardSpec
|
||||
status: DashboardStatus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,8 +734,6 @@ QueryVariableSpec: {
|
||||
allValue?: string
|
||||
placeholder?: string
|
||||
allowCustomValue: bool | *true
|
||||
staticOptions?: [...VariableOption]
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
}
|
||||
|
||||
// Query variable kind
|
||||
|
||||
@@ -1,964 +0,0 @@
|
||||
package v2alpha2
|
||||
|
||||
DashboardSpec: {
|
||||
annotations: [...AnnotationQueryKind] | *[]
|
||||
|
||||
// Configuration of dashboard cursor sync behavior.
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
// "Crosshair" for shared crosshair.
|
||||
// "Tooltip" for shared crosshair AND shared tooltip.
|
||||
cursorSync: DashboardCursorSync
|
||||
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
|
||||
// Whether a dashboard is editable or not.
|
||||
editable?: bool | *true
|
||||
|
||||
elements: [ElementReference.name]: Element | *{}
|
||||
|
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
|
||||
|
||||
// Links with references to other dashboards or external websites.
|
||||
links: [...DashboardLink] | *[]
|
||||
|
||||
// When set to true, the dashboard will redraw panels at an interval matching the pixel width.
|
||||
// This will keep data "moving left" regardless of the query refresh rate. This setting helps
|
||||
// avoid dashboards presenting stale live data.
|
||||
liveNow?: bool
|
||||
|
||||
// When set to true, the dashboard will load all panels in the dashboard when it's loaded.
|
||||
preload: bool | *false
|
||||
|
||||
// Plugins only. The version of the dashboard installed together with the plugin.
|
||||
// This is used to determine if the dashboard should be updated when the plugin is updated.
|
||||
revision?: uint16
|
||||
|
||||
// Tags associated with dashboard.
|
||||
tags: [...string] | *[]
|
||||
|
||||
timeSettings: TimeSettingsSpec
|
||||
|
||||
// Title of dashboard.
|
||||
title: string
|
||||
|
||||
// Configured template variables.
|
||||
variables: [...VariableKind] | *[]
|
||||
}
|
||||
|
||||
// Supported dashboard elements
|
||||
Element: PanelKind | LibraryPanelKind // |* more element types in the future
|
||||
|
||||
LibraryPanelKind: {
|
||||
kind: "LibraryPanel"
|
||||
spec: LibraryPanelKindSpec
|
||||
}
|
||||
|
||||
LibraryPanelKindSpec: {
|
||||
// Panel ID for the library panel in the dashboard
|
||||
id: number
|
||||
// Title for the library panel in the dashboard
|
||||
title: string
|
||||
|
||||
libraryPanel: LibraryPanelRef
|
||||
}
|
||||
|
||||
// A library panel is a reusable panel that you can use in any dashboard.
|
||||
// When you make a change to a library panel, that change propagates to all instances of where the panel is used.
|
||||
// Library panels streamline reuse of panels across multiple dashboards.
|
||||
LibraryPanelRef: {
|
||||
// Library panel name
|
||||
name: string
|
||||
// Library panel uid
|
||||
uid: string
|
||||
}
|
||||
|
||||
AnnotationPanelFilter: {
|
||||
// Should the specified panels be included or excluded
|
||||
exclude?: bool | *false
|
||||
|
||||
// Panel IDs that should be included or excluded
|
||||
ids: [...uint32]
|
||||
}
|
||||
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
// "Crosshair" for shared crosshair.
|
||||
// "Tooltip" for shared crosshair AND shared tooltip.
|
||||
DashboardCursorSync: "Crosshair" | "Tooltip" | *"Off"
|
||||
|
||||
// Links with references to other dashboards or external resources
|
||||
DashboardLink: {
|
||||
// Title to display with the link
|
||||
title: string
|
||||
// Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
// FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType`
|
||||
type: DashboardLinkType
|
||||
// Icon name to be displayed with the link
|
||||
icon: string
|
||||
// Tooltip to display when the user hovers their mouse over it
|
||||
tooltip: string
|
||||
// Link URL. Only required/valid if the type is link
|
||||
url?: string
|
||||
// List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards
|
||||
tags: [...string] | *[]
|
||||
// If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards
|
||||
asDropdown: bool | *false
|
||||
// If true, the link will be opened in a new tab
|
||||
targetBlank: bool | *false
|
||||
// If true, includes current template variables values in the link as query params
|
||||
includeVars: bool | *false
|
||||
// If true, includes current time range in the link as query params
|
||||
keepTime: bool | *false
|
||||
}
|
||||
|
||||
DataSourceRef: {
|
||||
// The plugin type-id
|
||||
type?: string
|
||||
|
||||
// Specific datasource instance
|
||||
uid?: string
|
||||
}
|
||||
|
||||
// A topic is attached to DataFrame metadata in query results.
|
||||
// This specifies where the data should be used.
|
||||
DataTopic: "series" | "annotations" | "alertStates" @cog(kind="enum",memberNames="Series|Annotations|AlertStates")
|
||||
|
||||
// Transformations allow to manipulate data returned by a query before the system applies a visualization.
|
||||
// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries,
|
||||
// use the output of one transformation as the input to another transformation, etc.
|
||||
DataTransformerConfig: {
|
||||
// Unique identifier of transformer
|
||||
id: string
|
||||
// Disabled transformations are skipped
|
||||
disabled?: bool
|
||||
// Optional frame matcher. When missing it will be applied to all results
|
||||
filter?: MatcherConfig
|
||||
// Where to pull DataFrames from as input to transformation
|
||||
topic?: DataTopic
|
||||
// Options to be passed to the transformer
|
||||
// Valid options depend on the transformer id
|
||||
options: _
|
||||
}
|
||||
|
||||
DataLink: {
|
||||
title: string
|
||||
url: string
|
||||
targetBlank?: bool
|
||||
}
|
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
|
||||
// Each column within this structure is called a field. A field can represent a single time series or table column.
|
||||
// Field options allow you to change how the data is displayed in your visualizations.
|
||||
FieldConfigSource: {
|
||||
// Defaults are the options applied to all fields.
|
||||
defaults: FieldConfig
|
||||
// Overrides are the options applied to specific fields overriding the defaults.
|
||||
overrides: [...{
|
||||
matcher: MatcherConfig
|
||||
properties: [...DynamicConfigValue]
|
||||
}]
|
||||
}
|
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
|
||||
// Each column within this structure is called a field. A field can represent a single time series or table column.
|
||||
// Field options allow you to change how the data is displayed in your visualizations.
|
||||
FieldConfig: {
|
||||
// The display value for this field. This supports template variables blank is auto
|
||||
displayName?: string
|
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels
|
||||
// When this property is configured, this value is used rather than the default naming strategy.
|
||||
displayNameFromDS?: string
|
||||
|
||||
// Human readable field metadata
|
||||
description?: string
|
||||
|
||||
// An explicit path to the field in the datasource. When the frame meta includes a path,
|
||||
// This will default to `${frame.meta.path}/${field.name}
|
||||
//
|
||||
// When defined, this value can be used as an identifier within the datasource scope, and
|
||||
// may be used to update the results
|
||||
path?: string
|
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately
|
||||
writeable?: bool
|
||||
|
||||
// True if data source field supports ad-hoc filters
|
||||
filterable?: bool
|
||||
|
||||
// Unit a field should use. The unit you select is applied to all fields except time.
|
||||
// You can use the units ID availables in Grafana or a custom unit.
|
||||
// Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts
|
||||
// As custom unit, you can use the following formats:
|
||||
// `suffix:<suffix>` for custom unit that should go after value.
|
||||
// `prefix:<prefix>` for custom unit that should go before value.
|
||||
// `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`.
|
||||
// `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.
|
||||
// `count:<unit>` for a custom count unit.
|
||||
// `currency:<unit>` for custom a currency unit.
|
||||
unit?: string
|
||||
|
||||
// Specify the number of decimals Grafana includes in the rendered value.
|
||||
// If you leave this field blank, Grafana automatically truncates the number of decimals based on the value.
|
||||
// For example 1.1234 will display as 1.12 and 100.456 will display as 100.
|
||||
// To display all decimals, set the unit to `String`.
|
||||
decimals?: number
|
||||
|
||||
// The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
|
||||
min?: number
|
||||
// The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
|
||||
max?: number
|
||||
|
||||
// Convert input values into a display string
|
||||
mappings?: [...ValueMapping]
|
||||
|
||||
// Map numeric values to states
|
||||
thresholds?: ThresholdsConfig
|
||||
|
||||
// Panel color configuration
|
||||
color?: FieldColor
|
||||
|
||||
// The behavior when clicking on a result
|
||||
links?: [...]
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
// custom is specified by the FieldConfig field
|
||||
// in panel plugin schemas.
|
||||
custom?: {...}
|
||||
}
|
||||
|
||||
DynamicConfigValue: {
|
||||
id: string | *""
|
||||
value?: _
|
||||
}
|
||||
|
||||
// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.
|
||||
// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.
|
||||
MatcherConfig: {
|
||||
// The matcher id. This is used to find the matcher implementation from registry.
|
||||
id: string | *""
|
||||
// The matcher options. This is specific to the matcher implementation.
|
||||
options?: _
|
||||
}
|
||||
|
||||
Threshold: {
|
||||
value: number
|
||||
color: string
|
||||
}
|
||||
|
||||
ThresholdsMode: "absolute" | "percentage"
|
||||
|
||||
ThresholdsConfig: {
|
||||
mode: ThresholdsMode
|
||||
steps: [...Threshold]
|
||||
}
|
||||
|
||||
ValueMapping: ValueMap | RangeMap | RegexMap | SpecialValueMap
|
||||
|
||||
// Supported value mapping types
|
||||
// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
|
||||
// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
|
||||
// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
|
||||
// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.
|
||||
MappingType: "value" | "range" | "regex" | "special"
|
||||
|
||||
// Maps text values to a color or different display text and color.
|
||||
// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
|
||||
ValueMap: {
|
||||
type: MappingType & "value"
|
||||
// Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } }
|
||||
options: [string]: ValueMappingResult
|
||||
}
|
||||
|
||||
// Maps numerical ranges to a display text and color.
|
||||
// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
|
||||
RangeMap: {
|
||||
type: MappingType & "range"
|
||||
// Range to match against and the result to apply when the value is within the range
|
||||
options: {
|
||||
// Min value of the range. It can be null which means -Infinity
|
||||
from: float64 | null
|
||||
// Max value of the range. It can be null which means +Infinity
|
||||
to: float64 | null
|
||||
// Config to apply when the value is within the range
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Maps regular expressions to replacement text and a color.
|
||||
// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
|
||||
RegexMap: {
|
||||
type: MappingType & "regex"
|
||||
// Regular expression to match against and the result to apply when the value matches the regex
|
||||
options: {
|
||||
// Regular expression to match against
|
||||
pattern: string
|
||||
// Config to apply when the value matches the regex
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.
|
||||
// See SpecialValueMatch to see the list of special values.
|
||||
// For example, you can configure a special value mapping so that null values appear as N/A.
|
||||
SpecialValueMap: {
|
||||
type: MappingType & "special"
|
||||
options: {
|
||||
// Special value to match against
|
||||
match: SpecialValueMatch
|
||||
// Config to apply when the value matches the special value
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Special value types supported by the `SpecialValueMap`
|
||||
SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cog(kind="enum",memberNames="True|False|Null|NaN|NullAndNaN|Empty")
|
||||
|
||||
// Result used as replacement with text and color when the value matches
|
||||
ValueMappingResult: {
|
||||
// Text to display when the value matches
|
||||
text?: string
|
||||
// Text to use when the value matches
|
||||
color?: string
|
||||
// Icon to display when the value matches. Only specific visualizations.
|
||||
icon?: string
|
||||
// Position in the mapping array. Only used internally.
|
||||
index?: int32
|
||||
}
|
||||
|
||||
// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.
|
||||
// Continuous color interpolates a color using the percentage of a value relative to min and max.
|
||||
// Accepted values are:
|
||||
// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold
|
||||
// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations
|
||||
// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations
|
||||
// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode
|
||||
// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode
|
||||
// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode
|
||||
// `continuous-YlRd`: Continuous Yellow-Red palette mode
|
||||
// `continuous-BlPu`: Continuous Blue-Purple palette mode
|
||||
// `continuous-YlBl`: Continuous Yellow-Blue palette mode
|
||||
// `continuous-blues`: Continuous Blue palette mode
|
||||
// `continuous-reds`: Continuous Red palette mode
|
||||
// `continuous-greens`: Continuous Green palette mode
|
||||
// `continuous-purples`: Continuous Purple palette mode
|
||||
// `shades`: Shades of a single color. Specify a single color, useful in an override rule.
|
||||
// `fixed`: Fixed color mode. Specify a single color, useful in an override rule.
|
||||
FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades"
|
||||
|
||||
// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.
|
||||
FieldColorSeriesByMode: "min" | "max" | "last"
|
||||
|
||||
// Map a field to a color.
|
||||
FieldColor: {
|
||||
// The main color scheme mode.
|
||||
mode: FieldColorModeId
|
||||
// The fixed color value for fixed or shades color modes.
|
||||
fixedColor?: string
|
||||
// Some visualizations need to know how to assign a series color from by value color schemes.
|
||||
seriesBy?: FieldColorSeriesByMode
|
||||
}
|
||||
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
DashboardLinkType: "link" | "dashboards"
|
||||
|
||||
// --- Common types ---
|
||||
Kind: {
|
||||
kind: string
|
||||
spec: _
|
||||
metadata?: _
|
||||
}
|
||||
|
||||
// --- Kinds ---
|
||||
VizConfigSpec: {
|
||||
pluginVersion: string
|
||||
options: [string]: _
|
||||
fieldConfig: FieldConfigSource
|
||||
}
|
||||
|
||||
VizConfigKind: {
|
||||
// The kind of a VizConfigKind is the plugin ID
|
||||
kind: string
|
||||
spec: VizConfigSpec
|
||||
}
|
||||
|
||||
AnnotationQuerySpec: {
|
||||
datasource?: DataSourceRef
|
||||
query?: DataQueryKind
|
||||
enable: bool
|
||||
hide: bool
|
||||
iconColor: string
|
||||
name: string
|
||||
builtIn?: bool | *false
|
||||
filter?: AnnotationPanelFilter
|
||||
legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties
|
||||
}
|
||||
|
||||
AnnotationQueryKind: {
|
||||
kind: "AnnotationQuery"
|
||||
spec: AnnotationQuerySpec
|
||||
}
|
||||
|
||||
QueryOptionsSpec: {
|
||||
timeFrom?: string
|
||||
maxDataPoints?: int
|
||||
timeShift?: string
|
||||
queryCachingTTL?: int
|
||||
interval?: string
|
||||
cacheTimeout?: string
|
||||
hideTimeOverride?: bool
|
||||
}
|
||||
|
||||
DataQueryKind: {
|
||||
// The kind of a DataQueryKind is the datasource type
|
||||
kind: string
|
||||
spec: [string]: _
|
||||
}
|
||||
|
||||
PanelQuerySpec: {
|
||||
query: DataQueryKind
|
||||
datasource?: DataSourceRef
|
||||
|
||||
refId: string
|
||||
hidden: bool
|
||||
}
|
||||
|
||||
PanelQueryKind: {
|
||||
kind: "PanelQuery"
|
||||
spec: PanelQuerySpec
|
||||
}
|
||||
|
||||
TransformationKind: {
|
||||
// The kind of a TransformationKind is the transformation ID
|
||||
kind: string
|
||||
spec: DataTransformerConfig
|
||||
}
|
||||
|
||||
QueryGroupSpec: {
|
||||
queries: [...PanelQueryKind]
|
||||
transformations: [...TransformationKind]
|
||||
queryOptions: QueryOptionsSpec
|
||||
}
|
||||
|
||||
QueryGroupKind: {
|
||||
kind: "QueryGroup"
|
||||
spec: QueryGroupSpec
|
||||
}
|
||||
|
||||
TimeRangeOption: {
|
||||
display: string | *"Last 6 hours"
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
}
|
||||
|
||||
// Time configuration
|
||||
// It defines the default time config for the time picker, the refresh picker for the specific dashboard.
|
||||
TimeSettingsSpec: {
|
||||
// Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc".
|
||||
timezone?: string | *"browser"
|
||||
// Start time range for dashboard.
|
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z".
|
||||
from: string | *"now-6h"
|
||||
// End time range for dashboard.
|
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z".
|
||||
to: string | *"now"
|
||||
// Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d".
|
||||
autoRefresh: string | *"" // v1: refresh
|
||||
// Interval options available in the refresh picker dropdown.
|
||||
autoRefreshIntervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // v1: timepicker.refresh_intervals
|
||||
// Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.
|
||||
quickRanges?: [...TimeRangeOption] // v1: timepicker.quick_ranges , not exposed in the UI
|
||||
// Whether timepicker is visible or not.
|
||||
hideTimepicker: bool | *false // v1: timepicker.hidden
|
||||
// Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday".
|
||||
weekStart?: "saturday" | "monday" | "sunday"
|
||||
// The month that the fiscal year starts on. 0 = January, 11 = December
|
||||
fiscalYearStartMonth: int | *0
|
||||
// Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.
|
||||
nowDelay?: string // v1: timepicker.nowDelay
|
||||
}
|
||||
|
||||
RepeatMode: "variable" // other repeat modes will be added in the future: label, frame
|
||||
|
||||
RepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
direction?: "h" | "v"
|
||||
maxPerRow?: int
|
||||
}
|
||||
|
||||
RowRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
TabRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
AutoGridRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
GridLayoutItemSpec: {
|
||||
x: int
|
||||
y: int
|
||||
width: int
|
||||
height: int
|
||||
element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
||||
repeat?: RepeatOptions
|
||||
}
|
||||
|
||||
GridLayoutItemKind: {
|
||||
kind: "GridLayoutItem"
|
||||
spec: GridLayoutItemSpec
|
||||
}
|
||||
|
||||
GridLayoutSpec: {
|
||||
items: [...GridLayoutItemKind]
|
||||
}
|
||||
|
||||
GridLayoutKind: {
|
||||
kind: "GridLayout"
|
||||
spec: GridLayoutSpec
|
||||
}
|
||||
|
||||
RowsLayoutKind: {
|
||||
kind: "RowsLayout"
|
||||
spec: RowsLayoutSpec
|
||||
}
|
||||
|
||||
RowsLayoutSpec: {
|
||||
rows: [...RowsLayoutRowKind]
|
||||
}
|
||||
|
||||
RowsLayoutRowKind: {
|
||||
kind: "RowsLayoutRow"
|
||||
spec: RowsLayoutRowSpec
|
||||
}
|
||||
|
||||
RowsLayoutRowSpec: {
|
||||
title?: string
|
||||
collapse?: bool
|
||||
hideHeader?: bool
|
||||
fillScreen?: bool
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
repeat?: RowRepeatOptions
|
||||
layout: GridLayoutKind | AutoGridLayoutKind | TabsLayoutKind | RowsLayoutKind
|
||||
}
|
||||
|
||||
AutoGridLayoutKind: {
|
||||
kind: "AutoGridLayout"
|
||||
spec: AutoGridLayoutSpec
|
||||
}
|
||||
|
||||
AutoGridLayoutSpec: {
|
||||
maxColumnCount?: number | *3
|
||||
columnWidthMode: "narrow" | *"standard" | "wide" | "custom"
|
||||
columnWidth?: number
|
||||
rowHeightMode: "short" | *"standard" | "tall" | "custom"
|
||||
rowHeight?: number
|
||||
fillScreen?: bool | *false
|
||||
items: [...AutoGridLayoutItemKind]
|
||||
}
|
||||
|
||||
AutoGridLayoutItemKind: {
|
||||
kind: "AutoGridLayoutItem"
|
||||
spec: AutoGridLayoutItemSpec
|
||||
}
|
||||
|
||||
AutoGridLayoutItemSpec: {
|
||||
element: ElementReference
|
||||
repeat?: AutoGridRepeatOptions
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
}
|
||||
|
||||
TabsLayoutKind: {
|
||||
kind: "TabsLayout"
|
||||
spec: TabsLayoutSpec
|
||||
}
|
||||
|
||||
TabsLayoutSpec: {
|
||||
tabs: [...TabsLayoutTabKind]
|
||||
}
|
||||
|
||||
TabsLayoutTabKind: {
|
||||
kind: "TabsLayoutTab"
|
||||
spec: TabsLayoutTabSpec
|
||||
}
|
||||
|
||||
TabsLayoutTabSpec: {
|
||||
title?: string
|
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
repeat?: TabRepeatOptions
|
||||
}
|
||||
|
||||
PanelSpec: {
|
||||
id: number
|
||||
title: string
|
||||
description: string
|
||||
links: [...DataLink]
|
||||
data: QueryGroupKind
|
||||
vizConfig: VizConfigKind
|
||||
transparent?: bool
|
||||
}
|
||||
|
||||
PanelKind: {
|
||||
kind: "Panel"
|
||||
spec: PanelSpec
|
||||
}
|
||||
|
||||
ElementReference: {
|
||||
kind: "ElementReference"
|
||||
name: string
|
||||
}
|
||||
|
||||
// Start FIXME: variables - in CUE PR - this are things that should be added into the cue schema
|
||||
// TODO: properties such as `hide`, `skipUrlSync`, `multi` are type boolean, and in the old schema they are conditional,
|
||||
// should we make them conditional in the new schema as well? or should we make them required but default to false?
|
||||
|
||||
// Variable types
|
||||
VariableValue: VariableValueSingle | [...VariableValueSingle]
|
||||
|
||||
VariableValueSingle: string | bool | number | CustomVariableValue
|
||||
|
||||
// Custom formatter variable
|
||||
CustomFormatterVariable: {
|
||||
name: string
|
||||
type: VariableType
|
||||
multi: bool
|
||||
includeAll: bool
|
||||
}
|
||||
|
||||
// Custom variable value
|
||||
CustomVariableValue: {
|
||||
// The format name or function used in the expression
|
||||
formatter: *null | string | VariableCustomFormatterFn
|
||||
}
|
||||
|
||||
// Custom formatter function
|
||||
VariableCustomFormatterFn: {
|
||||
value: _
|
||||
legacyVariableModel: {
|
||||
name: string
|
||||
type: VariableType
|
||||
multi: bool
|
||||
includeAll: bool
|
||||
}
|
||||
legacyDefaultFormatter?: VariableCustomFormatterFn
|
||||
}
|
||||
|
||||
// Dashboard variable type
|
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on.
|
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only).
|
||||
// `constant`: Define a hidden constant.
|
||||
// `datasource`: Quickly change the data source for an entire dashboard.
|
||||
// `interval`: Interval variables represent time spans.
|
||||
// `textbox`: Display a free text input field with an optional default value.
|
||||
// `custom`: Define the variable options manually using a comma-separated list.
|
||||
// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables
|
||||
VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" |
|
||||
"system" | "snapshot"
|
||||
|
||||
VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
// `alphabeticalAsc`: Alphabetical ASC
|
||||
// `alphabeticalDesc`: Alphabetical DESC
|
||||
// `numericalAsc`: Numerical ASC
|
||||
// `numericalDesc`: Numerical DESC
|
||||
// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC
|
||||
// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC
|
||||
// `naturalAsc`: Natural ASC
|
||||
// `naturalDesc`: Natural DESC
|
||||
// VariableSort enum with default value
|
||||
VariableSort: "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc"
|
||||
|
||||
// Options to config when to refresh a variable
|
||||
// `never`: Never refresh the variable
|
||||
// `onDashboardLoad`: Queries the data source every time the dashboard loads.
|
||||
// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes.
|
||||
VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
|
||||
// Determine if the variable shows on dashboard
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
// FIXME: should we introduce this? --- Variable value option
|
||||
VariableValueOption: {
|
||||
label: string
|
||||
value: VariableValueSingle
|
||||
group?: string
|
||||
}
|
||||
|
||||
// Variable option specification
|
||||
VariableOption: {
|
||||
// Whether the option is selected or not
|
||||
selected?: bool
|
||||
// Text to be displayed for the option
|
||||
text: string | [...string]
|
||||
// Value of the option
|
||||
value: string | [...string]
|
||||
}
|
||||
|
||||
// Query variable specification
|
||||
QueryVariableSpec: {
|
||||
name: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
refresh: VariableRefresh
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
datasource?: DataSourceRef
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
placeholder?: string
|
||||
allowCustomValue: bool | *true
|
||||
staticOptions?: [...VariableOption]
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
}
|
||||
|
||||
// Query variable kind
|
||||
QueryVariableKind: {
|
||||
kind: "QueryVariable"
|
||||
spec: QueryVariableSpec
|
||||
}
|
||||
|
||||
// Text variable specification
|
||||
TextVariableSpec: {
|
||||
name: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
query: string | *""
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Text variable kind
|
||||
TextVariableKind: {
|
||||
kind: "TextVariable"
|
||||
spec: TextVariableSpec
|
||||
}
|
||||
|
||||
// Constant variable specification
|
||||
ConstantVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Constant variable kind
|
||||
ConstantVariableKind: {
|
||||
kind: "ConstantVariable"
|
||||
spec: ConstantVariableSpec
|
||||
}
|
||||
|
||||
// Datasource variable specification
|
||||
DatasourceVariableSpec: {
|
||||
name: string | *""
|
||||
pluginId: string | *""
|
||||
refresh: VariableRefresh
|
||||
regex: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Datasource variable kind
|
||||
DatasourceVariableKind: {
|
||||
kind: "DatasourceVariable"
|
||||
spec: DatasourceVariableSpec
|
||||
}
|
||||
|
||||
// Interval variable specification
|
||||
IntervalVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
auto: bool | *false
|
||||
auto_min: string | *""
|
||||
auto_count: int | *0
|
||||
refresh: VariableRefresh
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Interval variable kind
|
||||
IntervalVariableKind: {
|
||||
kind: "IntervalVariable"
|
||||
spec: IntervalVariableSpec
|
||||
}
|
||||
|
||||
// Custom variable specification
|
||||
CustomVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Custom variable kind
|
||||
CustomVariableKind: {
|
||||
kind: "CustomVariable"
|
||||
spec: CustomVariableSpec
|
||||
}
|
||||
|
||||
// GroupBy variable specification
|
||||
GroupByVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
defaultValue?: VariableOption
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Group variable kind
|
||||
GroupByVariableKind: {
|
||||
kind: "GroupByVariable"
|
||||
spec: GroupByVariableSpec
|
||||
}
|
||||
|
||||
// Adhoc variable specification
|
||||
AdhocVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
baseFilters: [...AdHocFilterWithLabels] | *[]
|
||||
filters: [...AdHocFilterWithLabels] | *[]
|
||||
defaultKeys: [...MetricFindValue] | *[]
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Define the MetricFindValue type
|
||||
MetricFindValue: {
|
||||
text: string
|
||||
value?: string | number
|
||||
group?: string
|
||||
expandable?: bool
|
||||
}
|
||||
|
||||
// Define the AdHocFilterWithLabels type
|
||||
AdHocFilterWithLabels: {
|
||||
key: string
|
||||
operator: string
|
||||
value: string
|
||||
values?: [...string]
|
||||
keyLabel?: string
|
||||
valueLabels?: [...string]
|
||||
forceEdit?: bool
|
||||
origin?: FilterOrigin
|
||||
// @deprecated
|
||||
condition?: string
|
||||
}
|
||||
|
||||
// Adhoc variable kind
|
||||
AdhocVariableKind: {
|
||||
kind: "AdhocVariable"
|
||||
spec: AdhocVariableSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingGroupKind: {
|
||||
kind: "ConditionalRenderingGroup"
|
||||
spec: ConditionalRenderingGroupSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingGroupSpec: {
|
||||
visibility: "show" | "hide"
|
||||
condition: "and" | "or"
|
||||
items: [...ConditionalRenderingVariableKind | ConditionalRenderingDataKind | ConditionalRenderingTimeRangeSizeKind]
|
||||
}
|
||||
|
||||
ConditionalRenderingVariableKind: {
|
||||
kind: "ConditionalRenderingVariable"
|
||||
spec: ConditionalRenderingVariableSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingVariableSpec: {
|
||||
variable: string
|
||||
operator: "equals" | "notEquals"
|
||||
value: string
|
||||
}
|
||||
|
||||
ConditionalRenderingDataKind: {
|
||||
kind: "ConditionalRenderingData"
|
||||
spec: ConditionalRenderingDataSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingDataSpec: {
|
||||
value: bool
|
||||
}
|
||||
|
||||
ConditionalRenderingTimeRangeSizeKind: {
|
||||
kind: "ConditionalRenderingTimeRangeSize"
|
||||
spec: ConditionalRenderingTimeRangeSizeSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingTimeRangeSizeSpec: {
|
||||
value: string
|
||||
}
|
||||
@@ -219,10 +219,6 @@ lineage: schemas: [{
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
regex?: string
|
||||
// Additional static options for query variable
|
||||
staticOptions?: [...#VariableOption]
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
...
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
|
||||
@@ -219,10 +219,6 @@ lineage: schemas: [{
|
||||
// Optional field, if you want to extract part of a series name or metric node segment.
|
||||
// Named capture groups can be used to separate the display text and value.
|
||||
regex?: string
|
||||
// Additional static options for query variable
|
||||
staticOptions?: [...#VariableOption]
|
||||
// Ordering of static options in relation to options returned from data source for query variable
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
...
|
||||
} @cuetsy(kind="interface") @grafana(TSVeneer="type") @grafanamaturity(NeedsExpertReview)
|
||||
|
||||
|
||||
@@ -738,8 +738,6 @@ QueryVariableSpec: {
|
||||
allValue?: string
|
||||
placeholder?: string
|
||||
allowCustomValue: bool | *true
|
||||
staticOptions?: [...VariableOption]
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
}
|
||||
|
||||
// Query variable kind
|
||||
|
||||
@@ -763,9 +763,7 @@ type DashboardRepeatOptions struct {
|
||||
|
||||
// NewDashboardRepeatOptions creates a new DashboardRepeatOptions object.
|
||||
func NewDashboardRepeatOptions() *DashboardRepeatOptions {
|
||||
return &DashboardRepeatOptions{
|
||||
Mode: DashboardRepeatMode,
|
||||
}
|
||||
return &DashboardRepeatOptions{}
|
||||
}
|
||||
|
||||
// other repeat modes will be added in the future: label, frame
|
||||
@@ -940,9 +938,7 @@ type DashboardRowRepeatOptions struct {
|
||||
|
||||
// NewDashboardRowRepeatOptions creates a new DashboardRowRepeatOptions object.
|
||||
func NewDashboardRowRepeatOptions() *DashboardRowRepeatOptions {
|
||||
return &DashboardRowRepeatOptions{
|
||||
Mode: DashboardRepeatMode,
|
||||
}
|
||||
return &DashboardRowRepeatOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
@@ -1015,9 +1011,7 @@ type DashboardAutoGridRepeatOptions struct {
|
||||
|
||||
// NewDashboardAutoGridRepeatOptions creates a new DashboardAutoGridRepeatOptions object.
|
||||
func NewDashboardAutoGridRepeatOptions() *DashboardAutoGridRepeatOptions {
|
||||
return &DashboardAutoGridRepeatOptions{
|
||||
Mode: DashboardRepeatMode,
|
||||
}
|
||||
return &DashboardAutoGridRepeatOptions{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
@@ -1083,9 +1077,7 @@ type DashboardTabRepeatOptions struct {
|
||||
|
||||
// NewDashboardTabRepeatOptions creates a new DashboardTabRepeatOptions object.
|
||||
func NewDashboardTabRepeatOptions() *DashboardTabRepeatOptions {
|
||||
return &DashboardTabRepeatOptions{
|
||||
Mode: DashboardRepeatMode,
|
||||
}
|
||||
return &DashboardTabRepeatOptions{}
|
||||
}
|
||||
|
||||
// Links with references to other dashboards or external resources
|
||||
@@ -1222,26 +1214,24 @@ func NewDashboardQueryVariableKind() *DashboardQueryVariableKind {
|
||||
// Query variable specification
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardQueryVariableSpec struct {
|
||||
Name string `json:"name"`
|
||||
Current DashboardVariableOption `json:"current"`
|
||||
Label *string `json:"label,omitempty"`
|
||||
Hide DashboardVariableHide `json:"hide"`
|
||||
Refresh DashboardVariableRefresh `json:"refresh"`
|
||||
SkipUrlSync bool `json:"skipUrlSync"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
|
||||
Query DashboardDataQueryKind `json:"query"`
|
||||
Regex string `json:"regex"`
|
||||
Sort DashboardVariableSort `json:"sort"`
|
||||
Definition *string `json:"definition,omitempty"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
Multi bool `json:"multi"`
|
||||
IncludeAll bool `json:"includeAll"`
|
||||
AllValue *string `json:"allValue,omitempty"`
|
||||
Placeholder *string `json:"placeholder,omitempty"`
|
||||
AllowCustomValue bool `json:"allowCustomValue"`
|
||||
StaticOptions []DashboardVariableOption `json:"staticOptions,omitempty"`
|
||||
StaticOptionsOrder *DashboardQueryVariableSpecStaticOptionsOrder `json:"staticOptionsOrder,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Current DashboardVariableOption `json:"current"`
|
||||
Label *string `json:"label,omitempty"`
|
||||
Hide DashboardVariableHide `json:"hide"`
|
||||
Refresh DashboardVariableRefresh `json:"refresh"`
|
||||
SkipUrlSync bool `json:"skipUrlSync"`
|
||||
Description *string `json:"description,omitempty"`
|
||||
Datasource *DashboardDataSourceRef `json:"datasource,omitempty"`
|
||||
Query DashboardDataQueryKind `json:"query"`
|
||||
Regex string `json:"regex"`
|
||||
Sort DashboardVariableSort `json:"sort"`
|
||||
Definition *string `json:"definition,omitempty"`
|
||||
Options []DashboardVariableOption `json:"options"`
|
||||
Multi bool `json:"multi"`
|
||||
IncludeAll bool `json:"includeAll"`
|
||||
AllValue *string `json:"allValue,omitempty"`
|
||||
Placeholder *string `json:"placeholder,omitempty"`
|
||||
AllowCustomValue bool `json:"allowCustomValue"`
|
||||
}
|
||||
|
||||
// NewDashboardQueryVariableSpec creates a new DashboardQueryVariableSpec object.
|
||||
@@ -1697,9 +1687,7 @@ type DashboardAdHocFilterWithLabels struct {
|
||||
|
||||
// NewDashboardAdHocFilterWithLabels creates a new DashboardAdHocFilterWithLabels object.
|
||||
func NewDashboardAdHocFilterWithLabels() *DashboardAdHocFilterWithLabels {
|
||||
return &DashboardAdHocFilterWithLabels{
|
||||
Origin: DashboardFilterOrigin,
|
||||
}
|
||||
return &DashboardAdHocFilterWithLabels{}
|
||||
}
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
@@ -1892,15 +1880,6 @@ const (
|
||||
DashboardTimeSettingsSpecWeekStartSunday DashboardTimeSettingsSpecWeekStart = "sunday"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardQueryVariableSpecStaticOptionsOrder string
|
||||
|
||||
const (
|
||||
DashboardQueryVariableSpecStaticOptionsOrderBefore DashboardQueryVariableSpecStaticOptionsOrder = "before"
|
||||
DashboardQueryVariableSpecStaticOptionsOrderAfter DashboardQueryVariableSpecStaticOptionsOrder = "after"
|
||||
DashboardQueryVariableSpecStaticOptionsOrderSorted DashboardQueryVariableSpecStaticOptionsOrder = "sorted"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardPanelKindOrLibraryPanelKind struct {
|
||||
PanelKind *DashboardPanelKind `json:"PanelKind,omitempty"`
|
||||
|
||||
@@ -3327,25 +3327,6 @@ func schema_pkg_apis_dashboard_v2alpha1_DashboardQueryVariableSpec(ref common.Re
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"staticOptions": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1.DashboardVariableOption"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"staticOptionsOrder": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"name", "current", "hide", "refresh", "skipUrlSync", "query", "regex", "sort", "options", "multi", "includeAll", "allowCustomValue"},
|
||||
},
|
||||
|
||||
@@ -20,7 +20,6 @@ API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardQueryGroupSpec,Queries
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardQueryGroupSpec,Transformations
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardQueryVariableSpec,StaticOptions
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardRowsLayoutSpec,Rows
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardSpec,Annotations
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1,DashboardSpec,Links
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package v2alpha2
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
const (
|
||||
// APIGroup is the API group used by all kinds in this package
|
||||
APIGroup = "dashboard.grafana.app"
|
||||
// APIVersion is the API version used by all kinds in this package
|
||||
APIVersion = "v2alpha2"
|
||||
)
|
||||
|
||||
var (
|
||||
// GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
|
||||
GroupVersion = schema.GroupVersion{
|
||||
Group: APIGroup,
|
||||
Version: APIVersion,
|
||||
}
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
|
||||
type DashboardJSONCodec struct{}
|
||||
|
||||
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
|
||||
func (*DashboardJSONCodec) Read(reader io.Reader, into resource.Object) error {
|
||||
return json.NewDecoder(reader).Decode(into)
|
||||
}
|
||||
|
||||
// Write writes JSON-encoded bytes into `writer` marshaled from `from`
|
||||
func (*DashboardJSONCodec) Write(writer io.Writer, from resource.Object) error {
|
||||
return json.NewEncoder(writer).Encode(from)
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Codec = &DashboardJSONCodec{}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
time "time"
|
||||
)
|
||||
|
||||
// metadata contains embedded CommonMetadata and can be extended with custom string fields
|
||||
// TODO: use CommonMetadata instead of redefining here; currently needs to be defined here
|
||||
// without external reference as using the CommonMetadata reference breaks thema codegen.
|
||||
type DashboardMetadata struct {
|
||||
UpdateTimestamp time.Time `json:"updateTimestamp"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
Uid string `json:"uid"`
|
||||
CreationTimestamp time.Time `json:"creationTimestamp"`
|
||||
DeletionTimestamp *time.Time `json:"deletionTimestamp,omitempty"`
|
||||
Finalizers []string `json:"finalizers"`
|
||||
ResourceVersion string `json:"resourceVersion"`
|
||||
Generation int64 `json:"generation"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
Labels map[string]string `json:"labels"`
|
||||
}
|
||||
|
||||
// NewDashboardMetadata creates a new DashboardMetadata object.
|
||||
func NewDashboardMetadata() *DashboardMetadata {
|
||||
return &DashboardMetadata{
|
||||
Finalizers: []string{},
|
||||
Labels: map[string]string{},
|
||||
}
|
||||
}
|
||||
@@ -1,319 +0,0 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type Dashboard struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
|
||||
// Spec is the spec of the Dashboard
|
||||
Spec DashboardSpec `json:"spec" yaml:"spec"`
|
||||
|
||||
Status DashboardStatus `json:"status" yaml:"status"`
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetSpec() any {
|
||||
return o.Spec
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetSpec(spec any) error {
|
||||
cast, ok := spec.(DashboardSpec)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
|
||||
}
|
||||
o.Spec = cast
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetSubresources() map[string]any {
|
||||
return map[string]any{
|
||||
"status": o.Status,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetSubresource(name string) (any, bool) {
|
||||
switch name {
|
||||
case "status":
|
||||
return o.Status, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetSubresource(name string, value any) error {
|
||||
switch name {
|
||||
case "status":
|
||||
cast, ok := value.(DashboardStatus)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set status type %#v, not of type DashboardStatus", value)
|
||||
}
|
||||
o.Status = cast
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("subresource '%s' does not exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetStaticMetadata() resource.StaticMetadata {
|
||||
gvk := o.GroupVersionKind()
|
||||
return resource.StaticMetadata{
|
||||
Name: o.ObjectMeta.Name,
|
||||
Namespace: o.ObjectMeta.Namespace,
|
||||
Group: gvk.Group,
|
||||
Version: gvk.Version,
|
||||
Kind: gvk.Kind,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetStaticMetadata(metadata resource.StaticMetadata) {
|
||||
o.Name = metadata.Name
|
||||
o.Namespace = metadata.Namespace
|
||||
o.SetGroupVersionKind(schema.GroupVersionKind{
|
||||
Group: metadata.Group,
|
||||
Version: metadata.Version,
|
||||
Kind: metadata.Kind,
|
||||
})
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetCommonMetadata() resource.CommonMetadata {
|
||||
dt := o.DeletionTimestamp
|
||||
var deletionTimestamp *time.Time
|
||||
if dt != nil {
|
||||
deletionTimestamp = &dt.Time
|
||||
}
|
||||
// Legacy ExtraFields support
|
||||
extraFields := make(map[string]any)
|
||||
if o.Annotations != nil {
|
||||
extraFields["annotations"] = o.Annotations
|
||||
}
|
||||
if o.ManagedFields != nil {
|
||||
extraFields["managedFields"] = o.ManagedFields
|
||||
}
|
||||
if o.OwnerReferences != nil {
|
||||
extraFields["ownerReferences"] = o.OwnerReferences
|
||||
}
|
||||
return resource.CommonMetadata{
|
||||
UID: string(o.UID),
|
||||
ResourceVersion: o.ResourceVersion,
|
||||
Generation: o.Generation,
|
||||
Labels: o.Labels,
|
||||
CreationTimestamp: o.CreationTimestamp.Time,
|
||||
DeletionTimestamp: deletionTimestamp,
|
||||
Finalizers: o.Finalizers,
|
||||
UpdateTimestamp: o.GetUpdateTimestamp(),
|
||||
CreatedBy: o.GetCreatedBy(),
|
||||
UpdatedBy: o.GetUpdatedBy(),
|
||||
ExtraFields: extraFields,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetCommonMetadata(metadata resource.CommonMetadata) {
|
||||
o.UID = types.UID(metadata.UID)
|
||||
o.ResourceVersion = metadata.ResourceVersion
|
||||
o.Generation = metadata.Generation
|
||||
o.Labels = metadata.Labels
|
||||
o.CreationTimestamp = metav1.NewTime(metadata.CreationTimestamp)
|
||||
if metadata.DeletionTimestamp != nil {
|
||||
dt := metav1.NewTime(*metadata.DeletionTimestamp)
|
||||
o.DeletionTimestamp = &dt
|
||||
} else {
|
||||
o.DeletionTimestamp = nil
|
||||
}
|
||||
o.Finalizers = metadata.Finalizers
|
||||
if o.Annotations == nil {
|
||||
o.Annotations = make(map[string]string)
|
||||
}
|
||||
if !metadata.UpdateTimestamp.IsZero() {
|
||||
o.SetUpdateTimestamp(metadata.UpdateTimestamp)
|
||||
}
|
||||
if metadata.CreatedBy != "" {
|
||||
o.SetCreatedBy(metadata.CreatedBy)
|
||||
}
|
||||
if metadata.UpdatedBy != "" {
|
||||
o.SetUpdatedBy(metadata.UpdatedBy)
|
||||
}
|
||||
// Legacy support for setting Annotations, ManagedFields, and OwnerReferences via ExtraFields
|
||||
if metadata.ExtraFields != nil {
|
||||
if annotations, ok := metadata.ExtraFields["annotations"]; ok {
|
||||
if cast, ok := annotations.(map[string]string); ok {
|
||||
o.Annotations = cast
|
||||
}
|
||||
}
|
||||
if managedFields, ok := metadata.ExtraFields["managedFields"]; ok {
|
||||
if cast, ok := managedFields.([]metav1.ManagedFieldsEntry); ok {
|
||||
o.ManagedFields = cast
|
||||
}
|
||||
}
|
||||
if ownerReferences, ok := metadata.ExtraFields["ownerReferences"]; ok {
|
||||
if cast, ok := ownerReferences.([]metav1.OwnerReference); ok {
|
||||
o.OwnerReferences = cast
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetCreatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/createdBy"]
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetCreatedBy(createdBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetUpdateTimestamp() time.Time {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
parsed, _ := time.Parse(time.RFC3339, o.ObjectMeta.Annotations["grafana.com/updateTimestamp"])
|
||||
return parsed
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetUpdateTimestamp(updateTimestamp time.Time) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updateTimestamp"] = updateTimestamp.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
func (o *Dashboard) GetUpdatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
|
||||
}
|
||||
|
||||
func (o *Dashboard) SetUpdatedBy(updatedBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
|
||||
}
|
||||
|
||||
func (o *Dashboard) Copy() resource.Object {
|
||||
return resource.CopyObject(o)
|
||||
}
|
||||
|
||||
func (o *Dashboard) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *Dashboard) DeepCopy() *Dashboard {
|
||||
cpy := &Dashboard{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *Dashboard) DeepCopyInto(dst *Dashboard) {
|
||||
dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion
|
||||
dst.TypeMeta.Kind = o.TypeMeta.Kind
|
||||
o.ObjectMeta.DeepCopyInto(&dst.ObjectMeta)
|
||||
o.Spec.DeepCopyInto(&dst.Spec)
|
||||
o.Status.DeepCopyInto(&dst.Status)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.Object = &Dashboard{}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardList struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ListMeta `json:"metadata" yaml:"metadata"`
|
||||
Items []Dashboard `json:"items" yaml:"items"`
|
||||
}
|
||||
|
||||
func (o *DashboardList) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *DashboardList) Copy() resource.ListObject {
|
||||
cpy := &DashboardList{
|
||||
TypeMeta: o.TypeMeta,
|
||||
Items: make([]Dashboard, len(o.Items)),
|
||||
}
|
||||
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
|
||||
for i := 0; i < len(o.Items); i++ {
|
||||
if item, ok := o.Items[i].Copy().(*Dashboard); ok {
|
||||
cpy.Items[i] = *item
|
||||
}
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *DashboardList) GetItems() []resource.Object {
|
||||
items := make([]resource.Object, len(o.Items))
|
||||
for i := 0; i < len(o.Items); i++ {
|
||||
items[i] = &o.Items[i]
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (o *DashboardList) SetItems(items []resource.Object) {
|
||||
o.Items = make([]Dashboard, len(items))
|
||||
for i := 0; i < len(items); i++ {
|
||||
o.Items[i] = *items[i].(*Dashboard)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *DashboardList) DeepCopy() *DashboardList {
|
||||
cpy := &DashboardList{}
|
||||
o.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *DashboardList) DeepCopyInto(dst *DashboardList) {
|
||||
resource.CopyObjectInto(dst, o)
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.ListObject = &DashboardList{}
|
||||
|
||||
// Copy methods for all subresource types
|
||||
|
||||
// DeepCopy creates a full deep copy of Spec
|
||||
func (s *DashboardSpec) DeepCopy() *DashboardSpec {
|
||||
cpy := &DashboardSpec{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies Spec into another Spec object
|
||||
func (s *DashboardSpec) DeepCopyInto(dst *DashboardSpec) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
|
||||
// DeepCopy creates a full deep copy of DashboardStatus
|
||||
func (s *DashboardStatus) DeepCopy() *DashboardStatus {
|
||||
cpy := &DashboardStatus{}
|
||||
s.DeepCopyInto(cpy)
|
||||
return cpy
|
||||
}
|
||||
|
||||
// DeepCopyInto deep copies DashboardStatus into another DashboardStatus object
|
||||
func (s *DashboardStatus) DeepCopyInto(dst *DashboardStatus) {
|
||||
resource.CopyObjectInto(dst, s)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
)
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v2alpha2", &Dashboard{}, &DashboardList{}, resource.WithKind("Dashboard"),
|
||||
resource.WithPlural("dashboards"), resource.WithScope(resource.NamespacedScope))
|
||||
kindDashboard = resource.Kind{
|
||||
Schema: schemaDashboard,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
resource.KindEncodingJSON: &DashboardJSONCodec{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Kind returns a resource.Kind for this Schema with a JSON codec
|
||||
func DashboardKind() resource.Kind {
|
||||
return kindDashboard
|
||||
}
|
||||
|
||||
// Schema returns a resource.SimpleSchema representation of Dashboard
|
||||
func DashboardSchema() *resource.SimpleSchema {
|
||||
return schemaDashboard
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Schema = kindDashboard
|
||||
@@ -1,968 +0,0 @@
|
||||
// This file is managed by grafana-app-sdk - DO NOT EDIT MANUALLY
|
||||
// Source: apps/dashboard/kinds/v2alpha2/dashboard_spec.cue
|
||||
// To sync changes, run: make generate in apps/dashboard
|
||||
|
||||
package v2alpha2
|
||||
|
||||
DashboardSpec: {
|
||||
annotations: [...AnnotationQueryKind] | *[]
|
||||
|
||||
// Configuration of dashboard cursor sync behavior.
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
// "Crosshair" for shared crosshair.
|
||||
// "Tooltip" for shared crosshair AND shared tooltip.
|
||||
cursorSync: DashboardCursorSync
|
||||
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
|
||||
// Whether a dashboard is editable or not.
|
||||
editable?: bool | *true
|
||||
|
||||
elements: [ElementReference.name]: Element | *{}
|
||||
|
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
|
||||
|
||||
// Links with references to other dashboards or external websites.
|
||||
links: [...DashboardLink] | *[]
|
||||
|
||||
// When set to true, the dashboard will redraw panels at an interval matching the pixel width.
|
||||
// This will keep data "moving left" regardless of the query refresh rate. This setting helps
|
||||
// avoid dashboards presenting stale live data.
|
||||
liveNow?: bool
|
||||
|
||||
// When set to true, the dashboard will load all panels in the dashboard when it's loaded.
|
||||
preload: bool | *false
|
||||
|
||||
// Plugins only. The version of the dashboard installed together with the plugin.
|
||||
// This is used to determine if the dashboard should be updated when the plugin is updated.
|
||||
revision?: uint16
|
||||
|
||||
// Tags associated with dashboard.
|
||||
tags: [...string] | *[]
|
||||
|
||||
timeSettings: TimeSettingsSpec
|
||||
|
||||
// Title of dashboard.
|
||||
title: string
|
||||
|
||||
// Configured template variables.
|
||||
variables: [...VariableKind] | *[]
|
||||
}
|
||||
|
||||
// Supported dashboard elements
|
||||
Element: PanelKind | LibraryPanelKind // |* more element types in the future
|
||||
|
||||
LibraryPanelKind: {
|
||||
kind: "LibraryPanel"
|
||||
spec: LibraryPanelKindSpec
|
||||
}
|
||||
|
||||
LibraryPanelKindSpec: {
|
||||
// Panel ID for the library panel in the dashboard
|
||||
id: number
|
||||
// Title for the library panel in the dashboard
|
||||
title: string
|
||||
|
||||
libraryPanel: LibraryPanelRef
|
||||
}
|
||||
|
||||
// A library panel is a reusable panel that you can use in any dashboard.
|
||||
// When you make a change to a library panel, that change propagates to all instances of where the panel is used.
|
||||
// Library panels streamline reuse of panels across multiple dashboards.
|
||||
LibraryPanelRef: {
|
||||
// Library panel name
|
||||
name: string
|
||||
// Library panel uid
|
||||
uid: string
|
||||
}
|
||||
|
||||
AnnotationPanelFilter: {
|
||||
// Should the specified panels be included or excluded
|
||||
exclude?: bool | *false
|
||||
|
||||
// Panel IDs that should be included or excluded
|
||||
ids: [...uint32]
|
||||
}
|
||||
|
||||
// "Off" for no shared crosshair or tooltip (default).
|
||||
// "Crosshair" for shared crosshair.
|
||||
// "Tooltip" for shared crosshair AND shared tooltip.
|
||||
DashboardCursorSync: "Crosshair" | "Tooltip" | *"Off"
|
||||
|
||||
// Links with references to other dashboards or external resources
|
||||
DashboardLink: {
|
||||
// Title to display with the link
|
||||
title: string
|
||||
// Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
// FIXME: The type is generated as `type: DashboardLinkType | dashboardLinkType.Link;` but it should be `type: DashboardLinkType`
|
||||
type: DashboardLinkType
|
||||
// Icon name to be displayed with the link
|
||||
icon: string
|
||||
// Tooltip to display when the user hovers their mouse over it
|
||||
tooltip: string
|
||||
// Link URL. Only required/valid if the type is link
|
||||
url?: string
|
||||
// List of tags to limit the linked dashboards. If empty, all dashboards will be displayed. Only valid if the type is dashboards
|
||||
tags: [...string] | *[]
|
||||
// If true, all dashboards links will be displayed in a dropdown. If false, all dashboards links will be displayed side by side. Only valid if the type is dashboards
|
||||
asDropdown: bool | *false
|
||||
// If true, the link will be opened in a new tab
|
||||
targetBlank: bool | *false
|
||||
// If true, includes current template variables values in the link as query params
|
||||
includeVars: bool | *false
|
||||
// If true, includes current time range in the link as query params
|
||||
keepTime: bool | *false
|
||||
}
|
||||
|
||||
DataSourceRef: {
|
||||
// The plugin type-id
|
||||
type?: string
|
||||
|
||||
// Specific datasource instance
|
||||
uid?: string
|
||||
}
|
||||
|
||||
// A topic is attached to DataFrame metadata in query results.
|
||||
// This specifies where the data should be used.
|
||||
DataTopic: "series" | "annotations" | "alertStates" @cog(kind="enum",memberNames="Series|Annotations|AlertStates")
|
||||
|
||||
// Transformations allow to manipulate data returned by a query before the system applies a visualization.
|
||||
// Using transformations you can: rename fields, join time series data, perform mathematical operations across queries,
|
||||
// use the output of one transformation as the input to another transformation, etc.
|
||||
DataTransformerConfig: {
|
||||
// Unique identifier of transformer
|
||||
id: string
|
||||
// Disabled transformations are skipped
|
||||
disabled?: bool
|
||||
// Optional frame matcher. When missing it will be applied to all results
|
||||
filter?: MatcherConfig
|
||||
// Where to pull DataFrames from as input to transformation
|
||||
topic?: DataTopic
|
||||
// Options to be passed to the transformer
|
||||
// Valid options depend on the transformer id
|
||||
options: _
|
||||
}
|
||||
|
||||
DataLink: {
|
||||
title: string
|
||||
url: string
|
||||
targetBlank?: bool
|
||||
}
|
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
|
||||
// Each column within this structure is called a field. A field can represent a single time series or table column.
|
||||
// Field options allow you to change how the data is displayed in your visualizations.
|
||||
FieldConfigSource: {
|
||||
// Defaults are the options applied to all fields.
|
||||
defaults: FieldConfig
|
||||
// Overrides are the options applied to specific fields overriding the defaults.
|
||||
overrides: [...{
|
||||
matcher: MatcherConfig
|
||||
properties: [...DynamicConfigValue]
|
||||
}]
|
||||
}
|
||||
|
||||
// The data model used in Grafana, namely the data frame, is a columnar-oriented table structure that unifies both time series and table query results.
|
||||
// Each column within this structure is called a field. A field can represent a single time series or table column.
|
||||
// Field options allow you to change how the data is displayed in your visualizations.
|
||||
FieldConfig: {
|
||||
// The display value for this field. This supports template variables blank is auto
|
||||
displayName?: string
|
||||
|
||||
// This can be used by data sources that return and explicit naming structure for values and labels
|
||||
// When this property is configured, this value is used rather than the default naming strategy.
|
||||
displayNameFromDS?: string
|
||||
|
||||
// Human readable field metadata
|
||||
description?: string
|
||||
|
||||
// An explicit path to the field in the datasource. When the frame meta includes a path,
|
||||
// This will default to `${frame.meta.path}/${field.name}
|
||||
//
|
||||
// When defined, this value can be used as an identifier within the datasource scope, and
|
||||
// may be used to update the results
|
||||
path?: string
|
||||
|
||||
// True if data source can write a value to the path. Auth/authz are supported separately
|
||||
writeable?: bool
|
||||
|
||||
// True if data source field supports ad-hoc filters
|
||||
filterable?: bool
|
||||
|
||||
// Unit a field should use. The unit you select is applied to all fields except time.
|
||||
// You can use the units ID availables in Grafana or a custom unit.
|
||||
// Available units in Grafana: https://github.com/grafana/grafana/blob/main/packages/grafana-data/src/valueFormats/categories.ts
|
||||
// As custom unit, you can use the following formats:
|
||||
// `suffix:<suffix>` for custom unit that should go after value.
|
||||
// `prefix:<prefix>` for custom unit that should go before value.
|
||||
// `time:<format>` For custom date time formats type for example `time:YYYY-MM-DD`.
|
||||
// `si:<base scale><unit characters>` for custom SI units. For example: `si: mF`. This one is a bit more advanced as you can specify both a unit and the source data scale. So if your source data is represented as milli (thousands of) something prefix the unit with that SI scale character.
|
||||
// `count:<unit>` for a custom count unit.
|
||||
// `currency:<unit>` for custom a currency unit.
|
||||
unit?: string
|
||||
|
||||
// Specify the number of decimals Grafana includes in the rendered value.
|
||||
// If you leave this field blank, Grafana automatically truncates the number of decimals based on the value.
|
||||
// For example 1.1234 will display as 1.12 and 100.456 will display as 100.
|
||||
// To display all decimals, set the unit to `String`.
|
||||
decimals?: number
|
||||
|
||||
// The minimum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
|
||||
min?: number
|
||||
// The maximum value used in percentage threshold calculations. Leave blank for auto calculation based on all series and fields.
|
||||
max?: number
|
||||
|
||||
// Convert input values into a display string
|
||||
mappings?: [...ValueMapping]
|
||||
|
||||
// Map numeric values to states
|
||||
thresholds?: ThresholdsConfig
|
||||
|
||||
// Panel color configuration
|
||||
color?: FieldColor
|
||||
|
||||
// The behavior when clicking on a result
|
||||
links?: [...]
|
||||
|
||||
// Alternative to empty string
|
||||
noValue?: string
|
||||
|
||||
// custom is specified by the FieldConfig field
|
||||
// in panel plugin schemas.
|
||||
custom?: {...}
|
||||
}
|
||||
|
||||
DynamicConfigValue: {
|
||||
id: string | *""
|
||||
value?: _
|
||||
}
|
||||
|
||||
// Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation.
|
||||
// It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type.
|
||||
MatcherConfig: {
|
||||
// The matcher id. This is used to find the matcher implementation from registry.
|
||||
id: string | *""
|
||||
// The matcher options. This is specific to the matcher implementation.
|
||||
options?: _
|
||||
}
|
||||
|
||||
Threshold: {
|
||||
value: number
|
||||
color: string
|
||||
}
|
||||
|
||||
ThresholdsMode: "absolute" | "percentage"
|
||||
|
||||
ThresholdsConfig: {
|
||||
mode: ThresholdsMode
|
||||
steps: [...Threshold]
|
||||
}
|
||||
|
||||
ValueMapping: ValueMap | RangeMap | RegexMap | SpecialValueMap
|
||||
|
||||
// Supported value mapping types
|
||||
// `value`: Maps text values to a color or different display text and color. For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
|
||||
// `range`: Maps numerical ranges to a display text and color. For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
|
||||
// `regex`: Maps regular expressions to replacement text and a color. For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
|
||||
// `special`: Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color. See SpecialValueMatch to see the list of special values. For example, you can configure a special value mapping so that null values appear as N/A.
|
||||
MappingType: "value" | "range" | "regex" | "special"
|
||||
|
||||
// Maps text values to a color or different display text and color.
|
||||
// For example, you can configure a value mapping so that all instances of the value 10 appear as Perfection! rather than the number.
|
||||
ValueMap: {
|
||||
type: MappingType & "value"
|
||||
// Map with <value_to_match>: ValueMappingResult. For example: { "10": { text: "Perfection!", color: "green" } }
|
||||
options: [string]: ValueMappingResult
|
||||
}
|
||||
|
||||
// Maps numerical ranges to a display text and color.
|
||||
// For example, if a value is within a certain range, you can configure a range value mapping to display Low or High rather than the number.
|
||||
RangeMap: {
|
||||
type: MappingType & "range"
|
||||
// Range to match against and the result to apply when the value is within the range
|
||||
options: {
|
||||
// Min value of the range. It can be null which means -Infinity
|
||||
from: float64 | null
|
||||
// Max value of the range. It can be null which means +Infinity
|
||||
to: float64 | null
|
||||
// Config to apply when the value is within the range
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Maps regular expressions to replacement text and a color.
|
||||
// For example, if a value is www.example.com, you can configure a regex value mapping so that Grafana displays www and truncates the domain.
|
||||
RegexMap: {
|
||||
type: MappingType & "regex"
|
||||
// Regular expression to match against and the result to apply when the value matches the regex
|
||||
options: {
|
||||
// Regular expression to match against
|
||||
pattern: string
|
||||
// Config to apply when the value matches the regex
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Maps special values like Null, NaN (not a number), and boolean values like true and false to a display text and color.
|
||||
// See SpecialValueMatch to see the list of special values.
|
||||
// For example, you can configure a special value mapping so that null values appear as N/A.
|
||||
SpecialValueMap: {
|
||||
type: MappingType & "special"
|
||||
options: {
|
||||
// Special value to match against
|
||||
match: SpecialValueMatch
|
||||
// Config to apply when the value matches the special value
|
||||
result: ValueMappingResult
|
||||
}
|
||||
}
|
||||
|
||||
// Special value types supported by the `SpecialValueMap`
|
||||
SpecialValueMatch: "true" | "false" | "null" | "nan" | "null+nan" | "empty" @cog(kind="enum",memberNames="True|False|Null|NaN|NullAndNaN|Empty")
|
||||
|
||||
// Result used as replacement with text and color when the value matches
|
||||
ValueMappingResult: {
|
||||
// Text to display when the value matches
|
||||
text?: string
|
||||
// Text to use when the value matches
|
||||
color?: string
|
||||
// Icon to display when the value matches. Only specific visualizations.
|
||||
icon?: string
|
||||
// Position in the mapping array. Only used internally.
|
||||
index?: int32
|
||||
}
|
||||
|
||||
// Color mode for a field. You can specify a single color, or select a continuous (gradient) color schemes, based on a value.
|
||||
// Continuous color interpolates a color using the percentage of a value relative to min and max.
|
||||
// Accepted values are:
|
||||
// `thresholds`: From thresholds. Informs Grafana to take the color from the matching threshold
|
||||
// `palette-classic`: Classic palette. Grafana will assign color by looking up a color in a palette by series index. Useful for Graphs and pie charts and other categorical data visualizations
|
||||
// `palette-classic-by-name`: Classic palette (by name). Grafana will assign color by looking up a color in a palette by series name. Useful for Graphs and pie charts and other categorical data visualizations
|
||||
// `continuous-GrYlRd`: ontinuous Green-Yellow-Red palette mode
|
||||
// `continuous-RdYlGr`: Continuous Red-Yellow-Green palette mode
|
||||
// `continuous-BlYlRd`: Continuous Blue-Yellow-Red palette mode
|
||||
// `continuous-YlRd`: Continuous Yellow-Red palette mode
|
||||
// `continuous-BlPu`: Continuous Blue-Purple palette mode
|
||||
// `continuous-YlBl`: Continuous Yellow-Blue palette mode
|
||||
// `continuous-blues`: Continuous Blue palette mode
|
||||
// `continuous-reds`: Continuous Red palette mode
|
||||
// `continuous-greens`: Continuous Green palette mode
|
||||
// `continuous-purples`: Continuous Purple palette mode
|
||||
// `shades`: Shades of a single color. Specify a single color, useful in an override rule.
|
||||
// `fixed`: Fixed color mode. Specify a single color, useful in an override rule.
|
||||
FieldColorModeId: "thresholds" | "palette-classic" | "palette-classic-by-name" | "continuous-GrYlRd" | "continuous-RdYlGr" | "continuous-BlYlRd" | "continuous-YlRd" | "continuous-BlPu" | "continuous-YlBl" | "continuous-blues" | "continuous-reds" | "continuous-greens" | "continuous-purples" | "fixed" | "shades"
|
||||
|
||||
// Defines how to assign a series color from "by value" color schemes. For example for an aggregated data points like a timeseries, the color can be assigned by the min, max or last value.
|
||||
FieldColorSeriesByMode: "min" | "max" | "last"
|
||||
|
||||
// Map a field to a color.
|
||||
FieldColor: {
|
||||
// The main color scheme mode.
|
||||
mode: FieldColorModeId
|
||||
// The fixed color value for fixed or shades color modes.
|
||||
fixedColor?: string
|
||||
// Some visualizations need to know how to assign a series color from by value color schemes.
|
||||
seriesBy?: FieldColorSeriesByMode
|
||||
}
|
||||
|
||||
// Dashboard Link type. Accepted values are dashboards (to refer to another dashboard) and link (to refer to an external resource)
|
||||
DashboardLinkType: "link" | "dashboards"
|
||||
|
||||
// --- Common types ---
|
||||
Kind: {
|
||||
kind: string
|
||||
spec: _
|
||||
metadata?: _
|
||||
}
|
||||
|
||||
// --- Kinds ---
|
||||
VizConfigSpec: {
|
||||
pluginVersion: string
|
||||
options: [string]: _
|
||||
fieldConfig: FieldConfigSource
|
||||
}
|
||||
|
||||
VizConfigKind: {
|
||||
// The kind of a VizConfigKind is the plugin ID
|
||||
kind: string
|
||||
spec: VizConfigSpec
|
||||
}
|
||||
|
||||
AnnotationQuerySpec: {
|
||||
datasource?: DataSourceRef
|
||||
query?: DataQueryKind
|
||||
enable: bool
|
||||
hide: bool
|
||||
iconColor: string
|
||||
name: string
|
||||
builtIn?: bool | *false
|
||||
filter?: AnnotationPanelFilter
|
||||
legacyOptions?: [string]: _ //Catch-all field for datasource-specific properties
|
||||
}
|
||||
|
||||
AnnotationQueryKind: {
|
||||
kind: "AnnotationQuery"
|
||||
spec: AnnotationQuerySpec
|
||||
}
|
||||
|
||||
QueryOptionsSpec: {
|
||||
timeFrom?: string
|
||||
maxDataPoints?: int
|
||||
timeShift?: string
|
||||
queryCachingTTL?: int
|
||||
interval?: string
|
||||
cacheTimeout?: string
|
||||
hideTimeOverride?: bool
|
||||
}
|
||||
|
||||
DataQueryKind: {
|
||||
// The kind of a DataQueryKind is the datasource type
|
||||
kind: string
|
||||
spec: [string]: _
|
||||
}
|
||||
|
||||
PanelQuerySpec: {
|
||||
query: DataQueryKind
|
||||
datasource?: DataSourceRef
|
||||
|
||||
refId: string
|
||||
hidden: bool
|
||||
}
|
||||
|
||||
PanelQueryKind: {
|
||||
kind: "PanelQuery"
|
||||
spec: PanelQuerySpec
|
||||
}
|
||||
|
||||
TransformationKind: {
|
||||
// The kind of a TransformationKind is the transformation ID
|
||||
kind: string
|
||||
spec: DataTransformerConfig
|
||||
}
|
||||
|
||||
QueryGroupSpec: {
|
||||
queries: [...PanelQueryKind]
|
||||
transformations: [...TransformationKind]
|
||||
queryOptions: QueryOptionsSpec
|
||||
}
|
||||
|
||||
QueryGroupKind: {
|
||||
kind: "QueryGroup"
|
||||
spec: QueryGroupSpec
|
||||
}
|
||||
|
||||
TimeRangeOption: {
|
||||
display: string | *"Last 6 hours"
|
||||
from: string | *"now-6h"
|
||||
to: string | *"now"
|
||||
}
|
||||
|
||||
// Time configuration
|
||||
// It defines the default time config for the time picker, the refresh picker for the specific dashboard.
|
||||
TimeSettingsSpec: {
|
||||
// Timezone of dashboard. Accepted values are IANA TZDB zone ID or "browser" or "utc".
|
||||
timezone?: string | *"browser"
|
||||
// Start time range for dashboard.
|
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z".
|
||||
from: string | *"now-6h"
|
||||
// End time range for dashboard.
|
||||
// Accepted values are relative time strings like "now-6h" or absolute time strings like "2020-07-10T08:00:00.000Z".
|
||||
to: string | *"now"
|
||||
// Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d".
|
||||
autoRefresh: string | *"" // v1: refresh
|
||||
// Interval options available in the refresh picker dropdown.
|
||||
autoRefreshIntervals: [...string] | *["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"] // v1: timepicker.refresh_intervals
|
||||
// Selectable options available in the time picker dropdown. Has no effect on provisioned dashboard.
|
||||
quickRanges?: [...TimeRangeOption] // v1: timepicker.quick_ranges , not exposed in the UI
|
||||
// Whether timepicker is visible or not.
|
||||
hideTimepicker: bool | *false // v1: timepicker.hidden
|
||||
// Day when the week starts. Expressed by the name of the day in lowercase, e.g. "monday".
|
||||
weekStart?: "saturday" | "monday" | "sunday"
|
||||
// The month that the fiscal year starts on. 0 = January, 11 = December
|
||||
fiscalYearStartMonth: int | *0
|
||||
// Override the now time by entering a time delay. Use this option to accommodate known delays in data aggregation to avoid null values.
|
||||
nowDelay?: string // v1: timepicker.nowDelay
|
||||
}
|
||||
|
||||
RepeatMode: "variable" // other repeat modes will be added in the future: label, frame
|
||||
|
||||
RepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
direction?: "h" | "v"
|
||||
maxPerRow?: int
|
||||
}
|
||||
|
||||
RowRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
TabRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
AutoGridRepeatOptions: {
|
||||
mode: RepeatMode
|
||||
value: string
|
||||
}
|
||||
|
||||
GridLayoutItemSpec: {
|
||||
x: int
|
||||
y: int
|
||||
width: int
|
||||
height: int
|
||||
element: ElementReference // reference to a PanelKind from dashboard.spec.elements Expressed as JSON Schema reference
|
||||
repeat?: RepeatOptions
|
||||
}
|
||||
|
||||
GridLayoutItemKind: {
|
||||
kind: "GridLayoutItem"
|
||||
spec: GridLayoutItemSpec
|
||||
}
|
||||
|
||||
GridLayoutSpec: {
|
||||
items: [...GridLayoutItemKind]
|
||||
}
|
||||
|
||||
GridLayoutKind: {
|
||||
kind: "GridLayout"
|
||||
spec: GridLayoutSpec
|
||||
}
|
||||
|
||||
RowsLayoutKind: {
|
||||
kind: "RowsLayout"
|
||||
spec: RowsLayoutSpec
|
||||
}
|
||||
|
||||
RowsLayoutSpec: {
|
||||
rows: [...RowsLayoutRowKind]
|
||||
}
|
||||
|
||||
RowsLayoutRowKind: {
|
||||
kind: "RowsLayoutRow"
|
||||
spec: RowsLayoutRowSpec
|
||||
}
|
||||
|
||||
RowsLayoutRowSpec: {
|
||||
title?: string
|
||||
collapse?: bool
|
||||
hideHeader?: bool
|
||||
fillScreen?: bool
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
repeat?: RowRepeatOptions
|
||||
layout: GridLayoutKind | AutoGridLayoutKind | TabsLayoutKind | RowsLayoutKind
|
||||
}
|
||||
|
||||
AutoGridLayoutKind: {
|
||||
kind: "AutoGridLayout"
|
||||
spec: AutoGridLayoutSpec
|
||||
}
|
||||
|
||||
AutoGridLayoutSpec: {
|
||||
maxColumnCount?: number | *3
|
||||
columnWidthMode: "narrow" | *"standard" | "wide" | "custom"
|
||||
columnWidth?: number
|
||||
rowHeightMode: "short" | *"standard" | "tall" | "custom"
|
||||
rowHeight?: number
|
||||
fillScreen?: bool | *false
|
||||
items: [...AutoGridLayoutItemKind]
|
||||
}
|
||||
|
||||
AutoGridLayoutItemKind: {
|
||||
kind: "AutoGridLayoutItem"
|
||||
spec: AutoGridLayoutItemSpec
|
||||
}
|
||||
|
||||
AutoGridLayoutItemSpec: {
|
||||
element: ElementReference
|
||||
repeat?: AutoGridRepeatOptions
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
}
|
||||
|
||||
TabsLayoutKind: {
|
||||
kind: "TabsLayout"
|
||||
spec: TabsLayoutSpec
|
||||
}
|
||||
|
||||
TabsLayoutSpec: {
|
||||
tabs: [...TabsLayoutTabKind]
|
||||
}
|
||||
|
||||
TabsLayoutTabKind: {
|
||||
kind: "TabsLayoutTab"
|
||||
spec: TabsLayoutTabSpec
|
||||
}
|
||||
|
||||
TabsLayoutTabSpec: {
|
||||
title?: string
|
||||
layout: GridLayoutKind | RowsLayoutKind | AutoGridLayoutKind | TabsLayoutKind
|
||||
conditionalRendering?: ConditionalRenderingGroupKind
|
||||
repeat?: TabRepeatOptions
|
||||
}
|
||||
|
||||
PanelSpec: {
|
||||
id: number
|
||||
title: string
|
||||
description: string
|
||||
links: [...DataLink]
|
||||
data: QueryGroupKind
|
||||
vizConfig: VizConfigKind
|
||||
transparent?: bool
|
||||
}
|
||||
|
||||
PanelKind: {
|
||||
kind: "Panel"
|
||||
spec: PanelSpec
|
||||
}
|
||||
|
||||
ElementReference: {
|
||||
kind: "ElementReference"
|
||||
name: string
|
||||
}
|
||||
|
||||
// Start FIXME: variables - in CUE PR - this are things that should be added into the cue schema
|
||||
// TODO: properties such as `hide`, `skipUrlSync`, `multi` are type boolean, and in the old schema they are conditional,
|
||||
// should we make them conditional in the new schema as well? or should we make them required but default to false?
|
||||
|
||||
// Variable types
|
||||
VariableValue: VariableValueSingle | [...VariableValueSingle]
|
||||
|
||||
VariableValueSingle: string | bool | number | CustomVariableValue
|
||||
|
||||
// Custom formatter variable
|
||||
CustomFormatterVariable: {
|
||||
name: string
|
||||
type: VariableType
|
||||
multi: bool
|
||||
includeAll: bool
|
||||
}
|
||||
|
||||
// Custom variable value
|
||||
CustomVariableValue: {
|
||||
// The format name or function used in the expression
|
||||
formatter: *null | string | VariableCustomFormatterFn
|
||||
}
|
||||
|
||||
// Custom formatter function
|
||||
VariableCustomFormatterFn: {
|
||||
value: _
|
||||
legacyVariableModel: {
|
||||
name: string
|
||||
type: VariableType
|
||||
multi: bool
|
||||
includeAll: bool
|
||||
}
|
||||
legacyDefaultFormatter?: VariableCustomFormatterFn
|
||||
}
|
||||
|
||||
// Dashboard variable type
|
||||
// `query`: Query-generated list of values such as metric names, server names, sensor IDs, data centers, and so on.
|
||||
// `adhoc`: Key/value filters that are automatically added to all metric queries for a data source (Prometheus, Loki, InfluxDB, and Elasticsearch only).
|
||||
// `constant`: Define a hidden constant.
|
||||
// `datasource`: Quickly change the data source for an entire dashboard.
|
||||
// `interval`: Interval variables represent time spans.
|
||||
// `textbox`: Display a free text input field with an optional default value.
|
||||
// `custom`: Define the variable options manually using a comma-separated list.
|
||||
// `system`: Variables defined by Grafana. See: https://grafana.com/docs/grafana/latest/dashboards/variables/add-template-variables/#global-variables
|
||||
VariableType: "query" | "adhoc" | "groupby" | "constant" | "datasource" | "interval" | "textbox" | "custom" |
|
||||
"system" | "snapshot"
|
||||
|
||||
VariableKind: QueryVariableKind | TextVariableKind | ConstantVariableKind | DatasourceVariableKind | IntervalVariableKind | CustomVariableKind | GroupByVariableKind | AdhocVariableKind
|
||||
|
||||
// Sort variable options
|
||||
// Accepted values are:
|
||||
// `disabled`: No sorting
|
||||
// `alphabeticalAsc`: Alphabetical ASC
|
||||
// `alphabeticalDesc`: Alphabetical DESC
|
||||
// `numericalAsc`: Numerical ASC
|
||||
// `numericalDesc`: Numerical DESC
|
||||
// `alphabeticalCaseInsensitiveAsc`: Alphabetical Case Insensitive ASC
|
||||
// `alphabeticalCaseInsensitiveDesc`: Alphabetical Case Insensitive DESC
|
||||
// `naturalAsc`: Natural ASC
|
||||
// `naturalDesc`: Natural DESC
|
||||
// VariableSort enum with default value
|
||||
VariableSort: "disabled" | "alphabeticalAsc" | "alphabeticalDesc" | "numericalAsc" | "numericalDesc" | "alphabeticalCaseInsensitiveAsc" | "alphabeticalCaseInsensitiveDesc" | "naturalAsc" | "naturalDesc"
|
||||
|
||||
// Options to config when to refresh a variable
|
||||
// `never`: Never refresh the variable
|
||||
// `onDashboardLoad`: Queries the data source every time the dashboard loads.
|
||||
// `onTimeRangeChanged`: Queries the data source when the dashboard time range changes.
|
||||
VariableRefresh: *"never" | "onDashboardLoad" | "onTimeRangeChanged"
|
||||
|
||||
// Determine if the variable shows on dashboard
|
||||
// Accepted values are `dontHide` (show label and value), `hideLabel` (show value only), `hideVariable` (show nothing).
|
||||
VariableHide: *"dontHide" | "hideLabel" | "hideVariable"
|
||||
|
||||
// Determine the origin of the adhoc variable filter
|
||||
FilterOrigin: "dashboard"
|
||||
|
||||
// FIXME: should we introduce this? --- Variable value option
|
||||
VariableValueOption: {
|
||||
label: string
|
||||
value: VariableValueSingle
|
||||
group?: string
|
||||
}
|
||||
|
||||
// Variable option specification
|
||||
VariableOption: {
|
||||
// Whether the option is selected or not
|
||||
selected?: bool
|
||||
// Text to be displayed for the option
|
||||
text: string | [...string]
|
||||
// Value of the option
|
||||
value: string | [...string]
|
||||
}
|
||||
|
||||
// Query variable specification
|
||||
QueryVariableSpec: {
|
||||
name: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
refresh: VariableRefresh
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
datasource?: DataSourceRef
|
||||
query: DataQueryKind
|
||||
regex: string | *""
|
||||
sort: VariableSort
|
||||
definition?: string
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
placeholder?: string
|
||||
allowCustomValue: bool | *true
|
||||
staticOptions?: [...VariableOption]
|
||||
staticOptionsOrder?: "before" | "after" | "sorted"
|
||||
}
|
||||
|
||||
// Query variable kind
|
||||
QueryVariableKind: {
|
||||
kind: "QueryVariable"
|
||||
spec: QueryVariableSpec
|
||||
}
|
||||
|
||||
// Text variable specification
|
||||
TextVariableSpec: {
|
||||
name: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
query: string | *""
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Text variable kind
|
||||
TextVariableKind: {
|
||||
kind: "TextVariable"
|
||||
spec: TextVariableSpec
|
||||
}
|
||||
|
||||
// Constant variable specification
|
||||
ConstantVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Constant variable kind
|
||||
ConstantVariableKind: {
|
||||
kind: "ConstantVariable"
|
||||
spec: ConstantVariableSpec
|
||||
}
|
||||
|
||||
// Datasource variable specification
|
||||
DatasourceVariableSpec: {
|
||||
name: string | *""
|
||||
pluginId: string | *""
|
||||
refresh: VariableRefresh
|
||||
regex: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Datasource variable kind
|
||||
DatasourceVariableKind: {
|
||||
kind: "DatasourceVariable"
|
||||
spec: DatasourceVariableSpec
|
||||
}
|
||||
|
||||
// Interval variable specification
|
||||
IntervalVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
auto: bool | *false
|
||||
auto_min: string | *""
|
||||
auto_count: int | *0
|
||||
refresh: VariableRefresh
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Interval variable kind
|
||||
IntervalVariableKind: {
|
||||
kind: "IntervalVariable"
|
||||
spec: IntervalVariableSpec
|
||||
}
|
||||
|
||||
// Custom variable specification
|
||||
CustomVariableSpec: {
|
||||
name: string | *""
|
||||
query: string | *""
|
||||
current: VariableOption
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
includeAll: bool | *false
|
||||
allValue?: string
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Custom variable kind
|
||||
CustomVariableKind: {
|
||||
kind: "CustomVariable"
|
||||
spec: CustomVariableSpec
|
||||
}
|
||||
|
||||
// GroupBy variable specification
|
||||
GroupByVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
defaultValue?: VariableOption
|
||||
current: VariableOption | *{
|
||||
text: ""
|
||||
value: ""
|
||||
}
|
||||
options: [...VariableOption] | *[]
|
||||
multi: bool | *false
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Group variable kind
|
||||
GroupByVariableKind: {
|
||||
kind: "GroupByVariable"
|
||||
spec: GroupByVariableSpec
|
||||
}
|
||||
|
||||
// Adhoc variable specification
|
||||
AdhocVariableSpec: {
|
||||
name: string | *""
|
||||
datasource?: DataSourceRef
|
||||
baseFilters: [...AdHocFilterWithLabels] | *[]
|
||||
filters: [...AdHocFilterWithLabels] | *[]
|
||||
defaultKeys: [...MetricFindValue] | *[]
|
||||
label?: string
|
||||
hide: VariableHide
|
||||
skipUrlSync: bool | *false
|
||||
description?: string
|
||||
allowCustomValue: bool | *true
|
||||
}
|
||||
|
||||
// Define the MetricFindValue type
|
||||
MetricFindValue: {
|
||||
text: string
|
||||
value?: string | number
|
||||
group?: string
|
||||
expandable?: bool
|
||||
}
|
||||
|
||||
// Define the AdHocFilterWithLabels type
|
||||
AdHocFilterWithLabels: {
|
||||
key: string
|
||||
operator: string
|
||||
value: string
|
||||
values?: [...string]
|
||||
keyLabel?: string
|
||||
valueLabels?: [...string]
|
||||
forceEdit?: bool
|
||||
origin?: FilterOrigin
|
||||
// @deprecated
|
||||
condition?: string
|
||||
}
|
||||
|
||||
// Adhoc variable kind
|
||||
AdhocVariableKind: {
|
||||
kind: "AdhocVariable"
|
||||
spec: AdhocVariableSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingGroupKind: {
|
||||
kind: "ConditionalRenderingGroup"
|
||||
spec: ConditionalRenderingGroupSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingGroupSpec: {
|
||||
visibility: "show" | "hide"
|
||||
condition: "and" | "or"
|
||||
items: [...ConditionalRenderingVariableKind | ConditionalRenderingDataKind | ConditionalRenderingTimeRangeSizeKind]
|
||||
}
|
||||
|
||||
ConditionalRenderingVariableKind: {
|
||||
kind: "ConditionalRenderingVariable"
|
||||
spec: ConditionalRenderingVariableSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingVariableSpec: {
|
||||
variable: string
|
||||
operator: "equals" | "notEquals"
|
||||
value: string
|
||||
}
|
||||
|
||||
ConditionalRenderingDataKind: {
|
||||
kind: "ConditionalRenderingData"
|
||||
spec: ConditionalRenderingDataSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingDataSpec: {
|
||||
value: bool
|
||||
}
|
||||
|
||||
ConditionalRenderingTimeRangeSizeKind: {
|
||||
kind: "ConditionalRenderingTimeRangeSize"
|
||||
spec: ConditionalRenderingTimeRangeSizeSpec
|
||||
}
|
||||
|
||||
ConditionalRenderingTimeRangeSizeSpec: {
|
||||
value: string
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,34 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v2alpha2
|
||||
|
||||
// ConversionStatus is the status of the conversion of the dashboard.
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardConversionStatus struct {
|
||||
// Whether from another version has failed.
|
||||
// If true, means that the dashboard is not valid,
|
||||
// and the caller should instead fetch the stored version.
|
||||
Failed bool `json:"failed"`
|
||||
// The version which was stored when the dashboard was created / updated.
|
||||
// Fetching this version should always succeed.
|
||||
StoredVersion string `json:"storedVersion"`
|
||||
// The error message from the conversion.
|
||||
// Empty if the conversion has not failed.
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
// NewDashboardConversionStatus creates a new DashboardConversionStatus object.
|
||||
func NewDashboardConversionStatus() *DashboardConversionStatus {
|
||||
return &DashboardConversionStatus{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type DashboardStatus struct {
|
||||
// Optional conversion status.
|
||||
Conversion *DashboardConversionStatus `json:"conversion,omitempty"`
|
||||
}
|
||||
|
||||
// NewDashboardStatus creates a new DashboardStatus object.
|
||||
func NewDashboardStatus() *DashboardStatus {
|
||||
return &DashboardStatus{}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// +k8s:openapi-gen=true
|
||||
// +k8s:defaulter-gen=TypeMeta
|
||||
// +k8s:conversion-gen=github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard
|
||||
// +groupName=dashboard.grafana.app
|
||||
|
||||
// NOTE (@radiohead): we do not use package-wide deepcopy generation
|
||||
// because grafana-app-sdk already provides deepcopy functions.
|
||||
// Kinds which are not generated by the SDK are explicitly opted in to deepcopy generation.
|
||||
|
||||
package v2alpha2 // import "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
@@ -1,103 +0,0 @@
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
const (
|
||||
GROUP = "dashboard.grafana.app"
|
||||
VERSION = "v2alpha2"
|
||||
APIVERSION = GROUP + "/" + VERSION
|
||||
)
|
||||
|
||||
var DashboardResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"dashboards", "dashboard", "Dashboard",
|
||||
func() runtime.Object { return &Dashboard{} },
|
||||
func() runtime.Object { return &DashboardList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Format: "string", Description: "The dashboard name"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
dash, ok := obj.(*Dashboard)
|
||||
if ok {
|
||||
if dash != nil {
|
||||
return []interface{}{
|
||||
dash.Name,
|
||||
dash.Spec.Title,
|
||||
dash.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("expected dashboard")
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
var LibraryPanelResourceInfo = utils.NewResourceInfo(GROUP, VERSION,
|
||||
"librarypanels", "librarypanel", "LibraryPanel",
|
||||
func() runtime.Object { return &LibraryPanel{} },
|
||||
func() runtime.Object { return &LibraryPanelList{} },
|
||||
utils.TableColumns{
|
||||
Definition: []metav1.TableColumnDefinition{
|
||||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Title", Type: "string", Description: "The dashboard name"},
|
||||
{Name: "Type", Type: "string", Description: "the panel type"},
|
||||
{Name: "Created At", Type: "date"},
|
||||
},
|
||||
Reader: func(obj any) ([]interface{}, error) {
|
||||
panel, ok := obj.(*LibraryPanel)
|
||||
if ok {
|
||||
if panel != nil {
|
||||
return []interface{}{
|
||||
panel.Name,
|
||||
panel.Spec.Title,
|
||||
panel.Spec.Type,
|
||||
panel.CreationTimestamp.UTC().Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("expected library panel")
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
var (
|
||||
SchemeBuilder runtime.SchemeBuilder
|
||||
localSchemeBuilder = &SchemeBuilder
|
||||
AddToScheme = localSchemeBuilder.AddToScheme
|
||||
schemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
|
||||
}
|
||||
|
||||
// Adds the list of known types to the given scheme.
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(schemeGroupVersion,
|
||||
&Dashboard{},
|
||||
&DashboardList{},
|
||||
&DashboardWithAccessInfo{},
|
||||
&DashboardVersionList{},
|
||||
&VersionsQueryOptions{},
|
||||
&LibraryPanel{},
|
||||
&LibraryPanelList{},
|
||||
&metav1.PartialObjectMetadata{},
|
||||
&metav1.PartialObjectMetadataList{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, schemeGroupVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
return RegisterDefaults(scheme)
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
data "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardVersionList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []DashboardVersionInfo `json:"items"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
type DashboardVersionInfo struct {
|
||||
// The internal ID for this version (will be replaced with resourceVersion)
|
||||
Version int `json:"version"`
|
||||
|
||||
// If the dashboard came from a previous version, it is set here
|
||||
ParentVersion int `json:"parentVersion,omitempty"`
|
||||
|
||||
// The creation timestamp for this version
|
||||
Created int64 `json:"created"`
|
||||
|
||||
// The user who created this version
|
||||
CreatedBy string `json:"createdBy,omitempty"`
|
||||
|
||||
// Message passed while saving the version
|
||||
Message string `json:"message,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:conversion-gen:explicit-from=net/url.Values
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type VersionsQueryOptions struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// Path is the URL path
|
||||
// +optional
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// +optional
|
||||
Version int64 `json:"version,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type LibraryPanel struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object's metadata
|
||||
// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Panel properties
|
||||
Spec LibraryPanelSpec `json:"spec"`
|
||||
|
||||
// Status will show errors
|
||||
Status *LibraryPanelStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type LibraryPanelList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []LibraryPanel `json:"items"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
type LibraryPanelSpec struct {
|
||||
// The panel type
|
||||
Type string `json:"type"`
|
||||
|
||||
// The panel type
|
||||
PluginVersion string `json:"pluginVersion,omitempty"`
|
||||
|
||||
// The panel title
|
||||
Title string `json:"title,omitempty"`
|
||||
|
||||
// Library panel description
|
||||
Description string `json:"description,omitempty"`
|
||||
|
||||
// The options schema depends on the panel type
|
||||
Options common.Unstructured `json:"options"`
|
||||
|
||||
// The fieldConfig schema depends on the panel type
|
||||
FieldConfig common.Unstructured `json:"fieldConfig"`
|
||||
|
||||
// The default datasource type
|
||||
Datasource *data.DataSourceRef `json:"datasource,omitempty"`
|
||||
|
||||
// The datasource queries
|
||||
// +listType=set
|
||||
Targets []data.DataQuery `json:"targets,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
type LibraryPanelStatus struct {
|
||||
// Translation warnings (mostly things that were in SQL columns but not found in the saved body)
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
|
||||
// The properties previously stored in SQL that are not included in this model
|
||||
Missing common.Unstructured `json:"missing,omitempty"`
|
||||
}
|
||||
|
||||
// This is like the legacy DTO where access and metadata are all returned in a single call
|
||||
// +k8s:deepcopy-gen=true
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
type DashboardWithAccessInfo struct {
|
||||
Dashboard `json:",inline"`
|
||||
|
||||
Access DashboardAccess `json:"access"`
|
||||
}
|
||||
|
||||
// Information about how the requesting user can use a given dashboard
|
||||
// +k8s:deepcopy-gen=true
|
||||
type DashboardAccess struct {
|
||||
// Metadata fields
|
||||
Slug string `json:"slug,omitempty"`
|
||||
Url string `json:"url,omitempty"`
|
||||
|
||||
// The permissions part
|
||||
CanSave bool `json:"canSave"`
|
||||
CanEdit bool `json:"canEdit"`
|
||||
CanAdmin bool `json:"canAdmin"`
|
||||
CanStar bool `json:"canStar"`
|
||||
CanDelete bool `json:"canDelete"`
|
||||
AnnotationsPermissions *AnnotationPermission `json:"annotationsPermissions"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
type AnnotationPermission struct {
|
||||
Dashboard AnnotationActions `json:"dashboard"`
|
||||
Organization AnnotationActions `json:"organization"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
type AnnotationActions struct {
|
||||
CanAdd bool `json:"canAdd"`
|
||||
CanEdit bool `json:"canEdit"`
|
||||
CanDelete bool `json:"canDelete"`
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
json "encoding/json"
|
||||
fmt "fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
"cuelang.org/go/cue"
|
||||
"cuelang.org/go/cue/cuecontext"
|
||||
"cuelang.org/go/cue/errors"
|
||||
cuejson "cuelang.org/go/encoding/json"
|
||||
)
|
||||
|
||||
func ValidateDashboardSpec(obj *Dashboard) field.ErrorList {
|
||||
data, err := json.Marshal(obj.Spec)
|
||||
if err != nil {
|
||||
return field.ErrorList{
|
||||
field.Invalid(field.NewPath("spec"), field.OmitValueType{}, err.Error()),
|
||||
}
|
||||
}
|
||||
|
||||
if err := cuejson.Validate(data, getCueSchema()); err != nil {
|
||||
errs := field.ErrorList{}
|
||||
|
||||
for _, e := range errors.Errors(err) {
|
||||
if
|
||||
// We don't want to return confusing "empty disjunction" errors,
|
||||
// because the users don't necessarily understand what to do with them.
|
||||
// For empty disjunctions, CUE will also return more specific errors,
|
||||
// so we can safely ignore the generic ones.
|
||||
strings.Contains(e.Error(), "disjunction") ||
|
||||
// We don't want to return errors about unknown fields either.
|
||||
strings.Contains(e.Error(), "field not allowed") {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Contains(e.Error(), "mismatched types null and list") {
|
||||
// Go populates empty slices as nil, which the cue validator does not like
|
||||
continue
|
||||
}
|
||||
|
||||
// We want to manually format the error message,
|
||||
// because e.Error() contains the full CUE path.
|
||||
format, args := e.Msg()
|
||||
|
||||
errs = append(errs, field.Invalid(
|
||||
field.NewPath(formatErrorPath(e.Path())),
|
||||
field.OmitValueType{},
|
||||
fmt.Sprintf(format, args...),
|
||||
))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatErrorPath(path []string) string {
|
||||
return strings.Join(path, ".")
|
||||
}
|
||||
|
||||
var (
|
||||
compiledSchema cue.Value
|
||||
getSchemaOnce sync.Once
|
||||
)
|
||||
|
||||
//go:embed dashboard_spec.cue
|
||||
var schemaSource string
|
||||
|
||||
func getCueSchema() cue.Value {
|
||||
getSchemaOnce.Do(func() {
|
||||
cueCtx := cuecontext.New()
|
||||
compiledSchema = cueCtx.CompileString(schemaSource).LookupPath(
|
||||
cue.ParsePath("DashboardSpec"),
|
||||
)
|
||||
})
|
||||
|
||||
return compiledSchema
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by conversion-gen. DO NOT EDIT.
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
url "net/url"
|
||||
unsafe "unsafe"
|
||||
|
||||
dashboard "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard"
|
||||
conversion "k8s.io/apimachinery/pkg/conversion"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func init() {
|
||||
localSchemeBuilder.Register(RegisterConversions)
|
||||
}
|
||||
|
||||
// RegisterConversions adds conversion functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
if err := s.AddGeneratedConversionFunc((*AnnotationActions)(nil), (*dashboard.AnnotationActions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(a.(*AnnotationActions), b.(*dashboard.AnnotationActions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*dashboard.AnnotationActions)(nil), (*AnnotationActions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(a.(*dashboard.AnnotationActions), b.(*AnnotationActions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*AnnotationPermission)(nil), (*dashboard.AnnotationPermission)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v2alpha2_AnnotationPermission_To_dashboard_AnnotationPermission(a.(*AnnotationPermission), b.(*dashboard.AnnotationPermission), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*dashboard.AnnotationPermission)(nil), (*AnnotationPermission)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_dashboard_AnnotationPermission_To_v2alpha2_AnnotationPermission(a.(*dashboard.AnnotationPermission), b.(*AnnotationPermission), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*DashboardAccess)(nil), (*dashboard.DashboardAccess)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v2alpha2_DashboardAccess_To_dashboard_DashboardAccess(a.(*DashboardAccess), b.(*dashboard.DashboardAccess), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*dashboard.DashboardAccess)(nil), (*DashboardAccess)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_dashboard_DashboardAccess_To_v2alpha2_DashboardAccess(a.(*dashboard.DashboardAccess), b.(*DashboardAccess), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*url.Values)(nil), (*VersionsQueryOptions)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_url_Values_To_v2alpha2_VersionsQueryOptions(a.(*url.Values), b.(*VersionsQueryOptions), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func autoConvert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(in *AnnotationActions, out *dashboard.AnnotationActions, s conversion.Scope) error {
|
||||
out.CanAdd = in.CanAdd
|
||||
out.CanEdit = in.CanEdit
|
||||
out.CanDelete = in.CanDelete
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions is an autogenerated conversion function.
|
||||
func Convert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(in *AnnotationActions, out *dashboard.AnnotationActions, s conversion.Scope) error {
|
||||
return autoConvert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(in *dashboard.AnnotationActions, out *AnnotationActions, s conversion.Scope) error {
|
||||
out.CanAdd = in.CanAdd
|
||||
out.CanEdit = in.CanEdit
|
||||
out.CanDelete = in.CanDelete
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions is an autogenerated conversion function.
|
||||
func Convert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(in *dashboard.AnnotationActions, out *AnnotationActions, s conversion.Scope) error {
|
||||
return autoConvert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v2alpha2_AnnotationPermission_To_dashboard_AnnotationPermission(in *AnnotationPermission, out *dashboard.AnnotationPermission, s conversion.Scope) error {
|
||||
if err := Convert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(&in.Dashboard, &out.Dashboard, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_v2alpha2_AnnotationActions_To_dashboard_AnnotationActions(&in.Organization, &out.Organization, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v2alpha2_AnnotationPermission_To_dashboard_AnnotationPermission is an autogenerated conversion function.
|
||||
func Convert_v2alpha2_AnnotationPermission_To_dashboard_AnnotationPermission(in *AnnotationPermission, out *dashboard.AnnotationPermission, s conversion.Scope) error {
|
||||
return autoConvert_v2alpha2_AnnotationPermission_To_dashboard_AnnotationPermission(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_dashboard_AnnotationPermission_To_v2alpha2_AnnotationPermission(in *dashboard.AnnotationPermission, out *AnnotationPermission, s conversion.Scope) error {
|
||||
if err := Convert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(&in.Dashboard, &out.Dashboard, s); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := Convert_dashboard_AnnotationActions_To_v2alpha2_AnnotationActions(&in.Organization, &out.Organization, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_dashboard_AnnotationPermission_To_v2alpha2_AnnotationPermission is an autogenerated conversion function.
|
||||
func Convert_dashboard_AnnotationPermission_To_v2alpha2_AnnotationPermission(in *dashboard.AnnotationPermission, out *AnnotationPermission, s conversion.Scope) error {
|
||||
return autoConvert_dashboard_AnnotationPermission_To_v2alpha2_AnnotationPermission(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v2alpha2_DashboardAccess_To_dashboard_DashboardAccess(in *DashboardAccess, out *dashboard.DashboardAccess, s conversion.Scope) error {
|
||||
out.Slug = in.Slug
|
||||
out.Url = in.Url
|
||||
out.CanSave = in.CanSave
|
||||
out.CanEdit = in.CanEdit
|
||||
out.CanAdmin = in.CanAdmin
|
||||
out.CanStar = in.CanStar
|
||||
out.CanDelete = in.CanDelete
|
||||
out.AnnotationsPermissions = (*dashboard.AnnotationPermission)(unsafe.Pointer(in.AnnotationsPermissions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v2alpha2_DashboardAccess_To_dashboard_DashboardAccess is an autogenerated conversion function.
|
||||
func Convert_v2alpha2_DashboardAccess_To_dashboard_DashboardAccess(in *DashboardAccess, out *dashboard.DashboardAccess, s conversion.Scope) error {
|
||||
return autoConvert_v2alpha2_DashboardAccess_To_dashboard_DashboardAccess(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_dashboard_DashboardAccess_To_v2alpha2_DashboardAccess(in *dashboard.DashboardAccess, out *DashboardAccess, s conversion.Scope) error {
|
||||
out.Slug = in.Slug
|
||||
out.Url = in.Url
|
||||
out.CanSave = in.CanSave
|
||||
out.CanEdit = in.CanEdit
|
||||
out.CanAdmin = in.CanAdmin
|
||||
out.CanStar = in.CanStar
|
||||
out.CanDelete = in.CanDelete
|
||||
out.AnnotationsPermissions = (*AnnotationPermission)(unsafe.Pointer(in.AnnotationsPermissions))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_dashboard_DashboardAccess_To_v2alpha2_DashboardAccess is an autogenerated conversion function.
|
||||
func Convert_dashboard_DashboardAccess_To_v2alpha2_DashboardAccess(in *dashboard.DashboardAccess, out *DashboardAccess, s conversion.Scope) error {
|
||||
return autoConvert_dashboard_DashboardAccess_To_v2alpha2_DashboardAccess(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_url_Values_To_v2alpha2_VersionsQueryOptions(in *url.Values, out *VersionsQueryOptions, s conversion.Scope) error {
|
||||
// WARNING: Field TypeMeta does not have json tag, skipping.
|
||||
|
||||
if values, ok := map[string][]string(*in)["path"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_string(&values, &out.Path, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Path = ""
|
||||
}
|
||||
if values, ok := map[string][]string(*in)["version"]; ok && len(values) > 0 {
|
||||
if err := runtime.Convert_Slice_string_To_int64(&values, &out.Version, s); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
out.Version = 0
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_url_Values_To_v2alpha2_VersionsQueryOptions is an autogenerated conversion function.
|
||||
func Convert_url_Values_To_v2alpha2_VersionsQueryOptions(in *url.Values, out *VersionsQueryOptions, s conversion.Scope) error {
|
||||
return autoConvert_url_Values_To_v2alpha2_VersionsQueryOptions(in, out, s)
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v2alpha2
|
||||
|
||||
import (
|
||||
v0alpha1 "github.com/grafana/grafana-plugin-sdk-go/experimental/apis/data/v0alpha1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnnotationActions) DeepCopyInto(out *AnnotationActions) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnnotationActions.
|
||||
func (in *AnnotationActions) DeepCopy() *AnnotationActions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AnnotationActions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AnnotationPermission) DeepCopyInto(out *AnnotationPermission) {
|
||||
*out = *in
|
||||
out.Dashboard = in.Dashboard
|
||||
out.Organization = in.Organization
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AnnotationPermission.
|
||||
func (in *AnnotationPermission) DeepCopy() *AnnotationPermission {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AnnotationPermission)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardAccess) DeepCopyInto(out *DashboardAccess) {
|
||||
*out = *in
|
||||
if in.AnnotationsPermissions != nil {
|
||||
in, out := &in.AnnotationsPermissions, &out.AnnotationsPermissions
|
||||
*out = new(AnnotationPermission)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardAccess.
|
||||
func (in *DashboardAccess) DeepCopy() *DashboardAccess {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardAccess)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardVersionInfo) DeepCopyInto(out *DashboardVersionInfo) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardVersionInfo.
|
||||
func (in *DashboardVersionInfo) DeepCopy() *DashboardVersionInfo {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardVersionInfo)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardVersionList) DeepCopyInto(out *DashboardVersionList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]DashboardVersionInfo, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardVersionList.
|
||||
func (in *DashboardVersionList) DeepCopy() *DashboardVersionList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardVersionList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardVersionList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardWithAccessInfo) DeepCopyInto(out *DashboardWithAccessInfo) {
|
||||
*out = *in
|
||||
in.Dashboard.DeepCopyInto(&out.Dashboard)
|
||||
in.Access.DeepCopyInto(&out.Access)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardWithAccessInfo.
|
||||
func (in *DashboardWithAccessInfo) DeepCopy() *DashboardWithAccessInfo {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardWithAccessInfo)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *DashboardWithAccessInfo) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LibraryPanel) DeepCopyInto(out *LibraryPanel) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
if in.Status != nil {
|
||||
in, out := &in.Status, &out.Status
|
||||
*out = new(LibraryPanelStatus)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LibraryPanel.
|
||||
func (in *LibraryPanel) DeepCopy() *LibraryPanel {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LibraryPanel)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *LibraryPanel) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LibraryPanelList) DeepCopyInto(out *LibraryPanelList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]LibraryPanel, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LibraryPanelList.
|
||||
func (in *LibraryPanelList) DeepCopy() *LibraryPanelList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LibraryPanelList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *LibraryPanelList) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LibraryPanelSpec) DeepCopyInto(out *LibraryPanelSpec) {
|
||||
*out = *in
|
||||
in.Options.DeepCopyInto(&out.Options)
|
||||
in.FieldConfig.DeepCopyInto(&out.FieldConfig)
|
||||
if in.Datasource != nil {
|
||||
in, out := &in.Datasource, &out.Datasource
|
||||
*out = new(v0alpha1.DataSourceRef)
|
||||
**out = **in
|
||||
}
|
||||
if in.Targets != nil {
|
||||
in, out := &in.Targets, &out.Targets
|
||||
*out = make([]v0alpha1.DataQuery, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LibraryPanelSpec.
|
||||
func (in *LibraryPanelSpec) DeepCopy() *LibraryPanelSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LibraryPanelSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LibraryPanelStatus) DeepCopyInto(out *LibraryPanelStatus) {
|
||||
*out = *in
|
||||
if in.Warnings != nil {
|
||||
in, out := &in.Warnings, &out.Warnings
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
in.Missing.DeepCopyInto(&out.Missing)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LibraryPanelStatus.
|
||||
func (in *LibraryPanelStatus) DeepCopy() *LibraryPanelStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(LibraryPanelStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *VersionsQueryOptions) DeepCopyInto(out *VersionsQueryOptions) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VersionsQueryOptions.
|
||||
func (in *VersionsQueryOptions) DeepCopy() *VersionsQueryOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(VersionsQueryOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *VersionsQueryOptions) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,67 +0,0 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAdHocFilterWithLabels,ValueLabels
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAdHocFilterWithLabels,Values
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAdhocVariableSpec,BaseFilters
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAdhocVariableSpec,DefaultKeys
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAdhocVariableSpec,Filters
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAnnotationPanelFilter,Ids
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardAutoGridLayoutSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardConditionalRenderingGroupSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardCustomVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardDashboardLink,Tags
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardDatasourceVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardFieldConfig,Links
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardFieldConfig,Mappings
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardFieldConfigSource,Overrides
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutSpec,Items
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGroupByVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardIntervalVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardMetadata,Finalizers
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardPanelSpec,Links
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryGroupSpec,Queries
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryGroupSpec,Transformations
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableSpec,Options
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableSpec,StaticOptions
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardRowsLayoutSpec,Rows
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardSpec,Annotations
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardSpec,Links
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardSpec,Tags
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardSpec,Variables
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardStringOrArrayOfString,ArrayOfString
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardTabsLayoutSpec,Tabs
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardThresholdsConfig,Steps
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardTimeSettingsSpec,AutoRefreshIntervals
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardTimeSettingsSpec,QuickRanges
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardV2alpha2FieldConfigSourceOverrides,Properties
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,LibraryPanelStatus,Warnings
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingDataKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingTimeRangeSizeKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardConditionalRenderingVariableKindOrConditionalRenderingDataKindOrConditionalRenderingTimeRangeSizeKind,ConditionalRenderingVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,AutoGridLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,GridLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,RowsLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrAutoGridLayoutKindOrTabsLayoutKindOrRowsLayoutKind,TabsLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind,AutoGridLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind,GridLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind,RowsLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind,TabsLayoutKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardIntervalVariableSpec,AutoCount
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardIntervalVariableSpec,AutoMin
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardPanelKindOrLibraryPanelKind,LibraryPanelKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardPanelKindOrLibraryPanelKind,PanelKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,AdhocVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,ConstantVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,CustomVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,DatasourceVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,GroupByVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,IntervalVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,QueryVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardQueryVariableKindOrTextVariableKindOrConstantVariableKindOrDatasourceVariableKindOrIntervalVariableKindOrCustomVariableKindOrGroupByVariableKindOrAdhocVariableKind,TextVariableKind
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardStringOrArrayOfString,ArrayOfString
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardStringOrArrayOfString,String
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardStringOrFloat64,Float64
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardStringOrFloat64,String
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,RangeMap
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,RegexMap
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,SpecialValueMap
|
||||
API rule violation: names_match,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardValueMapOrRangeMapOrRegexMapOrSpecialValueMap,ValueMap
|
||||
API rule violation: streaming_list_type_json_tags,github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2,DashboardList,ListMeta
|
||||
@@ -14,7 +14,6 @@ import (
|
||||
v0alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
v1beta1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
v2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
v2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
)
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
@@ -37,10 +36,6 @@ var appManifestData = app.ManifestData{
|
||||
{
|
||||
Name: "v2alpha1",
|
||||
},
|
||||
|
||||
{
|
||||
Name: "v2alpha2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -58,7 +53,6 @@ var kindVersionToGoType = map[string]resource.Kind{
|
||||
"Dashboard/v0alpha1": v0alpha1.DashboardKind(),
|
||||
"Dashboard/v1beta1": v1beta1.DashboardKind(),
|
||||
"Dashboard/v2alpha1": v2alpha1.DashboardKind(),
|
||||
"Dashboard/v2alpha2": v2alpha2.DashboardKind(),
|
||||
}
|
||||
|
||||
// ManifestGoTypeAssociator returns the associated resource.Kind instance for a given Kind and Version, if one exists.
|
||||
|
||||
@@ -6,77 +6,167 @@ import (
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func RegisterConversions(s *runtime.Scheme) error {
|
||||
// v0 conversions
|
||||
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V0_to_V1(a.(*dashv0.Dashboard), b.(*dashv1.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv2alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V0_to_V2alpha1(a.(*dashv0.Dashboard), b.(*dashv2alpha1.Dashboard), scope)
|
||||
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V0_to_V2(a.(*dashv0.Dashboard), b.(*dashv2.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv0.Dashboard)(nil), (*dashv2alpha2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V0_to_V2alpha2(a.(*dashv0.Dashboard), b.(*dashv2alpha2.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// v1 conversions
|
||||
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V1_to_V0(a.(*dashv1.Dashboard), b.(*dashv0.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv2alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V1_to_V2alpha1(a.(*dashv1.Dashboard), b.(*dashv2alpha1.Dashboard), scope)
|
||||
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V1_to_V2(a.(*dashv1.Dashboard), b.(*dashv2.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv1.Dashboard)(nil), (*dashv2alpha2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V1_to_V2alpha2(a.(*dashv1.Dashboard), b.(*dashv2alpha2.Dashboard), scope)
|
||||
if err := s.AddConversionFunc((*dashv2.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2_to_V0(a.(*dashv2.Dashboard), b.(*dashv0.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2_to_V1(a.(*dashv2.Dashboard), b.(*dashv1.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V0_to_V1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
out.Spec.Object = in.Spec.Object
|
||||
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: dashv0.VERSION,
|
||||
},
|
||||
}
|
||||
|
||||
// v2alpha1 conversions
|
||||
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha1_to_V0(a.(*dashv2alpha1.Dashboard), b.(*dashv0.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha1_to_V1(a.(*dashv2alpha1.Dashboard), b.(*dashv1.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2alpha1.Dashboard)(nil), (*dashv2alpha2.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha1_to_V2alpha2(a.(*dashv2alpha1.Dashboard), b.(*dashv2alpha2.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// v2alpha2 conversions
|
||||
if err := s.AddConversionFunc((*dashv2alpha2.Dashboard)(nil), (*dashv0.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha2_to_V0(a.(*dashv2alpha2.Dashboard), b.(*dashv0.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2alpha2.Dashboard)(nil), (*dashv1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha2_to_V1(a.(*dashv2alpha2.Dashboard), b.(*dashv1.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddConversionFunc((*dashv2alpha2.Dashboard)(nil), (*dashv2alpha1.Dashboard)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_V2alpha2_to_V2alpha1(a.(*dashv2alpha2.Dashboard), b.(*dashv2alpha1.Dashboard), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
if err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION); err != nil {
|
||||
out.Status.Conversion.Failed = true
|
||||
out.Status.Conversion.Error = err.Error()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V0_to_V2(in *dashv0.Dashboard, out *dashv2.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO (@radiohead): implement V0 to V2 conversion
|
||||
// This is the bare minimum conversion that is needed to make the dashboard servable.
|
||||
|
||||
if v, ok := in.Spec.Object["title"]; ok {
|
||||
if title, ok := v.(string); ok {
|
||||
out.Spec.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
|
||||
out.Spec.Layout = dashv2.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
|
||||
GridLayoutKind: &dashv2.DashboardGridLayoutKind{
|
||||
Kind: "GridLayout",
|
||||
Spec: dashv2.DashboardGridLayoutSpec{},
|
||||
},
|
||||
}
|
||||
|
||||
out.Status = dashv2.DashboardStatus{
|
||||
Conversion: &dashv2.DashboardConversionStatus{
|
||||
StoredVersion: dashv0.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V1_to_V0(in *dashv1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
out.Spec.Object = in.Spec.Object
|
||||
|
||||
out.Status = dashv0.DashboardStatus{
|
||||
Conversion: &dashv0.DashboardConversionStatus{
|
||||
StoredVersion: dashv1.VERSION,
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V1_to_V2(in *dashv1.Dashboard, out *dashv2.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO (@radiohead): implement V1 to V2 conversion
|
||||
// This is the bare minimum conversion that is needed to make the dashboard servable.
|
||||
|
||||
if v, ok := in.Spec.Object["title"]; ok {
|
||||
if title, ok := v.(string); ok {
|
||||
out.Spec.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
|
||||
out.Spec.Layout = dashv2.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
|
||||
GridLayoutKind: &dashv2.DashboardGridLayoutKind{
|
||||
Kind: "GridLayout",
|
||||
Spec: dashv2.DashboardGridLayoutSpec{},
|
||||
},
|
||||
}
|
||||
|
||||
out.Status = dashv2.DashboardStatus{
|
||||
Conversion: &dashv2.DashboardConversionStatus{
|
||||
StoredVersion: dashv1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2_to_V0(in *dashv2.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2 to V0 conversion
|
||||
|
||||
out.Status = dashv0.DashboardStatus{
|
||||
Conversion: &dashv0.DashboardConversionStatus{
|
||||
StoredVersion: dashv2.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2_to_V1(in *dashv2.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2 to V1 conversion
|
||||
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: dashv2.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -11,8 +11,7 @@ import (
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
dashv2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/testutil"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
@@ -26,8 +25,7 @@ func TestConversionMatrixExist(t *testing.T) {
|
||||
versions := []v1.Object{
|
||||
&dashv0.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV0"}}},
|
||||
&dashv1.Dashboard{Spec: common.Unstructured{Object: map[string]any{"title": "dashboardV1"}}},
|
||||
&dashv2alpha1.Dashboard{Spec: dashv2alpha1.DashboardSpec{Title: "dashboardV2alpha1"}},
|
||||
&dashv2alpha2.Dashboard{Spec: dashv2alpha2.DashboardSpec{Title: "dashboardV2alpha2"}},
|
||||
&dashv2.Dashboard{Spec: dashv2.DashboardSpec{Title: "dashboardV2"}},
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration"
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func Convert_V0_to_V1(in *dashv0.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
out.Spec.Object = in.Spec.Object
|
||||
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: dashv0.VERSION,
|
||||
},
|
||||
}
|
||||
|
||||
if err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION); err != nil {
|
||||
out.Status.Conversion.Failed = true
|
||||
out.Status.Conversion.Error = err.Error()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V0_to_V2alpha1(in *dashv0.Dashboard, out *dashv2alpha1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO (@radiohead): implement V0 to V2 conversion
|
||||
// This is the bare minimum conversion that is needed to make the dashboard servable.
|
||||
|
||||
if v, ok := in.Spec.Object["title"]; ok {
|
||||
if title, ok := v.(string); ok {
|
||||
out.Spec.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
|
||||
out.Spec.Layout = dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
|
||||
GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{
|
||||
Kind: "GridLayout",
|
||||
Spec: dashv2alpha1.DashboardGridLayoutSpec{},
|
||||
},
|
||||
}
|
||||
|
||||
out.Status = dashv2alpha1.DashboardStatus{
|
||||
Conversion: &dashv2alpha1.DashboardConversionStatus{
|
||||
StoredVersion: dashv0.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V0_to_V2alpha2(in *dashv0.Dashboard, out *dashv2alpha2.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V0 to V2alpha2 conversion
|
||||
|
||||
out.Status = dashv2alpha2.DashboardStatus{
|
||||
Conversion: &dashv2alpha2.DashboardConversionStatus{
|
||||
StoredVersion: dashv0.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
)
|
||||
|
||||
func Convert_V1_to_V0(in *dashv1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
out.Spec.Object = in.Spec.Object
|
||||
|
||||
out.Status = dashv0.DashboardStatus{
|
||||
Conversion: &dashv0.DashboardConversionStatus{
|
||||
StoredVersion: dashv1.VERSION,
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V1_to_V2alpha1(in *dashv1.Dashboard, out *dashv2alpha1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO (@radiohead): implement V1 to V2 conversion
|
||||
// This is the bare minimum conversion that is needed to make the dashboard servable.
|
||||
|
||||
if v, ok := in.Spec.Object["title"]; ok {
|
||||
if title, ok := v.(string); ok {
|
||||
out.Spec.Title = title
|
||||
}
|
||||
}
|
||||
|
||||
// We need to make sure the layout is set to some value, otherwise the JSON marshaling will fail.
|
||||
out.Spec.Layout = dashv2alpha1.DashboardGridLayoutKindOrRowsLayoutKindOrAutoGridLayoutKindOrTabsLayoutKind{
|
||||
GridLayoutKind: &dashv2alpha1.DashboardGridLayoutKind{
|
||||
Kind: "GridLayout",
|
||||
Spec: dashv2alpha1.DashboardGridLayoutSpec{},
|
||||
},
|
||||
}
|
||||
|
||||
out.Status = dashv2alpha1.DashboardStatus{
|
||||
Conversion: &dashv2alpha1.DashboardConversionStatus{
|
||||
StoredVersion: dashv1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V1_to_V2alpha2(in *dashv1.Dashboard, out *dashv2alpha2.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V1 to V2alpha2 conversion
|
||||
|
||||
out.Status = dashv2alpha2.DashboardStatus{
|
||||
Conversion: &dashv2alpha2.DashboardConversionStatus{
|
||||
StoredVersion: dashv1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package conversion
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/conversion"
|
||||
|
||||
dashv0 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v0alpha1"
|
||||
dashv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
|
||||
dashv2alpha1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha1"
|
||||
dashv2alpha2 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v2alpha2"
|
||||
)
|
||||
|
||||
func Convert_V2alpha1_to_V0(in *dashv2alpha1.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2 to V0 conversion
|
||||
|
||||
out.Status = dashv0.DashboardStatus{
|
||||
Conversion: &dashv0.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2alpha1_to_V1(in *dashv2alpha1.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2 to V1 conversion
|
||||
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2alpha1_to_V2alpha2(in *dashv2alpha1.Dashboard, out *dashv2alpha2.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2alpha1 to V2alpha2 conversion
|
||||
|
||||
out.Status = dashv2alpha2.DashboardStatus{
|
||||
Conversion: &dashv2alpha2.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha1.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func Convert_V2alpha2_to_V0(in *dashv2alpha2.Dashboard, out *dashv0.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2alpha2 to V0 conversion
|
||||
|
||||
out.Status = dashv0.DashboardStatus{
|
||||
Conversion: &dashv0.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha2.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2alpha2_to_V1(in *dashv2alpha2.Dashboard, out *dashv1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2alpha2 to V1 conversion
|
||||
|
||||
out.Status = dashv1.DashboardStatus{
|
||||
Conversion: &dashv1.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha2.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Convert_V2alpha2_to_V2alpha1(in *dashv2alpha2.Dashboard, out *dashv2alpha1.Dashboard, scope conversion.Scope) error {
|
||||
out.ObjectMeta = in.ObjectMeta
|
||||
|
||||
// TODO: implement V2alpha2 to V2alpha1 conversion
|
||||
|
||||
out.Status = dashv2alpha1.DashboardStatus{
|
||||
Conversion: &dashv2alpha1.DashboardConversionStatus{
|
||||
StoredVersion: dashv2alpha2.VERSION,
|
||||
Failed: true,
|
||||
Error: "backend conversion not yet implemented",
|
||||
},
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
MIN_VERSION = 28
|
||||
MIN_VERSION = 32
|
||||
LATEST_VERSION = 41
|
||||
)
|
||||
|
||||
@@ -26,10 +26,6 @@ type DataSourceInfoProvider interface {
|
||||
|
||||
func GetMigrations(dsInfoProvider DataSourceInfoProvider) map[int]SchemaVersionMigrationFunc {
|
||||
return map[int]SchemaVersionMigrationFunc{
|
||||
29: V29,
|
||||
30: V30,
|
||||
31: V31,
|
||||
32: V32,
|
||||
33: V33(dsInfoProvider),
|
||||
34: V34,
|
||||
35: V35,
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
package schemaversion
|
||||
|
||||
// V29 migrates query variables to ensure their refresh property is set to 1 (on dashboard load)
|
||||
// if it is not 1 or 2, and clears their options array if present.
|
||||
//
|
||||
// Example before migration:
|
||||
//
|
||||
// "templating": {
|
||||
// "list": [
|
||||
// { "type": "query", "refresh": 0, "options": [{ "text": "A", "value": "A" }] },
|
||||
// { "type": "query", "refresh": 2, "options": [{ "text": "B", "value": "B" }] },
|
||||
// { "type": "query", "options": [{ "text": "C", "value": "C" }] }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// Example after migration:
|
||||
//
|
||||
// "templating": {
|
||||
// "list": [
|
||||
// { "type": "query", "refresh": 1, "options": [] },
|
||||
// { "type": "query", "refresh": 2, "options": [] },
|
||||
// { "type": "query", "refresh": 1, "options": [] }
|
||||
// ]
|
||||
// }
|
||||
func V29(dashboard map[string]interface{}) error {
|
||||
dashboard["schemaVersion"] = 29
|
||||
|
||||
templating, ok := dashboard["templating"].(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
list, ok := templating["list"].([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for _, v := range list {
|
||||
variable, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if variable["type"] != "query" {
|
||||
continue
|
||||
}
|
||||
// Set refresh to 1 if not 1 or 2
|
||||
refresh, hasRefresh := variable["refresh"]
|
||||
refreshInt := 0
|
||||
if r, ok := refresh.(int); ok {
|
||||
refreshInt = r
|
||||
}
|
||||
if !hasRefresh || (refreshInt != 1 && refreshInt != 2) {
|
||||
variable["refresh"] = 1
|
||||
}
|
||||
// Clear options if present
|
||||
if _, hasOptions := variable["options"]; hasOptions {
|
||||
variable["options"] = []interface{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
package schemaversion_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func TestV29(t *testing.T) {
|
||||
tests := []migrationTestCase{
|
||||
{
|
||||
name: "query variables get migrated with refresh and options",
|
||||
input: map[string]interface{}{
|
||||
"title": "V29 Query Variables Migration Test Dashboard",
|
||||
"schemaVersion": 28,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "query", "name": "never_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 0},
|
||||
map[string]interface{}{"type": "query", "name": "never_refresh_without_options", "options": []interface{}{}, "refresh": 0},
|
||||
map[string]interface{}{"type": "query", "name": "dashboard_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "dashboard_refresh_without_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "timerange_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "timerange_refresh_without_options", "options": []interface{}{}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "no_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}},
|
||||
map[string]interface{}{"type": "query", "name": "no_refresh_without_options", "options": []interface{}{}},
|
||||
map[string]interface{}{"type": "query", "name": "unknown_refresh_with_options", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}, "refresh": 2001},
|
||||
map[string]interface{}{"type": "query", "name": "unknown_refresh_without_options", "options": []interface{}{}, "refresh": 2001},
|
||||
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
|
||||
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
|
||||
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
|
||||
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V29 Query Variables Migration Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "query", "name": "never_refresh_with_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "never_refresh_without_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "dashboard_refresh_with_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "dashboard_refresh_without_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "timerange_refresh_with_options", "options": []interface{}{}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "timerange_refresh_without_options", "options": []interface{}{}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "no_refresh_with_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "no_refresh_without_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "unknown_refresh_with_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "unknown_refresh_without_options", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
|
||||
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
|
||||
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
|
||||
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-query variables remain unchanged",
|
||||
input: map[string]interface{}{
|
||||
"title": "V29 Non-Query Variables Test Dashboard",
|
||||
"schemaVersion": 28,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
|
||||
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
|
||||
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
|
||||
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V29 Non-Query Variables Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "custom", "name": "custom", "options": []interface{}{map[string]interface{}{"text": "custom", "value": "custom"}}},
|
||||
map[string]interface{}{"type": "textbox", "name": "textbox", "options": []interface{}{map[string]interface{}{"text": "Hello", "value": "World"}}},
|
||||
map[string]interface{}{"type": "datasource", "name": "datasource", "options": []interface{}{map[string]interface{}{"text": "ds", "value": "ds"}}},
|
||||
map[string]interface{}{"type": "interval", "name": "interval", "options": []interface{}{map[string]interface{}{"text": "1m", "value": "1m"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all query variables should have options removed",
|
||||
input: map[string]interface{}{
|
||||
"title": "V29 Query Variables Options Removal Test Dashboard",
|
||||
"schemaVersion": 28,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "query", "name": "query1", "options": []interface{}{map[string]interface{}{"text": "A", "value": "A"}}},
|
||||
map[string]interface{}{"type": "query", "name": "query2", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query3", "options": []interface{}{map[string]interface{}{"text": "B", "value": "B"}, map[string]interface{}{"text": "C", "value": "C"}}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "query4", "options": []interface{}{map[string]interface{}{"text": "D", "value": "D"}}, "refresh": 0},
|
||||
map[string]interface{}{"type": "query", "name": "query5", "options": []interface{}{map[string]interface{}{"text": "E", "value": "E"}}, "refresh": 2001},
|
||||
map[string]interface{}{"type": "query", "name": "query6", "options": []interface{}{}},
|
||||
map[string]interface{}{"type": "query", "name": "query7", "options": []interface{}{map[string]interface{}{"text": "F", "value": "F"}}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query8", "options": []interface{}{map[string]interface{}{"text": "G", "value": "G"}}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "query9", "options": []interface{}{map[string]interface{}{"text": "H", "value": "H"}}},
|
||||
map[string]interface{}{"type": "query", "name": "query10", "options": []interface{}{map[string]interface{}{"text": "I", "value": "I"}}, "refresh": 999},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V29 Query Variables Options Removal Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"templating": map[string]interface{}{
|
||||
"list": []interface{}{
|
||||
map[string]interface{}{"type": "query", "name": "query1", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query2", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query3", "options": []interface{}{}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "query4", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query5", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query6", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query7", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query8", "options": []interface{}{}, "refresh": 2},
|
||||
map[string]interface{}{"type": "query", "name": "query9", "options": []interface{}{}, "refresh": 1},
|
||||
map[string]interface{}{"type": "query", "name": "query10", "options": []interface{}{}, "refresh": 1},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runMigrationTests(t, tests, schemaversion.V29)
|
||||
}
|
||||
@@ -1,393 +0,0 @@
|
||||
package schemaversion
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// V30 upgrades value mappings and migrates tooltip options for panels.
|
||||
//
|
||||
// This migration addresses two key improvements to panel configurations:
|
||||
// 1. Value mappings upgrade: Converts legacy value mapping format to the new structured format
|
||||
// 2. Tooltip options migration: Renames tooltipOptions to tooltip in specific panel types: timeseries, xychart, xychart2
|
||||
//
|
||||
// The migration works by:
|
||||
// 1. Iterating through all panels in the dashboard, including nested panels in collapsed rows
|
||||
// 2. For each panel, upgrading value mappings in fieldConfig.defaults and fieldConfig.overrides
|
||||
// 3. Migrating tooltip options for timeseries, xychart, and xychart2 panels
|
||||
// 4. Preserving all other panel configurations and options
|
||||
//
|
||||
// Value Mappings Migration:
|
||||
// - Converts legacy mapping types (1 = ValueToText, 2 = RangeToText) to new format
|
||||
// - Handles special "null" values by converting them to SpecialValue mappings
|
||||
// - Preserves threshold colors when available
|
||||
// - Consolidates multiple value mappings into a single ValueToText mapping
|
||||
// - Maintains range mappings as separate RangeToText mappings
|
||||
//
|
||||
// Tooltip Options Migration:
|
||||
// - Renames panel.options.tooltipOptions to panel.options.tooltip
|
||||
// - Only applies to timeseries, xychart, and xychart2 panel types
|
||||
// - Preserves all tooltip configuration options
|
||||
//
|
||||
// Example value mappings transformation:
|
||||
//
|
||||
// Before migration:
|
||||
//
|
||||
// panel: {
|
||||
// "fieldConfig": {
|
||||
// "defaults": {
|
||||
// "mappings": [
|
||||
// { "id": 0, "text": "Up", "type": 1, "value": "1" },
|
||||
// { "id": 1, "text": "Down", "type": 1, "value": "0" },
|
||||
// { "from": "10", "to": "20", "text": "Medium", "type": 2 }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// After migration:
|
||||
//
|
||||
// panel: {
|
||||
// "fieldConfig": {
|
||||
// "defaults": {
|
||||
// "mappings": [
|
||||
// {
|
||||
// "type": "value",
|
||||
// "options": {
|
||||
// "1": { "text": "Up" },
|
||||
// "0": { "text": "Down" }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// "type": "range",
|
||||
// "options": {
|
||||
// "from": 10,
|
||||
// "to": 20,
|
||||
// "result": { "text": "Medium" }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Example tooltip options transformation:
|
||||
//
|
||||
// Before migration:
|
||||
//
|
||||
// panel: {
|
||||
// "type": "timeseries",
|
||||
// "options": {
|
||||
// "tooltipOptions": { "mode": "multi" }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// After migration:
|
||||
//
|
||||
// panel: {
|
||||
// "type": "timeseries",
|
||||
// "options": {
|
||||
// "tooltip": { "mode": "multi" }
|
||||
// }
|
||||
// }
|
||||
func V30(dashboard map[string]interface{}) error {
|
||||
dashboard["schemaVersion"] = 30
|
||||
|
||||
panels, ok := dashboard["panels"].([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, panel := range panels {
|
||||
panelMap, ok := panel.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
upgradeValueMappingsForPanel(panelMap)
|
||||
migrateTooltipOptions(panelMap)
|
||||
|
||||
// Handle nested panels in collapsed rows
|
||||
nestedPanels, hasNested := panelMap["panels"].([]interface{})
|
||||
if !hasNested {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, nestedPanel := range nestedPanels {
|
||||
nestedPanelMap, ok := nestedPanel.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
upgradeValueMappingsForPanel(nestedPanelMap)
|
||||
migrateTooltipOptions(nestedPanelMap)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// upgradeValueMappingsForPanel migrates value mappings from old format to new format
|
||||
func upgradeValueMappingsForPanel(panel map[string]interface{}) {
|
||||
fieldConfig, ok := panel["fieldConfig"].(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Upgrade defaults mappings
|
||||
if defaults, ok := fieldConfig["defaults"].(map[string]interface{}); ok {
|
||||
if mappings, ok := defaults["mappings"].([]interface{}); ok {
|
||||
var thresholds map[string]interface{}
|
||||
if t, ok := defaults["thresholds"].(map[string]interface{}); ok {
|
||||
thresholds = t
|
||||
}
|
||||
defaults["mappings"] = upgradeValueMappings(mappings, thresholds)
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade overrides mappings
|
||||
overrides, hasOverrides := fieldConfig["overrides"].([]interface{})
|
||||
if !hasOverrides {
|
||||
return
|
||||
}
|
||||
|
||||
for _, override := range overrides {
|
||||
overrideMap, ok := override.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
properties, hasProperties := overrideMap["properties"].([]interface{})
|
||||
if !hasProperties {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, property := range properties {
|
||||
propertyMap, ok := property.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if propertyMap["id"] != "mappings" {
|
||||
continue
|
||||
}
|
||||
|
||||
mappings, ok := propertyMap["value"].([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
propertyMap["value"] = upgradeValueMappings(mappings, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// upgradeValueMappings converts legacy value mappings to new format
|
||||
func upgradeValueMappings(oldMappings []interface{}, thresholds map[string]interface{}) []interface{} {
|
||||
if len(oldMappings) == 0 {
|
||||
return oldMappings
|
||||
}
|
||||
|
||||
valueMaps := map[string]interface{}{
|
||||
"type": "value",
|
||||
"options": map[string]interface{}{},
|
||||
}
|
||||
|
||||
var newMappings []interface{}
|
||||
hasValueMappings := false
|
||||
|
||||
for _, mapping := range oldMappings {
|
||||
if mappingMap, ok := mapping.(map[string]interface{}); ok {
|
||||
// Check if this is already the new format
|
||||
if mappingType, ok := mappingMap["type"].(string); ok && mappingType != "" {
|
||||
if mappingType == "value" {
|
||||
// Consolidate existing value mappings
|
||||
if options, ok := mappingMap["options"].(map[string]interface{}); ok {
|
||||
valueMapsOptions := valueMaps["options"].(map[string]interface{})
|
||||
for k, v := range options {
|
||||
valueMapsOptions[k] = v
|
||||
}
|
||||
hasValueMappings = true
|
||||
}
|
||||
} else {
|
||||
newMappings = append(newMappings, mappingMap)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle legacy format
|
||||
var color interface{}
|
||||
if thresholds != nil {
|
||||
// Try to get color from threshold based on the mapping value
|
||||
if value, ok := mappingMap["value"]; ok {
|
||||
if valueStr, ok := value.(string); ok {
|
||||
if numeric, err := strconv.ParseFloat(valueStr, 64); err == nil {
|
||||
color = getActiveThresholdColor(numeric, thresholds)
|
||||
}
|
||||
}
|
||||
}
|
||||
// For range mappings, use the 'from' value to determine color
|
||||
if fromVal, ok := mappingMap["from"]; ok {
|
||||
if fromStr, ok := fromVal.(string); ok {
|
||||
if numeric, err := strconv.ParseFloat(fromStr, 64); err == nil {
|
||||
color = getActiveThresholdColor(numeric, thresholds)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert legacy type numbers to new format
|
||||
if mappingType, ok := mappingMap["type"].(float64); ok {
|
||||
switch int(mappingType) {
|
||||
case 1: // ValueToText
|
||||
if value, ok := mappingMap["value"]; ok {
|
||||
if valueStr, ok := value.(string); ok && valueStr == "null" {
|
||||
// Handle null values as special value mapping
|
||||
// For null values, use the base threshold color (lowest step)
|
||||
if thresholds != nil {
|
||||
color = getBaseThresholdColor(thresholds)
|
||||
}
|
||||
newMappings = append(newMappings, map[string]interface{}{
|
||||
"type": "special",
|
||||
"options": map[string]interface{}{
|
||||
"match": "null",
|
||||
"result": map[string]interface{}{
|
||||
"text": mappingMap["text"],
|
||||
"color": color,
|
||||
},
|
||||
},
|
||||
})
|
||||
} else {
|
||||
// Regular value mapping
|
||||
valueMapsOptions := valueMaps["options"].(map[string]interface{})
|
||||
result := map[string]interface{}{
|
||||
"text": mappingMap["text"],
|
||||
}
|
||||
if color != nil {
|
||||
result["color"] = color
|
||||
}
|
||||
valueMapsOptions[stringifyValue(value)] = result
|
||||
hasValueMappings = true
|
||||
}
|
||||
}
|
||||
case 2: // RangeToText
|
||||
result := map[string]interface{}{
|
||||
"text": mappingMap["text"],
|
||||
}
|
||||
if color != nil {
|
||||
result["color"] = color
|
||||
}
|
||||
|
||||
from, _ := strconv.ParseFloat(stringifyValue(mappingMap["from"]), 64)
|
||||
to, _ := strconv.ParseFloat(stringifyValue(mappingMap["to"]), 64)
|
||||
|
||||
newMappings = append(newMappings, map[string]interface{}{
|
||||
"type": "range",
|
||||
"options": map[string]interface{}{
|
||||
"from": from,
|
||||
"to": to,
|
||||
"result": result,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add consolidated value mappings at the beginning if any exist
|
||||
if hasValueMappings {
|
||||
newMappings = append([]interface{}{valueMaps}, newMappings...)
|
||||
}
|
||||
|
||||
return newMappings
|
||||
}
|
||||
|
||||
// getActiveThresholdColor returns the color for a value based on thresholds
|
||||
func getActiveThresholdColor(value float64, thresholds map[string]interface{}) interface{} {
|
||||
if steps, ok := thresholds["steps"].([]interface{}); ok {
|
||||
var activeStep map[string]interface{}
|
||||
|
||||
for _, step := range steps {
|
||||
if stepMap, ok := step.(map[string]interface{}); ok {
|
||||
if stepValue, ok := stepMap["value"]; ok {
|
||||
if stepValue == nil {
|
||||
// Null value represents negative infinity - this is always the base color
|
||||
activeStep = stepMap
|
||||
continue
|
||||
}
|
||||
|
||||
if stepNum, ok := stepValue.(float64); ok {
|
||||
if value >= stepNum {
|
||||
activeStep = stepMap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if activeStep != nil {
|
||||
return activeStep["color"]
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getBaseThresholdColor returns the base threshold color (first step with null value)
|
||||
func getBaseThresholdColor(thresholds map[string]interface{}) interface{} {
|
||||
if steps, ok := thresholds["steps"].([]interface{}); ok {
|
||||
for _, step := range steps {
|
||||
if stepMap, ok := step.(map[string]interface{}); ok {
|
||||
if stepValue, ok := stepMap["value"]; ok {
|
||||
if stepValue == nil {
|
||||
// This is the base color (null value step)
|
||||
return stepMap["color"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// migrateTooltipOptions renames tooltipOptions to tooltip for specific panel types
|
||||
func migrateTooltipOptions(panel map[string]interface{}) {
|
||||
panelType, ok := panel["type"].(string)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Only migrate for specific panel types
|
||||
if panelType != "timeseries" && panelType != "xychart" && panelType != "xychart2" {
|
||||
return
|
||||
}
|
||||
|
||||
options, ok := panel["options"].(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
tooltipOptions, ok := options["tooltipOptions"]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Rename tooltipOptions to tooltip
|
||||
options["tooltip"] = tooltipOptions
|
||||
delete(options, "tooltipOptions")
|
||||
}
|
||||
|
||||
// stringifyValue converts a value to string
|
||||
func stringifyValue(value interface{}) string {
|
||||
switch v := value.(type) {
|
||||
case string:
|
||||
return v
|
||||
case float64:
|
||||
return strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.Itoa(v)
|
||||
case bool:
|
||||
return strconv.FormatBool(v)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -1,499 +0,0 @@
|
||||
package schemaversion_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func TestV30(t *testing.T) {
|
||||
tests := []migrationTestCase{
|
||||
{
|
||||
name: "panel with legacy value mappings gets upgraded to new format",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 Value Mappings Migration Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with legacy value mappings",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"thresholds": map[string]interface{}{
|
||||
"mode": "absolute",
|
||||
"steps": []interface{}{
|
||||
map[string]interface{}{
|
||||
"color": "green",
|
||||
"value": nil,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"color": "red",
|
||||
"value": float64(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
"mappings": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 0,
|
||||
"text": "Up",
|
||||
"type": float64(1),
|
||||
"value": "1",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": 1,
|
||||
"text": "Down",
|
||||
"type": float64(1),
|
||||
"value": "0",
|
||||
},
|
||||
map[string]interface{}{
|
||||
"from": "10",
|
||||
"to": "20",
|
||||
"text": "Medium",
|
||||
"type": float64(2),
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": float64(1),
|
||||
"value": "null",
|
||||
"text": "Null Value",
|
||||
},
|
||||
},
|
||||
},
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "test-field",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "mappings",
|
||||
"value": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 0,
|
||||
"text": "Override Up",
|
||||
"type": float64(1),
|
||||
"value": "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 Value Mappings Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with legacy value mappings",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"thresholds": map[string]interface{}{
|
||||
"mode": "absolute",
|
||||
"steps": []interface{}{
|
||||
map[string]interface{}{
|
||||
"color": "green",
|
||||
"value": nil,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"color": "red",
|
||||
"value": float64(80),
|
||||
},
|
||||
},
|
||||
},
|
||||
"mappings": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "value",
|
||||
"options": map[string]interface{}{
|
||||
"1": map[string]interface{}{
|
||||
"text": "Up",
|
||||
"color": "green",
|
||||
},
|
||||
"0": map[string]interface{}{
|
||||
"text": "Down",
|
||||
"color": "green",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "range",
|
||||
"options": map[string]interface{}{
|
||||
"from": float64(10),
|
||||
"to": float64(20),
|
||||
"result": map[string]interface{}{
|
||||
"text": "Medium",
|
||||
"color": "green",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "special",
|
||||
"options": map[string]interface{}{
|
||||
"match": "null",
|
||||
"result": map[string]interface{}{
|
||||
"text": "Null Value",
|
||||
"color": "green",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"overrides": []interface{}{
|
||||
map[string]interface{}{
|
||||
"matcher": map[string]interface{}{
|
||||
"id": "byName",
|
||||
"options": "test-field",
|
||||
},
|
||||
"properties": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "mappings",
|
||||
"value": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "value",
|
||||
"options": map[string]interface{}{
|
||||
"1": map[string]interface{}{
|
||||
"text": "Override Up",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with tooltip options gets migrated to tooltip",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 Tooltip Options Migration Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with tooltipOptions",
|
||||
"id": 1,
|
||||
"options": map[string]interface{}{
|
||||
"tooltipOptions": map[string]interface{}{
|
||||
"mode": "multi",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "xychart",
|
||||
"title": "XY Chart with tooltipOptions",
|
||||
"id": 2,
|
||||
"options": map[string]interface{}{
|
||||
"tooltipOptions": map[string]interface{}{
|
||||
"mode": "single",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "xychart2",
|
||||
"title": "XY Chart2 with tooltipOptions",
|
||||
"id": 3,
|
||||
"options": map[string]interface{}{
|
||||
"tooltipOptions": map[string]interface{}{
|
||||
"mode": "single",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 Tooltip Options Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with tooltipOptions",
|
||||
"id": 1,
|
||||
"options": map[string]interface{}{
|
||||
"tooltip": map[string]interface{}{
|
||||
"mode": "multi",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "xychart",
|
||||
"title": "XY Chart with tooltipOptions",
|
||||
"id": 2,
|
||||
"options": map[string]interface{}{
|
||||
"tooltip": map[string]interface{}{
|
||||
"mode": "single",
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "xychart2",
|
||||
"title": "XY Chart2 with tooltipOptions",
|
||||
"id": 3,
|
||||
"options": map[string]interface{}{
|
||||
"tooltip": map[string]interface{}{
|
||||
"mode": "single",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with nested panels in collapsed row gets migrated",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 Nested Panels Migration Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "row",
|
||||
"title": "Collapsed Row",
|
||||
"id": 1,
|
||||
"collapsed": true,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with tooltipOptions",
|
||||
"id": 2,
|
||||
"options": map[string]interface{}{
|
||||
"tooltipOptions": map[string]interface{}{
|
||||
"mode": "multi",
|
||||
},
|
||||
},
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"mappings": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": 0,
|
||||
"text": "On",
|
||||
"type": float64(1),
|
||||
"value": "1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 Nested Panels Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "row",
|
||||
"title": "Collapsed Row",
|
||||
"id": 1,
|
||||
"collapsed": true,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with tooltipOptions",
|
||||
"id": 2,
|
||||
"options": map[string]interface{}{
|
||||
"tooltip": map[string]interface{}{
|
||||
"mode": "multi",
|
||||
},
|
||||
},
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"mappings": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "value",
|
||||
"options": map[string]interface{}{
|
||||
"1": map[string]interface{}{
|
||||
"text": "On",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with no mappings or tooltip options remains unchanged",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 No Changes Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no relevant configurations",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"unit": "bytes",
|
||||
},
|
||||
},
|
||||
"options": map[string]interface{}{
|
||||
"legend": map[string]interface{}{
|
||||
"displayMode": "list",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 No Changes Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no relevant configurations",
|
||||
"id": 1,
|
||||
"fieldConfig": map[string]interface{}{
|
||||
"defaults": map[string]interface{}{
|
||||
"unit": "bytes",
|
||||
},
|
||||
},
|
||||
"options": map[string]interface{}{
|
||||
"legend": map[string]interface{}{
|
||||
"displayMode": "list",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panels remain unchanged when no V30 specific migrations apply",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 Panel Types Unchanged Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph panel",
|
||||
"id": 1,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat panel",
|
||||
"id": 2,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Already timeseries panel",
|
||||
"id": 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 Panel Types Unchanged Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph panel",
|
||||
"id": 1,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "singlestat",
|
||||
"title": "Singlestat panel",
|
||||
"id": 2,
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Already timeseries panel",
|
||||
"id": 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "graph panels with different configurations remain unchanged in V30",
|
||||
input: map[string]interface{}{
|
||||
"title": "V30 Graph Panel Configurations Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with series mode and legend values",
|
||||
"id": 1,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "series",
|
||||
},
|
||||
"legend": map[string]interface{}{
|
||||
"values": true,
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with series mode",
|
||||
"id": 2,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "series",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with histogram mode",
|
||||
"id": 3,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "histogram",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with default configuration",
|
||||
"id": 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V30 Graph Panel Configurations Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with series mode and legend values",
|
||||
"id": 1,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "series",
|
||||
},
|
||||
"legend": map[string]interface{}{
|
||||
"values": true,
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with series mode",
|
||||
"id": 2,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "series",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with histogram mode",
|
||||
"id": 3,
|
||||
"xaxis": map[string]interface{}{
|
||||
"mode": "histogram",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "graph",
|
||||
"title": "Graph with default configuration",
|
||||
"id": 4,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runMigrationTests(t, tests, schemaversion.V30)
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package schemaversion
|
||||
|
||||
// V31 adds a merge transformer after any labelsToFields transformer in panel transformations.
|
||||
//
|
||||
// This migration addresses data processing workflow optimization by automatically inserting
|
||||
// a merge transformation after the labelsToFields transformation. When the labelsToFields
|
||||
// transformer converts time series labels to individual fields, it can result in multiple
|
||||
// data frames that need to be consolidated for proper visualization and analysis.
|
||||
//
|
||||
// The migration works by:
|
||||
// 1. Iterating through all panels in the dashboard, including nested panels in collapsed rows
|
||||
// 2. Examining the transformations array within each panel
|
||||
// 3. Identifying transformations with id 'labelsToFields'
|
||||
// 4. Inserting a merge transformation with empty options immediately after each labelsToFields transformation
|
||||
// 5. Preserving the original labelsToFields options (mode, keepLabels, valueLabel, etc.) without modification
|
||||
// 6. Using empty options for merge transformations to enable optimal default consolidation behavior
|
||||
// 7. Preserving the original order and configuration of all other transformations
|
||||
//
|
||||
// The migration handles complex scenarios including:
|
||||
// - Multiple labelsToFields transformations within a single panel
|
||||
// - Panels with mixed transformation types
|
||||
// - Nested panels within collapsed row panels
|
||||
// - Panels with no transformations (left unchanged)
|
||||
// - Panels with transformations but no labelsToFields (left unchanged)
|
||||
//
|
||||
// Example transformation:
|
||||
//
|
||||
// Before migration:
|
||||
//
|
||||
// panel: {
|
||||
// "transformations": [
|
||||
// { "id": "organize", "options": {} },
|
||||
// { "id": "labelsToFields", "options": {} },
|
||||
// { "id": "calculateField", "options": {} },
|
||||
// { "id": "labelsToFields", "options": { "mode": "rows", "keepLabels": ["job", "instance"] } },
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// After migration:
|
||||
//
|
||||
// panel: {
|
||||
// "transformations": [
|
||||
// { "id": "organize", "options": {} },
|
||||
// { "id": "labelsToFields", "options": {} },
|
||||
// { "id": "merge", "options": {} },
|
||||
// { "id": "calculateField", "options": {} },
|
||||
// { "id": "labelsToFields", "options": { "mode": "rows", "keepLabels": ["job", "instance"] } },
|
||||
// { "id": "merge", "options": {} }
|
||||
// ]
|
||||
// }
|
||||
func V31(dashboard map[string]interface{}) error {
|
||||
dashboard["schemaVersion"] = int(31)
|
||||
|
||||
panels, ok := dashboard["panels"].([]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Process all panels, including nested ones
|
||||
processPanelsV31(panels)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processPanelsV31 recursively processes panels, including nested panels within rows
|
||||
func processPanelsV31(panels []interface{}) {
|
||||
for _, panel := range panels {
|
||||
p, ok := panel.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Process nested panels if this is a row panel
|
||||
if p["type"] == "row" {
|
||||
nestedPanels, hasNested := p["panels"].([]interface{})
|
||||
if !hasNested {
|
||||
continue
|
||||
}
|
||||
processPanelsV31(nestedPanels)
|
||||
continue
|
||||
}
|
||||
|
||||
transformations, ok := p["transformations"].([]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if we have any labelsToFields transformations
|
||||
hasLabelsToFields := false
|
||||
for _, transformation := range transformations {
|
||||
t, ok := transformation.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if t["id"] == "labelsToFields" {
|
||||
hasLabelsToFields = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !hasLabelsToFields {
|
||||
continue
|
||||
}
|
||||
|
||||
// Create new transformations array with merge transformations added
|
||||
newTransformations := []interface{}{}
|
||||
|
||||
for _, transformation := range transformations {
|
||||
t, ok := transformation.(map[string]interface{})
|
||||
if !ok {
|
||||
newTransformations = append(newTransformations, transformation)
|
||||
continue
|
||||
}
|
||||
|
||||
// Add the current transformation (preserving all original options)
|
||||
newTransformations = append(newTransformations, transformation)
|
||||
|
||||
// If this is a labelsToFields transformation, add a merge transformation after it
|
||||
// with empty options to enable optimal default consolidation behavior
|
||||
if t["id"] == "labelsToFields" {
|
||||
mergeTransformation := map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
}
|
||||
newTransformations = append(newTransformations, mergeTransformation)
|
||||
}
|
||||
}
|
||||
|
||||
// Update the panel with the new transformations
|
||||
p["transformations"] = newTransformations
|
||||
}
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
package schemaversion_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func TestV31(t *testing.T) {
|
||||
tests := []migrationTestCase{
|
||||
{
|
||||
name: "panel with basic labelsToFields transformation gets merge transformation added",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 LabelsToFields Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with basic labelsToFields",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 LabelsToFields Migration Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with basic labelsToFields",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with labelsToFields options preserved during migration",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 LabelsToFields Options Preservation Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with labelsToFields options",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{
|
||||
"mode": "rows",
|
||||
"keepLabels": []interface{}{"job", "instance"},
|
||||
"valueLabel": "value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 LabelsToFields Options Preservation Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with labelsToFields options",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{
|
||||
"mode": "rows",
|
||||
"keepLabels": []interface{}{"job", "instance"},
|
||||
"valueLabel": "value",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with multiple labelsToFields transformations",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 Multiple LabelsToFields Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with multiple labelsToFields",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "calculateField",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{
|
||||
"mode": "rows",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 Multiple LabelsToFields Migration Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with multiple labelsToFields",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "calculateField",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{
|
||||
"mode": "rows",
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with no transformations remains unchanged",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 No Transformations Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no transformations",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 No Transformations Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no transformations",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "panel with transformations but no labelsToFields remains unchanged",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 Other Transformations Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with other transformations",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "reduce",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 Other Transformations Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with other transformations",
|
||||
"id": 1,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "reduce",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nested panels in row with labelsToFields transformation",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 Nested Panels Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "row",
|
||||
"title": "Row with nested panels",
|
||||
"id": 1,
|
||||
"collapsed": false,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with labelsToFields",
|
||||
"id": 2,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel without labelsToFields",
|
||||
"id": 3,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 Nested Panels Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "row",
|
||||
"title": "Row with nested panels",
|
||||
"id": 1,
|
||||
"collapsed": false,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with labelsToFields",
|
||||
"id": 2,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "labelsToFields",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"id": "merge",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel without labelsToFields",
|
||||
"id": 3,
|
||||
"transformations": []interface{}{
|
||||
map[string]interface{}{
|
||||
"id": "organize",
|
||||
"options": map[string]interface{}{},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "dashboard with no panels",
|
||||
input: map[string]interface{}{
|
||||
"title": "V31 No Panels Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V31 No Panels Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
},
|
||||
},
|
||||
}
|
||||
runMigrationTests(t, tests, schemaversion.V31)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package schemaversion
|
||||
|
||||
// V32 is a no-op migration that serves as a placeholder for consistency.
|
||||
//
|
||||
// The migration performs no modifications to the dashboard structure and simply
|
||||
// updates the schema version number.
|
||||
// Example (no changes made):
|
||||
//
|
||||
// Before migration:
|
||||
//
|
||||
// dashboard: {
|
||||
// "title": "My Dashboard",
|
||||
// "schemaVersion": 31,
|
||||
// "panels": [...]
|
||||
// }
|
||||
//
|
||||
// After migration:
|
||||
//
|
||||
// dashboard: {
|
||||
// "title": "My Dashboard",
|
||||
// "schemaVersion": 32,
|
||||
// "panels": [...] // unchanged
|
||||
// }
|
||||
func V32(dashboard map[string]interface{}) error {
|
||||
dashboard["schemaVersion"] = int(32)
|
||||
return nil
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package schemaversion_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/apps/dashboard/pkg/migration/schemaversion"
|
||||
)
|
||||
|
||||
func TestV32(t *testing.T) {
|
||||
tests := []migrationTestCase{
|
||||
{
|
||||
name: "v32 no-op migration updates schema version only",
|
||||
input: map[string]interface{}{
|
||||
"title": "V32 No-Op Migration Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel remains unchanged",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]interface{}{
|
||||
"title": "V32 No-Op Migration Test Dashboard",
|
||||
"schemaVersion": 32,
|
||||
"panels": []interface{}{
|
||||
map[string]interface{}{
|
||||
"type": "timeseries",
|
||||
"title": "Panel remains unchanged",
|
||||
"id": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
runMigrationTests(t, tests, schemaversion.V32)
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
{
|
||||
"title": "V29 Query Variables Refresh and Options Migration Test Dashboard",
|
||||
"schemaVersion": 28,
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"name": "never_refresh_with_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [
|
||||
{"text": "A", "value": "A"},
|
||||
{"text": "B", "value": "B"}
|
||||
],
|
||||
"refresh": 0
|
||||
},
|
||||
{
|
||||
"name": "never_refresh_without_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [],
|
||||
"refresh": 0
|
||||
},
|
||||
{
|
||||
"name": "dashboard_refresh_with_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [
|
||||
{"text": "C", "value": "C"}
|
||||
],
|
||||
"refresh": 1
|
||||
},
|
||||
{
|
||||
"name": "dashboard_refresh_without_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [],
|
||||
"refresh": 1
|
||||
},
|
||||
{
|
||||
"name": "timerange_refresh_with_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [
|
||||
{"text": "D", "value": "D"},
|
||||
{"text": "E", "value": "E"}
|
||||
],
|
||||
"refresh": 2
|
||||
},
|
||||
{
|
||||
"name": "timerange_refresh_without_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [],
|
||||
"refresh": 2
|
||||
},
|
||||
{
|
||||
"name": "no_refresh_with_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [
|
||||
{"text": "F", "value": "F"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no_refresh_without_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": []
|
||||
},
|
||||
{
|
||||
"name": "unknown_refresh_with_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [
|
||||
{"text": "G", "value": "G"}
|
||||
],
|
||||
"refresh": 2001
|
||||
},
|
||||
{
|
||||
"name": "unknown_refresh_without_options",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": [],
|
||||
"refresh": 2001
|
||||
},
|
||||
{
|
||||
"name": "custom_variable",
|
||||
"type": "custom",
|
||||
"options": [
|
||||
{"text": "custom", "value": "custom"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "textbox_variable",
|
||||
"type": "textbox",
|
||||
"options": [
|
||||
{"text": "Hello", "value": "World"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "datasource_variable",
|
||||
"type": "datasource",
|
||||
"options": [
|
||||
{"text": "ds", "value": "ds"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "interval_variable",
|
||||
"type": "interval",
|
||||
"options": [
|
||||
{"text": "1m", "value": "1m"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"panels": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Test Panel",
|
||||
"type": "timeseries",
|
||||
"datasource": "prometheus",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"expr": "up"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
{
|
||||
"title": "V30 Value Mappings and Tooltip Options Migration Test Dashboard",
|
||||
"schemaVersion": 29,
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with legacy value mappings and tooltip options",
|
||||
"id": 1,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"mappings": [
|
||||
{
|
||||
"id": 0,
|
||||
"text": "Up",
|
||||
"type": 1,
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"text": "Down",
|
||||
"type": 1,
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"from": "10",
|
||||
"to": "20",
|
||||
"text": "Medium",
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"type": 1,
|
||||
"value": "null",
|
||||
"text": "Null Value"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "test-field"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"id": 0,
|
||||
"text": "Override Up",
|
||||
"type": 1,
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"tooltipOptions": {
|
||||
"mode": "multi"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "xychart",
|
||||
"title": "XY Chart with tooltip options only",
|
||||
"id": 2,
|
||||
"options": {
|
||||
"tooltipOptions": {
|
||||
"mode": "single"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "xychart2",
|
||||
"title": "XY Chart2 with tooltip options",
|
||||
"id": 3,
|
||||
"options": {
|
||||
"tooltipOptions": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "graph",
|
||||
"title": "Graph panel gets migrated to timeseries and tooltip",
|
||||
"id": 4,
|
||||
"options": {
|
||||
"tooltip": {
|
||||
"mode": "single"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Panel with complex value mappings",
|
||||
"id": 5,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{
|
||||
"id": 0,
|
||||
"text": "Critical",
|
||||
"type": 1,
|
||||
"value": "100"
|
||||
},
|
||||
{
|
||||
"from": "50",
|
||||
"to": "99",
|
||||
"text": "Warning",
|
||||
"type": 2
|
||||
},
|
||||
{
|
||||
"from": "0",
|
||||
"to": "49",
|
||||
"text": "OK",
|
||||
"type": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "row",
|
||||
"title": "Collapsed Row with nested panels",
|
||||
"id": 6,
|
||||
"collapsed": true,
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with both migrations",
|
||||
"id": 7,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{
|
||||
"id": 0,
|
||||
"text": "On",
|
||||
"type": 1,
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"text": "Off",
|
||||
"type": 1,
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"tooltipOptions": {
|
||||
"mode": "multi"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no relevant configurations",
|
||||
"id": 8,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "bytes"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"legend": {
|
||||
"displayMode": "list"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Panel with empty mappings array - should return null",
|
||||
"id": 9,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": []
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "empty-field"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
{
|
||||
"title": "V31 LabelsToFields Merge Migration Test Dashboard",
|
||||
"schemaVersion": 30,
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with basic labelsToFields transformation",
|
||||
"id": 1,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with labelsToFields options preserved",
|
||||
"id": 9,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {
|
||||
"mode": "rows",
|
||||
"keepLabels": ["job", "instance", "region"],
|
||||
"valueLabel": "value"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with multiple labelsToFields transformations",
|
||||
"id": 2,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "calculateField",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {
|
||||
"mode": "rows"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with no transformations",
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with other transformations only",
|
||||
"id": 4,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "reduce",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "row",
|
||||
"title": "Row with nested panels",
|
||||
"id": 5,
|
||||
"collapsed": false,
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel with labelsToFields",
|
||||
"id": 6,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Nested panel without labelsToFields",
|
||||
"id": 7,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "organize",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with labelsToFields and existing merge",
|
||||
"id": 8,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "merge",
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "reduce",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"title": "V32 No-Op Migration Test Dashboard",
|
||||
"schemaVersion": 31,
|
||||
"panels": [
|
||||
{
|
||||
"type": "timeseries",
|
||||
"title": "Panel with transformations remains unchanged",
|
||||
"id": 1,
|
||||
"transformations": [
|
||||
{
|
||||
"id": "labelsToFields",
|
||||
"options": {
|
||||
"mode": "rows",
|
||||
"keepLabels": ["job", "instance"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "merge",
|
||||
"options": {}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "graph",
|
||||
"title": "Graph panel remains unchanged",
|
||||
"id": 2,
|
||||
"yAxes": [
|
||||
{
|
||||
"show": true,
|
||||
"min": null,
|
||||
"max": null
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "row",
|
||||
"title": "Row with nested panels",
|
||||
"id": 3,
|
||||
"collapsed": false,
|
||||
"panels": [
|
||||
{
|
||||
"type": "stat",
|
||||
"title": "Nested stat panel",
|
||||
"id": 4,
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"unit": "bytes"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"name": "environment",
|
||||
"type": "query",
|
||||
"datasource": "prometheus",
|
||||
"options": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"name": "Deployments",
|
||||
"datasource": "grafana",
|
||||
"enable": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"]
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user