Merge remote-tracking branch 'origin/grafana-git-ui-sync' into feature/unified-onboarding

This commit is contained in:
Ryan McKinley
2025-03-11 22:47:18 +03:00
418 changed files with 71072 additions and 9559 deletions
+12 -15
View File
@@ -5,6 +5,12 @@
//
exports[`better eslint`] = {
value: `{
"apps/dashboard/tshack/v0alpha1_spec_gen.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"apps/dashboard/tshack/v1alpha1_spec_gen.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"e2e/old-arch/utils/support/types.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
@@ -2384,10 +2390,8 @@ exports[`better eslint`] = {
],
"public/app/features/alerting/unified/components/rules/CloudRules.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"]
],
"public/app/features/alerting/unified/components/rules/EditRuleGroupModal.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
@@ -2959,10 +2963,6 @@ exports[`better eslint`] = {
"public/app/features/auth-config/utils/data.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/features/browse-dashboards/BrowseDashboardsPage.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
],
"public/app/features/browse-dashboards/api/browseDashboardsAPI.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`@reduxjs/toolkit/query/react\`)", "0"]
],
@@ -2977,7 +2977,7 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "1"]
],
"public/app/features/browse-dashboards/components/NewProvisionedFolderForm.tsx:5381": [
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "1"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "2"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "3"],
@@ -2987,14 +2987,15 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "7"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "8"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "9"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"],
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "10"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "12"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "13"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "14"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "15"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "17"]
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "17"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "18"]
],
"public/app/features/browse-dashboards/state/index.ts:5381": [
[0, 0, 0, "Do not use export all (\`export * from ...\`)", "0"],
@@ -6061,10 +6062,6 @@ exports[`better eslint`] = {
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "2"],
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"]
],
"public/app/features/scopes/index.ts:5381": [
[0, 0, 0, "Do not re-export imported variable (\`./ScopesDashboards\`)", "0"],
[0, 0, 0, "Do not re-export imported variable (\`./instance\`)", "1"]
],
"public/app/features/search/page/components/ActionRow.tsx:5381": [
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
],
+103 -103
View File
@@ -25,7 +25,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-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.23.7-alpine
image: golang:1.24.1-alpine
name: lint-starlark
trigger:
event:
@@ -437,7 +437,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -446,21 +446,21 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- apk add --update build-base shared-mime-info shared-mime-info-lang
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend
- commands:
- apk add --update build-base
@@ -469,7 +469,7 @@ steps:
| grep -o '\(.*\)/' | sort -u)
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend-integration
trigger:
event:
@@ -524,7 +524,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- echo $(/usr/bin/github-app-external-token) > /github-app/token
@@ -569,16 +569,16 @@ steps:
- apk add --update make
- make gen-go
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- go run scripts/modowners/modowners.go check go.mod
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-modfile
- commands:
- apk add --update make
- make swagger-validate
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-openapi-spec
trigger:
event:
@@ -655,7 +655,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -665,7 +665,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -674,7 +674,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- yarn install --immutable || yarn install --immutable
@@ -712,7 +712,7 @@ steps:
- /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.7 --yarn-cache=$$YARN_CACHE_FOLDER
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.1 --yarn-cache=$$YARN_CACHE_FOLDER
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
@@ -1110,7 +1110,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
@@ -1124,7 +1124,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1133,14 +1133,14 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- dockerize -wait tcp://postgres:5432 -timeout 120s
@@ -1161,7 +1161,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: postgres-integration-tests
- commands:
- dockerize -wait tcp://mysql80:3306 -timeout 120s
@@ -1182,7 +1182,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql80
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: mysql-8.0-integration-tests
- commands:
- dockerize -wait tcp://redis:6379 -timeout 120s
@@ -1198,7 +1198,7 @@ steps:
- wait-for-redis
environment:
REDIS_URL: redis://redis:6379/0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -1214,7 +1214,7 @@ steps:
- wait-for-memcached
environment:
MEMCACHED_HOSTS: memcached:11211
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: memcached-integration-tests
- commands:
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
@@ -1230,7 +1230,7 @@ steps:
environment:
AM_TENANT_ID: test
AM_URL: http://mimir_backend:8080
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: remote-alertmanager-integration-tests
trigger:
event:
@@ -1312,7 +1312,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
trigger:
event:
@@ -1433,7 +1433,7 @@ steps:
&& return 1; fi
depends_on:
- clone-enterprise
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: swagger-gen
trigger:
event:
@@ -1538,7 +1538,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -1549,7 +1549,7 @@ steps:
- CODEGEN_VERIFY=1 make gen-cue
depends_on:
- clone-enterprise
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1559,14 +1559,14 @@ steps:
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on:
- clone-enterprise
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- apk add --update build-base
@@ -1574,7 +1574,7 @@ steps:
- go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES}
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: sqlite-benchmark-integration-tests
- commands:
- apk add --update build-base
@@ -1586,7 +1586,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: postgres-benchmark-integration-tests
- commands:
- apk add --update build-base
@@ -1597,7 +1597,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql80
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: mysql-8.0-benchmark-integration-tests
trigger:
event:
@@ -1669,7 +1669,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
trigger:
branch: main
@@ -1852,7 +1852,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -1861,21 +1861,21 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- apk add --update build-base shared-mime-info shared-mime-info-lang
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend
- commands:
- apk add --update build-base
@@ -1884,7 +1884,7 @@ steps:
| grep -o '\(.*\)/' | sort -u)
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend-integration
trigger:
branch: main
@@ -1929,22 +1929,22 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- apk add --update make
- make gen-go
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- go run scripts/modowners/modowners.go check go.mod
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-modfile
- commands:
- apk add --update make
- make swagger-validate
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-openapi-spec
- commands:
- ./bin/build verify-drone
@@ -2076,7 +2076,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- '# It is required that code generated from Thema/CUE be committed and in sync
@@ -2086,7 +2086,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -2095,7 +2095,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- yarn install --immutable || yarn install --immutable
@@ -2132,7 +2132,7 @@ steps:
- /src/grafana-build artifacts -a targz:grafana:linux/amd64 -a targz:grafana:linux/arm64
-a targz:grafana:linux/arm/v7 -a docker:grafana:linux/amd64 -a docker:grafana:linux/amd64:ubuntu
-a docker:grafana:linux/arm64 -a docker:grafana:linux/arm64:ubuntu -a docker:grafana:linux/arm/v7
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.23.7 --yarn-cache=$$YARN_CACHE_FOLDER
-a docker:grafana:linux/arm/v7:ubuntu --go-version=1.24.1 --yarn-cache=$$YARN_CACHE_FOLDER
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.21.3
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
@@ -2607,7 +2607,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
@@ -2621,7 +2621,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -2630,14 +2630,14 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- dockerize -wait tcp://postgres:5432 -timeout 120s
@@ -2658,7 +2658,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: postgres-integration-tests
- commands:
- dockerize -wait tcp://mysql80:3306 -timeout 120s
@@ -2679,7 +2679,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql80
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: mysql-8.0-integration-tests
- commands:
- dockerize -wait tcp://redis:6379 -timeout 120s
@@ -2695,7 +2695,7 @@ steps:
- wait-for-redis
environment:
REDIS_URL: redis://redis:6379/0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -2711,7 +2711,7 @@ steps:
- wait-for-memcached
environment:
MEMCACHED_HOSTS: memcached:11211
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: memcached-integration-tests
- commands:
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
@@ -2727,7 +2727,7 @@ steps:
environment:
AM_TENANT_ID: test
AM_URL: http://mimir_backend:8080
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: remote-alertmanager-integration-tests
trigger:
branch: main
@@ -2996,7 +2996,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -3005,21 +3005,21 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- apk add --update build-base shared-mime-info shared-mime-info-lang
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend
- commands:
- apk add --update build-base
@@ -3028,7 +3028,7 @@ steps:
| grep -o '\(.*\)/' | sort -u)
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend-integration
trigger:
branch:
@@ -3071,22 +3071,22 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- apk add --update make
- make gen-go
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- go run scripts/modowners/modowners.go check go.mod
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-modfile
- commands:
- apk add --update make
- make swagger-validate
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: validate-openapi-spec
trigger:
branch:
@@ -3165,7 +3165,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- echo $DRONE_RUNNER_NAME
@@ -3179,7 +3179,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -3188,14 +3188,14 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- dockerize -wait tcp://postgres:5432 -timeout 120s
@@ -3216,7 +3216,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: postgres-integration-tests
- commands:
- dockerize -wait tcp://mysql80:3306 -timeout 120s
@@ -3237,7 +3237,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql80
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: mysql-8.0-integration-tests
- commands:
- dockerize -wait tcp://redis:6379 -timeout 120s
@@ -3253,7 +3253,7 @@ steps:
- wait-for-redis
environment:
REDIS_URL: redis://redis:6379/0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -3269,7 +3269,7 @@ steps:
- wait-for-memcached
environment:
MEMCACHED_HOSTS: memcached:11211
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: memcached-integration-tests
- commands:
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
@@ -3285,7 +3285,7 @@ steps:
environment:
AM_TENANT_ID: test
AM_URL: http://mimir_backend:8080
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: remote-alertmanager-integration-tests
trigger:
branch:
@@ -3385,7 +3385,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition oss
@@ -3517,7 +3517,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- ./bin/build artifacts docker fetch --edition oss
@@ -3658,7 +3658,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- ./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket
@@ -3750,7 +3750,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- yarn install --immutable || yarn install --immutable
@@ -3850,7 +3850,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- depends_on:
- compile-build-cmd
@@ -3947,7 +3947,7 @@ steps:
depends_on: []
environment:
CGO_ENABLED: 0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: compile-build-cmd
- commands:
- ./bin/build publish grafana-com --edition oss ${DRONE_TAG}
@@ -4009,7 +4009,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4084,7 +4084,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4201,7 +4201,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4352,7 +4352,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -4361,21 +4361,21 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- apk add --update build-base shared-mime-info shared-mime-info-lang
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend
- commands:
- apk add --update build-base
@@ -4384,7 +4384,7 @@ steps:
| grep -o '\(.*\)/' | sort -u)
depends_on:
- wire-install
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: test-backend-integration
trigger:
cron:
@@ -4438,7 +4438,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4582,7 +4582,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4689,7 +4689,7 @@ steps:
- export GITHUB_TOKEN=$(cat /github-app/token)
- dagger run --silent /src/grafana-build artifacts -a $${ARTIFACTS} --grafana-ref=$${GRAFANA_REF}
--enterprise-ref=$${ENTERPRISE_REF} --grafana-repo=$${GRAFANA_REPO} --version=$${VERSION}
--go-version=1.23.7
--go-version=1.24.1
depends_on:
- github-app-generate-token
environment:
@@ -4710,7 +4710,7 @@ steps:
from_secret: grafana_api_key
GCP_KEY_BASE64:
from_secret: gcp_key_base64
GO_VERSION: 1.23.7
GO_VERSION: 1.24.1
GPG_PASSPHRASE:
from_secret: packages_gpg_passphrase
GPG_PRIVATE_KEY:
@@ -4848,7 +4848,7 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-cue
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-cue
- commands:
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
@@ -4857,14 +4857,14 @@ steps:
- apk add --update make
- CODEGEN_VERIFY=1 make gen-jsonnet
depends_on: []
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: verify-gen-jsonnet
- commands:
- apk add --update make
- make gen-go
depends_on:
- verify-gen-cue
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: wire-install
- commands:
- dockerize -wait tcp://postgres:5432 -timeout 120s
@@ -4885,7 +4885,7 @@ steps:
GRAFANA_TEST_DB: postgres
PGPASSWORD: grafanatest
POSTGRES_HOST: postgres
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: postgres-integration-tests
- commands:
- dockerize -wait tcp://mysql80:3306 -timeout 120s
@@ -4906,7 +4906,7 @@ steps:
environment:
GRAFANA_TEST_DB: mysql
MYSQL_HOST: mysql80
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: mysql-8.0-integration-tests
- commands:
- dockerize -wait tcp://redis:6379 -timeout 120s
@@ -4922,7 +4922,7 @@ steps:
- wait-for-redis
environment:
REDIS_URL: redis://redis:6379/0
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: redis-integration-tests
- commands:
- dockerize -wait tcp://memcached:11211 -timeout 120s
@@ -4938,7 +4938,7 @@ steps:
- wait-for-memcached
environment:
MEMCACHED_HOSTS: memcached:11211
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: memcached-integration-tests
- commands:
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
@@ -4954,7 +4954,7 @@ steps:
environment:
AM_TENANT_ID: test
AM_URL: http://mimir_backend:8080
image: golang:1.23.7-alpine
image: golang:1.24.1-alpine
name: remote-alertmanager-integration-tests
trigger:
event:
@@ -5257,7 +5257,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.23.7-alpine
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.1-alpine
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.11.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
@@ -5295,7 +5295,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.23.7-alpine
- trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.1-alpine
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22.11.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
@@ -5564,6 +5564,6 @@ kind: secret
name: gcr_credentials
---
kind: signature
hmac: 33f2e5615dfd7889899f9f8f16f7716190fa637fe98f1efd7e29607f8946be7d
hmac: f55fddb4c6faf30b232ae778ec6c022c9f3d32955879e8a764c715642712c5ea
...
+1
View File
@@ -72,6 +72,7 @@
/pkg/registry/apis/provisioning @grafana/grafana-git-ui-sync-team
/apps/alerting/ @grafana/alerting-backend
/apps/dashboard/ @grafana/grafana-app-platform-squad @grafana/dashboards-squad
/apps/playlist/ @grafana/grafana-app-platform-squad
/apps/investigations/ @fcjack @matryer @svennergr
/apps/advisor/ @grafana/plugins-platform-backend
+1 -1
View File
@@ -8,7 +8,7 @@ name: "CodeQL"
on:
workflow_dispatch:
push:
branches: [main, v1.8.x, v2.0.x, v2.1.x, v2.6.x, v3.0.x, v3.1.x, v4.0.x, v4.1.x, v4.2.x, v4.3.x, v4.4.x, v4.5.x, v4.6.x, v4.7.x, v5.0.x, v5.1.x, v5.2.x, v5.3.x, v5.4.x, v6.0.x, v6.1.x, v6.2.x, v6.3.x, v6.4.x, v6.5.x, v6.6.x, v6.7.x, v7.0.x, v7.1.x, v7.2.x]
branches: [main, v*.*.*]
paths-ignore:
- '**/*.cue'
- '**/*.json'
+2 -2
View File
@@ -40,7 +40,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: "go"
@@ -50,4 +50,4 @@ jobs:
make build-go
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
@@ -28,9 +28,9 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: "javascript"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
@@ -26,9 +26,9 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
uses: github/codeql-action/init@v3
with:
languages: "python"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@v3
+3
View File
@@ -19,6 +19,9 @@ vendor
# TS generate from cue by cuetsy
**/*.gen.ts
# TS generated by grafana-app-sdk
**/*_gen.ts
# Auto-generated theme files
theme.light.generated.json
theme.dark.generated.json
-2
View File
@@ -131,14 +131,12 @@ else
i18n-extract-enterprise:
@echo "Extracting i18n strings for Enterprise"
yarn run i18next --config public/locales/i18next-parser-enterprise.config.cjs
node ./public/locales/pseudo.mjs --mode enterprise
endif
.PHONY: i18n-extract
i18n-extract: i18n-extract-enterprise
@echo "Extracting i18n strings for OSS"
yarn run i18next --config public/locales/i18next-parser.config.cjs
node ./public/locales/pseudo.mjs --mode oss
##@ Building
.PHONY: gen-cue
+14
View File
@@ -13,6 +13,7 @@ import (
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
"github.com/grafana/grafana/apps/advisor/pkg/app/checkscheduler"
"github.com/grafana/grafana/apps/advisor/pkg/app/checktyperegisterer"
"github.com/grafana/grafana/pkg/infra/log"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
@@ -24,6 +25,12 @@ func New(cfg app.Config) (app.App, error) {
return nil, fmt.Errorf("invalid config type")
}
checkRegistry := specificConfig.CheckRegistry
stackID := specificConfig.StackID
namespace, err := checks.GetNamespace(stackID)
if err != nil {
return nil, err
}
log := log.New("advisor.app")
// Prepare storage client
clientGenerator := k8s.NewClientRegistry(cfg.KubeConfig, k8s.ClientConfig{})
@@ -60,6 +67,13 @@ func New(cfg app.Config) (app.App, error) {
},
Watcher: &simple.Watcher{
AddFunc: func(ctx context.Context, obj resource.Object) error {
log.Debug("Adding check", "namespace", obj.GetNamespace())
if obj.GetNamespace() != namespace {
log.Debug("Skipping check in namespace", "namespace", obj.GetNamespace())
return nil
} else {
log.Debug("Processing check in namespace", "namespace", obj.GetNamespace())
}
check, err := getCheck(obj, checkMap)
if err != nil {
return err
+5 -5
View File
@@ -62,13 +62,13 @@ require (
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
+12 -12
View File
@@ -180,22 +180,22 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+18
View File
@@ -0,0 +1,18 @@
.PHONY: generate
generate:
@grafana-app-sdk generate \
--source=./kinds/ \
--gogenpath=../../pkg/apis \
--tsgenpath=../../packages/grafana-schema/src/schema \
--grouping=group \
--defencoding=none \
--genoperatorstate=false \
--noschemasinmanifest
# This is a workaround for SDK codegen not producing correct output for v0alpha1
@rm ../../packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts
@cp ./tshack/v0alpha1_spec_gen.ts ../../packages/grafana-schema/src/schema/dashboard/v0alpha1/types.spec.gen.ts
# Same for v1alpha1
@rm ../../packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts
@cp ./tshack/v1alpha1_spec_gen.ts ../../packages/grafana-schema/src/schema/dashboard/v1alpha1/types.spec.gen.ts
+4
View File
@@ -0,0 +1,4 @@
module: "github.com/grafana/grafana/sdkkinds/dashboard"
language: {
version: "v0.9.0"
}
+68
View File
@@ -0,0 +1,68 @@
package kinds
import (
"github.com/grafana/grafana/sdkkinds/dashboard/v0alpha1"
"github.com/grafana/grafana/sdkkinds/dashboard/v1alpha1"
"github.com/grafana/grafana/sdkkinds/dashboard/v2alpha1"
)
// Status is the shared status of all dashboard versions.
DashboardStatus: {
// Optional conversion status.
conversion?: ConversionStatus
}
// ConversionStatus is the status of the conversion of the dashboard.
ConversionStatus: {
// 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
// The version which was stored when the dashboard was created / updated.
// Fetching this version should always succeed.
storedVersion: string
// The error message from the conversion.
// Empty if the conversion has not failed.
error: string
}
dashboard: {
kind: "Dashboard"
pluralName: "Dashboards"
current: "v0alpha1"
codegen: {
ts: {
enabled: true
config: {
enumsAsUnionTypes: true
}
}
go: {
enabled: true
}
}
versions: {
"v0alpha1": {
schema: {
spec: v0alpha1.DashboardSpec
status: DashboardStatus
}
}
"v1alpha1": {
schema: {
spec: v1alpha1.DashboardSpec
status: DashboardStatus
}
}
"v2alpha1": {
schema: {
spec: v2alpha1.DashboardSpec
status: DashboardStatus
}
}
}
}
+9
View File
@@ -0,0 +1,9 @@
package kinds
manifest: {
appName: "dashboard"
groupOverride: "dashboard.grafana.app"
kinds: [
dashboard,
]
}
@@ -0,0 +1,6 @@
package v0alpha1
// TODO: this outputs nothing.
// For now, we use unstructured for the spec,
// but it cannot be produced by the SDK codegen.
DashboardSpec: [string]: _
@@ -0,0 +1,6 @@
package v1alpha1
// TODO: this outputs nothing.
// For now, we use unstructured for the spec,
// but it cannot be produced by the SDK codegen.
DashboardSpec: [string]: _
@@ -0,0 +1,923 @@
package v2alpha1
DashboardSpec: {
// Title of dashboard.
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 | ResponsiveGridLayoutKind | 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
// 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: [...uint8]
}
// "Off" for no shared crosshair or tooltip (default).
// "Crosshair" for shared crosshair.
// "Tooltip" for shared crosshair AND shared tooltip.
DashboardCursorSync: "Off" | "Crosshair" | "Tooltip"
// 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" @cog(kind="enum",memberNames="ValueToText|RangeToText|RegexToText|SpecialValue")
// 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: {
// TODO (@radiohead): Something broke in cog / app SDK codegen
// And this is no longer producing valid TS / Go output
// type: MappingType & "value"
type: "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: {
// TODO (@radiohead): Something broke in cog / app SDK codegen
// And this is no longer producing valid TS / Go output
// type: MappingType & "range"
type: "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: {
// TODO (@radiohead): Something broke in cog / app SDK codegen
// And this is no longer producing valid TS / Go output
// type: MappingType & "regex"
type: "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: {
// TODO (@radiohead): Something broke in cog / app SDK codegen
// And this is no longer producing valid TS / Go output
// type: MappingType & "special"
type: "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
}
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 // 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
// 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
}
ResponsiveGridRepeatOptions: {
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
}
GridLayoutRowKind: {
kind: "GridLayoutRow"
spec: GridLayoutRowSpec
}
GridLayoutRowSpec: {
y: int
collapsed: bool
title: string
elements: [...GridLayoutItemKind] // Grid items in the row will have their Y value be relative to the rows Y value. This means a panel positioned at Y: 0 in a row with Y: 10 will be positioned at Y: 11 (row header has a heigh of 1) in the dashboard.
repeat?: RowRepeatOptions
}
GridLayoutSpec: {
items: [...GridLayoutItemKind | GridLayoutRowKind]
}
GridLayoutKind: {
kind: "GridLayout"
spec: GridLayoutSpec
}
RowsLayoutKind: {
kind: "RowsLayout"
spec: RowsLayoutSpec
}
RowsLayoutSpec: {
rows: [...RowsLayoutRowKind]
}
RowsLayoutRowKind: {
kind: "RowsLayoutRow"
spec: RowsLayoutRowSpec
}
RowsLayoutRowSpec: {
title?: string
collapsed: bool
repeat?: RowRepeatOptions
layout: GridLayoutKind | ResponsiveGridLayoutKind | TabsLayoutKind
}
ResponsiveGridLayoutKind: {
kind: "ResponsiveGridLayout"
spec: ResponsiveGridLayoutSpec
}
ResponsiveGridLayoutSpec: {
row: string
col: string
items: [...ResponsiveGridLayoutItemKind]
}
ResponsiveGridLayoutItemKind: {
kind: "ResponsiveGridLayoutItem"
spec: ResponsiveGridLayoutItemSpec
}
ResponsiveGridLayoutItemSpec: {
element: ElementReference
repeat?: ResponsiveGridRepeatOptions
}
TabsLayoutKind: {
kind: "TabsLayout"
spec: TabsLayoutSpec
}
TabsLayoutSpec: {
tabs: [...TabsLayoutTabKind]
}
TabsLayoutTabKind: {
kind: "TabsLayoutTab"
spec: TabsLayoutTabSpec
}
TabsLayoutTabSpec: {
title?: string
layout: GridLayoutKind | RowsLayoutKind | ResponsiveGridLayoutKind
}
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"
// 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
}
// 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
}
// 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
}
// Custom variable kind
CustomVariableKind: {
kind: "CustomVariable"
spec: CustomVariableSpec
}
// GroupBy variable specification
GroupByVariableSpec: {
name: string | *""
datasource?: DataSourceRef
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
}
// 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
// @deprecated
condition?: string
}
// Adhoc variable kind
AdhocVariableKind: {
kind: "AdhocVariable"
spec: AdhocVariableSpec
}
@@ -0,0 +1,7 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface Spec {
[key: string]: any;
}
export const defaultSpec = (): Spec => ({});
@@ -0,0 +1,7 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface Spec {
[key: string]: any;
}
export const defaultSpec = (): Spec => ({});
+5 -4
View File
@@ -56,13 +56,14 @@ require (
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
+10 -10
View File
@@ -126,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+5 -4
View File
@@ -57,13 +57,14 @@ require (
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
+10 -10
View File
@@ -126,22 +126,22 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 h1:BEj3SPM81McUZHYjRS5pEgNgnmzGJ5tRpU5krWnV8Bs=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0/go.mod h1:9cKLGBDzI/F3NoHLQGm4ZrYdIHsvGt6ej6hUowxY0J4=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
-3
View File
@@ -1534,9 +1534,6 @@ timeout = 10s
# Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled.
default_datasource_uid =
# Suffix to apply to the data source URL for remote write requests.
remote_write_path_suffix = /push
# Optional custom headers to include in recording rule write requests.
[recording_rules.custom_headers]
# exampleHeader = exampleValue
-3
View File
@@ -1516,9 +1516,6 @@ timeout = 30s
# Only has effect if the grafanaManagedRecordRulesDatasources feature toggle is enabled.
default_datasource_uid =
# Suffix to apply to the data source URL for remote write requests.
remote_write_path_suffix = /push
# Optional custom headers to include in recording rule write requests.
[recording_rules.custom_headers]
# exampleHeader = exampleValue
@@ -147,6 +147,12 @@ Your data sources, including credentials, are migrated securely and seamlessly t
The migration assistant supports any plugins found in the plugins catalog. As long as the plugin is signed or is a core plugin built into Grafana, it is eligible for migration. Due to security reasons, unsigned plugins are not supported in Grafana Cloud. If you are using any unsigned private plugins, Grafana recommends you seek an alternative plugin for the catalog or work on a strategy to deprecate certain functionality from your self-managed instance.
Upgrade any plugins you intend to migrate before using the migration assistant as any migrated plugins will be configured on the Grafana Cloud instance as the latest version of that plugin.
{{< admonition type="caution">}}
If you want to migrate Enterprise plugins, check what type of plan your Grafana Cloud instance is on and whether or not this plan requires an Enterprise plugin add-on.
{{< /admonition >}}
### Grafana Alerting resources
The migration assistant can migrate the majority of Grafana Alerting resources to your Grafana Cloud instance. These include:
@@ -94,7 +94,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
| `fixed:authentication.config:writer` | `fixed_0rYhZ2Qnzs8AdB1nX7gexk3fHDw` | `settings:read` for scope `settings:auth.saml:*` <br> `settings:write` for scope `settings:auth.saml:*` | Read and update authentication and SAML settings. |
| `fixed:dashboards:creator` | `fixed_ZorKUcEPCM01A1fPakEzGBUyU64` | `dashboards:create`<br>`folders:read` | Create dashboards. |
| `fixed:dashboards:reader` | `fixed_Sgr67JTOhjQGFlzYRahOe45TdWM` | `dashboards:read` | Read all dashboards. |
| `fixed:dashboards:writer` | `fixed_OK2YOQGIoI1G031hVzJB6rAJQAs` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:edit`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
| `fixed:dashboards:writer` | `fixed_OK2YOQGIoI1G031hVzJB6rAJQAs` | All permissions from `fixed:dashboards:reader` and <br>`dashboards:write`<br>`dashboards:delete`<br>`dashboards:create`<br>`dashboards.permissions:read`<br>`dashboards.permissions:write` | Read, create, update, and delete all dashboards. |
| `fixed:dashboards.insights:reader` | `fixed_JlBJ2_gizP8zhgaeGE2rjyZe2Rs` | `dashboards.insights:read` | Read dashboard insights data and see presence indicators. |
| `fixed:dashboards.permissions:reader` | `fixed_f17oxuXW_58LL8mYJsm4T_mCeIw` | `dashboards.permissions:read` | Read all dashboard permissions. |
| `fixed:dashboards.permissions:writer` | `fixed_CcznxhWX_Yqn8uWMXMQ-b5iFW9k` | All permissions from `fixed:dashboards.permissions:reader` and <br>`dashboards.permissions:write` | Read and update all dashboard permissions. |
@@ -197,11 +197,7 @@ However, you can export it by manually copying the content and name of the notif
All notification policies are provisioned through a single resource: the root of the notification policy tree.
{{% admonition type="warning" %}}
Since the policy tree is a single resource, provisioning it overwrites a policy tree created through any other means.
{{< /admonition >}}
{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="<GRAFANA_VERSION>" >}}
To export the notification policy tree from the Grafana UI, complete the following steps.
@@ -702,11 +702,7 @@ Create or reset the notification policy tree using provisioning files in your Gr
In Grafana, the entire notification policy tree is considered a single, large resource. Add new specific policies as sub-policies under the root policy. Since specific policies may depend on each other, you cannot provision subsets of the policy tree; the entire tree must be defined in a single place.
{{% admonition type="warning" %}}
Since the policy tree is a single resource, provisioning it will overwrite a policy tree created through any other means.
{{< /admonition >}}
{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="<GRAFANA_VERSION>" >}}
1. Find the notification policy tree in Grafana.
1. [Export](ref:export_policies) and download a provisioning file for your notification policy tree.
@@ -341,11 +341,7 @@ In this section, we'll create Terraform configurations for each alerting resourc
[Notification policies](ref:notification-policy) defines how to route alert instances to your contact points.
{{% admonition type="warning" %}}
Since the policy tree is a single resource, provisioning the `grafana_notification_policy` resource will overwrite a policy tree created through any other means.
{{< /admonition >}}
{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="<GRAFANA_VERSION>" >}}
1. Find the default notification policy tree. Alternatively, consider writing the resource in code as demonstrated in the example below.
@@ -81,8 +81,6 @@ Grafana enables you to share dashboards and panels with other users within your
- Reports
- Library panels
You can also invite new members to your organization from the **Share** menu. For more information, refer to [Invite new members](#invite-new-members).
You must have an authorized viewer permission to see an image rendered by a direct link. The same permission is also required to view embedded links unless you have anonymous access permission enabled for your Grafana instance.
{{< admonition type="note" >}}
@@ -140,7 +138,7 @@ Learn how to configure and manage externally shared dashboards in [Externally sh
### Schedule a report
{{< admonition type="note" >}}
This feature is only available in Grafana Enterprise.
This feature is only available on Grafana Enterprise.
{{< /admonition >}}
To share your dashboard as a report, follow these steps:
@@ -204,6 +202,10 @@ In addition to sharing dashboards as links, reports, and snapshots, you can expo
### Export a dashboard as PDF
{{< admonition type="note" >}}
This feature is only available on Grafana Enterprise.
{{< /admonition >}}
To export a dashboard in its current state as a PDF, follow these steps:
1. Click **Dashboards** in the main menu.
@@ -365,23 +367,3 @@ To delete existing snapshots, follow these steps:
1. Click the red **x** next to the snapshot URL that you want to delete.
The snapshot is immediately deleted. You may need to clear your browser cache or use a private or incognito browser to confirm this.
## Invite new members
{{< admonition type="note" >}}
This feature is only available on Grafana Cloud.
{{< /admonition >}}
You can invite new members to your organization using the **Share** drop-down menu. You must have the `OrgUsersAdd` permission to use this feature.
To invite a new member to your organization, follow these steps:
1. Click **Dashboards** in the main menu and open any dashboard.
1. Click the **Share** drop-down list in the top-right corner and select **Invite new member**.
The **Members** page of your Grafana Cloud Portal opens.
1. Enter the email address of the new member in the provided field.
1. Make a selection in the **Role** drop-down list.
1. (Optional) Select the **Receive billing emails** checkbox, if applicable.
1. Click **Invite**.
@@ -2753,6 +2753,10 @@ Set the default start of the week, valid values are: `saturday`, `sunday`, `mond
Set this to `false` to disable expressions and hide them in the Grafana UI. Default is `true`.
#### `sql_expression_cell_limit`
Set the maximum number of cells that can be passed to a SQL expression. Default is `100000`.
### `[geomap]`
This section controls the defaults settings for **Geomap Plugin**.
@@ -60,7 +60,6 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
| `logRowsPopoverMenu` | Enable filtering menu displayed when text of a log line is selected | Yes |
| `lokiQueryHints` | Enables query hints for Loki | Yes |
| `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | |
| `promQLScope` | In-development feature that will allow injection of labels into prometheus queries. | Yes |
| `groupToNestedTableTransformation` | Enables the group to nested table transformation | Yes |
| `newPDFRendering` | New implementation for the dashboard-to-PDF rendering | Yes |
| `tlsMemcached` | Use TLS-enabled memcached in the enterprise caching feature | Yes |
@@ -184,7 +183,7 @@ Experimental features might be changed or removed without prior notice.
| `kubernetesFeatureToggles` | Use the kubernetes API for feature toggle management in the frontend |
| `newFolderPicker` | Enables the nested folder picker without having nested folders enabled |
| `scopeApi` | In-development feature flag for the scope api using the app platform. |
| `sqlExpressions` | Enables using SQL and DuckDB functions as Expressions. |
| `sqlExpressions` | Enables SQL Expressions, which can execute SQL queries against data source results. |
| `nodeGraphDotLayout` | Changed the layout algorithm for the node graph |
| `kubernetesAggregator` | Enable grafana's embedded kube-aggregator |
| `expressionParser` | Enable new expression parser |
@@ -228,6 +227,7 @@ Experimental features might be changed or removed without prior notice.
| `newLogsPanel` | Enables the new logs panel in Explore |
| `pluginsCDNSyncLoader` | Load plugins from CDN synchronously |
| `assetSriChecks` | Enables SRI checks for Grafana JavaScript assets |
| `extraLanguages` | Enables additional languages |
## Development feature toggles
@@ -22,23 +22,23 @@ weight: 500
# Configure SAML authentication using the configuration file
{{% admonition type="note" %}}
Available in [Grafana Enterprise](../../../../introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud).
Available in [Grafana Enterprise](/docs/grafana/<GRAFANA_VERSION>/introduction/grafana-enterprise/) and [Grafana Cloud](/docs/grafana-cloud).
{{% /admonition %}}
SAML authentication integration allows your Grafana users to log in by using an external SAML 2.0 Identity Provider (IdP). To enable this, Grafana becomes a Service Provider (SP) in the authentication flow, interacting with the IdP to exchange user information.
You can configure SAML authentication in Grafana through one of the following methods:
- the Grafana configuration file
- the API (refer to [SSO Settings API](../../../../developers/http_api/sso-settings/))
- the user interface (refer to [Configure SAML authentication using the Grafana user interface](../saml-ui/))
- the Terraform provider (refer to [Terraform docs](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/sso_settings))
- The Grafana configuration file
- The API (refer to [SSO Settings API](/docs/grafana/<GRAFANA_VERSION>/developers/http_api/sso-settings/)
- The user interface (refer to [Configure SAML authentication using the Grafana user interface](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-authentication/saml-ui/)
- The Terraform provider (refer to [Terraform docs](https://registry.terraform.io/providers/grafana/grafana/<GRAFANA_VERSION>/docs/resources/sso_settings))
{{% admonition type="note" %}}
The API and Terraform support are available in Public Preview in Grafana v11.1 behind the `ssoSettingsSAML` feature toggle. You must also enable the `ssoSettingsApi` flag.
{{% /admonition %}}
All methods offer the same configuration options, but you might prefer using the Grafana configuration file or the Terraform provider if you want to keep all of Grafana's authentication settings in one place. Grafana Cloud users do not have access to Grafana configuration file, so they should configure SAML through the other methods.
All methods offer the same configuration options. However, if you want to keep all of Grafana authentication settings in one place, use the Grafana configuration file or the Terraform provider. If you are a Grafana Cloud user, you do not have access to Grafana configuration file. Instead, configure SAML through the other methods.
{{% admonition type="note" %}}
Configuration in the API takes precedence over the configuration in the Grafana configuration file. SAML settings from the API will override any SAML configuration set in the Grafana configuration file.
@@ -46,6 +46,10 @@ Configuration in the API takes precedence over the configuration in the Grafana
## Supported SAML
The following indicate what Grafana supports.
### Bindings
Grafana supports the following SAML 2.0 bindings:
- From the Service Provider (SP) to the Identity Provider (IdP):
@@ -56,12 +60,13 @@ Grafana supports the following SAML 2.0 bindings:
- From the Identity Provider (IdP) to the Service Provider (SP):
- `HTTP-POST` binding
In terms of security:
### Security
- Grafana supports signed and encrypted assertions.
- Grafana does not support signed or encrypted requests.
Grafana supports signed and encrypted assertions, and does _not_ support encrypted requests.
In terms of initiation, Grafana supports:
### Initiation
Grafana supports:
- SP-initiated requests
- IdP-initiated requests
@@ -71,7 +76,7 @@ By default, SP-initiated requests are enabled. For instructions on how to enable
{{% admonition type="note" %}}
It is possible to set up Grafana with SAML authentication using Azure AD. However, if an Azure AD user belongs to more than 150 groups, a Graph API endpoint is shared instead.
Grafana versions 11.1 and below, do not support fetching the groups from the Graph API endpoint. As a result, users with more than 150 groups will not be able to retrieve their groups. Instead, it is recommended that you use OIDC/OAuth workflows,.
Grafana versions 11.1 and below, do not support fetching the groups from the Graph API endpoint. As a result, users with more than 150 groups will not be able to retrieve their groups. Instead, it is recommended that you use OIDC/OAuth workflows.
As of Grafana 11.2, the SAML integration offers a mechanism to retrieve user groups from the Graph API.
@@ -84,16 +89,16 @@ Related links:
### Edit SAML options in the Grafana config file
1. In the `[auth.saml]` section in the Grafana configuration file, set [`enabled`](../../../configure-grafana/enterprise-configuration/#enabled) to `true`.
1. Configure the [certificate and private key](#certificate-and-private-key).
1. In the `[auth.saml]` section in the Grafana configuration file, set [`enabled`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#enabled-3) to `true`.
1. Optionally, configure the [certificate and private key](#certificate-and-private-key").
1. On the Okta application page where you have been redirected after application created, navigate to the **Sign On** tab and find **Identity Provider metadata** link in the **Settings** section.
1. Set the [`idp_metadata_url`](../../../configure-grafana/enterprise-configuration/#idp_metadata_url) to the URL obtained from the previous step. The URL should look like `https://<your-org-id>.okta.com/app/<application-id>/sso/saml/metadata`.
1. Set the [`idp_metadata_url`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#idp_metadata_url) to the URL obtained from the previous step. The URL should look like `https://<your-org-id>.okta.com/app/<application-id>/sso/saml/metadata`.
1. Set the following options to the attribute names configured at the **step 10** of the SAML integration setup. You can find this attributes on the **General** tab of the application page (**ATTRIBUTE STATEMENTS** and **GROUP ATTRIBUTE STATEMENTS** in the **SAML Settings** section).
- [`assertion_attribute_login`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_login)
- [`assertion_attribute_email`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_email)
- [`assertion_attribute_name`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_name)
- [`assertion_attribute_groups`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_groups)
1. (Optional) Set the `name` parameter in the `[auth.saml]` section in the Grafana configuration file. This parameter replaces SAML in the Grafana user interface in locations such as the sign-in button.
- [`assertion_attribute_login`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_login)
- [`assertion_attribute_email`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_email)
- [`assertion_attribute_name`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_name)
- [`assertion_attribute_groups`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_groups)
1. Optionally, set the `name` parameter in the `[auth.saml]` section in the Grafana configuration file. This parameter replaces SAML in the Grafana user interface in locations such as the sign-in button.
1. Save the configuration file and then restart the Grafana server.
When you are finished, the Grafana configuration might look like this example:
@@ -119,7 +124,7 @@ assertion_attribute_groups = Group
To use the SAML integration, in the `auth.saml` section of in the Grafana custom configuration file, set `enabled` to `true`.
Refer to [Configuration](../../../configure-grafana/) for more information about configuring Grafana.
Refer to [Configuration](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/) for more information about configuring Grafana.
## Additional configuration for HTTP-Post binding
@@ -133,9 +138,11 @@ For Grafana Cloud instances, please contact Grafana Support to update the `conte
## Certificate and private key
Commonly, the certificate and key are embedded in the [IDP metadata](#configure-the-saml-toolkit-application-endpoints) and refreshed as needed by Grafana automatically. However, if your IdP expects signed requests, you must supply a certificate and private key.
The SAML SSO standard uses asymmetric encryption to exchange information between the SP (Grafana) and the IdP. To perform such encryption, you need a public part and a private part. In this case, the X.509 certificate provides the public part, while the private key provides the private part. The private key needs to be issued in a [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) format.
Grafana supports two ways of specifying both the `certificate` and `private_key`.
If you are directly supplying the certificate and key, Grafana supports two ways of specifying both the `certificate` and `private_key`:
- Without a suffix (`certificate` or `private_key`), the configuration assumes you've supplied the base64-encoded file contents.
- With the `_path` suffix (`certificate_path` or `private_key_path`), then Grafana treats the value entered as a file path and attempts to read the file from the file system.
@@ -144,9 +151,9 @@ Grafana supports two ways of specifying both the `certificate` and `private_key`
You can only use one form of each configuration option. Using multiple forms, such as both `certificate` and `certificate_path`, results in an error.
{{% /admonition %}}
---
Always work with your company's security team on setting up certificates and private keys. If you need to generate them yourself (such as in the short term, for testing purposes, and so on), use the following example to generate your certificate and private key, including the step of ensuring that the key is generated with the [PKCS#8](https://en.wikipedia.org/wiki/PKCS_8) format.
### Generate private key for SAML authentication:
### Example of private key generation for SAML authentication
An example of how to generate a self-signed certificate and private key that's valid for one year:
@@ -154,7 +161,15 @@ An example of how to generate a self-signed certificate and private key that's v
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes​
```
The generated `key.pem` and `cert.pem` files are then used for certificate and private_key.
Base64-encode the cert.pem and key.pem files:
(-w0 switch is not needed on Mac, only for Linux)
```sh
$ base64 -i key.pem -o key.pem.base64
$ base64 -i cert.pem -o cert.pem.base64
```
The base64-encoded values (`key.pem.base64, cert.pem.base64` files) are then used for certificate and private key.
The key you provide should look like:
@@ -169,16 +184,15 @@ The key you provide should look like:
Grafana supports user authentication through Azure AD, which is useful when you want users to access Grafana using single sign-on. This topic shows you how to configure SAML authentication in Grafana with [Azure AD](https://azure.microsoft.com/en-us/services/active-directory/).
**Before you begin:**
**Before you begin**
- Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana.
- [Roles and permissions](../../../../administration/roles-and-permissions/).
- Learn the limitations of Azure AD SAML integration.
- [Azure AD SAML limitations](https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#groups-overage-claim)
- Configure SAML integration with Azure AD, create an app integration inside the Azure AD organization first.
- [Add app integration in Azure AD](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/add-application-portal-configure)
- If you have users that belong to more than 150 groups, you need to configure a registered application to provide an Azure Graph API to retrieve the groups.
- [Setup Azure AD Graph API applications](#set-up-saml-with-azure-ad)
Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana, refer to [Roles and permissions](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/).
Learn the [limitations of Azure AD SAML] (https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference#groups-overage-claim) integration.
Configure SAML integration with Azure AD, [creating an Enterprise Application](#add-microsoft-entra-saml-toolkit-from-the-gallery) inside the Azure AD organization first and then [enable single sign-on](#configure-the-saml-toolkit-application-endpoints).
If you have users that belong to more than 150 groups, configure a registered application to provide an Azure Graph API to retrieve the groups. Refer to [Setup Azure AD Graph API applications](#configure-a-graph-api-application-in-azure-ad).
### Generate self-signed certificates
@@ -277,7 +291,7 @@ Grafana supports user authentication through Okta, which is useful when you want
**Before you begin:**
- To configure SAML integration with Okta, create an app integration inside the Okta organization first. [Add app integration in Okta](https://help.okta.com/en/prod/Content/Topics/Apps/apps-overview-add-apps.htm)
- Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana, refer to [Roles and permissions](../../../../administration/roles-and-permissions/).
- Ensure you have permission to administer SAML authentication. For more information about roles and permissions in Grafana, refer to [Roles and permissions](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/).
**To set up SAML with Okta:**
@@ -391,7 +405,7 @@ Additionally, Grafana did not support IdP sessions and could not include the `Se
Starting from Grafana version 11.5, Grafana uses the `NameID` from the SAML assertion to create the logout request. If the `NameID` is not present in the assertion, Grafana defaults to using the user's `Login` attribute. Additionally, Grafana supports including the `SessionIndex` in the logout request if it is provided in the SAML assertion by the IdP.
{{% admonition type="note" %}}
These improvements are available in public preview behind the `improvedExternalSessionHandling` feature toggle, starting from Grafana v11.5. To enable it, refer to the [Configure feature toggles](../../../configure-grafana/feature-toggles/)
These improvements are available in public preview behind the `improvedExternalSessionHandlingSAML` feature toggle, starting from Grafana v11.5. To enable it, refer to the [Configure feature toggles](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/feature-toggles/)
{{% /admonition %}}
### Assertion mapping
@@ -435,11 +449,11 @@ auto_login = true
Group synchronization allows you to map user groups from an identity provider to Grafana teams and roles.
To use SAML group synchronization, set [`assertion_attribute_groups`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_groups) to the attribute name where you store user groups.
To use SAML group synchronization, set [`assertion_attribute_groups`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_groups) to the attribute name where you store user groups.
Then Grafana will use attribute values extracted from SAML assertion to add user to Grafana teams and grant them roles.
{{% admonition type="note" %}}
Team sync allows you sync users from SAML to Grafana teams. It does not automatically create teams in Grafana. You need to create teams in Grafana before you can use this feature.
Team sync allows you sync users from SAML to Grafana teams, but you must create teams in Grafana before you can use this feature. It does not automatically create teams in Grafana.
{{% /admonition %}}
Given the following partial SAML assertion:
@@ -474,22 +488,22 @@ The following `External Group ID`s would be valid for configuring team sync or r
- `admins_group`
- `division_1`
To learn more about how to configure group synchronization, refer to [Configure team sync](../../configure-team-sync/) and [Configure group attribute sync](https://grafana.com/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-group-attribute-sync) documentation.
To learn more about how to configure group synchronization, refer to [Configure team sync](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-group-attribute-sync/) and [Configure group attribute sync](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-security/configure-group-attribute-sync) documentation.
### Configure role sync
Role sync allows you to map user roles from an identity provider to Grafana. To enable role sync, configure role attribute and possible values for the Editor, Admin, and Grafana Admin roles. For more information about user roles, refer to [Roles and permissions](../../../../administration/roles-and-permissions/).
Role sync allows you to map user roles from an identity provider to Grafana. To enable role sync, configure role attribute and possible values for the Editor, Admin, and Grafana Admin roles. For more information about user roles, refer to [Roles and permissions](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/).
1. In the configuration file, set [`assertion_attribute_role`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_role) option to the attribute name where the role information will be extracted from.
1. Set the [`role_values_none`](../../../configure-grafana/enterprise-configuration/#role_values_none) option to the values mapped to the `None` role.
1. Set the [`role_values_viewer`](../../../configure-grafana/enterprise-configuration/#role_values_viewer) option to the values mapped to the `Viewer` role.
1. Set the [`role_values_editor`](../../../configure-grafana/enterprise-configuration/#role_values_editor) option to the values mapped to the `Editor` role.
1. Set the [`role_values_admin`](../../../configure-grafana/enterprise-configuration/#role_values_admin) option to the values mapped to the organization `Admin` role.
1. Set the [`role_values_grafana_admin`](../../../configure-grafana/enterprise-configuration/#role_values_grafana_admin) option to the values mapped to the `Grafana Admin` role.
1. In the configuration file, set [`assertion_attribute_role`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_role) option to the attribute name where the role information will be extracted from.
1. Set the [`role_values_none`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_none) option to the values mapped to the `None` role.
1. Set the [`role_values_viewer`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_viewer) option to the values mapped to the `Viewer` role.
1. Set the [`role_values_editor`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_editor) option to the values mapped to the `Editor` role.
1. Set the [`role_values_admin`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_admin) option to the values mapped to the organization `Admin` role.
1. Set the [`role_values_grafana_admin`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#role_values_grafana_admin) option to the values mapped to the `Grafana Admin` role.
If a user role doesn't match any of configured values, then the role specified by the `auto_assign_org_role` config option will be assigned. If the `auto_assign_org_role` field is not set then the user role will default to `Viewer`.
For more information about roles and permissions in Grafana, refer to [Roles and permissions](../../../../administration/roles-and-permissions/).
For more information about roles and permissions in Grafana, refer to [Roles and permissions](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/).
Example configuration:
@@ -518,8 +532,8 @@ skip_org_role_sync = true
Organization mapping allows you to assign users to particular organization in Grafana depending on attribute value obtained from identity provider.
1. In configuration file, set [`assertion_attribute_org`](../../../configure-grafana/enterprise-configuration/#assertion_attribute_org) to the attribute name you store organization info in. This attribute can be an array if you want a user to be in multiple organizations.
1. Set [`org_mapping`](../../../configure-grafana/enterprise-configuration/#org_mapping) option to the comma-separated list of `Organization:OrgId` pairs to map organization from IdP to Grafana organization specified by ID. If you want users to have different roles in multiple organizations, you can set this option to a comma-separated list of `Organization:OrgId:Role` mappings.
1. In configuration file, set [`assertion_attribute_org`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#assertion_attribute_org) to the attribute name you store organization info in. This attribute can be an array if you want a user to be in multiple organizations.
1. Set [`org_mapping`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#org_mapping) option to the comma-separated list of `Organization:OrgId` pairs to map organization from IdP to Grafana organization specified by ID. If you want users to have different roles in multiple organizations, you can set this option to a comma-separated list of `Organization:OrgId:Role` mappings.
For example, use following configuration to assign users from `Engineering` organization to the Grafana organization with ID `2` as Editor and users from `Sales` - to the org with ID `3` as Admin, based on `Org` assertion attribute value:
@@ -566,7 +580,7 @@ You can use `*` as the Grafana organization in the mapping if you want all users
### Configure allowed organizations
With the [`allowed_organizations`](../../../configure-grafana/enterprise-configuration/#allowed_organizations) option you can specify a list of organizations where the user must be a member of at least one of them to be able to log in to Grafana.
With the [`allowed_organizations`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/enterprise-configuration/#allowed_organizations) option you can specify a list of organizations where the user must be a member of at least one of them to be able to log in to Grafana.
To put values containing spaces in the list, use the following JSON syntax:
@@ -632,11 +646,11 @@ resource "grafana_sso_settings" "saml_sso_settings" {
}
```
Go to [Terraform Registry](https://registry.terraform.io/providers/grafana/grafana/latest/docs/resources/sso_settings) for a complete reference on using the `grafana_sso_settings` resource.
Go to [Terraform Registry](https://registry.terraform.io/providers/grafana/grafana/<GRAFANA_VERSION>/docs/resources/sso_settings) for a complete reference on using the `grafana_sso_settings` resource.
## Troubleshoot SAML authentication in Grafana
To troubleshoot and get more log information, enable SAML debug logging in the configuration file. Refer to [Configuration](../../../configure-grafana/#filters) for more information.
To troubleshoot and get more log information, enable SAML debug logging in the configuration file. Refer to [Configuration](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#filters) for more information.
```ini
[log]
@@ -692,7 +706,7 @@ The keys you provide should look like:
When the user logs in using SAML and gets presented with "origin not allowed", the user might be issuing the login from an IdP (identity provider) service or the user is behind a reverse proxy. This potentially happens as Grafana's CSRF checks deem the requests to be invalid. For more information [CSRF](https://owasp.org/www-community/attacks/csrf).
To solve this issue, you can configure either the [`csrf_trusted_origins`](../../../configure-grafana/#csrf_trusted_origins) or [`csrf_additional_headers`](../../../configure-grafana/#csrf_additional_headers) option in the SAML configuration.
To solve this issue, you can configure either the [`csrf_trusted_origins`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#csrf_trusted_origins) or [`csrf_additional_headers`](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#csrf_additional_headers) option in the SAML configuration.
Example of a configuration file:
@@ -735,7 +749,7 @@ Ensure cookie_secure is set to true to ensure that cookies are only sent over HT
## Configure SAML authentication in Grafana
The table below describes all SAML configuration options. Continue reading below for details on specific options. Like any other Grafana configuration, you can apply these options as [environment variables](../../../configure-grafana/#override-configuration-with-environment-variables).
The table below describes all SAML configuration options. Continue reading below for details on specific options. Like any other Grafana configuration, you can apply these options as [environment variables](/docs/grafana/<GRAFANA_VERSION>/setup-grafana/configure-grafana/#override-configuration-with-environment-variables).
| Setting | Required | Description | Default |
| ---------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------- |
@@ -150,7 +150,7 @@ To install plugins in the Docker container, complete the following steps:
```bash
docker run -d -p 3000:3000 --name=grafana \
-e "GF_PLUGINS_PREINSTALL=grafana-clock-panel 1.0.1" \
-e "GF_PLUGINS_PREINSTALL=grafana-clock-panel@1.0.1" \
grafana/grafana-enterprise
```
@@ -1386,6 +1386,8 @@ Status: Conflict
### <span id="route-put-policy-tree"></span> Sets the notification policy tree. (_RoutePutPolicyTree_)
{{< docs/shared lookup="alerts/warning-provisioning-tree.md" source="grafana" version="<GRAFANA_VERSION>" >}}
```
PUT /api/v1/provisioning/policies
```
@@ -0,0 +1,9 @@
---
title: 'Warning Provisioning Tree'
---
{{% admonition type="warning" %}}
Since the policy tree is a single resource, provisioning it will overwrite all policies in the notification policy tree. However, it does not affect internal policies created when alert rules directly select a contact point.
{{< /admonition >}}
+10 -10
View File
@@ -76,7 +76,7 @@ require (
github.com/googleapis/go-sql-spanner v1.11.1 // @grafana/grafana-search-and-storage
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20250307175047-1d263576d356 // @grafana/alerting-backend
github.com/grafana/alerting v0.0.0-20250310104713-16b885f1c79e // @grafana/alerting-backend
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 // @grafana/identity-access-team
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
@@ -92,7 +92,7 @@ require (
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 // @grafana/grafana-backend-group
github.com/grafana/grafana-plugin-sdk-go v0.267.0 // @grafana/plugins-platform-backend
github.com/grafana/grafana-plugin-sdk-go v0.270.0 // @grafana/plugins-platform-backend
github.com/grafana/loki/v3 v3.2.1 // @grafana/observability-logs
github.com/grafana/otel-profiling-go v0.5.1 // @grafana/grafana-backend-group
github.com/grafana/pyroscope-go/godeltaprof v0.1.8 // @grafana/observability-traces-and-profiling
@@ -160,15 +160,15 @@ require (
github.com/yudai/gojsondiff v1.0.0 // @grafana/grafana-backend-group
go.opentelemetry.io/collector/pdata v1.22.0 // @grafana/grafana-backend-group
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // @grafana/plugins-platform-backend
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 // @grafana/grafana-operator-experience-squad
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // @grafana/grafana-operator-experience-squad
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel v1.35.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/sdk v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/trace v1.34.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/sdk v1.35.0 // @grafana/grafana-backend-group
go.opentelemetry.io/otel/trace v1.35.0 // @grafana/grafana-backend-group
go.uber.org/atomic v1.11.0 // @grafana/alerting-backend
go.uber.org/goleak v1.3.0 // @grafana/grafana-search-and-storage
go.uber.org/zap v1.27.0 // @grafana/identity-access-team
@@ -335,7 +335,7 @@ require (
github.com/dolthub/maphash v0.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/edsrzf/mmap-go v1.2.0 // indirect
github.com/elazarl/goproxy v1.7.1 // indirect
github.com/elazarl/goproxy v1.7.2 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emicklei/proto v1.13.2 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
@@ -523,10 +523,10 @@ require (
go.opencensus.io v0.24.0 // @grafana/grafana-backend-group
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // @grafana/sharing-squad
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // @grafana/sharing-squad
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
+20 -20
View File
@@ -1108,8 +1108,8 @@ github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E
github.com/efficientgo/core v1.0.0-rc.3 h1:X6CdgycYWDcbYiJr1H1+lQGzx13o7bq3EUkbB9DsSPc=
github.com/efficientgo/core v1.0.0-rc.3/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrjaL6x/GLcQ4vJps=
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elazarl/goproxy v1.7.1 h1:1P7LPSxbqtNxusFnXclj6O56pjfq1xOQZ6a0mwwKUlY=
github.com/elazarl/goproxy v1.7.1/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
@@ -1559,8 +1559,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/alerting v0.0.0-20250307175047-1d263576d356 h1:71o8Bxw/wg+aBRUASiGux67gDEUC2bqq3I+x2hw8eIc=
github.com/grafana/alerting v0.0.0-20250307175047-1d263576d356/go.mod h1:hdGB3dSl8Ma9Rjo2YiAEAjMkZ5HiNJbNDqRKDefRZrM=
github.com/grafana/alerting v0.0.0-20250310104713-16b885f1c79e h1:noJzp/qZGIto4XdZkvj2EKQ1bQeqCRs0bedxdJN17sQ=
github.com/grafana/alerting v0.0.0-20250310104713-16b885f1c79e/go.mod h1:HfvjmU3UqCIpoy9Z2wgKGrZ4A5vz+yQlP9ZXvCfEkiA=
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa h1:08Wh/svkv8WpDuOBBKAzSPa14gKjYLZvQJsHWXLjPuc=
github.com/grafana/authlib v0.0.0-20250305132846-37f49eb947fa/go.mod h1:XVpdLhaeYqz414FmGnW00/0vTe1x8c0GRH3KaeRtyg0=
github.com/grafana/authlib/types v0.0.0-20250224151205-5ef97131cc82 h1:DnRUYiAotHXnrfYJCvhH1NkiyWVcPm5Pd+P7Ugqt/d8=
@@ -1593,8 +1593,8 @@ github.com/grafana/grafana-google-sdk-go v0.2.1 h1:XeFdKnkXBjOJjXc1gf4iMx4h5aCHT
github.com/grafana/grafana-google-sdk-go v0.2.1/go.mod h1:RiITSHwBhqVTTd3se3HQq5Ncs/wzzhTB9OK5N0J0PEU=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79 h1:r+mU5bGMzcXCRVAuOrTn54S80qbfVkvTdUJZfSfTNbs=
github.com/grafana/grafana-openapi-client-go v0.0.0-20231213163343-bd475d63fb79/go.mod h1:wc6Hbh3K2TgCUSfBC/BOzabItujtHMESZeFk5ZhdxhQ=
github.com/grafana/grafana-plugin-sdk-go v0.267.0 h1:4AcuLEE0UeFG0Eo3P8r1FDS89hgZZ73K550DRz1fgjQ=
github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q=
github.com/grafana/grafana-plugin-sdk-go v0.270.0 h1:SST2Vo//ITQG5h401gC4jTkJpTHy8OV91sf2uDh6M/8=
github.com/grafana/grafana-plugin-sdk-go v0.270.0/go.mod h1:Z0Cvkj4WGadz6F6FQmK4NGb/hD18kTTNkSqFb97Rfyc=
github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8 h1:mG/6nDlEBVxWlo2GQJVASzucw3ByPIBsec06XcPrjgQ=
github.com/grafana/grafana/apps/advisor v0.0.0-20250220163425-b4c4b9abbdc8/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250220163425-b4c4b9abbdc8 h1:w42GlvkmHG4nM/p1kb2nKmROVP+AHtL3qWEYMhnhCVM=
@@ -2522,11 +2522,11 @@ go.opentelemetry.io/contrib/detectors/gcp v1.33.0/go.mod h1:ZHrLmr4ikK2AwRj9QL+c
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.55.0/go.mod h1:rsg1EO8LXSs2po50PB5CeY/MSVlhghuKBgXlKnqm6ks=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 h1:iQZYNQ7WwIcYXzOPR46FQv9O0dS1PW16RjvR0TjDOe8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 h1:D3htJISCUU/wOVlKwisVKancWm+2U4h9xDEaiMkiyRE=
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0/go.mod h1:DAX1bsj+uDm2ZuOQH/RgZRx7RQZWyzV5W2WR/0UX8JA=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 h1:Xx1N6cDr8iWy1Cz6OcY7oS0ACdt/6HDYTdu4KskuC7s=
@@ -2534,8 +2534,8 @@ go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948Fy
go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
@@ -2549,19 +2549,19 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zb
go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
+1
View File
@@ -1436,6 +1436,7 @@ go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv+
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
go.opentelemetry.io/otel/sdk/metric v1.30.0/go.mod h1:waS6P3YqFNzeP01kuo/MBBYqaoBJl7efRQHOaydhy1Y=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
+4 -4
View File
@@ -223,7 +223,6 @@
"postcss-reporter": "7.1.0",
"postcss-scss": "4.0.9",
"prettier": "3.4.2",
"pseudoizer": "^0.1.0",
"react-refresh": "0.14.0",
"react-select-event": "5.5.1",
"redux-mock-store": "1.5.5",
@@ -278,8 +277,8 @@
"@grafana/prometheus": "workspace:*",
"@grafana/runtime": "workspace:*",
"@grafana/saga-icons": "workspace:*",
"@grafana/scenes": "6.2.1",
"@grafana/scenes-react": "6.2.1",
"@grafana/scenes": "6.3.1",
"@grafana/scenes-react": "6.3.1",
"@grafana/schema": "workspace:*",
"@grafana/sql": "workspace:*",
"@grafana/ui": "workspace:*",
@@ -336,6 +335,7 @@
"history": "4.10.1",
"i18next": "^24.0.0",
"i18next-browser-languagedetector": "^8.0.0",
"i18next-pseudo": "^2.2.1",
"immer": "10.1.1",
"immutable": "5.0.3",
"ix": "^7.0.0",
@@ -365,7 +365,7 @@
"ol": "7.4.0",
"ol-ext": "4.0.26",
"pluralize": "^8.0.0",
"prismjs": "1.29.0",
"prismjs": "1.30.0",
"rc-slider": "11.1.8",
"rc-tree": "5.13.0",
"re-resizable": "6.10.3",
@@ -43,9 +43,6 @@ export interface ThemeComponents {
sidemenu: {
width: number;
};
menuTabs: {
height: number;
};
horizontalDrawer: {
defaultHeight: number;
};
@@ -96,6 +93,7 @@ export function createComponents(colors: ThemeColors, shadows: ThemeShadows): Th
sidemenu: {
width: 57,
},
// @ts-expect-error (added here to not crash plugins that might use it)
menuTabs: {
height: 5,
},
@@ -61,9 +61,10 @@ const desertBloomTheme: NewThemeOptions = {
disabledBackground: 'rgba(168, 156, 134, 0.06)',
disabledOpacity: 0.38,
},
gradients: {
brandHorizontal: 'linear-gradient(270deg, #FF6F61 0%, #ece0d1 100%)',
brandVertical: 'linear-gradient(0.01deg, #FF6F61 0.01%, #ece0d1 99.99%)',
brandHorizontal: 'linear-gradient(270deg,rgba(255, 111, 97, 1) 0%, rgba(255, 167, 58, 1) 100%)',
brandVertical: 'linear-gradient(0deg, rgba(255, 111, 97, 1) 0%, rgba(255, 167, 58, 1) 100%)',
},
contrastThreshold: 3,
hoverFactor: 0.03,
@@ -284,4 +284,5 @@ export interface AuthSettings {
disableLogin?: boolean;
passwordlessEnabled?: boolean;
basicAuthStrongPasswordPolicy?: boolean;
disableSignoutMenu?: boolean;
}
@@ -258,4 +258,5 @@ export interface FeatureToggles {
alertRuleRestore?: boolean;
grafanaManagedRecordingRulesDatasources?: boolean;
inviteUserExperimental?: boolean;
extraLanguages?: boolean;
}
+1 -1
View File
@@ -64,7 +64,7 @@
"moment-timezone": "0.5.47",
"monaco-promql": "1.7.4",
"pluralize": "8.0.0",
"prismjs": "1.29.0",
"prismjs": "1.30.0",
"react-highlight-words": "0.21.0",
"react-use": "17.6.0",
"react-window": "1.8.11",
@@ -0,0 +1,49 @@
/*
* This file was generated by grafana-app-sdk. DO NOT EDIT.
*/
import { Spec } from './types.spec.gen';
import { Status } from './types.status.gen';
export interface Metadata {
name: string;
namespace: string;
generateName?: string;
selfLink?: string;
uid?: string;
resourceVersion?: string;
generation?: number;
creationTimestamp?: string;
deletionTimestamp?: string;
deletionGracePeriodSeconds?: number;
labels?: Record<string, string>;
annotations?: Record<string, string>;
ownerReferences?: OwnerReference[];
finalizers?: string[];
managedFields?: ManagedFieldsEntry[];
}
export interface OwnerReference {
apiVersion: string;
kind: string;
name: string;
uid: string;
controller?: boolean;
blockOwnerDeletion?: boolean;
}
export interface ManagedFieldsEntry {
manager?: string;
operation?: string;
apiVersion?: string;
time?: string;
fieldsType?: string;
subresource?: string;
}
export interface Dashboard {
kind: string;
apiVersion: string;
metadata: Metadata;
spec: Spec;
status: Status;
}
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// 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.
export interface Metadata {
updateTimestamp: string;
createdBy: string;
uid: string;
creationTimestamp: string;
deletionTimestamp?: string;
finalizers: string[];
resourceVersion: string;
generation: number;
updatedBy: string;
labels: Record<string, string>;
}
export const defaultMetadata = (): Metadata => ({
updateTimestamp: "",
createdBy: "",
uid: "",
creationTimestamp: "",
finalizers: [],
resourceVersion: "",
generation: 0,
updatedBy: "",
labels: {},
});
@@ -0,0 +1,7 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface Spec {
[key: string]: any;
}
export const defaultSpec = (): Spec => ({});
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// ConversionStatus is the status of the conversion of the dashboard.
export interface ConversionStatus {
// 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: boolean;
// The version which was stored when the dashboard was created / updated.
// Fetching this version should always succeed.
storedVersion: string;
// The error message from the conversion.
// Empty if the conversion has not failed.
error: string;
}
export const defaultConversionStatus = (): ConversionStatus => ({
failed: false,
storedVersion: "",
error: "",
});
export interface Status {
// Optional conversion status.
conversion?: ConversionStatus;
}
export const defaultStatus = (): Status => ({
});
@@ -0,0 +1,49 @@
/*
* This file was generated by grafana-app-sdk. DO NOT EDIT.
*/
import { Spec } from './types.spec.gen';
import { Status } from './types.status.gen';
export interface Metadata {
name: string;
namespace: string;
generateName?: string;
selfLink?: string;
uid?: string;
resourceVersion?: string;
generation?: number;
creationTimestamp?: string;
deletionTimestamp?: string;
deletionGracePeriodSeconds?: number;
labels?: Record<string, string>;
annotations?: Record<string, string>;
ownerReferences?: OwnerReference[];
finalizers?: string[];
managedFields?: ManagedFieldsEntry[];
}
export interface OwnerReference {
apiVersion: string;
kind: string;
name: string;
uid: string;
controller?: boolean;
blockOwnerDeletion?: boolean;
}
export interface ManagedFieldsEntry {
manager?: string;
operation?: string;
apiVersion?: string;
time?: string;
fieldsType?: string;
subresource?: string;
}
export interface Dashboard {
kind: string;
apiVersion: string;
metadata: Metadata;
spec: Spec;
status: Status;
}
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// 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.
export interface Metadata {
updateTimestamp: string;
createdBy: string;
uid: string;
creationTimestamp: string;
deletionTimestamp?: string;
finalizers: string[];
resourceVersion: string;
generation: number;
updatedBy: string;
labels: Record<string, string>;
}
export const defaultMetadata = (): Metadata => ({
updateTimestamp: "",
createdBy: "",
uid: "",
creationTimestamp: "",
finalizers: [],
resourceVersion: "",
generation: 0,
updatedBy: "",
labels: {},
});
@@ -0,0 +1,7 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
export interface Spec {
[key: string]: any;
}
export const defaultSpec = (): Spec => ({});
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// ConversionStatus is the status of the conversion of the dashboard.
export interface ConversionStatus {
// 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: boolean;
// The version which was stored when the dashboard was created / updated.
// Fetching this version should always succeed.
storedVersion: string;
// The error message from the conversion.
// Empty if the conversion has not failed.
error: string;
}
export const defaultConversionStatus = (): ConversionStatus => ({
failed: false,
storedVersion: "",
error: "",
});
export interface Status {
// Optional conversion status.
conversion?: ConversionStatus;
}
export const defaultStatus = (): Status => ({
});
@@ -0,0 +1,49 @@
/*
* This file was generated by grafana-app-sdk. DO NOT EDIT.
*/
import { Spec } from './types.spec.gen';
import { Status } from './types.status.gen';
export interface Metadata {
name: string;
namespace: string;
generateName?: string;
selfLink?: string;
uid?: string;
resourceVersion?: string;
generation?: number;
creationTimestamp?: string;
deletionTimestamp?: string;
deletionGracePeriodSeconds?: number;
labels?: Record<string, string>;
annotations?: Record<string, string>;
ownerReferences?: OwnerReference[];
finalizers?: string[];
managedFields?: ManagedFieldsEntry[];
}
export interface OwnerReference {
apiVersion: string;
kind: string;
name: string;
uid: string;
controller?: boolean;
blockOwnerDeletion?: boolean;
}
export interface ManagedFieldsEntry {
manager?: string;
operation?: string;
apiVersion?: string;
time?: string;
fieldsType?: string;
subresource?: string;
}
export interface Dashboard {
kind: string;
apiVersion: string;
metadata: Metadata;
spec: Spec;
status: Status;
}
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// 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.
export interface Metadata {
updateTimestamp: string;
createdBy: string;
uid: string;
creationTimestamp: string;
deletionTimestamp?: string;
finalizers: string[];
resourceVersion: string;
generation: number;
updatedBy: string;
labels: Record<string, string>;
}
export const defaultMetadata = (): Metadata => ({
updateTimestamp: "",
createdBy: "",
uid: "",
creationTimestamp: "",
finalizers: [],
resourceVersion: "",
generation: 0,
updatedBy: "",
labels: {},
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
// ConversionStatus is the status of the conversion of the dashboard.
export interface ConversionStatus {
// 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: boolean;
// The version which was stored when the dashboard was created / updated.
// Fetching this version should always succeed.
storedVersion: string;
// The error message from the conversion.
// Empty if the conversion has not failed.
error: string;
}
export const defaultConversionStatus = (): ConversionStatus => ({
failed: false,
storedVersion: "",
error: "",
});
export interface Status {
// Optional conversion status.
conversion?: ConversionStatus;
}
export const defaultStatus = (): Status => ({
});
+1 -1
View File
@@ -82,7 +82,7 @@
"moment": "2.30.1",
"monaco-editor": "0.34.1",
"ol": "7.4.0",
"prismjs": "1.29.0",
"prismjs": "1.30.0",
"rc-cascader": "3.33.0",
"rc-drawer": "7.2.0",
"rc-picker": "4.9.2",
@@ -1,10 +1,10 @@
import { StoryFn, Meta } from '@storybook/react';
import { useState } from 'react';
import { Combobox } from '../Combobox/Combobox';
import { Checkbox } from '../Forms/Checkbox';
import { RadioButtonGroup } from '../Forms/RadioButtonGroup/RadioButtonGroup';
import { Input } from '../Input/Input';
import { Select } from '../Select/Select';
import { Switch } from '../Switch/Switch';
import { TextArea } from '../TextArea/TextArea';
@@ -97,11 +97,12 @@ Basic.args = {
};
export const AllComponents: StoryFn = (args) => {
const [selected, setSelected] = useState('');
const [checkBoxTest, setCheckBoxTest] = useState(false);
const [textAreaValue, setTextAreaValue] = useState('');
const [inputTextValue, setInputTextValue] = useState('');
const [switchTest, setSwitchTest] = useState(false);
const [comoboboxValue, setComboboxValue] = useState('');
const [radioButtonValue, setRadioButtonValue] = useState('');
const [checkBoxValue, setCheckBoxValue] = useState(false);
const [textAreaValue, setTextAreaValue] = useState('');
const [switchValue, setSwitchValue] = useState(false);
return (
<div>
@@ -117,13 +118,14 @@ export const AllComponents: StoryFn = (args) => {
/>
)}
</AutoSaveField>
<AutoSaveField onFinishChange={args.inputSuccessful ? getSuccess : getError} label="Select as child" {...args}>
<AutoSaveField onFinishChange={args.inputSuccessful ? getSuccess : getError} label="Combobox as child" {...args}>
{(onChange) => (
<Select
<Combobox
options={themeOptions}
value={args.weekPickerValue}
value={comoboboxValue}
onChange={(v) => {
onChange(v.value);
setComboboxValue(v.value);
}}
/>
)}
@@ -136,9 +138,9 @@ export const AllComponents: StoryFn = (args) => {
{(onChange) => (
<RadioButtonGroup
options={themeOptions}
value={selected}
value={radioButtonValue}
onChange={(themeOption) => {
setSelected(themeOption);
setRadioButtonValue(themeOption);
onChange(themeOption);
}}
/>
@@ -154,11 +156,11 @@ export const AllComponents: StoryFn = (args) => {
label="Checkbox test"
description="This is a checkbox input"
name="checkbox-test"
value={checkBoxTest}
value={checkBoxValue}
onChange={(e) => {
const value = e.currentTarget.checked;
onChange(value);
setCheckBoxTest(value);
setCheckBoxValue(value);
}}
/>
)}
@@ -188,10 +190,10 @@ export const AllComponents: StoryFn = (args) => {
<Switch
label="Switch test"
name="switch-test"
value={switchTest}
value={switchValue}
onChange={(e) => {
onChange(e.currentTarget.checked);
setSwitchTest(e.currentTarget.checked);
setSwitchValue(e.currentTarget.checked);
}}
/>
)}
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import { Combobox } from '../Combobox/Combobox';
import { Input } from '../Input/Input';
import { Select } from '../Select/Select';
import { Field } from './Field';
@@ -27,9 +27,13 @@ describe('Field', () => {
});
it('renders with the inputId of its children', () => {
const comboboxOptions = [
{ label: 'Option 1', value: 'option-1' },
{ label: 'Option 2', value: 'option-2' },
];
render(
<Field label="My other label">
<Select inputId="my-select-input" onChange={() => {}} />
<Combobox id="my-select-input" options={comboboxOptions} onChange={() => {}} />
</Field>
);
@@ -1,8 +1,8 @@
import { action } from '@storybook/addon-actions';
import { Meta, StoryFn } from '@storybook/react';
import { useState } from 'react';
import { Combobox } from '../Combobox/Combobox';
import { Input } from '../Input/Input';
import { Select } from '../Select/Select';
import { InlineField } from './InlineField';
import mdx from './InlineField.mdx';
@@ -75,24 +75,22 @@ grow.args = {
grow: true,
};
export const withSelect: StoryFn<typeof InlineField> = (args) => {
export const withCombobox: StoryFn<typeof InlineField> = (args) => {
const comboboxOptions = [
{ value: 1, label: 'One' },
{ value: 2, label: 'Two' },
];
const [selected, setSelected] = useState(1);
return (
<InlineField {...args}>
<Select
width={16}
onChange={action('item selected')}
options={[
{ value: 1, label: 'One' },
{ value: 2, label: 'Two' },
]}
/>
<Combobox width={16} onChange={(v) => setSelected(v.value)} options={comboboxOptions} value={selected} />
</InlineField>
);
};
withSelect.args = {
withCombobox.args = {
...basic.args,
label: 'Select option',
label: 'Combobox option',
};
export const multiple: StoryFn<typeof InlineField> = () => {
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import { Combobox } from '../Combobox/Combobox';
import { Input } from '../Input/Input';
import { Select } from '../Select/Select';
import { InlineField } from './InlineField';
@@ -27,9 +27,13 @@ describe('InlineField', () => {
});
it('renders with the inputId of its children', () => {
const comboboxOptions = [
{ label: 'Option 1', value: '1' },
{ label: 'Option 2', value: '2' },
];
render(
<InlineField label="My other label">
<Select inputId="my-select-input" onChange={() => {}} />
<Combobox id="my-select-input" options={comboboxOptions} onChange={() => {}} />
</InlineField>
);
@@ -5,10 +5,11 @@ import { GrafanaTheme2 } from '@grafana/data';
import { StringSelector, selectors } from '@grafana/e2e-selectors';
import { useStyles2 } from '../../../themes';
import { getFocusStyles, getMouseFocusStyles } from '../../../themes/mixins';
import { getFocusStyles, getInternalRadius, getMouseFocusStyles } from '../../../themes/mixins';
import { Tooltip } from '../../Tooltip/Tooltip';
import { getPropertiesForButtonSize } from '../commonStyles';
export const RADIO_GROUP_PADDING = 2;
export type RadioButtonSize = 'sm' | 'md';
export interface RadioButtonProps {
@@ -130,7 +131,7 @@ const getRadioButtonStyles = (theme: GrafanaTheme2, size: RadioButtonSize, fullW
lineHeight: `${labelHeight}px`,
color: textColor,
padding: theme.spacing(0, padding),
borderRadius: theme.shape.radius.default,
borderRadius: getInternalRadius(theme, RADIO_GROUP_PADDING),
background: theme.colors.background.primary,
cursor: 'pointer',
userSelect: 'none',
@@ -7,8 +7,7 @@ import { GrafanaTheme2, SelectableValue, toIconName } from '@grafana/data';
import { useStyles2 } from '../../../themes';
import { Icon } from '../../Icon/Icon';
import { RadioButtonSize, RadioButton } from './RadioButton';
import { RadioButtonSize, RadioButton, RADIO_GROUP_PADDING } from './RadioButton';
export interface RadioButtonGroupProps<T> {
value?: T;
id?: string;
@@ -119,7 +118,7 @@ const getStyles = (theme: GrafanaTheme2) => {
flexWrap: 'nowrap',
border: `1px solid ${theme.components.input.borderColor}`,
borderRadius: theme.shape.radius.default,
padding: '2px',
padding: RADIO_GROUP_PADDING,
'&:hover': {
borderColor: theme.components.input.borderHover,
},
@@ -6,8 +6,9 @@ import { SelectableValue, GrafanaTheme2 } from '@grafana/data';
import { IconButton } from '../../components/IconButton/IconButton';
import { TabsBar, Tab, TabContent } from '../../components/Tabs';
import { useStyles2, useTheme2 } from '../../themes';
import { useStyles2 } from '../../themes';
import { IconName } from '../../types/icon';
import { Box } from '../Layout/Box/Box';
import { ScrollContainer } from '../ScrollContainer/ScrollContainer';
export interface TabConfig {
@@ -28,14 +29,11 @@ export interface TabbedContainerProps {
export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose, testId }: TabbedContainerProps) {
const [activeTab, setActiveTab] = useState(tabs.some((tab) => tab.value === defaultTab) ? defaultTab : tabs[0].value);
const styles = useStyles2(getStyles);
const theme = useTheme2();
const onSelectTab = (item: SelectableValue<string>) => {
setActiveTab(item.value!);
};
const autoHeight = `calc(100% - (${theme.spacing(theme.components.menuTabs.height)} + ${theme.spacing(1)}))`;
return (
<div className={styles.container} data-testid={testId}>
<TabsBar className={styles.tabs}>
@@ -48,9 +46,11 @@ export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose, t
icon={t.icon}
/>
))}
<IconButton className={styles.close} onClick={onClose} name="times" tooltip={closeIconTooltip ?? 'Close'} />
<Box grow={1} display="flex" justifyContent="flex-end" paddingRight={1}>
<IconButton size="lg" onClick={onClose} name="times" tooltip={closeIconTooltip ?? 'Close'} />
</Box>
</TabsBar>
<ScrollContainer height={autoHeight}>
<ScrollContainer>
<TabContent className={styles.tabContent}>{tabs.find((t) => t.value === activeTab)?.content}</TabContent>
</ScrollContainer>
</div>
@@ -60,21 +60,17 @@ export function TabbedContainer({ tabs, defaultTab, closeIconTooltip, onClose, t
const getStyles = (theme: GrafanaTheme2) => ({
container: css({
height: '100%',
display: 'flex',
flexDirection: 'column',
flex: '1 1 0',
minHeight: 0,
}),
tabContent: css({
padding: theme.spacing(2),
backgroundColor: theme.colors.background.primary,
height: `100%`,
}),
close: css({
position: 'absolute',
right: '16px',
top: '5px',
cursor: 'pointer',
fontSize: theme.typography.size.lg,
}),
tabs: css({
paddingTop: theme.spacing(1),
paddingTop: theme.spacing(0.5),
borderColor: theme.colors.border.weak,
ul: {
marginLeft: theme.spacing(2),
@@ -92,11 +92,11 @@ const getStyles = (theme: GrafanaTheme2) => {
position: 'relative',
display: 'flex',
whiteSpace: 'nowrap',
padding: theme.spacing(0.5),
padding: theme.spacing(0, 0.5),
}),
link: css({
color: theme.colors.text.secondary,
padding: theme.spacing(1, 1.5, 0.5),
padding: theme.spacing(1, 1.5, 1),
borderRadius: theme.shape.radius.default,
display: 'block',
@@ -114,7 +114,7 @@ const getStyles = (theme: GrafanaTheme2) => {
position: 'absolute',
left: 0,
right: 0,
height: '4px',
height: '2px',
borderRadius: theme.shape.radius.default,
bottom: 0,
},
@@ -36,8 +36,7 @@ const getStyles = (theme: GrafanaTheme2) => ({
tabs: css({
position: 'relative',
display: 'flex',
height: theme.spacing(theme.components.menuTabs.height),
alignItems: 'stretch',
alignItems: 'center',
}),
});
@@ -0,0 +1,46 @@
import { Meta, StoryFn } from '@storybook/react';
import { BorderRadiusContainer } from './BorderRadius';
const meta: Meta = {
title: 'Docs Overview/Theme',
component: BorderRadiusContainer,
decorators: [],
parameters: {
layout: 'centered',
},
args: {
referenceBorderRadius: 20,
referenceBorderWidth: 10,
offset: 0,
borderWidth: 2,
},
argTypes: {
offset: {
control: {
min: 0,
},
},
referenceBorderRadius: {
control: {
min: 0,
},
},
referenceBorderWidth: {
control: {
min: 0,
},
},
borderWidth: {
control: {
min: 0,
},
},
},
};
export const OffsetBorderRadius: StoryFn<typeof BorderRadiusContainer> = (args) => {
return <BorderRadiusContainer {...args} />;
};
export default meta;
@@ -0,0 +1,87 @@
import { css } from '@emotion/css';
import { GrafanaTheme2 } from '@grafana/data';
import { useStyles2 } from '../../themes';
import { getInternalRadius, getExternalRadius } from '../../themes/mixins';
import { Stack } from '../Layout/Stack/Stack';
import { Text } from '../Text/Text';
interface DemoBoxProps {
referenceBorderRadius: number;
referenceBorderWidth: number;
offset: number;
borderWidth: number;
}
export const BorderRadiusContainer = ({
referenceBorderRadius,
referenceBorderWidth,
offset,
borderWidth,
}: DemoBoxProps) => {
const styles = useStyles2(getStyles, referenceBorderRadius, referenceBorderWidth, offset, borderWidth);
return (
<Stack direction="column" alignItems="center" gap={4}>
<Stack alignItems="center">
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<Text variant="code">getInternalRadius</Text>
<div className={styles.baseForInternal}>
<div className={styles.internalContainer} />
</div>
</Stack>
<Stack alignItems="center">
{/* eslint-disable-next-line @grafana/no-untranslated-strings */}
<Text variant="code">getExternalRadius</Text>
<div className={styles.externalContainer}>
<div className={styles.baseForExternal} />
</div>
</Stack>
</Stack>
);
};
const getStyles = (
theme: GrafanaTheme2,
referenceBorderRadius: number,
referenceBorderWidth: number,
offset: number,
borderWidth: number
) => ({
baseForInternal: css({
backgroundColor: theme.colors.action.disabledBackground,
border: `${referenceBorderWidth}px dashed ${theme.colors.action.disabledText}`,
borderRadius: referenceBorderRadius,
display: 'flex',
height: '80px',
padding: offset,
width: '300px',
}),
baseForExternal: css({
backgroundColor: theme.colors.action.disabledBackground,
border: `${referenceBorderWidth}px dashed ${theme.colors.action.disabledText}`,
borderRadius: referenceBorderRadius,
height: '80px',
flex: 1,
width: '300px',
}),
internalContainer: css({
backgroundColor: theme.colors.background.primary,
border: `${borderWidth}px solid ${theme.colors.primary.main}`,
borderRadius: getInternalRadius(theme, offset, {
parentBorderRadius: referenceBorderRadius,
parentBorderWidth: referenceBorderWidth,
}),
flex: 1,
}),
externalContainer: css({
border: `${borderWidth}px solid ${theme.colors.primary.main}`,
borderRadius: getExternalRadius(theme, offset, {
childBorderRadius: referenceBorderRadius,
selfBorderWidth: borderWidth,
}),
display: 'flex',
flex: 1,
padding: offset,
}),
});
@@ -9,6 +9,7 @@ import { useTheme2 } from '../../themes/ThemeContext';
import { allButtonVariants, Button } from '../Button';
import { Card } from '../Card/Card';
import { CollapsableSection } from '../Collapse/CollapsableSection';
import { Combobox } from '../Combobox/Combobox';
import { Field } from '../Forms/Field';
import { InlineField } from '../Forms/InlineField';
import { InlineFieldRow } from '../Forms/InlineFieldRow';
@@ -17,7 +18,6 @@ import { Icon } from '../Icon/Icon';
import { Input } from '../Input/Input';
import { BackgroundColor, BorderColor, Box, BoxShadow } from '../Layout/Box/Box';
import { Stack } from '../Layout/Stack/Stack';
import { Select } from '../Select/Select';
import { Switch } from '../Switch/Switch';
import { Text, TextProps } from '../Text/Text';
@@ -150,8 +150,8 @@ export const ThemeDemo = () => {
<Field label="Input disabled" disabled>
<Input placeholder="Placeholder" value="Disabled value" />
</Field>
<Field label="Select">
<Select options={selectOptions} value={selectValue} onChange={(v) => setSelectValue(v?.value!)} />
<Field label="Combobox">
<Combobox options={selectOptions} value={selectValue} onChange={(v) => setSelectValue(v?.value!)} />
</Field>
<Field label="Radio label">
<RadioButtonGroup options={radioOptions} value={radioValue} onChange={setRadioValue} />
+52
View File
@@ -82,3 +82,55 @@ export const getTooltipContainerStyles = (theme: GrafanaTheme2) => ({
borderRadius: theme.shape.radius.default,
zIndex: theme.zIndex.tooltip,
});
interface ExternalRadiusAdditionalOptions {
selfBorderWidth?: number;
childBorderRadius?: number;
}
/**
* Calculates a border radius for an element, based on border radius of its child.
*
* @param theme
* @param offset - The distance to offset from the child element, should be >= 0.
* @param additionalOptions
* @param additionalOptions.selfBorderWidth - The border width of the element itself (default: 1)
* @param additionalOptions.childBorderRadius - The border radius of the child element (default: theme default radius)
* @returns A CSS calc() expression that returns the relative external radius value
*/
export const getExternalRadius = (
theme: GrafanaTheme2,
offset: number,
additionalOptions: ExternalRadiusAdditionalOptions = {}
) => {
const { selfBorderWidth = 1, childBorderRadius } = additionalOptions;
const childBorderRadiusPx = childBorderRadius !== undefined ? `${childBorderRadius}px` : theme.shape.radius.default;
return `calc(max(0px, ${childBorderRadiusPx} + ${offset}px + ${selfBorderWidth}px))`;
};
interface InternalRadiusAdditionalOptions {
parentBorderWidth?: number;
parentBorderRadius?: number;
}
/**
* Calculates a border radius for an element, based on border radius of its parent.
*
* @param theme
* @param offset - The distance to offset from the parent element, should be >= 0.
* @param additionalOptions
* @param additionalOptions.parentBorderWidth - The border width of the parent element (default: 1)
* @param additionalOptions.parentBorderRadius - The border radius of the parent element (default: theme default radius)
* @returns A CSS calc() expression that returns the relative internal radius value
*/
export const getInternalRadius = (
theme: GrafanaTheme2,
offset: number,
additionalOptions: InternalRadiusAdditionalOptions = {}
) => {
const { parentBorderWidth = 1, parentBorderRadius } = additionalOptions;
const parentBorderRadiusPx =
parentBorderRadius !== undefined ? `${parentBorderRadius}px` : theme.shape.radius.default;
return `calc(max(0px, ${parentBorderRadiusPx} - ${offset}px - ${parentBorderWidth}px))`;
};
+17
View File
@@ -20,6 +20,23 @@ function initI18n() {
resources: {},
returnEmptyString: false,
lng: 'en-US', // this should be the locale of the phrases in our source JSX
postProcess: [
// Add pseudo processing even if we aren't necessarily going to use it
'pseudo',
],
});
}
if (process.env.NODE_ENV === 'development') {
import('i18next-pseudo').then((module) => {
const Pseudo = module.default;
i18next.use(
new Pseudo({
languageToPseudo: 'pseudo',
enabled: true,
wrapped: true,
})
);
});
}
}
+8 -8
View File
@@ -4,12 +4,12 @@ go 1.23.7
require (
github.com/emicklei/go-restful/v3 v3.11.0
github.com/grafana/grafana-plugin-sdk-go v0.267.0
github.com/grafana/grafana-plugin-sdk-go v0.270.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435
github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel v1.35.0
k8s.io/api v0.32.1
k8s.io/apimachinery v0.32.1
k8s.io/apiserver v0.32.1
@@ -37,7 +37,7 @@ require (
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/elazarl/goproxy v1.7.1 // indirect
github.com/elazarl/goproxy v1.7.2 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -122,15 +122,15 @@ require (
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 // indirect
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
+18 -18
View File
@@ -49,8 +49,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/elazarl/goproxy v1.7.1 h1:1P7LPSxbqtNxusFnXclj6O56pjfq1xOQZ6a0mwwKUlY=
github.com/elazarl/goproxy v1.7.1/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -135,8 +135,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/grafana-plugin-sdk-go v0.267.0 h1:4AcuLEE0UeFG0Eo3P8r1FDS89hgZZ73K550DRz1fgjQ=
github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q=
github.com/grafana/grafana-plugin-sdk-go v0.270.0 h1:SST2Vo//ITQG5h401gC4jTkJpTHy8OV91sf2uDh6M/8=
github.com/grafana/grafana-plugin-sdk-go v0.270.0/go.mod h1:Z0Cvkj4WGadz6F6FQmK4NGb/hD18kTTNkSqFb97Rfyc=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435 h1:lmw60EW7JWlAEvgggktOyVkH4hF1m/+LSF/Ap0NCyi8=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20240808213237-f4d2e064f435/go.mod h1:ORVFiW/KNRY52lNjkGwnFWCxNVfE97bJG2jr2fetq0I=
github.com/grafana/grafana/pkg/semconv v0.0.0-20240808213237-f4d2e064f435 h1:SNEeqY22DrGr5E9kGF1mKSqlOom14W9+b1u4XEGJowA=
@@ -356,32 +356,32 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0 h1:iQZYNQ7WwIcYXzOPR46FQv9O0dS1PW16RjvR0TjDOe8=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.59.0/go.mod h1:54CaSNqYEXvpzDh8KPjiMVoWm60t5R0dZRt0leEPgAs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0 h1:0tY123n7CdWMem7MOVdKOt0YfshufLCwfE5Bob+hQuM=
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.60.0/go.mod h1:CosX/aS4eHnG9D7nESYpV753l4j9q5j3SL/PUYd2lR8=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0 h1:D3htJISCUU/wOVlKwisVKancWm+2U4h9xDEaiMkiyRE=
go.opentelemetry.io/contrib/propagators/jaeger v1.34.0/go.mod h1:DAX1bsj+uDm2ZuOQH/RgZRx7RQZWyzV5W2WR/0UX8JA=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0 h1:Xx1N6cDr8iWy1Cz6OcY7oS0ACdt/6HDYTdu4KskuC7s=
go.opentelemetry.io/contrib/samplers/jaegerremote v0.28.0/go.mod h1:iWS+NvC948FyfnJbVfPN9h/8+vr8CR2FPn6XsLRkvH8=
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 h1:tgJ0uaNS4c98WRNUEx5U3aDlrDOI5Rs+1Vifcw4DJ8U=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0/go.mod h1:U7HYyW0zt/a9x5J1Kjs+r1f/d4ZHnYFclhYY2+YbeoE=
go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+1
View File
@@ -34,6 +34,7 @@ type FrontendSettingsAuthDTO struct {
DisableLogin bool `json:"disableLogin"`
BasicAuthStrongPasswordPolicy bool `json:"basicAuthStrongPasswordPolicy"`
PasswordlessEnabled bool `json:"passwordlessEnabled"`
DisableSignoutMenu bool `json:"disableSignoutMenu"`
}
type FrontendSettingsBuildInfoDTO struct {
+5 -2
View File
@@ -199,8 +199,11 @@ func (hs *HTTPServer) CreateFolder(c *contextmodel.ReqContext) response.Response
return apierrors.ToFolderErrorResponse(err)
}
if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil {
hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err)
// Only set default permissions if the Folder API Server is disabled.
if !hs.Features.IsEnabledGlobally(featuremgmt.FlagKubernetesClientDashboardsFolders) {
if err := hs.setDefaultFolderPermissions(c.Req.Context(), cmd.OrgID, cmd.SignedInUser, folder); err != nil {
hs.log.Error("Could not set the default folder permissions", "folder", folder.Title, "user", cmd.SignedInUser, "error", err)
}
}
// Clear permission cache for the user who's created the folder, so that new permissions are fetched for their next call
+91
View File
@@ -677,3 +677,94 @@ func TestGetFolderLegacyAndUnifiedStorage(t *testing.T) {
}
})
}
func TestSetDefaultPermissionsWhenCreatingFolder(t *testing.T) {
folderService := &foldertest.FakeService{}
setUpRBACGuardian(t)
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
type testCase struct {
description string
expectedCallsToSetPermissions int
expectedCode int
expectedFolder *folder.Folder
permissions []accesscontrol.Permission
featuresArr []any
input string
}
tcs := []testCase{
{
description: "folder creation succeeds, via legacy storage",
expectedCallsToSetPermissions: 1,
input: folderWithoutParentInput,
expectedCode: http.StatusOK,
expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}},
},
{
description: "folder creation succeeds, via API Server",
expectedCallsToSetPermissions: 0,
input: folderWithoutParentInput,
expectedCode: http.StatusOK,
expectedFolder: &folder.Folder{UID: "uid", Title: "Folder"},
permissions: []accesscontrol.Permission{{Action: dashboards.ActionFoldersCreate}},
featuresArr: []any{featuremgmt.FlagKubernetesClientDashboardsFolders},
},
}
// we need to save these values because they are defined at `setting` package level
// and modified when we invoke setting.NewCfgFromINIFile
prevCookieSameSiteDisabled := setting.CookieSameSiteDisabled
prevCookieSameSiteMode := setting.CookieSameSiteMode
cfg := setting.NewCfg()
cfg.Raw.Section("rbac").Key("resources_with_managed_permissions_on_creation").SetValue("folder")
tmpCfg, err := setting.NewCfgFromINIFile(cfg.Raw)
require.NoError(t, err)
cfg.RBAC = tmpCfg.RBAC
// restore previous values so other tests don't break
// ex: TestHTTPServer_RotateUserAuthToken
setting.CookieSameSiteDisabled = prevCookieSameSiteDisabled
setting.CookieSameSiteMode = prevCookieSameSiteMode
for _, tc := range tcs {
t.Run(tc.description, func(t *testing.T) {
folderService.ExpectedFolder = tc.expectedFolder
folderPermService := acmock.NewMockedPermissionsService()
folderPermService.On("SetPermissions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]accesscontrol.ResourcePermission{}, nil)
srv := SetupAPITestServer(t, func(hs *HTTPServer) {
hs.Cfg = cfg
featuresArr := append(tc.featuresArr, featuremgmt.FlagNestedFolders)
hs.Features = featuremgmt.WithFeatures(
featuresArr...,
)
hs.folderService = folderService
hs.folderPermissionsService = folderPermService
hs.accesscontrolService = actest.FakeService{}
})
input := strings.NewReader(tc.input)
req := srv.NewPostRequest("/api/folders", input)
req = webtest.RequestWithSignedInUser(req, userWithPermissions(1, tc.permissions))
resp, err := srv.SendJSON(req)
require.NoError(t, err)
require.Equal(t, tc.expectedCode, resp.StatusCode)
folder := dtos.Folder{}
err = json.NewDecoder(resp.Body).Decode(&folder)
require.NoError(t, err)
require.NoError(t, resp.Body.Close())
folderPermService.AssertNumberOfCalls(t, "SetPermissions", tc.expectedCallsToSetPermissions)
if tc.expectedCode == http.StatusOK {
assert.Equal(t, "uid", folder.UID)
assert.Equal(t, "Folder", folder.Title)
}
})
}
}
+1
View File
@@ -367,6 +367,7 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
OktaSkipOrgRoleSync: parseSkipOrgRoleSyncEnabled(oauthProviders[social.OktaProviderName]),
DisableLogin: hs.Cfg.DisableLogin,
BasicAuthStrongPasswordPolicy: hs.Cfg.BasicAuthStrongPasswordPolicy,
DisableSignoutMenu: hs.Cfg.DisableSignoutMenu,
}
if hs.Cfg.PasswordlessMagicLinkAuth.Enabled && hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagPasswordlessMagicLinkAuthentication) {
+3 -4
View File
@@ -23,7 +23,6 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@@ -35,9 +34,9 @@ require (
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/sync v0.11.0 // indirect
+10 -10
View File
@@ -74,16 +74,16 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -47,79 +47,127 @@ func RegisterConversions(s *runtime.Scheme) error {
func Convert_V0_to_V1(in *dashboardV0.Dashboard, out *dashboardV1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV1.DashboardStatus{
ConversionStatus: &dashboardV1.ConversionStatus{
out.Spec.Object = in.Spec.Object
out.Status = dashboardV1.DashboardStatus{
Conversion: &dashboardV1.DashboardConversionStatus{
StoredVersion: dashboardV0.VERSION,
},
}
err := migration.Migrate(out.Spec.Object, schemaversion.LATEST_VERSION)
if err != nil {
out.Status.ConversionStatus.Failed = true
out.Status.ConversionStatus.Error = err.Error()
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 *dashboardV0.Dashboard, out *dashboardV2.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV2.DashboardStatus{
ConversionStatus: &dashboardV2.ConversionStatus{
// 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 = dashboardV2.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &dashboardV2.DashboardGridLayoutKind{
Kind: "GridLayout",
Spec: dashboardV2.DashboardGridLayoutSpec{},
},
}
out.Status = dashboardV2.DashboardStatus{
Conversion: &dashboardV2.DashboardConversionStatus{
StoredVersion: dashboardV0.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
}
return nil
}
func Convert_V1_to_V0(in *dashboardV1.Dashboard, out *dashboardV0.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV0.DashboardStatus{
ConversionStatus: &dashboardV0.ConversionStatus{
out.Spec.Object = in.Spec.Object
out.Status = dashboardV0.DashboardStatus{
Conversion: &dashboardV0.DashboardConversionStatus{
StoredVersion: dashboardV1.VERSION,
},
}
return nil
}
func Convert_V1_to_V2(in *dashboardV1.Dashboard, out *dashboardV2.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV2.DashboardStatus{
ConversionStatus: &dashboardV2.ConversionStatus{
// 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 = dashboardV2.DashboardGridLayoutKindOrRowsLayoutKindOrResponsiveGridLayoutKindOrTabsLayoutKind{
GridLayoutKind: &dashboardV2.DashboardGridLayoutKind{
Kind: "GridLayout",
Spec: dashboardV2.DashboardGridLayoutSpec{},
},
}
out.Status = dashboardV2.DashboardStatus{
Conversion: &dashboardV2.DashboardConversionStatus{
StoredVersion: dashboardV1.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
}
return nil
}
func Convert_V2_to_V0(in *dashboardV2.Dashboard, out *dashboardV0.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV0.DashboardStatus{
ConversionStatus: &dashboardV0.ConversionStatus{
// TODO: implement V2 to V0 conversion
out.Status = dashboardV0.DashboardStatus{
Conversion: &dashboardV0.DashboardConversionStatus{
StoredVersion: dashboardV2.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
}
return nil
}
func Convert_V2_to_V1(in *dashboardV2.Dashboard, out *dashboardV1.Dashboard, scope conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
out.Spec = in.Spec
out.Status = &dashboardV1.DashboardStatus{
ConversionStatus: &dashboardV1.ConversionStatus{
// TODO: implement V2 to V1 conversion
out.Status = dashboardV1.DashboardStatus{
Conversion: &dashboardV1.DashboardConversionStatus{
StoredVersion: dashboardV2.VERSION,
Failed: true,
Error: "backend conversion not yet implemented",
},
}
return nil
}
+18
View File
@@ -0,0 +1,18 @@
package v0alpha1
import "k8s.io/apimachinery/pkg/runtime/schema"
const (
// Group is the API group used by all kinds in this package
Group = "dashboard.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: Group,
Version: Version,
}
)
@@ -0,0 +1,28 @@
//
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
package v0alpha1
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{}
@@ -0,0 +1,28 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
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{}
}
@@ -0,0 +1,269 @@
//
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
package v0alpha1
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()
}
// 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)
}
}
// Interface compliance compile-time check
var _ resource.ListObject = &DashboardList{}
@@ -0,0 +1,34 @@
//
// Code generated by grafana-app-sdk. DO NOT EDIT.
//
package v0alpha1
import (
"github.com/grafana/grafana-app-sdk/resource"
)
// schema is unexported to prevent accidental overwrites
var (
schemaDashboard = resource.NewSimpleSchema("dashboard.grafana.app", "v0alpha1", &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
@@ -0,0 +1,13 @@
package v0alpha1
import (
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
// +k8s:openapi-gen=true
type DashboardSpec = common.Unstructured
// NewDashboardSpec creates a new Spec object.
func NewDashboardSpec() *DashboardSpec {
return &DashboardSpec{}
}
@@ -0,0 +1,3 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
@@ -0,0 +1,34 @@
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
package v0alpha1
// 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{}
}
+41
View File
@@ -0,0 +1,41 @@
package v0alpha1
// TODO: these should be automatically generated by the SDK.
func (in *Dashboard) DeepCopyInto(out *Dashboard) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
}
func (in *Dashboard) DeepCopy() *Dashboard {
if in == nil {
return nil
}
out := new(Dashboard)
in.DeepCopyInto(out)
return out
}
func (in *DashboardList) DeepCopyInto(out *DashboardList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Dashboard, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
func (in *DashboardList) DeepCopy() *DashboardList {
if in == nil {
return nil
}
out := new(DashboardList)
in.DeepCopyInto(out)
return out
}
+4 -1
View File
@@ -1,7 +1,10 @@
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +k8s:conversion-gen=github.com/grafana/grafana/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 v0alpha1 // import "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1"
+7
View File
@@ -6,6 +6,7 @@ import (
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 SearchResults struct {
metav1.TypeMeta `json:",inline"`
@@ -32,11 +33,13 @@ type SearchResults struct {
Facets map[string]FacetResult `json:"facets,omitempty"`
}
// +k8s:deepcopy-gen=true
type SortBy struct {
Field string `json:"field"`
Descending bool `json:"desc,omitempty"`
}
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type SortableFields struct {
metav1.TypeMeta `json:",inline"`
@@ -45,12 +48,14 @@ type SortableFields struct {
Fields []SortableField `json:"fields"`
}
// +k8s:deepcopy-gen=true
type SortableField struct {
Field string `json:"string,omitempty"`
Display string `json:"display,omitempty"`
Type string `json:"type,omitempty"` // string or number
}
// +k8s:deepcopy-gen=true
type DashboardHit struct {
// Dashboard or folder
Resource string `json:"resource"` // dashboards | folders
@@ -70,6 +75,7 @@ type DashboardHit struct {
Explain *common.Unstructured `json:"explain,omitempty"`
}
// +k8s:deepcopy-gen=true
type FacetResult struct {
Field string `json:"field,omitempty"`
// The distinct terms
@@ -80,6 +86,7 @@ type FacetResult struct {
Terms []TermFacet `json:"terms,omitempty"`
}
// +k8s:deepcopy-gen=true
type TermFacet struct {
Term string `json:"term,omitempty"`
Count int64 `json:"count,omitempty"`
+11 -35
View File
@@ -7,40 +7,7 @@ import (
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type Dashboard 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"`
// The dashboard body (unstructured for now)
Spec common.Unstructured `json:"spec"`
// Optional dashboard status
Status *DashboardStatus `json:"status,omitempty"`
}
type DashboardStatus struct {
ConversionStatus *ConversionStatus `json:"conversion,omitempty"`
}
type ConversionStatus struct {
Failed bool `json:"failed,omitempty"`
StoredVersion string `json:"storedVersion,omitempty"`
Error string `json:"error,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DashboardList struct {
metav1.TypeMeta `json:",inline"`
// +optional
metav1.ListMeta `json:"metadata,omitempty"`
Items []Dashboard `json:"items,omitempty"`
}
// +k8s:deepcopy-gen=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type DashboardVersionList struct {
metav1.TypeMeta `json:",inline"`
@@ -50,6 +17,7 @@ type DashboardVersionList struct {
Items []DashboardVersionInfo `json:"items,omitempty"`
}
// +k8s:deepcopy-gen=true
type DashboardVersionInfo struct {
// The internal ID for this version (will be replaced with resourceVersion)
Version int `json:"version"`
@@ -67,6 +35,7 @@ type DashboardVersionInfo struct {
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 {
@@ -80,6 +49,7 @@ type VersionsQueryOptions struct {
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"`
@@ -95,6 +65,7 @@ type LibraryPanel struct {
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"`
@@ -104,6 +75,7 @@ type LibraryPanelList struct {
Items []LibraryPanel `json:"items,omitempty"`
}
// +k8s:deepcopy-gen=true
type LibraryPanelSpec struct {
// The panel type
Type string `json:"type"`
@@ -131,6 +103,7 @@ type LibraryPanelSpec struct {
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"`
@@ -140,6 +113,7 @@ type LibraryPanelStatus struct {
}
// 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"`
@@ -147,7 +121,7 @@ type DashboardWithAccessInfo struct {
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"`
@@ -162,11 +136,13 @@ type DashboardAccess struct {
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"`
@@ -46,54 +46,6 @@ func (in *AnnotationPermission) DeepCopy() *AnnotationPermission {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ConversionStatus) DeepCopyInto(out *ConversionStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConversionStatus.
func (in *ConversionStatus) DeepCopy() *ConversionStatus {
if in == nil {
return nil
}
out := new(ConversionStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Dashboard) DeepCopyInto(out *Dashboard) {
*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(DashboardStatus)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Dashboard.
func (in *Dashboard) DeepCopy() *Dashboard {
if in == nil {
return nil
}
out := new(Dashboard)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Dashboard) 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 *DashboardAccess) DeepCopyInto(out *DashboardAccess) {
*out = *in
@@ -144,60 +96,6 @@ func (in *DashboardHit) DeepCopy() *DashboardHit {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DashboardList) DeepCopyInto(out *DashboardList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]Dashboard, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardList.
func (in *DashboardList) DeepCopy() *DashboardList {
if in == nil {
return nil
}
out := new(DashboardList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *DashboardList) 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 *DashboardStatus) DeepCopyInto(out *DashboardStatus) {
*out = *in
if in.ConversionStatus != nil {
in, out := &in.ConversionStatus, &out.ConversionStatus
*out = new(ConversionStatus)
**out = **in
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardStatus.
func (in *DashboardStatus) DeepCopy() *DashboardStatus {
if in == nil {
return nil
}
out := new(DashboardStatus)
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
@@ -8,34 +8,38 @@
package v0alpha1
import (
commonv0alpha1 "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
common "k8s.io/kube-openapi/pkg/common"
spec "k8s.io/kube-openapi/pkg/validation/spec"
)
func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
return map[string]common.OpenAPIDefinition{
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus": schema_pkg_apis_dashboard_v0alpha1_ConversionStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess": schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardHit": schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.FacetResult": schema_pkg_apis_dashboard_v0alpha1_FacetResult(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanel": schema_pkg_apis_dashboard_v0alpha1_LibraryPanel(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelSpec(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SearchResults": schema_pkg_apis_dashboard_v0alpha1_SearchResults(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortBy": schema_pkg_apis_dashboard_v0alpha1_SortBy(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableField": schema_pkg_apis_dashboard_v0alpha1_SortableField(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableFields": schema_pkg_apis_dashboard_v0alpha1_SortableFields(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.TermFacet": schema_pkg_apis_dashboard_v0alpha1_TermFacet(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref),
"github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured": commonv0alpha1.Unstructured{}.OpenAPIDefinition(),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationActions": schema_pkg_apis_dashboard_v0alpha1_AnnotationActions(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.AnnotationPermission": schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.Dashboard": schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardAccess": schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardHit": schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardJSONCodec": schema_pkg_apis_dashboard_v0alpha1_DashboardJSONCodec(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardList": schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardMetadata": schema_pkg_apis_dashboard_v0alpha1_DashboardMetadata(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus": schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardVersionList": schema_pkg_apis_dashboard_v0alpha1_DashboardVersionList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardWithAccessInfo": schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.FacetResult": schema_pkg_apis_dashboard_v0alpha1_FacetResult(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanel": schema_pkg_apis_dashboard_v0alpha1_LibraryPanel(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelList": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelList(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelSpec": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelSpec(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.LibraryPanelStatus": schema_pkg_apis_dashboard_v0alpha1_LibraryPanelStatus(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SearchResults": schema_pkg_apis_dashboard_v0alpha1_SearchResults(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortBy": schema_pkg_apis_dashboard_v0alpha1_SortBy(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableField": schema_pkg_apis_dashboard_v0alpha1_SortableField(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.SortableFields": schema_pkg_apis_dashboard_v0alpha1_SortableFields(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.TermFacet": schema_pkg_apis_dashboard_v0alpha1_TermFacet(ref),
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.VersionsQueryOptions": schema_pkg_apis_dashboard_v0alpha1_VersionsQueryOptions(ref),
}
}
@@ -100,36 +104,6 @@ func schema_pkg_apis_dashboard_v0alpha1_AnnotationPermission(ref common.Referenc
}
}
func schema_pkg_apis_dashboard_v0alpha1_ConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
Properties: map[string]spec.Schema{
"failed": {
SchemaProps: spec.SchemaProps{
Type: []string{"boolean"},
Format: "",
},
},
"storedVersion": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
"error": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "",
},
},
},
},
},
}
}
func schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -152,25 +126,24 @@ func schema_pkg_apis_dashboard_v0alpha1_Dashboard(ref common.ReferenceCallback)
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "The dashboard body (unstructured for now)",
Description: "Spec is the spec of the Dashboard",
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "Optional dashboard status",
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"),
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"),
},
},
},
Required: []string{"spec"},
Required: []string{"metadata", "spec", "status"},
},
},
Dependencies: []string{
@@ -182,8 +155,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref common.ReferenceCall
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "Information about how the requesting user can use a given dashboard",
Type: []string{"object"},
Type: []string{"object"},
Properties: map[string]spec.Schema{
"slug": {
SchemaProps: spec.SchemaProps{
@@ -248,6 +220,44 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardAccess(ref common.ReferenceCall
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardConversionStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ConversionStatus is the status of the conversion of the dashboard.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"failed": {
SchemaProps: spec.SchemaProps{
Description: "Whether from another version has failed. If true, means that the dashboard is not valid, and the caller should instead fetch the stored version.",
Default: false,
Type: []string{"boolean"},
Format: "",
},
},
"storedVersion": {
SchemaProps: spec.SchemaProps{
Description: "The version which was stored when the dashboard was created / updated. Fetching this version should always succeed.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"error": {
SchemaProps: spec.SchemaProps{
Description: "The error message from the conversion. Empty if the conversion has not failed.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
Required: []string{"failed", "storedVersion", "error"},
},
},
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -328,6 +338,17 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardHit(ref common.ReferenceCallbac
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardJSONCodec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "DashboardJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding",
Type: []string{"object"},
},
},
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -368,6 +389,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallba
},
},
},
Required: []string{"metadata", "items"},
},
},
Dependencies: []string{
@@ -375,6 +397,102 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardList(ref common.ReferenceCallba
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardMetadata(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "metadata contains embedded CommonMetadata and can be extended with custom string fields without external reference as using the CommonMetadata reference breaks thema codegen.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"updateTimestamp": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
"createdBy": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"uid": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"creationTimestamp": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
"deletionTimestamp": {
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
"finalizers": {
SchemaProps: spec.SchemaProps{
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
"resourceVersion": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"generation": {
SchemaProps: spec.SchemaProps{
Default: 0,
Type: []string{"integer"},
Format: "int64",
},
},
"updatedBy": {
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
"labels": {
SchemaProps: spec.SchemaProps{
Type: []string{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Allows: true,
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
},
Required: []string{"updateTimestamp", "createdBy", "uid", "creationTimestamp", "finalizers", "resourceVersion", "generation", "updatedBy", "labels"},
},
},
}
}
func schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
@@ -383,14 +501,15 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardStatus(ref common.ReferenceCall
Properties: map[string]spec.Schema{
"conversion": {
SchemaProps: spec.SchemaProps{
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus"),
Description: "Optional conversion status.",
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus"),
},
},
},
},
},
Dependencies: []string{
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.ConversionStatus"},
"github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardConversionStatus"},
}
}
@@ -514,21 +633,20 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref common.Refer
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "The dashboard body (unstructured for now)",
Description: "Spec is the spec of the Dashboard",
Ref: ref("github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1.Unstructured"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "Optional dashboard status",
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"),
Default: map[string]interface{}{},
Ref: ref("github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1.DashboardStatus"),
},
},
"access": {
@@ -538,7 +656,7 @@ func schema_pkg_apis_dashboard_v0alpha1_DashboardWithAccessInfo(ref common.Refer
},
},
},
Required: []string{"spec", "access"},
Required: []string{"metadata", "spec", "status", "access"},
},
},
Dependencies: []string{
@@ -1,8 +1,9 @@
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardHit,Tags
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardMetadata,Finalizers
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,FacetResult,Terms
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,LibraryPanelStatus,Warnings
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SearchResults,Hits
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortableFields,Fields
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,DashboardStatus,ConversionStatus
API rule violation: names_match,github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1,Unstructured,Object
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortBy,Descending
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1,SortableField,Field
+18
View File
@@ -0,0 +1,18 @@
package v1alpha1
import "k8s.io/apimachinery/pkg/runtime/schema"
const (
// Group is the API group used by all kinds in this package
Group = "dashboard.grafana.app"
// Version is the API version used by all kinds in this package
Version = "v1alpha1"
)
var (
// GroupVersion is a schema.GroupVersion consisting of the Group and Version constants for this package
GroupVersion = schema.GroupVersion{
Group: Group,
Version: Version,
}
)

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