Merge remote-tracking branch 'origin/main' into grafana-git-ui-sync
This commit is contained in:
187 files changed
+8864
-4317
No files matched your search
+21
-5
@@ -1548,7 +1548,7 @@ exports[`better eslint`] = {
|
||||
"public/app/features/alerting/unified/GrafanaRuleQueryViewer.tsx:5381": [
|
||||
[0, 0, 0, "\'@grafana/data/src/datetime/rangeutil\' import is restricted from being used by a pattern. Import from the public export instead.", "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 in text props in text props. Wrap text with <Trans /> or use t() or use t()", "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 />", "5"],
|
||||
@@ -1561,7 +1561,8 @@ exports[`better eslint`] = {
|
||||
[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 />", "15"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "16"]
|
||||
],
|
||||
"public/app/features/alerting/unified/NotificationPoliciesPage.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings in text props. Wrap text with <Trans /> or use t()", "0"],
|
||||
@@ -2342,6 +2343,9 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "11"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-editor/dag.test.ts:5381": [
|
||||
[0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-editor/labels/LabelsButtons.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"]
|
||||
@@ -2466,9 +2470,18 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "3"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "4"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-viewer/tabs/Query.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/alerting/unified/components/rule-viewer/tabs/Details.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "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 />", "5"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "6"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "7"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "8"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "9"],
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "10"]
|
||||
],
|
||||
"public/app/features/alerting/unified/components/rule-viewer/tabs/Routing.tsx:5381": [
|
||||
[0, 0, 0, "No untranslated strings. Wrap text with <Trans />", "0"]
|
||||
@@ -2922,6 +2935,9 @@ exports[`better eslint`] = {
|
||||
"public/app/features/alerting/unified/rule-list/components/RuleListIcon.tsx:5381": [
|
||||
[0, 0, 0, "\'@grafana/ui/src/components/Text/Text\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/state/AlertingQueryRunner.test.ts:5381": [
|
||||
[0, 0, 0, "\'@grafana/runtime/src/utils/DataSourceWithBackend\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
|
||||
],
|
||||
"public/app/features/alerting/unified/state/actions.ts:5381": [
|
||||
[0, 0, 0, "\'@grafana/runtime/src/utils/logging\' import is restricted from being used by a pattern. Import from the public export instead.", "0"]
|
||||
],
|
||||
|
||||
@@ -5,6 +5,10 @@ GOPATH ?= $(shell go env GOPATH)
|
||||
GOBIN ?= $(firstword $(subst :, ,${GOPATH}))/bin
|
||||
GO ?= $(shell which go)
|
||||
|
||||
# Add this near the top of the file, after the initial variable definitions
|
||||
ifndef VARIABLES_MK
|
||||
VARIABLES_MK := 1
|
||||
|
||||
# Below generated variables ensure that every time a tool under each variable is invoked, the correct version
|
||||
# will be used; reinstalling only if needed.
|
||||
# For example for bra variable:
|
||||
@@ -65,3 +69,4 @@ $(SWAGGER): $(BINGO_DIR)/swagger.mod
|
||||
@echo "(re)installing $(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37"
|
||||
@cd $(BINGO_DIR) && GOWORK=off $(GO) build -mod=mod -modfile=swagger.mod -o=$(GOBIN)/swagger-v0.30.6-0.20240310114303-db51e79a0e37 "github.com/go-swagger/go-swagger/cmd/swagger"
|
||||
|
||||
endif
|
||||
+41
-41
@@ -18,7 +18,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
@@ -69,7 +69,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go install github.com/bazelbuild/buildtools/buildifier@latest
|
||||
@@ -112,7 +112,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -170,7 +170,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -309,7 +309,7 @@ steps:
|
||||
path: /github-app
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -427,7 +427,7 @@ steps:
|
||||
path: /github-app
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -517,7 +517,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
@@ -642,7 +642,7 @@ steps:
|
||||
path: /github-app
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
@@ -713,7 +713,7 @@ steps:
|
||||
-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.5 --yarn-cache=$$YARN_CACHE_FOLDER
|
||||
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.5
|
||||
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.6
|
||||
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
|
||||
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
|
||||
> packages.txt
|
||||
@@ -770,7 +770,7 @@ steps:
|
||||
GF_APP_MODE: development
|
||||
GF_SERVER_HTTP_PORT: "3001"
|
||||
GF_SERVER_ROUTER_LOGGING: "1"
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: grafana-server
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite dashboards-suite
|
||||
@@ -1114,7 +1114,7 @@ steps:
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -1280,7 +1280,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1637,7 +1637,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1708,7 +1708,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1766,7 +1766,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1842,7 +1842,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -1922,7 +1922,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
@@ -1988,7 +1988,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -2063,7 +2063,7 @@ steps:
|
||||
path: /github-app
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
@@ -2133,7 +2133,7 @@ steps:
|
||||
-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.5 --yarn-cache=$$YARN_CACHE_FOLDER
|
||||
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.5
|
||||
--build-id=$$DRONE_BUILD_NUMBER --ubuntu-base=ubuntu:22.04 --alpine-base=alpine:3.20.6
|
||||
--tag-format='{{ .version_base }}-{{ .buildID }}-{{ .arch }}' --ubuntu-tag-format='{{
|
||||
.version_base }}-{{ .buildID }}-ubuntu-{{ .arch }}' --verify='false' --grafana-dir=$$PWD
|
||||
> packages.txt
|
||||
@@ -2194,7 +2194,7 @@ steps:
|
||||
GF_APP_MODE: development
|
||||
GF_SERVER_HTTP_PORT: "3001"
|
||||
GF_SERVER_ROUTER_LOGGING: "1"
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: grafana-server
|
||||
- commands:
|
||||
- ./bin/build e2e-tests --port 3001 --suite dashboards-suite
|
||||
@@ -2611,7 +2611,7 @@ steps:
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -2856,7 +2856,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -2912,7 +2912,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -2986,7 +2986,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -3064,7 +3064,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- go build -o ./bin/build -ldflags '-extldflags -static' ./pkg/build/cmd
|
||||
@@ -3169,7 +3169,7 @@ steps:
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -3372,7 +3372,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
@@ -3504,7 +3504,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- mkdir -p bin
|
||||
@@ -3994,7 +3994,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4069,7 +4069,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4231,7 +4231,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4333,7 +4333,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -4387,7 +4387,7 @@ services: []
|
||||
steps:
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -4468,7 +4468,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4612,7 +4612,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4740,7 +4740,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
ALPINE_BASE: alpine:3.20.5
|
||||
ALPINE_BASE: alpine:3.20.6
|
||||
CDN_DESTINATION:
|
||||
from_secret: rgm_cdn_destination
|
||||
DESTINATION:
|
||||
@@ -4879,7 +4879,7 @@ steps:
|
||||
name: grabpl
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
image: alpine:3.20.5
|
||||
image: alpine:3.20.6
|
||||
name: identify-runner
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -5303,7 +5303,7 @@ steps:
|
||||
- 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
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM grafana/grafana-ci-deploy:1.3.3
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.5
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine:3.20.6
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM ubuntu:22.04
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM byrnedo/alpine-curl:0.1.8
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM plugins/slack
|
||||
@@ -5341,7 +5341,7 @@ steps:
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL grafana/grafana-ci-deploy:1.3.3
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.5
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL alpine:3.20.6
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL ubuntu:22.04
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL byrnedo/alpine-curl:0.1.8
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL plugins/slack
|
||||
@@ -5605,6 +5605,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: ed11784cafc81bbb617e931f1aa392a9de8bde04dd5d14e51baf856a3942dd91
|
||||
hmac: a1d8f3defdf52b76049d8a8df1c21dd13cac9a2a9f80566abceffb4acd1b49d2
|
||||
|
||||
...
|
||||
+2
-1
@@ -73,7 +73,7 @@
|
||||
|
||||
/apps/alerting/ @grafana/alerting-backend
|
||||
/apps/playlist/ @grafana/grafana-app-platform-squad
|
||||
/apps/investigation/ @fcjack @matryer
|
||||
/apps/investigations/ @fcjack @matryer @svennergr
|
||||
/apps/advisor/ @grafana/plugins-platform-backend
|
||||
/pkg/api/ @grafana/grafana-backend-group
|
||||
/pkg/apis/ @grafana/grafana-app-platform-squad
|
||||
@@ -446,6 +446,7 @@
|
||||
/.betterer.eslint.config.js @grafana/frontend-ops
|
||||
/.gitattributes @grafana/frontend-ops
|
||||
/.gitignore @grafana/frontend-ops
|
||||
/.ignore @grafana/frontend-ops
|
||||
/.nvmrc @grafana/frontend-ops
|
||||
/.prettierignore @grafana/frontend-ops
|
||||
/.yarn @grafana/frontend-ops
|
||||
|
||||
@@ -8,7 +8,7 @@ updates:
|
||||
directories:
|
||||
- "/"
|
||||
- "/apps/playlist"
|
||||
- "/apps/investigation"
|
||||
- "/apps/investigations"
|
||||
- "/pkg/aggregator"
|
||||
- "/pkg/apimachinery"
|
||||
- "/pkg/apiserver"
|
||||
|
||||
+11
-5
@@ -40,6 +40,7 @@ __debug_bin*
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
/devenv/docker/blocks/auth/saml-enterprise
|
||||
/devenv/docker/blocks/auth/signer
|
||||
/devenv/docker/blocks/spanner_tests
|
||||
|
||||
/tmp
|
||||
tools/phantomjs/phantomjs
|
||||
@@ -106,14 +107,18 @@ profile.cov
|
||||
/pkg/cmd/grafana-cli/grafana-cli
|
||||
/pkg/cmd/grafana-server/grafana-server
|
||||
/pkg/cmd/grafana-server/debug
|
||||
/pkg/extensions/*
|
||||
/pkg/build/cmd/artifactspage.go
|
||||
/pkg/build/cmd/artifactspage.tmpl.html
|
||||
/pkg/build/cmd/exportversion.go
|
||||
/pkg/server/wireexts_enterprise.go
|
||||
|
||||
# Extensions
|
||||
/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go
|
||||
/pkg/server/wireexts_enterprise.go
|
||||
/pkg/build/cmd/enterprise.go
|
||||
/pkg/extensions/*
|
||||
!/pkg/extensions/.keep
|
||||
!/pkg/extensions/main.go
|
||||
/public/app/extensions
|
||||
!/public/app/extensions/.keep
|
||||
|
||||
|
||||
debug.test
|
||||
/examples/*/dist
|
||||
/packaging/**/*.rpm
|
||||
@@ -173,6 +178,7 @@ compilation-stats.json
|
||||
/e2e/benchmarks/**/results
|
||||
/e2e/build_results.zip
|
||||
/e2e/extensions
|
||||
!/e2e/extensions/.keep
|
||||
/e2e/extensions-suite
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
|
||||
+3
-3
@@ -191,10 +191,10 @@ linters-settings:
|
||||
allow: []
|
||||
deny:
|
||||
- pkg: github.com/grafana/grafana/pkg
|
||||
desc: apps/investigation is not allowed to import grafana core
|
||||
desc: apps/investigations is not allowed to import grafana core
|
||||
files:
|
||||
- ./apps/investigation/*
|
||||
- ./apps/investigation/**/*
|
||||
- ./apps/investigations/*
|
||||
- ./apps/investigations/**/*
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- ruleguard
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
# This is a trick to ignore files only by git but not by other tools
|
||||
!/public/app/extensions
|
||||
!/pkg/extensions/*
|
||||
!/pkg/cmd/grafana-cli/runner/wireexts_enterprise.go
|
||||
!/pkg/server/wireexts_enterprise.go
|
||||
!/pkg/build/cmd/enterprise.go
|
||||
!/pkg/extensions/*
|
||||
|
||||
# Enterprise emails
|
||||
!/emails/templates/enterprise_*
|
||||
!/public/emails/enterprise_*
|
||||
|
||||
# Enterprise reporting fonts
|
||||
!/public/fonts/dejavu
|
||||
|
||||
# Enterprise devenv
|
||||
!/devenv/docker/blocks/grafana-enterprise
|
||||
!/devenv/docker/blocks/saml-enterprise
|
||||
# This is the new place of the block, but I leave the previous here for a while
|
||||
!/devenv/docker/blocks/auth/saml-enterprise
|
||||
!/devenv/docker/blocks/auth/signer
|
||||
!/devenv/docker/blocks/spanner_tests
|
||||
+1
-1
@@ -73,7 +73,7 @@ COPY pkg/storage/unified/apistore pkg/storage/unified/apistore
|
||||
COPY pkg/semconv pkg/semconv
|
||||
COPY pkg/aggregator pkg/aggregator
|
||||
COPY apps/playlist apps/playlist
|
||||
COPY apps/investigation apps/investigation
|
||||
COPY apps/investigations apps/investigations
|
||||
COPY apps/advisor apps/advisor
|
||||
COPY apps apps
|
||||
COPY kindsv2 kindsv2
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
module: "github.com/grafana/grafana/apps/investigation/kinds"
|
||||
language: version: "v0.8.2"
|
||||
@@ -1,87 +0,0 @@
|
||||
package investigation
|
||||
|
||||
// This is our Investigation definition, which contains metadata about the kind, and the kind's schema
|
||||
investigation: {
|
||||
kind: "Investigation"
|
||||
pluralName: "Investigations"
|
||||
current: "v1alpha1"
|
||||
versions: {
|
||||
"v1alpha1": {
|
||||
codegen: {
|
||||
frontend: false
|
||||
backend: true
|
||||
}
|
||||
schema: {
|
||||
#InvestigationSpec: {
|
||||
title: string
|
||||
status: "open" | "closed"
|
||||
items: [...#InvestigationItem]
|
||||
}
|
||||
|
||||
// InvestigationItem is an item in an investigation.
|
||||
#InvestigationItem: {
|
||||
id: string
|
||||
title: string
|
||||
// type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions).
|
||||
type: string
|
||||
// url is the URL to the item.
|
||||
url: string
|
||||
// origin is where the item was created from.
|
||||
origin: string // "explore-metrics", "explore-logs", "explore-traces" (not an enum to allow for future extensions)
|
||||
// iconPath (optional) is the path to the icon for the item.
|
||||
iconPath?: string
|
||||
// timeRange (optional) is the time range of the item.
|
||||
timeRange: #AbsoluteTimeRange
|
||||
// note (optional) is a comment on the item.
|
||||
note?: [...#Comment]
|
||||
// queryType is the type of the query used to generate this item.
|
||||
queryType: "logs" | "metrics"
|
||||
// dataQuery contains the query used to generate this item.
|
||||
dataQuery: #DataQueryLogs | #DataQueryMetrics
|
||||
}
|
||||
|
||||
// DataQueryLogs is a data query for logs.
|
||||
#DataQueryLogs: {
|
||||
// refId is the reference ID of the query.
|
||||
refId: string
|
||||
// datasource is the datasource of the query.
|
||||
datasource: #DatasourceRef
|
||||
// expr is the expression of the query.
|
||||
expr: string
|
||||
// maxLines (optional) is used to limit the number of log rows returned.
|
||||
maxLines?: int64
|
||||
}
|
||||
|
||||
// DataQueryMetrics is a data query for metrics.
|
||||
#DataQueryMetrics: {
|
||||
refId: string
|
||||
datasource: #DatasourceRef
|
||||
expr: string
|
||||
}
|
||||
|
||||
// Comment is a comment on an investigation item.
|
||||
#Comment: {
|
||||
authorUserID: string
|
||||
bodyMarkdown: string
|
||||
}
|
||||
|
||||
// DatasourceRef is a reference to a datasource.
|
||||
#DatasourceRef: {
|
||||
uid: string
|
||||
type: string
|
||||
apiVersion: string
|
||||
name: string
|
||||
}
|
||||
|
||||
// AbsoluteTimeRange is a time range specified by absolute timestamps.
|
||||
#AbsoluteTimeRange: {
|
||||
from: number
|
||||
to: number
|
||||
}
|
||||
|
||||
// spec is the schema of our resource. The spec should include all the user-ediable information for the kind.
|
||||
spec: #InvestigationSpec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package investigation
|
||||
|
||||
manifest: {
|
||||
appName: "investigation"
|
||||
groupOverride: "investigation.grafana.app"
|
||||
kinds: [
|
||||
investigation,
|
||||
]
|
||||
}
|
||||
@@ -1,136 +0,0 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// AbsoluteTimeRange is a time range specified by absolute timestamps.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationAbsoluteTimeRange struct {
|
||||
From float64 `json:"from"`
|
||||
To float64 `json:"to"`
|
||||
}
|
||||
|
||||
// NewInvestigationAbsoluteTimeRange creates a new InvestigationAbsoluteTimeRange object.
|
||||
func NewInvestigationAbsoluteTimeRange() *InvestigationAbsoluteTimeRange {
|
||||
return &InvestigationAbsoluteTimeRange{}
|
||||
}
|
||||
|
||||
// Comment is a comment on an investigation item.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationComment struct {
|
||||
AuthorUserID string `json:"authorUserID"`
|
||||
BodyMarkdown string `json:"bodyMarkdown"`
|
||||
}
|
||||
|
||||
// NewInvestigationComment creates a new InvestigationComment object.
|
||||
func NewInvestigationComment() *InvestigationComment {
|
||||
return &InvestigationComment{}
|
||||
}
|
||||
|
||||
// DatasourceRef is a reference to a datasource.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationDatasourceRef struct {
|
||||
Uid string `json:"uid"`
|
||||
Type string `json:"type"`
|
||||
ApiVersion string `json:"apiVersion"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// NewInvestigationDatasourceRef creates a new InvestigationDatasourceRef object.
|
||||
func NewInvestigationDatasourceRef() *InvestigationDatasourceRef {
|
||||
return &InvestigationDatasourceRef{}
|
||||
}
|
||||
|
||||
// DataQueryLogs is a data query for logs.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationDataQueryLogs struct {
|
||||
// refId is the reference ID of the query.
|
||||
RefId string `json:"refId"`
|
||||
// datasource is the datasource of the query.
|
||||
Datasource InvestigationDatasourceRef `json:"datasource"`
|
||||
// expr is the expression of the query.
|
||||
Expr string `json:"expr"`
|
||||
// maxLines (optional) is used to limit the number of log rows returned.
|
||||
MaxLines *int64 `json:"maxLines,omitempty"`
|
||||
}
|
||||
|
||||
// NewInvestigationDataQueryLogs creates a new InvestigationDataQueryLogs object.
|
||||
func NewInvestigationDataQueryLogs() *InvestigationDataQueryLogs {
|
||||
return &InvestigationDataQueryLogs{
|
||||
Datasource: *NewInvestigationDatasourceRef(),
|
||||
}
|
||||
}
|
||||
|
||||
// DataQueryMetrics is a data query for metrics.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationDataQueryMetrics struct {
|
||||
RefId string `json:"refId"`
|
||||
Datasource InvestigationDatasourceRef `json:"datasource"`
|
||||
Expr string `json:"expr"`
|
||||
}
|
||||
|
||||
// NewInvestigationDataQueryMetrics creates a new InvestigationDataQueryMetrics object.
|
||||
func NewInvestigationDataQueryMetrics() *InvestigationDataQueryMetrics {
|
||||
return &InvestigationDataQueryMetrics{
|
||||
Datasource: *NewInvestigationDatasourceRef(),
|
||||
}
|
||||
}
|
||||
|
||||
// InvestigationItem is an item in an investigation.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationInvestigationItem struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
// type is the type of the item "timeseries", "heatmap", "log-table" (not an enum to allow for future extensions).
|
||||
Type string `json:"type"`
|
||||
// url is the URL to the item.
|
||||
Url string `json:"url"`
|
||||
// origin is where the item was created from.
|
||||
// "explore-metrics", "explore-logs", "explore-traces" (not an enum to allow for future extensions)
|
||||
Origin string `json:"origin"`
|
||||
// iconPath (optional) is the path to the icon for the item.
|
||||
IconPath *string `json:"iconPath,omitempty"`
|
||||
// timeRange (optional) is the time range of the item.
|
||||
TimeRange InvestigationAbsoluteTimeRange `json:"timeRange"`
|
||||
// note (optional) is a comment on the item.
|
||||
Note []InvestigationComment `json:"note,omitempty"`
|
||||
// queryType is the type of the query used to generate this item.
|
||||
QueryType InvestigationInvestigationItemQueryType `json:"queryType"`
|
||||
// dataQuery contains the query used to generate this item.
|
||||
DataQuery interface{} `json:"dataQuery"`
|
||||
}
|
||||
|
||||
// NewInvestigationInvestigationItem creates a new InvestigationInvestigationItem object.
|
||||
func NewInvestigationInvestigationItem() *InvestigationInvestigationItem {
|
||||
return &InvestigationInvestigationItem{
|
||||
TimeRange: *NewInvestigationAbsoluteTimeRange(),
|
||||
}
|
||||
}
|
||||
|
||||
// spec is the schema of our resource. The spec should include all the user-ediable information for the kind.
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationSpec struct {
|
||||
Title string `json:"title"`
|
||||
Status InvestigationSpecStatus `json:"status"`
|
||||
Items []InvestigationInvestigationItem `json:"items"`
|
||||
}
|
||||
|
||||
// NewInvestigationSpec creates a new InvestigationSpec object.
|
||||
func NewInvestigationSpec() *InvestigationSpec {
|
||||
return &InvestigationSpec{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationInvestigationItemQueryType string
|
||||
|
||||
const (
|
||||
InvestigationInvestigationItemQueryTypeLogs InvestigationInvestigationItemQueryType = "logs"
|
||||
InvestigationInvestigationItemQueryTypeMetrics InvestigationInvestigationItemQueryType = "metrics"
|
||||
)
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationSpecStatus string
|
||||
|
||||
const (
|
||||
InvestigationSpecStatusOpen InvestigationSpecStatus = "open"
|
||||
InvestigationSpecStatusClosed InvestigationSpecStatus = "closed"
|
||||
)
|
||||
@@ -1,537 +0,0 @@
|
||||
//go:build !ignore_autogenerated
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
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/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation": schema_pkg_apis_investigation_v1alpha1_Investigation(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange": schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment": schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryLogs": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDataQueryMetrics": schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef": schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem": schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationList": schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec": schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus": schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref),
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState": schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref),
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_Investigation(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
|
||||
},
|
||||
},
|
||||
"spec": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec"),
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "spec", "status"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationSpec", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationAbsoluteTimeRange(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "AbsoluteTimeRange is a time range specified by absolute timestamps.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"from": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
Type: []string{"number"},
|
||||
Format: "double",
|
||||
},
|
||||
},
|
||||
"to": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: 0,
|
||||
Type: []string{"number"},
|
||||
Format: "double",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"from", "to"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationComment(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Comment is a comment on an investigation item.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"authorUserID": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"bodyMarkdown": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"authorUserID", "bodyMarkdown"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryLogs(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DataQueryLogs is a data query for logs.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"refId": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "refId is the reference ID of the query.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"datasource": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "datasource is the datasource of the query.",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"),
|
||||
},
|
||||
},
|
||||
"expr": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "expr is the expression of the query.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"maxLines": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "maxLines (optional) is used to limit the number of log rows returned.",
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"refId", "datasource", "expr"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationDataQueryMetrics(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DataQueryMetrics is a data query for metrics.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"refId": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"datasource": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"),
|
||||
},
|
||||
},
|
||||
"expr": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"refId", "datasource", "expr"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationDatasourceRef"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationDatasourceRef(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "DatasourceRef is a reference to a datasource.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"uid": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"uid", "type", "apiVersion", "name"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationInvestigationItem(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "InvestigationItem is an item in an investigation.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"id": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"type": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "url is the URL to the item.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"origin": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "origin is where the item was created from. \"explore-metrics\", \"explore-logs\", \"explore-traces\" (not an enum to allow for future extensions)",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"iconPath": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "iconPath (optional) is the path to the icon for the item.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"timeRange": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "timeRange (optional) is the time range of the item.",
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange"),
|
||||
},
|
||||
},
|
||||
"note": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "note (optional) is a comment on the item.",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"queryType": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "queryType is the type of the query used to generate this item.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"dataQuery": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "dataQuery contains the query used to generate this item.",
|
||||
Type: []string{"object"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"id", "title", "type", "url", "origin", "timeRange", "queryType", "dataQuery"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationAbsoluteTimeRange", "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationComment"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationList(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"apiVersion": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"metadata": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.Investigation", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "spec is the schema of our resource. The spec should include all the user-ediable information for the kind.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"title": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"status": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"items": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"title", "status", "items"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationInvestigationItem"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"operatorStates": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "operatorStates is a map of operator ID to operator state evaluations. Any operator which consumes this kind SHOULD add its state evaluation information to this field.",
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"additionalFields": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "additionalFields is reserved for future use",
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1.InvestigationstatusOperatorState"},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_investigation_v1alpha1_InvestigationstatusOperatorState(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"lastEvaluation": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "lastEvaluation is the ResourceVersion last evaluated",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"state": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "state describes the state of the lastEvaluation. It is limited to three possible states for machine evaluation.",
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"descriptiveState": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "descriptiveState is an optional more descriptive state field which has no requirements on format",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"details": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "details contains any extra information that is operator-specific",
|
||||
Type: []string{"object"},
|
||||
AdditionalProperties: &spec.SchemaOrBool{
|
||||
Allows: true,
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"lastEvaluation", "state"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
//
|
||||
// This file is generated by grafana-app-sdk
|
||||
// DO NOT EDIT
|
||||
//
|
||||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
)
|
||||
|
||||
var (
|
||||
rawSchemaInvestigationv1alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource. The spec should include all the user-ediable information for the kind.","properties":{"items":{"items":{"properties":{"dataQuery":{"description":"dataQuery contains the query used to generate this item.","oneOf":[{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]},{"allOf":[{"required":["refId","datasource","expr"]},{"not":{"anyOf":[{"required":["refId","datasource","expr"]}]}}]}],"properties":{"datasource":{"description":"datasource is the datasource of the query.","properties":{"apiVersion":{"type":"string"},"name":{"type":"string"},"type":{"type":"string"},"uid":{"type":"string"}},"required":["uid","type","apiVersion","name"],"type":"object"},"expr":{"description":"expr is the expression of the query.","type":"string"},"maxLines":{"description":"maxLines (optional) is used to limit the number of log rows returned.","format":"int64","type":"integer"},"refId":{"description":"refId is the reference ID of the query.","type":"string"}},"type":"object"},"iconPath":{"description":"iconPath (optional) is the path to the icon for the item.","type":"string"},"id":{"type":"string"},"note":{"description":"note (optional) is a comment on the item.","items":{"properties":{"authorUserID":{"type":"string"},"bodyMarkdown":{"type":"string"}},"required":["authorUserID","bodyMarkdown"],"type":"object"},"type":"array"},"origin":{"description":"origin is where the item was created from.","type":"string"},"queryType":{"description":"queryType is the type of the query used to generate this item.","enum":["logs","metrics"],"type":"string"},"timeRange":{"description":"timeRange (optional) is the time range of the item.","properties":{"from":{"type":"number"},"to":{"type":"number"}},"required":["from","to"],"type":"object"},"title":{"type":"string"},"type":{"description":"type is the type of the item \"timeseries\", \"heatmap\", \"log-table\" (not an enum to allow for future extensions).","type":"string"},"url":{"description":"url is the URL to the item.","type":"string"}},"required":["id","title","type","url","origin","timeRange","queryType","dataQuery"],"type":"object"},"type":"array"},"status":{"enum":["open","closed"],"type":"string"},"title":{"type":"string"}},"required":["title","status","items"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaInvestigationv1alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaInvestigationv1alpha1, &versionSchemaInvestigationv1alpha1)
|
||||
)
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "investigation",
|
||||
Group: "investigation.grafana.app",
|
||||
Kinds: []app.ManifestKind{
|
||||
{
|
||||
Kind: "Investigation",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
Versions: []app.ManifestKindVersion{
|
||||
{
|
||||
Name: "v1alpha1",
|
||||
Schema: &versionSchemaInvestigationv1alpha1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func jsonToMap(j string) map[string]any {
|
||||
m := make(map[string]any)
|
||||
json.Unmarshal([]byte(j), &j)
|
||||
return m
|
||||
}
|
||||
|
||||
func LocalManifest() app.Manifest {
|
||||
return app.NewEmbeddedManifest(appManifestData)
|
||||
}
|
||||
|
||||
func RemoteManifest() app.Manifest {
|
||||
return app.NewAPIServerManifest("investigation")
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1,152 @@
|
||||
[
|
||||
{
|
||||
"id": "896312ce-65b0-4b50-ade1-e7f04fa22c66",
|
||||
"title": "Thursday morning investigation",
|
||||
"hasCustomName": false,
|
||||
"isFavorite": false,
|
||||
"collectables": [
|
||||
{
|
||||
"origin": "Explore Logs",
|
||||
"type": "timeseries",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "LABEL_BREAKDOWN_VALUES",
|
||||
"queryType": "range",
|
||||
"editorMode": "code",
|
||||
"supportingQueryType": "grafana-lokiexplore-app",
|
||||
"legendFormat": "{{detected_level}}",
|
||||
"expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)"
|
||||
}
|
||||
],
|
||||
"timeRange": {
|
||||
"to": "2025-02-13T11:31:20.536Z",
|
||||
"from": "2025-02-13T11:16:20.536Z",
|
||||
"raw": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "fe9k7u07b1a0wc"
|
||||
},
|
||||
"url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
|
||||
"id": "LABEL_BREAKDOWN_VALUES_detected_level",
|
||||
"title": "detected_level",
|
||||
"logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
|
||||
"createdAt": "2025-02-13T11:31:23.637Z"
|
||||
}
|
||||
],
|
||||
"createdAt": "2025-02-13T11:31:23.636Z",
|
||||
"updatedAt": "2025-02-13T11:31:23.637Z",
|
||||
"viewMode": {
|
||||
"mode": "compact",
|
||||
"showComments": true,
|
||||
"showTooltips": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "e9cf1958-d0ed-46b7-b597-9052c7648656",
|
||||
"title": "Thursday morning investigation",
|
||||
"hasCustomName": false,
|
||||
"isFavorite": false,
|
||||
"collectables": [
|
||||
{
|
||||
"origin": "Explore Logs",
|
||||
"type": "timeseries",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "LABEL_BREAKDOWN_VALUES",
|
||||
"queryType": "range",
|
||||
"editorMode": "code",
|
||||
"supportingQueryType": "grafana-lokiexplore-app",
|
||||
"legendFormat": "{{detected_level}}",
|
||||
"expr": "sum(count_over_time({service_name=\"web_app_1\"} | detected_level != \"\"[$__auto])) by (detected_level)"
|
||||
}
|
||||
],
|
||||
"timeRange": {
|
||||
"to": "2025-02-13T11:31:20.536Z",
|
||||
"from": "2025-02-13T11:16:20.536Z",
|
||||
"raw": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "fe9k7u07b1a0wc"
|
||||
},
|
||||
"url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
|
||||
"id": "LABEL_BREAKDOWN_VALUES_detected_level",
|
||||
"title": "detected_level",
|
||||
"logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
|
||||
"createdAt": "2025-02-13T11:31:23.638Z"
|
||||
},
|
||||
{
|
||||
"origin": "Explore Logs",
|
||||
"type": "timeseries",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "LABEL_BREAKDOWN_VALUES",
|
||||
"queryType": "range",
|
||||
"editorMode": "code",
|
||||
"supportingQueryType": "grafana-lokiexplore-app",
|
||||
"legendFormat": "{{service_name}}",
|
||||
"expr": "sum(count_over_time({service_name=\"web_app_1\",service_name != \"\"} [$__auto])) by (service_name)"
|
||||
}
|
||||
],
|
||||
"timeRange": {
|
||||
"to": "2025-02-13T11:31:20.536Z",
|
||||
"from": "2025-02-13T11:16:20.536Z",
|
||||
"raw": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "fe9k7u07b1a0wc"
|
||||
},
|
||||
"url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
|
||||
"id": "LABEL_BREAKDOWN_VALUES_service_name",
|
||||
"title": "service_name",
|
||||
"logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
|
||||
"createdAt": "2025-02-13T11:31:41.507Z"
|
||||
},
|
||||
{
|
||||
"origin": "Explore Logs",
|
||||
"type": "timeseries",
|
||||
"queries": [
|
||||
{
|
||||
"refId": "LABEL_BREAKDOWN_VALUES",
|
||||
"queryType": "range",
|
||||
"editorMode": "code",
|
||||
"supportingQueryType": "grafana-lokiexplore-app",
|
||||
"legendFormat": "{{service}}",
|
||||
"expr": "sum(count_over_time({service_name=\"web_app_1\",service != \"\"} [$__auto])) by (service)"
|
||||
}
|
||||
],
|
||||
"timeRange": {
|
||||
"to": "2025-02-13T11:31:20.536Z",
|
||||
"from": "2025-02-13T11:16:20.536Z",
|
||||
"raw": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
}
|
||||
},
|
||||
"datasource": {
|
||||
"uid": "fe9k7u07b1a0wc"
|
||||
},
|
||||
"url": "http://localhost:3000/a/grafana-lokiexplore-app/explore/service/web_app_1/labels?patterns=%5B%5D&from=now-15m&to=now&var-ds=fe9k7u07b1a0wc&var-filters=service_name%7C%3D%7Cweb_app_1&var-fields=&var-levels=&var-metadata=&var-patterns=&var-lineFilterV2=&var-lineFilters=&urlColumns=%5B%5D&visualizationType=%22logs%22&displayedFields=%5B%5D&timezone=browser&var-all-fields=&var-labelBy=$__all",
|
||||
"id": "LABEL_BREAKDOWN_VALUES_service",
|
||||
"title": "service",
|
||||
"logoPath": "public/plugins/grafana-lokiexplore-app/img/img/logo.svg",
|
||||
"createdAt": "2025-02-13T11:31:43.698Z"
|
||||
}
|
||||
],
|
||||
"createdAt": "2025-02-13T11:31:23.637Z",
|
||||
"updatedAt": "2025-02-13T11:31:43.698Z",
|
||||
"viewMode": {
|
||||
"mode": "compact",
|
||||
"showComments": true,
|
||||
"showTooltips": false
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
module github.com/grafana/grafana/apps/investigation
|
||||
module github.com/grafana/grafana/apps/investigations
|
||||
|
||||
go 1.23.4
|
||||
|
||||
File renamed without changes.
@@ -0,0 +1,51 @@
|
||||
package investigations
|
||||
|
||||
// Collectable represents an item collected during investigation
|
||||
#Collectable: {
|
||||
id: string
|
||||
createdAt: string
|
||||
|
||||
title: string
|
||||
origin: string
|
||||
type: string
|
||||
queries: [...#Query] // +listType=atomic
|
||||
timeRange: #TimeRange
|
||||
datasource: #DatasourceRef
|
||||
url: string
|
||||
logoPath?: string
|
||||
|
||||
note: string
|
||||
noteUpdatedAt: string
|
||||
}
|
||||
|
||||
#CollectableSummary: {
|
||||
id: string
|
||||
title: string
|
||||
logoPath: string
|
||||
origin: string
|
||||
}
|
||||
|
||||
// Query represents a data query
|
||||
#Query: {
|
||||
refId: string
|
||||
queryType: string
|
||||
editorMode: string
|
||||
supportingQueryType: string
|
||||
legendFormat: string
|
||||
expr: string
|
||||
}
|
||||
|
||||
// TimeRange represents a time range with both absolute and relative values
|
||||
#TimeRange: {
|
||||
from: string
|
||||
to: string
|
||||
raw: {
|
||||
from: string
|
||||
to: string
|
||||
}
|
||||
}
|
||||
|
||||
// DatasourceRef is a reference to a datasource
|
||||
#DatasourceRef: {
|
||||
uid: string
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
module: "github.com/grafana/grafana/apps/investigations"
|
||||
@@ -0,0 +1,64 @@
|
||||
package investigations
|
||||
|
||||
// This is our Investigation definition, which contains metadata about the kind, and the kind's schema
|
||||
investigation: {
|
||||
kind: "Investigation"
|
||||
group: "investigations.grafana.app"
|
||||
apiResource: {
|
||||
groupOverride: "investigations.grafana.app"
|
||||
}
|
||||
pluralName: "Investigations"
|
||||
current: "v0alpha1"
|
||||
versions: {
|
||||
"v0alpha1": {
|
||||
codegen: {
|
||||
frontend: true
|
||||
backend: true
|
||||
options: {
|
||||
generateObjectMeta: true
|
||||
generateClient: true
|
||||
k8sLike: true
|
||||
package: "github.com/grafana/grafana/apps/investigations"
|
||||
}
|
||||
}
|
||||
schema: {
|
||||
// spec is the schema of our resource
|
||||
spec: {
|
||||
title: string
|
||||
createdByProfile: #Person
|
||||
hasCustomName: bool
|
||||
isFavorite: bool
|
||||
overviewNote: string
|
||||
overviewNoteUpdatedAt: string
|
||||
collectables: [...#Collectable] // +listType=atomic
|
||||
viewMode: #ViewMode
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for investigation summaries
|
||||
#InvestigationSummary: {
|
||||
title: string
|
||||
createdByProfile: #Person
|
||||
hasCustomName: bool
|
||||
isFavorite: bool
|
||||
overviewNote: string
|
||||
overviewNoteUpdatedAt: string
|
||||
viewMode: #ViewMode
|
||||
collectableSummaries: [...#CollectableSummary] // +listType=atomic
|
||||
}
|
||||
|
||||
// Person represents a user profile with basic information
|
||||
#Person: {
|
||||
uid: string // Unique identifier for the user
|
||||
name: string // Display name of the user
|
||||
gravatarUrl: string // URL to user's Gravatar image
|
||||
}
|
||||
|
||||
#ViewMode: {
|
||||
mode: "compact" | "full"
|
||||
showComments: bool
|
||||
showTooltips: bool
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package investigations
|
||||
|
||||
investigationIndex: {
|
||||
kind: "InvestigationIndex"
|
||||
group: "investigations.grafana.app"
|
||||
apiResource: {
|
||||
groupOverride: "investigations.grafana.app"
|
||||
}
|
||||
pluralName: "InvestigationIndexes"
|
||||
current: "v0alpha1"
|
||||
versions: {
|
||||
"v0alpha1": {
|
||||
codegen: {
|
||||
frontend: true
|
||||
backend: true
|
||||
options: {
|
||||
generateObjectMeta: true
|
||||
generateClient: true
|
||||
k8sLike: true
|
||||
package: "github.com/grafana/grafana/apps/investigations"
|
||||
}
|
||||
}
|
||||
schema: {
|
||||
spec: {
|
||||
// Title of the index, e.g. 'Favorites' or 'My Investigations'
|
||||
title: string
|
||||
|
||||
// The Person who owns this investigation index
|
||||
owner: #Person
|
||||
|
||||
// Array of investigation summaries
|
||||
investigationSummaries: [...#InvestigationSummary] // +listType=atomic
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package investigations
|
||||
|
||||
manifest: {
|
||||
appName: "investigations"
|
||||
groupOverride: "investigations.grafana.app"
|
||||
kinds: [
|
||||
investigation,
|
||||
investigationIndex,
|
||||
]
|
||||
}
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
import "k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
const (
|
||||
// Group is the API group used by all kinds in this package
|
||||
Group = "investigation.grafana.app"
|
||||
Group = "investigations.grafana.app"
|
||||
// Version is the API version used by all kinds in this package
|
||||
Version = "v1alpha1"
|
||||
Version = "v0alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
time "time"
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
// Code generated by grafana-app-sdk. DO NOT EDIT.
|
||||
//
|
||||
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/resource"
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// schema is unexported to prevent accidental overwrites
|
||||
var (
|
||||
schemaInvestigation = resource.NewSimpleSchema("investigation.grafana.app", "v1alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"),
|
||||
schemaInvestigation = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &Investigation{}, &InvestigationList{}, resource.WithKind("Investigation"),
|
||||
resource.WithPlural("investigations"), resource.WithScope(resource.NamespacedScope))
|
||||
kindInvestigation = resource.Kind{
|
||||
Schema: schemaInvestigation,
|
||||
@@ -0,0 +1,140 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
// Person represents a user profile with basic information
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationPerson struct {
|
||||
// Unique identifier for the user
|
||||
Uid string `json:"uid"`
|
||||
// Display name of the user
|
||||
Name string `json:"name"`
|
||||
// URL to user's Gravatar image
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
|
||||
// NewInvestigationPerson creates a new InvestigationPerson object.
|
||||
func NewInvestigationPerson() *InvestigationPerson {
|
||||
return &InvestigationPerson{}
|
||||
}
|
||||
|
||||
// Collectable represents an item collected during investigation
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationCollectable struct {
|
||||
Id string `json:"id"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
Title string `json:"title"`
|
||||
Origin string `json:"origin"`
|
||||
Type string `json:"type"`
|
||||
// +listType=atomic
|
||||
Queries []InvestigationQuery `json:"queries"`
|
||||
TimeRange InvestigationTimeRange `json:"timeRange"`
|
||||
Datasource InvestigationDatasourceRef `json:"datasource"`
|
||||
Url string `json:"url"`
|
||||
LogoPath *string `json:"logoPath,omitempty"`
|
||||
Note string `json:"note"`
|
||||
NoteUpdatedAt string `json:"noteUpdatedAt"`
|
||||
}
|
||||
|
||||
// NewInvestigationCollectable creates a new InvestigationCollectable object.
|
||||
func NewInvestigationCollectable() *InvestigationCollectable {
|
||||
return &InvestigationCollectable{
|
||||
TimeRange: *NewInvestigationTimeRange(),
|
||||
Datasource: *NewInvestigationDatasourceRef(),
|
||||
}
|
||||
}
|
||||
|
||||
// Query represents a data query
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationQuery struct {
|
||||
RefId string `json:"refId"`
|
||||
QueryType string `json:"queryType"`
|
||||
EditorMode string `json:"editorMode"`
|
||||
SupportingQueryType string `json:"supportingQueryType"`
|
||||
LegendFormat string `json:"legendFormat"`
|
||||
Expr string `json:"expr"`
|
||||
}
|
||||
|
||||
// NewInvestigationQuery creates a new InvestigationQuery object.
|
||||
func NewInvestigationQuery() *InvestigationQuery {
|
||||
return &InvestigationQuery{}
|
||||
}
|
||||
|
||||
// TimeRange represents a time range with both absolute and relative values
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationTimeRange struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
Raw InvestigationV0alpha1TimeRangeRaw `json:"raw"`
|
||||
}
|
||||
|
||||
// NewInvestigationTimeRange creates a new InvestigationTimeRange object.
|
||||
func NewInvestigationTimeRange() *InvestigationTimeRange {
|
||||
return &InvestigationTimeRange{
|
||||
Raw: *NewInvestigationV0alpha1TimeRangeRaw(),
|
||||
}
|
||||
}
|
||||
|
||||
// DatasourceRef is a reference to a datasource
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationDatasourceRef struct {
|
||||
Uid string `json:"uid"`
|
||||
}
|
||||
|
||||
// NewInvestigationDatasourceRef creates a new InvestigationDatasourceRef object.
|
||||
func NewInvestigationDatasourceRef() *InvestigationDatasourceRef {
|
||||
return &InvestigationDatasourceRef{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationViewMode struct {
|
||||
Mode InvestigationViewModeMode `json:"mode"`
|
||||
ShowComments bool `json:"showComments"`
|
||||
ShowTooltips bool `json:"showTooltips"`
|
||||
}
|
||||
|
||||
// NewInvestigationViewMode creates a new InvestigationViewMode object.
|
||||
func NewInvestigationViewMode() *InvestigationViewMode {
|
||||
return &InvestigationViewMode{}
|
||||
}
|
||||
|
||||
// spec is the schema of our resource
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationSpec struct {
|
||||
Title string `json:"title"`
|
||||
CreatedByProfile InvestigationPerson `json:"createdByProfile"`
|
||||
HasCustomName bool `json:"hasCustomName"`
|
||||
IsFavorite bool `json:"isFavorite"`
|
||||
OverviewNote string `json:"overviewNote"`
|
||||
OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"`
|
||||
// +listType=atomic
|
||||
Collectables []InvestigationCollectable `json:"collectables"`
|
||||
ViewMode InvestigationViewMode `json:"viewMode"`
|
||||
}
|
||||
|
||||
// NewInvestigationSpec creates a new InvestigationSpec object.
|
||||
func NewInvestigationSpec() *InvestigationSpec {
|
||||
return &InvestigationSpec{
|
||||
CreatedByProfile: *NewInvestigationPerson(),
|
||||
ViewMode: *NewInvestigationViewMode(),
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationV0alpha1TimeRangeRaw struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
// NewInvestigationV0alpha1TimeRangeRaw creates a new InvestigationV0alpha1TimeRangeRaw object.
|
||||
func NewInvestigationV0alpha1TimeRangeRaw() *InvestigationV0alpha1TimeRangeRaw {
|
||||
return &InvestigationV0alpha1TimeRangeRaw{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationViewModeMode string
|
||||
|
||||
const (
|
||||
InvestigationViewModeModeCompact InvestigationViewModeMode = "compact"
|
||||
InvestigationViewModeModeFull InvestigationViewModeMode = "full"
|
||||
)
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
package v0alpha1
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationstatusOperatorState struct {
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
// InvestigationIndexJSONCodec is an implementation of resource.Codec for kubernetes JSON encoding
|
||||
type InvestigationIndexJSONCodec struct{}
|
||||
|
||||
// Read reads JSON-encoded bytes from `reader` and unmarshals them into `into`
|
||||
func (*InvestigationIndexJSONCodec) 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 (*InvestigationIndexJSONCodec) Write(writer io.Writer, from resource.Object) error {
|
||||
return json.NewEncoder(writer).Encode(from)
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Codec = &InvestigationIndexJSONCodec{}
|
||||
+28
@@ -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 InvestigationIndexMetadata 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"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexMetadata creates a new InvestigationIndexMetadata object.
|
||||
func NewInvestigationIndexMetadata() *InvestigationIndexMetadata {
|
||||
return &InvestigationIndexMetadata{}
|
||||
}
|
||||
+266
@@ -0,0 +1,266 @@
|
||||
//
|
||||
// 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 InvestigationIndex struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" yaml:"metadata"`
|
||||
Spec InvestigationIndexSpec `json:"spec" yaml:"spec"`
|
||||
InvestigationIndexStatus InvestigationIndexStatus `json:"status" yaml:"status"`
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) GetSpec() any {
|
||||
return o.Spec
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) SetSpec(spec any) error {
|
||||
cast, ok := spec.(InvestigationIndexSpec)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set spec type %#v, not of type Spec", spec)
|
||||
}
|
||||
o.Spec = cast
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) GetSubresources() map[string]any {
|
||||
return map[string]any{
|
||||
"status": o.InvestigationIndexStatus,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) GetSubresource(name string) (any, bool) {
|
||||
switch name {
|
||||
case "status":
|
||||
return o.InvestigationIndexStatus, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) SetSubresource(name string, value any) error {
|
||||
switch name {
|
||||
case "status":
|
||||
cast, ok := value.(InvestigationIndexStatus)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot set status type %#v, not of type InvestigationIndexStatus", value)
|
||||
}
|
||||
o.InvestigationIndexStatus = cast
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("subresource '%s' does not exist", name)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) 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 *InvestigationIndex) 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 *InvestigationIndex) 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 *InvestigationIndex) 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 *InvestigationIndex) GetCreatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/createdBy"]
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) SetCreatedBy(createdBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/createdBy"] = createdBy
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) 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 *InvestigationIndex) 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 *InvestigationIndex) GetUpdatedBy() string {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
return o.ObjectMeta.Annotations["grafana.com/updatedBy"]
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) SetUpdatedBy(updatedBy string) {
|
||||
if o.ObjectMeta.Annotations == nil {
|
||||
o.ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
|
||||
o.ObjectMeta.Annotations["grafana.com/updatedBy"] = updatedBy
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) Copy() resource.Object {
|
||||
return resource.CopyObject(o)
|
||||
}
|
||||
|
||||
func (o *InvestigationIndex) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.Object = &InvestigationIndex{}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexList struct {
|
||||
metav1.TypeMeta `json:",inline" yaml:",inline"`
|
||||
metav1.ListMeta `json:"metadata" yaml:"metadata"`
|
||||
Items []InvestigationIndex `json:"items" yaml:"items"`
|
||||
}
|
||||
|
||||
func (o *InvestigationIndexList) DeepCopyObject() runtime.Object {
|
||||
return o.Copy()
|
||||
}
|
||||
|
||||
func (o *InvestigationIndexList) Copy() resource.ListObject {
|
||||
cpy := &InvestigationIndexList{
|
||||
TypeMeta: o.TypeMeta,
|
||||
Items: make([]InvestigationIndex, len(o.Items)),
|
||||
}
|
||||
o.ListMeta.DeepCopyInto(&cpy.ListMeta)
|
||||
for i := 0; i < len(o.Items); i++ {
|
||||
if item, ok := o.Items[i].Copy().(*InvestigationIndex); ok {
|
||||
cpy.Items[i] = *item
|
||||
}
|
||||
}
|
||||
return cpy
|
||||
}
|
||||
|
||||
func (o *InvestigationIndexList) 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 *InvestigationIndexList) SetItems(items []resource.Object) {
|
||||
o.Items = make([]InvestigationIndex, len(items))
|
||||
for i := 0; i < len(items); i++ {
|
||||
o.Items[i] = *items[i].(*InvestigationIndex)
|
||||
}
|
||||
}
|
||||
|
||||
// Interface compliance compile-time check
|
||||
var _ resource.ListObject = &InvestigationIndexList{}
|
||||
@@ -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 (
|
||||
schemaInvestigationIndex = resource.NewSimpleSchema("investigations.grafana.app", "v0alpha1", &InvestigationIndex{}, &InvestigationIndexList{}, resource.WithKind("InvestigationIndex"),
|
||||
resource.WithPlural("investigationindexes"), resource.WithScope(resource.NamespacedScope))
|
||||
kindInvestigationIndex = resource.Kind{
|
||||
Schema: schemaInvestigationIndex,
|
||||
Codecs: map[resource.KindEncoding]resource.Codec{
|
||||
resource.KindEncodingJSON: &InvestigationIndexJSONCodec{},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// Kind returns a resource.Kind for this Schema with a JSON codec
|
||||
func InvestigationIndexKind() resource.Kind {
|
||||
return kindInvestigationIndex
|
||||
}
|
||||
|
||||
// Schema returns a resource.SimpleSchema representation of InvestigationIndex
|
||||
func InvestigationIndexSchema() *resource.SimpleSchema {
|
||||
return schemaInvestigationIndex
|
||||
}
|
||||
|
||||
// Interface compliance checks
|
||||
var _ resource.Schema = kindInvestigationIndex
|
||||
@@ -0,0 +1,92 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
// Person represents a user profile with basic information
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexPerson struct {
|
||||
// Unique identifier for the user
|
||||
Uid string `json:"uid"`
|
||||
// Display name of the user
|
||||
Name string `json:"name"`
|
||||
// URL to user's Gravatar image
|
||||
GravatarUrl string `json:"gravatarUrl"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexPerson creates a new InvestigationIndexPerson object.
|
||||
func NewInvestigationIndexPerson() *InvestigationIndexPerson {
|
||||
return &InvestigationIndexPerson{}
|
||||
}
|
||||
|
||||
// Type definition for investigation summaries
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexInvestigationSummary struct {
|
||||
Title string `json:"title"`
|
||||
CreatedByProfile InvestigationIndexPerson `json:"createdByProfile"`
|
||||
HasCustomName bool `json:"hasCustomName"`
|
||||
IsFavorite bool `json:"isFavorite"`
|
||||
OverviewNote string `json:"overviewNote"`
|
||||
OverviewNoteUpdatedAt string `json:"overviewNoteUpdatedAt"`
|
||||
ViewMode InvestigationIndexViewMode `json:"viewMode"`
|
||||
// +listType=atomic
|
||||
CollectableSummaries []InvestigationIndexCollectableSummary `json:"collectableSummaries"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexInvestigationSummary creates a new InvestigationIndexInvestigationSummary object.
|
||||
func NewInvestigationIndexInvestigationSummary() *InvestigationIndexInvestigationSummary {
|
||||
return &InvestigationIndexInvestigationSummary{
|
||||
CreatedByProfile: *NewInvestigationIndexPerson(),
|
||||
ViewMode: *NewInvestigationIndexViewMode(),
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexViewMode struct {
|
||||
Mode InvestigationIndexViewModeMode `json:"mode"`
|
||||
ShowComments bool `json:"showComments"`
|
||||
ShowTooltips bool `json:"showTooltips"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexViewMode creates a new InvestigationIndexViewMode object.
|
||||
func NewInvestigationIndexViewMode() *InvestigationIndexViewMode {
|
||||
return &InvestigationIndexViewMode{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexCollectableSummary struct {
|
||||
Id string `json:"id"`
|
||||
Title string `json:"title"`
|
||||
LogoPath string `json:"logoPath"`
|
||||
Origin string `json:"origin"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexCollectableSummary creates a new InvestigationIndexCollectableSummary object.
|
||||
func NewInvestigationIndexCollectableSummary() *InvestigationIndexCollectableSummary {
|
||||
return &InvestigationIndexCollectableSummary{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexSpec struct {
|
||||
// Title of the index, e.g. 'Favorites' or 'My Investigations'
|
||||
Title string `json:"title"`
|
||||
// The Person who owns this investigation index
|
||||
Owner InvestigationIndexPerson `json:"owner"`
|
||||
// Array of investigation summaries
|
||||
// +listType=atomic
|
||||
InvestigationSummaries []InvestigationIndexInvestigationSummary `json:"investigationSummaries"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexSpec creates a new InvestigationIndexSpec object.
|
||||
func NewInvestigationIndexSpec() *InvestigationIndexSpec {
|
||||
return &InvestigationIndexSpec{
|
||||
Owner: *NewInvestigationIndexPerson(),
|
||||
}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexViewModeMode string
|
||||
|
||||
const (
|
||||
InvestigationIndexViewModeModeCompact InvestigationIndexViewModeMode = "compact"
|
||||
InvestigationIndexViewModeModeFull InvestigationIndexViewModeMode = "full"
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
package v0alpha1
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexstatusOperatorState struct {
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
LastEvaluation string `json:"lastEvaluation"`
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
State InvestigationIndexStatusOperatorStateState `json:"state"`
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
DescriptiveState *string `json:"descriptiveState,omitempty"`
|
||||
// details contains any extra information that is operator-specific
|
||||
Details map[string]interface{} `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexstatusOperatorState creates a new InvestigationIndexstatusOperatorState object.
|
||||
func NewInvestigationIndexstatusOperatorState() *InvestigationIndexstatusOperatorState {
|
||||
return &InvestigationIndexstatusOperatorState{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexStatus struct {
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
OperatorStates map[string]InvestigationIndexstatusOperatorState `json:"operatorStates,omitempty"`
|
||||
// additionalFields is reserved for future use
|
||||
AdditionalFields map[string]interface{} `json:"additionalFields,omitempty"`
|
||||
}
|
||||
|
||||
// NewInvestigationIndexStatus creates a new InvestigationIndexStatus object.
|
||||
func NewInvestigationIndexStatus() *InvestigationIndexStatus {
|
||||
return &InvestigationIndexStatus{}
|
||||
}
|
||||
|
||||
// +k8s:openapi-gen=true
|
||||
type InvestigationIndexStatusOperatorStateState string
|
||||
|
||||
const (
|
||||
InvestigationIndexStatusOperatorStateStateSuccess InvestigationIndexStatusOperatorStateState = "success"
|
||||
InvestigationIndexStatusOperatorStateStateInProgress InvestigationIndexStatusOperatorStateState = "in_progress"
|
||||
InvestigationIndexStatusOperatorStateStateFailed InvestigationIndexStatusOperatorStateState = "failed"
|
||||
)
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// This file is generated by grafana-app-sdk
|
||||
// DO NOT EDIT
|
||||
//
|
||||
|
||||
package apis
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
)
|
||||
|
||||
var (
|
||||
rawSchemaInvestigationv0alpha1 = []byte(`{"spec":{"description":"spec is the schema of our resource","properties":{"collectables":{"items":{"properties":{"createdAt":{"type":"string"},"datasource":{"properties":{"uid":{"type":"string"}},"required":["uid"],"type":"object"},"id":{"type":"string"},"logoPath":{"type":"string"},"note":{"type":"string"},"noteUpdatedAt":{"type":"string"},"origin":{"type":"string"},"queries":{"items":{"properties":{"editorMode":{"type":"string"},"expr":{"type":"string"},"legendFormat":{"type":"string"},"queryType":{"type":"string"},"refId":{"type":"string"},"supportingQueryType":{"type":"string"}},"required":["refId","queryType","editorMode","supportingQueryType","legendFormat","expr"],"type":"object"},"type":"array"},"timeRange":{"properties":{"from":{"type":"string"},"raw":{"properties":{"from":{"type":"string"},"to":{"type":"string"}},"required":["from","to"],"type":"object"},"to":{"type":"string"}},"required":["from","to","raw"],"type":"object"},"title":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["id","createdAt","title","origin","type","queries","timeRange","datasource","url","note","noteUpdatedAt"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","collectables","viewMode"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaInvestigationv0alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaInvestigationv0alpha1, &versionSchemaInvestigationv0alpha1)
|
||||
rawSchemaInvestigationIndexv0alpha1 = []byte(`{"spec":{"properties":{"investigationSummaries":{"description":"Array of investigation summaries","items":{"properties":{"collectableSummaries":{"items":{"properties":{"id":{"type":"string"},"logoPath":{"type":"string"},"origin":{"type":"string"},"title":{"type":"string"}},"required":["id","title","logoPath","origin"],"type":"object"},"type":"array"},"createdByProfile":{"properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"hasCustomName":{"type":"boolean"},"isFavorite":{"type":"boolean"},"overviewNote":{"type":"string"},"overviewNoteUpdatedAt":{"type":"string"},"title":{"type":"string"},"viewMode":{"properties":{"mode":{"enum":["compact","full"],"type":"string"},"showComments":{"type":"boolean"},"showTooltips":{"type":"boolean"}},"required":["mode","showComments","showTooltips"],"type":"object"}},"required":["title","createdByProfile","hasCustomName","isFavorite","overviewNote","overviewNoteUpdatedAt","viewMode","collectableSummaries"],"type":"object"},"type":"array"},"owner":{"description":"The Person who owns this investigation index","properties":{"gravatarUrl":{"type":"string"},"name":{"type":"string"},"uid":{"type":"string"}},"required":["uid","name","gravatarUrl"],"type":"object"},"title":{"description":"Title of the index, e.g. 'Favorites' or 'My Investigations'","type":"string"}},"required":["title","owner","investigationSummaries"],"type":"object"},"status":{"properties":{"additionalFields":{"description":"additionalFields is reserved for future use","type":"object","x-kubernetes-preserve-unknown-fields":true},"operatorStates":{"additionalProperties":{"properties":{"descriptiveState":{"description":"descriptiveState is an optional more descriptive state field which has no requirements on format","type":"string"},"details":{"description":"details contains any extra information that is operator-specific","type":"object","x-kubernetes-preserve-unknown-fields":true},"lastEvaluation":{"description":"lastEvaluation is the ResourceVersion last evaluated","type":"string"},"state":{"description":"state describes the state of the lastEvaluation.\nIt is limited to three possible states for machine evaluation.","enum":["success","in_progress","failed"],"type":"string"}},"required":["lastEvaluation","state"],"type":"object"},"description":"operatorStates is a map of operator ID to operator state evaluations.\nAny operator which consumes this kind SHOULD add its state evaluation information to this field.","type":"object"}},"type":"object","x-kubernetes-preserve-unknown-fields":true}}`)
|
||||
versionSchemaInvestigationIndexv0alpha1 app.VersionSchema
|
||||
_ = json.Unmarshal(rawSchemaInvestigationIndexv0alpha1, &versionSchemaInvestigationIndexv0alpha1)
|
||||
)
|
||||
|
||||
var appManifestData = app.ManifestData{
|
||||
AppName: "investigations",
|
||||
Group: "investigations.grafana.app",
|
||||
Kinds: []app.ManifestKind{
|
||||
{
|
||||
Kind: "Investigation",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
Versions: []app.ManifestKindVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
Schema: &versionSchemaInvestigationv0alpha1,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Kind: "InvestigationIndex",
|
||||
Scope: "Namespaced",
|
||||
Conversion: false,
|
||||
Versions: []app.ManifestKindVersion{
|
||||
{
|
||||
Name: "v0alpha1",
|
||||
Schema: &versionSchemaInvestigationIndexv0alpha1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func jsonToMap(j string) map[string]any {
|
||||
m := make(map[string]any)
|
||||
json.Unmarshal([]byte(j), &j)
|
||||
return m
|
||||
}
|
||||
|
||||
func LocalManifest() app.Manifest {
|
||||
return app.NewEmbeddedManifest(appManifestData)
|
||||
}
|
||||
|
||||
func RemoteManifest() app.Manifest {
|
||||
return app.NewAPIServerManifest("investigations")
|
||||
}
|
||||
+11
-5
@@ -9,7 +9,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
investigationv1alpha1 "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1"
|
||||
investigationsv0alpha1 "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1"
|
||||
)
|
||||
|
||||
func New(cfg app.Config) (app.App, error) {
|
||||
@@ -24,7 +24,10 @@ func New(cfg app.Config) (app.App, error) {
|
||||
},
|
||||
ManagedKinds: []simple.AppManagedKind{
|
||||
{
|
||||
Kind: investigationv1alpha1.InvestigationKind(),
|
||||
Kind: investigationsv0alpha1.InvestigationKind(),
|
||||
},
|
||||
{
|
||||
Kind: investigationsv0alpha1.InvestigationIndexKind(),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -44,10 +47,13 @@ func New(cfg app.Config) (app.App, error) {
|
||||
|
||||
func GetKinds() map[schema.GroupVersion][]resource.Kind {
|
||||
gv := schema.GroupVersion{
|
||||
Group: investigationv1alpha1.InvestigationKind().Group(),
|
||||
Version: investigationv1alpha1.InvestigationKind().Version(),
|
||||
Group: investigationsv0alpha1.InvestigationKind().Group(),
|
||||
Version: investigationsv0alpha1.InvestigationKind().Version(),
|
||||
}
|
||||
return map[schema.GroupVersion][]resource.Kind{
|
||||
gv: {investigationv1alpha1.InvestigationKind()},
|
||||
gv: {
|
||||
investigationsv0alpha1.InvestigationKind(),
|
||||
investigationsv0alpha1.InvestigationIndexKind(),
|
||||
},
|
||||
}
|
||||
}
|
||||
+49
@@ -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 Investigation {
|
||||
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,132 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
// Person represents a user profile with basic information
|
||||
export interface Person {
|
||||
// Unique identifier for the user
|
||||
uid: string;
|
||||
// Display name of the user
|
||||
name: string;
|
||||
// URL to user's Gravatar image
|
||||
gravatarUrl: string;
|
||||
}
|
||||
|
||||
export const defaultPerson = (): Person => ({
|
||||
uid: "",
|
||||
name: "",
|
||||
gravatarUrl: "",
|
||||
});
|
||||
|
||||
// Collectable represents an item collected during investigation
|
||||
export interface Collectable {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
title: string;
|
||||
origin: string;
|
||||
type: string;
|
||||
// +listType=atomic
|
||||
queries: Query[];
|
||||
timeRange: TimeRange;
|
||||
datasource: DatasourceRef;
|
||||
url: string;
|
||||
logoPath?: string;
|
||||
note: string;
|
||||
noteUpdatedAt: string;
|
||||
}
|
||||
|
||||
export const defaultCollectable = (): Collectable => ({
|
||||
id: "",
|
||||
createdAt: "",
|
||||
title: "",
|
||||
origin: "",
|
||||
type: "",
|
||||
queries: [],
|
||||
timeRange: defaultTimeRange(),
|
||||
datasource: defaultDatasourceRef(),
|
||||
url: "",
|
||||
note: "",
|
||||
noteUpdatedAt: "",
|
||||
});
|
||||
|
||||
// Query represents a data query
|
||||
export interface Query {
|
||||
refId: string;
|
||||
queryType: string;
|
||||
editorMode: string;
|
||||
supportingQueryType: string;
|
||||
legendFormat: string;
|
||||
expr: string;
|
||||
}
|
||||
|
||||
export const defaultQuery = (): Query => ({
|
||||
refId: "",
|
||||
queryType: "",
|
||||
editorMode: "",
|
||||
supportingQueryType: "",
|
||||
legendFormat: "",
|
||||
expr: "",
|
||||
});
|
||||
|
||||
// TimeRange represents a time range with both absolute and relative values
|
||||
export interface TimeRange {
|
||||
from: string;
|
||||
to: string;
|
||||
raw: {
|
||||
from: string;
|
||||
to: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const defaultTimeRange = (): TimeRange => ({
|
||||
from: "",
|
||||
to: "",
|
||||
raw: {
|
||||
from: "",
|
||||
to: "",
|
||||
},
|
||||
});
|
||||
|
||||
// DatasourceRef is a reference to a datasource
|
||||
export interface DatasourceRef {
|
||||
uid: string;
|
||||
}
|
||||
|
||||
export const defaultDatasourceRef = (): DatasourceRef => ({
|
||||
uid: "",
|
||||
});
|
||||
|
||||
export interface ViewMode {
|
||||
mode: "compact" | "full";
|
||||
showComments: boolean;
|
||||
showTooltips: boolean;
|
||||
}
|
||||
|
||||
export const defaultViewMode = (): ViewMode => ({
|
||||
mode: "compact",
|
||||
showComments: false,
|
||||
showTooltips: false,
|
||||
});
|
||||
|
||||
// spec is the schema of our resource
|
||||
export interface Spec {
|
||||
title: string;
|
||||
createdByProfile: Person;
|
||||
hasCustomName: boolean;
|
||||
isFavorite: boolean;
|
||||
overviewNote: string;
|
||||
overviewNoteUpdatedAt: string;
|
||||
// +listType=atomic
|
||||
collectables: Collectable[];
|
||||
viewMode: ViewMode;
|
||||
}
|
||||
|
||||
export const defaultSpec = (): Spec => ({
|
||||
title: "",
|
||||
createdByProfile: defaultPerson(),
|
||||
hasCustomName: false,
|
||||
isFavorite: false,
|
||||
overviewNote: "",
|
||||
overviewNoteUpdatedAt: "",
|
||||
collectables: [],
|
||||
viewMode: defaultViewMode(),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
export interface OperatorState {
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
lastEvaluation: string;
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
state: "success" | "in_progress" | "failed";
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
descriptiveState?: string;
|
||||
// details contains any extra information that is operator-specific
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultOperatorState = (): OperatorState => ({
|
||||
lastEvaluation: "",
|
||||
state: "success",
|
||||
});
|
||||
|
||||
export interface Status {
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
operatorStates?: Record<string, OperatorState>;
|
||||
// additionalFields is reserved for future use
|
||||
additionalFields?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultStatus = (): Status => ({
|
||||
});
|
||||
|
||||
+49
@@ -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 InvestigationIndex {
|
||||
kind: string;
|
||||
apiVersion: string;
|
||||
metadata: Metadata;
|
||||
spec: Spec;
|
||||
status: Status;
|
||||
}
|
||||
+30
@@ -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: {},
|
||||
});
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
// Person represents a user profile with basic information
|
||||
export interface Person {
|
||||
// Unique identifier for the user
|
||||
uid: string;
|
||||
// Display name of the user
|
||||
name: string;
|
||||
// URL to user's Gravatar image
|
||||
gravatarUrl: string;
|
||||
}
|
||||
|
||||
export const defaultPerson = (): Person => ({
|
||||
uid: "",
|
||||
name: "",
|
||||
gravatarUrl: "",
|
||||
});
|
||||
|
||||
// Type definition for investigation summaries
|
||||
export interface InvestigationSummary {
|
||||
title: string;
|
||||
createdByProfile: Person;
|
||||
hasCustomName: boolean;
|
||||
isFavorite: boolean;
|
||||
overviewNote: string;
|
||||
overviewNoteUpdatedAt: string;
|
||||
viewMode: ViewMode;
|
||||
// +listType=atomic
|
||||
collectableSummaries: CollectableSummary[];
|
||||
}
|
||||
|
||||
export const defaultInvestigationSummary = (): InvestigationSummary => ({
|
||||
title: "",
|
||||
createdByProfile: defaultPerson(),
|
||||
hasCustomName: false,
|
||||
isFavorite: false,
|
||||
overviewNote: "",
|
||||
overviewNoteUpdatedAt: "",
|
||||
viewMode: defaultViewMode(),
|
||||
collectableSummaries: [],
|
||||
});
|
||||
|
||||
export interface ViewMode {
|
||||
mode: "compact" | "full";
|
||||
showComments: boolean;
|
||||
showTooltips: boolean;
|
||||
}
|
||||
|
||||
export const defaultViewMode = (): ViewMode => ({
|
||||
mode: "compact",
|
||||
showComments: false,
|
||||
showTooltips: false,
|
||||
});
|
||||
|
||||
export interface CollectableSummary {
|
||||
id: string;
|
||||
title: string;
|
||||
logoPath: string;
|
||||
origin: string;
|
||||
}
|
||||
|
||||
export const defaultCollectableSummary = (): CollectableSummary => ({
|
||||
id: "",
|
||||
title: "",
|
||||
logoPath: "",
|
||||
origin: "",
|
||||
});
|
||||
|
||||
export interface Spec {
|
||||
// Title of the index, e.g. 'Favorites' or 'My Investigations'
|
||||
title: string;
|
||||
// The Person who owns this investigation index
|
||||
owner: Person;
|
||||
// Array of investigation summaries
|
||||
// +listType=atomic
|
||||
investigationSummaries: InvestigationSummary[];
|
||||
}
|
||||
|
||||
export const defaultSpec = (): Spec => ({
|
||||
title: "",
|
||||
owner: defaultPerson(),
|
||||
investigationSummaries: [],
|
||||
});
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
|
||||
export interface OperatorState {
|
||||
// lastEvaluation is the ResourceVersion last evaluated
|
||||
lastEvaluation: string;
|
||||
// state describes the state of the lastEvaluation.
|
||||
// It is limited to three possible states for machine evaluation.
|
||||
state: "success" | "in_progress" | "failed";
|
||||
// descriptiveState is an optional more descriptive state field which has no requirements on format
|
||||
descriptiveState?: string;
|
||||
// details contains any extra information that is operator-specific
|
||||
details?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultOperatorState = (): OperatorState => ({
|
||||
lastEvaluation: "",
|
||||
state: "success",
|
||||
});
|
||||
|
||||
export interface Status {
|
||||
// operatorStates is a map of operator ID to operator state evaluations.
|
||||
// Any operator which consumes this kind SHOULD add its state evaluation information to this field.
|
||||
operatorStates?: Record<string, OperatorState>;
|
||||
// additionalFields is reserved for future use
|
||||
additionalFields?: Record<string, any>;
|
||||
}
|
||||
|
||||
export const defaultStatus = (): Status => ({
|
||||
});
|
||||
|
||||
@@ -46,11 +46,9 @@ Most [generally available](https://grafana.com/docs/release-life-cycle/#general-
|
||||
| `alertingInsights` | Show the new alerting insights landing page | Yes |
|
||||
| `panelMonitoring` | Enables panel monitoring through logs and measurements | Yes |
|
||||
| `formatString` | Enable format string transformer | Yes |
|
||||
| `transformationsVariableSupport` | Allows using variables in transformations | Yes |
|
||||
| `kubernetesPlaylists` | Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s | Yes |
|
||||
| `recoveryThreshold` | Enables feature recovery threshold (aka hysteresis) for threshold server-side expression | Yes |
|
||||
| `lokiStructuredMetadata` | Enables the loki data source to request structured metadata from the Loki server | Yes |
|
||||
| `managedPluginsInstall` | Install managed plugins directly from plugins catalog | Yes |
|
||||
| `addFieldFromCalculationStatFunctions` | Add cumulative and window functions to the add field from calculation transformation | Yes |
|
||||
| `annotationPermissionUpdate` | Change the way annotation permissions work by scoping them to folders and dashboards. | Yes |
|
||||
| `dashboardSceneForViewers` | Enables dashboard rendering using Scenes for viewer roles | Yes |
|
||||
|
||||
Whitespace-only changes.
@@ -75,7 +75,7 @@ require (
|
||||
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-20250207161551-04c87cf39038 // @grafana/alerting-backend
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c // @grafana/identity-access-team
|
||||
github.com/grafana/dashboard-linter v0.0.0-20241106223805-1e7999311752 // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
|
||||
@@ -207,7 +207,7 @@ require (
|
||||
require (
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173 // @grafana/plugins-platform-backend
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d // @grafana/alerting-backend
|
||||
github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d // @fcjack @matryer
|
||||
github.com/grafana/grafana/apps/investigations v0.0.0-20250217190546-9df6412e9255 // @fcjack @matryer
|
||||
github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250121113133-e747350fee2d // @grafana/grafana-app-platform-squad
|
||||
@@ -450,6 +450,8 @@ require (
|
||||
github.com/natefinch/wrap v0.2.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/oapi-codegen/runtime v1.0.0 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/oklog/ulid/v2 v2.1.0 // indirect
|
||||
@@ -560,11 +562,7 @@ require (
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/google/go-github/v64 v64.0.0 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
|
||||
)
|
||||
require github.com/google/go-github/v64 v64.0.0 // indirect
|
||||
|
||||
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream
|
||||
replace github.com/crewjam/saml => github.com/grafana/saml v0.4.15-0.20240917091248-ae3bbdad8a56
|
||||
|
||||
@@ -1530,8 +1530,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038 h1:dG/UKAjY/KlKp9fY8aEm+gSQHHRmPm5q+9cea3hRSu8=
|
||||
github.com/grafana/alerting v0.0.0-20250207161551-04c87cf39038/go.mod h1:QsnoKX/iYZxA4Cv+H+wC7uxutBD8qi8ZW5UJvD2TYmU=
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569 h1:bQw6fdcxVdZ6xmZVhMtRgGEKIT1Zc6y+i1PWF8tMX4Q=
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8=
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc h1:qx8avmRi3wx+SuAn6UV0pKma1fJ0/4POUYNc+2rxZkg=
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc/go.mod h1:HamgITzYqErFp8qLwYM/JwwE3z6fXft0Z+5n0Dx1rUM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c h1:b0sPDtt33uFdmvUJjSCld3kwE2E49dUvevuUDSJsEuo=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
|
||||
github.com/grafana/dashboard-linter v0.0.0-20241106223805-1e7999311752 h1:pIEPQPua6kCkMib/nOn//WLgtiA4urf2RPBViSF6CcE=
|
||||
@@ -1572,8 +1572,8 @@ github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173 h1:uO
|
||||
github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA=
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d h1:NRVOtiG1aUwOazBj9KM7X2o2shsM6TchqisezzoH1gw=
|
||||
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d/go.mod h1:AvleS6icyPmcBjihtx5jYEvdzLmHGBp66NuE0AMR57A=
|
||||
github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d h1:oNc/aDfDucQxLbRZK25yz3Cwc+dGo1C0Xmm2LaliWUQ=
|
||||
github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI=
|
||||
github.com/grafana/grafana/apps/investigations v0.0.0-20250217190546-9df6412e9255 h1:4bEH/v1cbODIie8rMAqrsUlXeWLu7ht9SXNIrdvMWmA=
|
||||
github.com/grafana/grafana/apps/investigations v0.0.0-20250217190546-9df6412e9255/go.mod h1:ygFcJP2McdSeMJVj/3YrKafZMc/lZBsp54HO51MtJYw=
|
||||
github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d h1:cJ+9jgHw8Z17rM+DCdxvyWxPqH9OB9fkPLbnWEO+QGg=
|
||||
github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA=
|
||||
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d h1:aBD5kzsIAh50vjNqUkWK9mNpLGIBYAnKkWtUepGNAiQ=
|
||||
|
||||
@@ -7,7 +7,7 @@ use (
|
||||
. // skip:golangci-lint
|
||||
./apps/advisor
|
||||
./apps/alerting/notifications
|
||||
./apps/investigation
|
||||
./apps/investigations
|
||||
./apps/playlist
|
||||
./pkg/aggregator
|
||||
./pkg/apimachinery
|
||||
|
||||
-1644
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -195,7 +195,7 @@
|
||||
"html-loader": "5.1.0",
|
||||
"html-webpack-plugin": "5.6.3",
|
||||
"http-server": "14.1.1",
|
||||
"i18next-parser": "9.1.0",
|
||||
"i18next-parser": "9.3.0",
|
||||
"ini": "^5.0.0",
|
||||
"jest": "29.7.0",
|
||||
"jest-canvas-mock": "2.5.2",
|
||||
|
||||
@@ -548,6 +548,7 @@ export {
|
||||
type PluginExtension,
|
||||
type PluginExtensionLink,
|
||||
type PluginExtensionComponent,
|
||||
type PluginExtensionComponentMeta,
|
||||
type PluginExtensionConfig,
|
||||
type PluginExtensionFunction,
|
||||
type PluginExtensionLinkConfig,
|
||||
|
||||
@@ -1,8 +1,22 @@
|
||||
import {
|
||||
SceneDataNode,
|
||||
SceneDataTransformer,
|
||||
SceneDeactivationHandler,
|
||||
SceneFlexItem,
|
||||
SceneFlexLayout,
|
||||
sceneGraph,
|
||||
SceneObject,
|
||||
SceneObjectBase,
|
||||
SceneVariable,
|
||||
SceneVariableSet,
|
||||
TestVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { DataTransformerConfig, LoadingState } from '@grafana/schema';
|
||||
|
||||
import { DataFrameView } from '../../dataframe/DataFrameView';
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { ScopedVars } from '../../types/ScopedVars';
|
||||
import { FieldType } from '../../types/dataFrame';
|
||||
import { DataTransformContext } from '../../types/transformations';
|
||||
import { DataFrame, FieldType } from '../../types/dataFrame';
|
||||
import { getDefaultTimeRange } from '../../types/time';
|
||||
import { BinaryOperationID } from '../../utils/binaryOperators';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { UnaryOperationID } from '../../utils/unaryOperators';
|
||||
@@ -565,51 +579,26 @@ describe('calculateField transformer w/ timeseries', () => {
|
||||
replaceFields: true,
|
||||
},
|
||||
};
|
||||
const context: DataTransformContext = {
|
||||
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
|
||||
if (!target) {
|
||||
return '';
|
||||
}
|
||||
const variables: ScopedVars = {
|
||||
var1: {
|
||||
value: 'Test',
|
||||
text: 'Test',
|
||||
},
|
||||
var2: {
|
||||
value: 5,
|
||||
text: '5',
|
||||
},
|
||||
__interval: {
|
||||
value: 10000,
|
||||
text: '10000',
|
||||
},
|
||||
};
|
||||
for (const key in variables) {
|
||||
if (target === `$${key}`) {
|
||||
return variables[key]!.value + '';
|
||||
}
|
||||
}
|
||||
return target;
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesA], context)).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"Test": 6,
|
||||
"TheTime": 1000,
|
||||
},
|
||||
{
|
||||
"Test": 105,
|
||||
"TheTime": 2000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
const data = setupTransformationScene(seriesA, cfg, [
|
||||
new TestVariable({ name: 'var1', value: 'Test' }),
|
||||
new TestVariable({ name: 'var2', value: 5 }),
|
||||
]);
|
||||
|
||||
const filtered = data[0];
|
||||
const rows = new DataFrameView(filtered).toArray();
|
||||
expect(rows).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"Test": 6,
|
||||
"TheTime": 1000,
|
||||
},
|
||||
{
|
||||
"Test": 105,
|
||||
"TheTime": 2000,
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('calculates centered moving average on odd window size', async () => {
|
||||
@@ -750,8 +739,6 @@ describe('calculateField transformer w/ timeseries', () => {
|
||||
await expect(transformDataFrame([cfg], [series])).toEmitValuesWith((received) => {
|
||||
const data = received[0][0];
|
||||
|
||||
//console.log(data.fields);
|
||||
|
||||
expect(data.fields.length).toEqual(2);
|
||||
expect(data.fields[1].values).toEqual([1, 1, 3, 3.5, 4.5]);
|
||||
});
|
||||
@@ -1185,3 +1172,63 @@ describe('calculateField transformer w/ timeseries', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function activateFullSceneTree(scene: SceneObject): SceneDeactivationHandler {
|
||||
const deactivationHandlers: SceneDeactivationHandler[] = [];
|
||||
|
||||
// Important that variables are activated before other children
|
||||
if (scene.state.$variables) {
|
||||
deactivationHandlers.push(activateFullSceneTree(scene.state.$variables));
|
||||
}
|
||||
|
||||
scene.forEachChild((child) => {
|
||||
// For query runners which by default use the container width for maxDataPoints calculation we are setting a width.
|
||||
// In real life this is done by the React component when VizPanel is rendered.
|
||||
if ('setContainerWidth' in child) {
|
||||
// @ts-expect-error
|
||||
child.setContainerWidth(500);
|
||||
}
|
||||
deactivationHandlers.push(activateFullSceneTree(child));
|
||||
});
|
||||
|
||||
deactivationHandlers.push(scene.activate());
|
||||
|
||||
return () => {
|
||||
for (const handler of deactivationHandlers) {
|
||||
handler();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function setupTransformationScene(
|
||||
inputData: DataFrame,
|
||||
cfg: DataTransformerConfig,
|
||||
variables: SceneVariable[]
|
||||
): DataFrame[] {
|
||||
class TestSceneObject extends SceneObjectBase<{}> {}
|
||||
const dataNode = new SceneDataNode({
|
||||
data: {
|
||||
state: LoadingState.Loading,
|
||||
timeRange: getDefaultTimeRange(),
|
||||
series: [inputData],
|
||||
},
|
||||
});
|
||||
|
||||
const transformationNode = new SceneDataTransformer({
|
||||
transformations: [cfg],
|
||||
});
|
||||
|
||||
const consumer = new TestSceneObject({
|
||||
$data: transformationNode,
|
||||
});
|
||||
|
||||
const scene = new SceneFlexLayout({
|
||||
$data: dataNode,
|
||||
$variables: new SceneVariableSet({ variables }),
|
||||
children: [new SceneFlexItem({ body: consumer })],
|
||||
});
|
||||
|
||||
activateFullSceneTree(scene);
|
||||
|
||||
return sceneGraph.getData(consumer).state.data?.series!;
|
||||
}
|
||||
@@ -139,10 +139,6 @@ export const calculateFieldTransformer: DataTransformerInfo<CalculateFieldTransf
|
||||
? ensureColumnsTransformer.operator(null, ctx)
|
||||
: noopTransformer.operator({}, ctx);
|
||||
|
||||
if (options.alias != null) {
|
||||
options.alias = ctx.interpolate(options.alias);
|
||||
}
|
||||
|
||||
return outerSource.pipe(
|
||||
operator,
|
||||
map((data) => {
|
||||
@@ -573,7 +569,8 @@ function findFieldValuesWithNameOrConstant(
|
||||
}
|
||||
|
||||
if (value.matcher && value.matcher.id === FieldMatcherID.byName) {
|
||||
const name = ctx.interpolate(value.matcher.options ?? '');
|
||||
const name = value.matcher.options ?? '';
|
||||
|
||||
for (const f of frame.fields) {
|
||||
if (name === getFieldDisplayName(f, frame, allFrames)) {
|
||||
if (f.type === FieldType.boolean) {
|
||||
@@ -584,7 +581,7 @@ function findFieldValuesWithNameOrConstant(
|
||||
}
|
||||
}
|
||||
|
||||
const v = parseFloat(value.fixed ?? ctx.interpolate(value.matcher?.options ?? ''));
|
||||
const v = parseFloat(value.fixed ?? value.matcher?.options ?? '');
|
||||
if (!isNaN(v)) {
|
||||
return new Array(frame.length).fill(v);
|
||||
}
|
||||
|
||||
@@ -28,15 +28,15 @@ export const filterFieldsTransformer: DataTransformerInfo<FilterOptions> = {
|
||||
}
|
||||
|
||||
if (typeof options.include?.options === 'string') {
|
||||
options.include.options = ctx.interpolate(options.include?.options);
|
||||
options.include.options = options.include?.options;
|
||||
} else if (typeof options.include?.options?.pattern === 'string') {
|
||||
options.include.options.pattern = ctx.interpolate(options.include?.options.pattern);
|
||||
options.include.options.pattern = options.include?.options.pattern;
|
||||
}
|
||||
|
||||
if (typeof options.exclude?.options === 'string') {
|
||||
options.exclude.options = ctx.interpolate(options.exclude?.options);
|
||||
options.exclude.options = options.exclude?.options;
|
||||
} else if (typeof options.exclude?.options?.pattern === 'string') {
|
||||
options.exclude.options.pattern = ctx.interpolate(options.exclude?.options.pattern);
|
||||
options.exclude.options.pattern = options.exclude?.options.pattern;
|
||||
}
|
||||
|
||||
return source.pipe(
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import {
|
||||
SceneDataNode,
|
||||
SceneDataTransformer,
|
||||
SceneDeactivationHandler,
|
||||
SceneFlexItem,
|
||||
SceneFlexLayout,
|
||||
sceneGraph,
|
||||
SceneObject,
|
||||
SceneObjectBase,
|
||||
SceneVariable,
|
||||
SceneVariableSet,
|
||||
TestVariable,
|
||||
} from '@grafana/scenes';
|
||||
import { DataTransformerConfig, LoadingState } from '@grafana/schema';
|
||||
|
||||
import { toDataFrame } from '../../dataframe/processDataFrame';
|
||||
import { ScopedVars } from '../../types/ScopedVars';
|
||||
import { FieldType } from '../../types/dataFrame';
|
||||
import { DataFrame, FieldType } from '../../types/dataFrame';
|
||||
import { getDefaultTimeRange } from '../../types/time';
|
||||
import { mockTransformationsRegistry } from '../../utils/tests/mockTransformationsRegistry';
|
||||
import { transformDataFrame } from '../transformDataFrame';
|
||||
|
||||
@@ -196,6 +211,7 @@ describe('filterByName transformer', () => {
|
||||
expect(filtered.fields[0].name).toBe('B');
|
||||
});
|
||||
});
|
||||
|
||||
it('it can use a variable with multiple comma separated', async () => {
|
||||
const cfg = {
|
||||
id: DataTransformerID.filterFieldsByName,
|
||||
@@ -207,31 +223,13 @@ describe('filterByName transformer', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const ctx = {
|
||||
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
|
||||
if (!target) {
|
||||
return '';
|
||||
}
|
||||
const variables: ScopedVars = {
|
||||
var: {
|
||||
value: 'B,D',
|
||||
text: 'Test',
|
||||
},
|
||||
};
|
||||
for (const key in variables) {
|
||||
return target.replace(`$${key}`, variables[key]!.value);
|
||||
}
|
||||
return target;
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('B');
|
||||
expect(filtered.fields[1].name).toBe('D');
|
||||
});
|
||||
const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [
|
||||
new TestVariable({ name: 'var', value: 'B,D' }),
|
||||
]);
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('B');
|
||||
expect(filtered.fields[1].name).toBe('D');
|
||||
});
|
||||
|
||||
it('it can use a variable with multiple comma separated values in {}', async () => {
|
||||
@@ -245,31 +243,14 @@ describe('filterByName transformer', () => {
|
||||
},
|
||||
};
|
||||
|
||||
const ctx = {
|
||||
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
|
||||
if (!target) {
|
||||
return '';
|
||||
}
|
||||
const variables: ScopedVars = {
|
||||
var: {
|
||||
value: '{B,D}',
|
||||
text: 'Test',
|
||||
},
|
||||
};
|
||||
for (const key in variables) {
|
||||
return target.replace(`$${key}`, variables[key]!.value);
|
||||
}
|
||||
return target;
|
||||
},
|
||||
};
|
||||
const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [
|
||||
new TestVariable({ name: 'var', value: 'B,D' }),
|
||||
]);
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('B');
|
||||
expect(filtered.fields[1].name).toBe('D');
|
||||
});
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('B');
|
||||
expect(filtered.fields[1].name).toBe('D');
|
||||
});
|
||||
|
||||
it('uses template variable substitution', async () => {
|
||||
@@ -277,35 +258,78 @@ describe('filterByName transformer', () => {
|
||||
id: DataTransformerID.filterFieldsByName,
|
||||
options: {
|
||||
include: {
|
||||
pattern: '/^$var1/',
|
||||
pattern: '/^$var/',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ctx = {
|
||||
interpolate: (target: string | undefined, scopedVars?: ScopedVars, format?: string | Function): string => {
|
||||
if (!target) {
|
||||
return '';
|
||||
}
|
||||
const variables: ScopedVars = {
|
||||
var1: {
|
||||
value: 'startsWith',
|
||||
text: 'Test',
|
||||
},
|
||||
};
|
||||
for (const key in variables) {
|
||||
return target.replace(`$${key}`, variables[key]!.value);
|
||||
}
|
||||
return target;
|
||||
},
|
||||
};
|
||||
const data = setupTransformationScene(seriesWithNamesToMatch, cfg, [
|
||||
new TestVariable({ name: 'var', value: 'startsWith' }),
|
||||
]);
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesWithNamesToMatch], ctx)).toEmitValuesWith((received) => {
|
||||
const data = received[0];
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('startsWithA');
|
||||
});
|
||||
const filtered = data[0];
|
||||
expect(filtered.fields.length).toBe(2);
|
||||
expect(filtered.fields[0].name).toBe('startsWithA');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function activateFullSceneTree(scene: SceneObject): SceneDeactivationHandler {
|
||||
const deactivationHandlers: SceneDeactivationHandler[] = [];
|
||||
|
||||
// Important that variables are activated before other children
|
||||
if (scene.state.$variables) {
|
||||
deactivationHandlers.push(activateFullSceneTree(scene.state.$variables));
|
||||
}
|
||||
|
||||
scene.forEachChild((child) => {
|
||||
// For query runners which by default use the container width for maxDataPoints calculation we are setting a width.
|
||||
// In real life this is done by the React component when VizPanel is rendered.
|
||||
if ('setContainerWidth' in child) {
|
||||
// @ts-expect-error
|
||||
child.setContainerWidth(500);
|
||||
}
|
||||
deactivationHandlers.push(activateFullSceneTree(child));
|
||||
});
|
||||
|
||||
deactivationHandlers.push(scene.activate());
|
||||
|
||||
return () => {
|
||||
for (const handler of deactivationHandlers) {
|
||||
handler();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function setupTransformationScene(
|
||||
inputData: DataFrame,
|
||||
cfg: DataTransformerConfig,
|
||||
variables: SceneVariable[]
|
||||
): DataFrame[] {
|
||||
class TestSceneObject extends SceneObjectBase<{}> {}
|
||||
const dataNode = new SceneDataNode({
|
||||
data: {
|
||||
state: LoadingState.Loading,
|
||||
timeRange: getDefaultTimeRange(),
|
||||
series: [inputData],
|
||||
},
|
||||
});
|
||||
|
||||
const transformationNode = new SceneDataTransformer({
|
||||
transformations: [cfg],
|
||||
});
|
||||
|
||||
const consumer = new TestSceneObject({
|
||||
$data: transformationNode,
|
||||
});
|
||||
|
||||
const scene = new SceneFlexLayout({
|
||||
$data: dataNode,
|
||||
$variables: new SceneVariableSet({ variables }),
|
||||
children: [new SceneFlexItem({ body: consumer })],
|
||||
});
|
||||
|
||||
activateFullSceneTree(scene);
|
||||
|
||||
return sceneGraph.getData(consumer).state.data?.series!;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DataTransformContext, DataTransformerInfo, MatcherConfig } from '../../types/transformations';
|
||||
import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
|
||||
import { FieldMatcherID } from '../matchers/ids';
|
||||
import { RegexpOrNamesMatcherOptions } from '../matchers/nameMatcher';
|
||||
|
||||
@@ -25,8 +25,8 @@ export const filterFieldsByNameTransformer: DataTransformerInfo<FilterFieldsByNa
|
||||
source.pipe(
|
||||
filterFieldsTransformer.operator(
|
||||
{
|
||||
include: getMatcherConfig(ctx, options.include, options.byVariable),
|
||||
exclude: getMatcherConfig(ctx, options.exclude, options.byVariable),
|
||||
include: getMatcherConfig(options.include, options.byVariable),
|
||||
exclude: getMatcherConfig(options.exclude, options.byVariable),
|
||||
},
|
||||
ctx
|
||||
)
|
||||
@@ -35,7 +35,6 @@ export const filterFieldsByNameTransformer: DataTransformerInfo<FilterFieldsByNa
|
||||
|
||||
// Exported to share with other implementations, but not exported to `@grafana/data`
|
||||
export const getMatcherConfig = (
|
||||
ctx: DataTransformContext,
|
||||
options?: RegexpOrNamesMatcherOptions,
|
||||
byVariable?: boolean
|
||||
): MatcherConfig | undefined => {
|
||||
@@ -46,7 +45,8 @@ export const getMatcherConfig = (
|
||||
const { names, pattern, variable } = options;
|
||||
|
||||
if (byVariable && variable) {
|
||||
const stringOfNames = ctx.interpolate(variable);
|
||||
const stringOfNames = variable;
|
||||
|
||||
if (/\{.*\}/.test(stringOfNames)) {
|
||||
const namesFromString = stringOfNames.slice(1).slice(0, -1).split(',');
|
||||
return { id: FieldMatcherID.byNames, options: { names: namesFromString } };
|
||||
|
||||
@@ -13,10 +13,6 @@ import {
|
||||
FilterByValueType,
|
||||
} from './filterByValue';
|
||||
import { DataTransformerID } from './ids';
|
||||
import * as utils from './utils';
|
||||
|
||||
const mockTransformationsVariableSupport = jest.spyOn(utils, 'transformationsVariableSupport');
|
||||
mockTransformationsVariableSupport.mockReturnValue(false);
|
||||
|
||||
const seriesAWithSingleField = toDataFrame({
|
||||
name: 'A',
|
||||
@@ -260,139 +256,6 @@ describe('FilterByValue transformer', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should interpolate dashboard variables', async () => {
|
||||
mockTransformationsVariableSupport.mockReturnValue(true);
|
||||
|
||||
const lower: MatcherConfig<BasicValueMatcherOptions<string | number>> = {
|
||||
id: ValueMatcherID.lower,
|
||||
options: { value: 'thiswillinterpolateto6' },
|
||||
};
|
||||
|
||||
const cfg: DataTransformerConfig<FilterByValueTransformerOptions> = {
|
||||
id: DataTransformerID.filterByValue,
|
||||
options: {
|
||||
type: FilterByValueType.exclude,
|
||||
match: FilterByValueMatch.all,
|
||||
filters: [
|
||||
{
|
||||
fieldName: 'numbers',
|
||||
config: lower,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const ctxmock = { interpolate: jest.fn(() => '6') };
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesAWithSingleField], ctxmock)).toEmitValuesWith((received) => {
|
||||
const processed = received[0];
|
||||
|
||||
expect(processed.length).toEqual(1);
|
||||
expect(processed[0].fields).toEqual([
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: [6000, 7000],
|
||||
state: {},
|
||||
},
|
||||
{
|
||||
name: 'numbers',
|
||||
type: FieldType.number,
|
||||
values: [6, 7],
|
||||
state: {},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should interpolate dashboard variables for regex matcher', async () => {
|
||||
mockTransformationsVariableSupport.mockReturnValue(true);
|
||||
|
||||
const regex: MatcherConfig<BasicValueMatcherOptions<string | number>> = {
|
||||
id: ValueMatcherID.regex,
|
||||
options: { value: '.*thiswillinterpolateto6' },
|
||||
};
|
||||
|
||||
const cfg: DataTransformerConfig<FilterByValueTransformerOptions> = {
|
||||
id: DataTransformerID.filterByValue,
|
||||
options: {
|
||||
type: FilterByValueType.include,
|
||||
match: FilterByValueMatch.all,
|
||||
filters: [
|
||||
{
|
||||
fieldName: 'numbers',
|
||||
config: regex,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const ctxmock = { interpolate: jest.fn(() => '6') };
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesAWithSingleField], ctxmock)).toEmitValuesWith((received) => {
|
||||
const processed = received[0];
|
||||
|
||||
expect(processed.length).toEqual(1);
|
||||
expect(processed[0].fields).toEqual([
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: [6000],
|
||||
state: {},
|
||||
},
|
||||
{
|
||||
name: 'numbers',
|
||||
type: FieldType.number,
|
||||
values: [6],
|
||||
state: {},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not interpolate dashboard variables when feature toggle is off', async () => {
|
||||
mockTransformationsVariableSupport.mockReturnValue(false);
|
||||
|
||||
const lower: MatcherConfig<BasicValueMatcherOptions<number | string>> = {
|
||||
id: ValueMatcherID.lower,
|
||||
options: { value: 'notinterpolating' },
|
||||
};
|
||||
|
||||
const cfg: DataTransformerConfig<FilterByValueTransformerOptions> = {
|
||||
id: DataTransformerID.filterByValue,
|
||||
options: {
|
||||
type: FilterByValueType.exclude,
|
||||
match: FilterByValueMatch.all,
|
||||
filters: [
|
||||
{
|
||||
fieldName: 'numbers',
|
||||
config: lower,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await expect(transformDataFrame([cfg], [seriesAWithSingleField])).toEmitValuesWith((received) => {
|
||||
const processed = received[0];
|
||||
|
||||
expect(processed.length).toEqual(1);
|
||||
expect(processed[0].fields).toEqual([
|
||||
{
|
||||
name: 'time',
|
||||
type: FieldType.time,
|
||||
values: [1000, 2000, 3000, 4000, 5000, 6000, 7000],
|
||||
state: {},
|
||||
},
|
||||
{
|
||||
name: 'numbers',
|
||||
type: FieldType.number,
|
||||
values: [1, 2, 3, 4, 5, 6, 7],
|
||||
state: {},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should match any condition', async () => {
|
||||
const lowerOrEqual: MatcherConfig<BasicValueMatcherOptions<number>> = {
|
||||
id: ValueMatcherID.lowerOrEqual,
|
||||
|
||||
@@ -4,11 +4,9 @@ import { getFieldDisplayName } from '../../field/fieldState';
|
||||
import { DataFrame, Field } from '../../types/dataFrame';
|
||||
import { DataTransformerInfo, MatcherConfig } from '../../types/transformations';
|
||||
import { getValueMatcher } from '../matchers';
|
||||
import { ValueMatcherID } from '../matchers/ids';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { noopTransformer } from './noop';
|
||||
import { transformationsVariableSupport } from './utils';
|
||||
|
||||
export enum FilterByValueType {
|
||||
exclude = 'exclude',
|
||||
@@ -50,50 +48,6 @@ export const filterByValueTransformer: DataTransformerInfo<FilterByValueTransfor
|
||||
return source.pipe(noopTransformer.operator({}, ctx));
|
||||
}
|
||||
|
||||
const interpolatedFilters: FilterByValueFilter[] = [];
|
||||
|
||||
if (transformationsVariableSupport()) {
|
||||
interpolatedFilters.push(
|
||||
...filters.map((filter) => {
|
||||
if (filter.config.id === ValueMatcherID.between) {
|
||||
let valueFrom = filter.config.options.from;
|
||||
let valueTo = filter.config.options.to;
|
||||
|
||||
if (typeof filter.config.options.from === 'string') {
|
||||
valueFrom = ctx.interpolate(valueFrom);
|
||||
}
|
||||
if (typeof filter.config.options.to === 'string') {
|
||||
valueTo = ctx.interpolate(valueTo);
|
||||
}
|
||||
|
||||
return {
|
||||
...filter,
|
||||
config: {
|
||||
...filter.config,
|
||||
options: {
|
||||
...filter.config.options,
|
||||
to: valueTo,
|
||||
from: valueFrom,
|
||||
},
|
||||
},
|
||||
};
|
||||
} else if (filter.config.options.value) {
|
||||
let value = filter.config.options.value;
|
||||
if (typeof filter.config.options.value === 'string') {
|
||||
value = ctx.interpolate(value);
|
||||
}
|
||||
|
||||
return {
|
||||
...filter,
|
||||
config: { ...filter.config, options: { ...filter.config.options, value } },
|
||||
};
|
||||
}
|
||||
|
||||
return filter;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return source.pipe(
|
||||
map((data) => {
|
||||
if (data.length === 0) {
|
||||
@@ -106,12 +60,7 @@ export const filterByValueTransformer: DataTransformerInfo<FilterByValueTransfor
|
||||
const rows = new Set<number>();
|
||||
const fieldIndexByName = groupFieldIndexByName(frame, data);
|
||||
|
||||
let matchers;
|
||||
if (transformationsVariableSupport()) {
|
||||
matchers = createFilterValueMatchers(interpolatedFilters, fieldIndexByName);
|
||||
} else {
|
||||
matchers = createFilterValueMatchers(filters, fieldIndexByName);
|
||||
}
|
||||
const matchers = createFilterValueMatchers(filters, fieldIndexByName);
|
||||
|
||||
for (let index = 0; index < frame.length; index++) {
|
||||
if (rows.has(index)) {
|
||||
|
||||
@@ -65,9 +65,9 @@ export const groupingToMatrixTransformer: DataTransformerInfo<GroupingToMatrixTr
|
||||
operator: (options: GroupingToMatrixTransformerOptions, ctx: DataTransformContext) => (source) =>
|
||||
source.pipe(
|
||||
map((data) => {
|
||||
const columnFieldMatch = ctx.interpolate(options.columnField || DEFAULT_COLUMN_FIELD);
|
||||
const rowFieldMatch = ctx.interpolate(options.rowField || DEFAULT_ROW_FIELD);
|
||||
const valueFieldMatch = ctx.interpolate(options.valueField || DEFAULT_VALUE_FIELD);
|
||||
const columnFieldMatch = options.columnField || DEFAULT_COLUMN_FIELD;
|
||||
const rowFieldMatch = options.rowField || DEFAULT_ROW_FIELD;
|
||||
const valueFieldMatch = options.valueField || DEFAULT_VALUE_FIELD;
|
||||
const emptyValue = options.emptyValue || DEFAULT_EMPTY_VALUE;
|
||||
|
||||
// Accept only single queries
|
||||
|
||||
@@ -11,7 +11,6 @@ import { roundDecimals } from '../../utils/numbers';
|
||||
import { DataTransformerID } from './ids';
|
||||
import { AlignedData, join } from './joinDataFrames';
|
||||
import { nullToValueField } from './nulls/nullToValue';
|
||||
import { transformationsVariableSupport } from './utils';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -112,9 +111,6 @@ export const histogramTransformer: SynchronousDataTransformerInfo<HistogramTrans
|
||||
bucketOffset: number | undefined = undefined;
|
||||
|
||||
if (options.bucketSize) {
|
||||
if (transformationsVariableSupport()) {
|
||||
options.bucketSize = ctx.interpolate(options.bucketSize.toString());
|
||||
}
|
||||
if (typeof options.bucketSize === 'string') {
|
||||
bucketSize = parseFloat(options.bucketSize);
|
||||
} else {
|
||||
@@ -127,9 +123,6 @@ export const histogramTransformer: SynchronousDataTransformerInfo<HistogramTrans
|
||||
}
|
||||
|
||||
if (options.bucketOffset) {
|
||||
if (transformationsVariableSupport()) {
|
||||
options.bucketOffset = ctx.interpolate(options.bucketOffset.toString());
|
||||
}
|
||||
if (typeof options.bucketOffset === 'string') {
|
||||
bucketOffset = parseFloat(options.bucketOffset);
|
||||
} else {
|
||||
|
||||
@@ -38,7 +38,7 @@ export const joinByFieldTransformer: SynchronousDataTransformerInfo<JoinByFieldO
|
||||
return (data: DataFrame[]) => {
|
||||
if (data.length > 1) {
|
||||
if (options.byField && !joinBy) {
|
||||
joinBy = fieldMatchers.get(FieldMatcherID.byName).get(ctx.interpolate(options.byField));
|
||||
joinBy = fieldMatchers.get(FieldMatcherID.byName).get(options.byField);
|
||||
}
|
||||
const joined = joinDataFrames({ frames: data, joinBy, mode: options.mode });
|
||||
if (joined) {
|
||||
|
||||
@@ -3,7 +3,6 @@ import { map } from 'rxjs/operators';
|
||||
import { DataTransformerInfo } from '../../types/transformations';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { transformationsVariableSupport } from './utils';
|
||||
|
||||
export interface LimitTransformerOptions {
|
||||
limitField?: number | string;
|
||||
@@ -25,11 +24,7 @@ export const limitTransformer: DataTransformerInfo<LimitTransformerOptions> = {
|
||||
let limit = DEFAULT_LIMIT_FIELD;
|
||||
if (options.limitField !== undefined) {
|
||||
if (typeof options.limitField === 'string') {
|
||||
if (transformationsVariableSupport()) {
|
||||
limit = parseInt(ctx.interpolate(options.limitField), 10);
|
||||
} else {
|
||||
limit = parseInt(options.limitField, 10);
|
||||
}
|
||||
limit = parseInt(options.limitField, 10);
|
||||
} else {
|
||||
limit = options.limitField;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import { DataFrame } from '../../types/dataFrame';
|
||||
import { DataTransformContext, DataTransformerInfo } from '../../types/transformations';
|
||||
|
||||
import { DataTransformerID } from './ids';
|
||||
import { transformationsVariableSupport } from './utils';
|
||||
|
||||
export interface SortByField {
|
||||
field: string;
|
||||
@@ -59,12 +58,7 @@ function attachFieldIndex(frame: DataFrame, sort: SortByField[], ctx: DataTransf
|
||||
// null or undefined
|
||||
return s;
|
||||
}
|
||||
if (transformationsVariableSupport()) {
|
||||
return {
|
||||
...s,
|
||||
index: frame.fields.findIndex((f) => ctx.interpolate(s.field) === getFieldDisplayName(f, frame)),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...s,
|
||||
index: frame.fields.findIndex((f) => s.field === getFieldDisplayName(f, frame)),
|
||||
|
||||
@@ -6,11 +6,6 @@ declare global {
|
||||
grafanaBootData?: BootData;
|
||||
}
|
||||
}
|
||||
|
||||
export const transformationsVariableSupport = () => {
|
||||
return window?.grafanaBootData?.settings?.featureToggles?.transformationsVariableSupport;
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve the maximum number of fields in a series of a dataframe.
|
||||
*/
|
||||
|
||||
@@ -105,7 +105,6 @@ export interface FeatureToggles {
|
||||
enableNativeHTTPHistogram?: boolean;
|
||||
disableClassicHTTPHistogram?: boolean;
|
||||
formatString?: boolean;
|
||||
transformationsVariableSupport?: boolean;
|
||||
kubernetesPlaylists?: boolean;
|
||||
kubernetesSnapshots?: boolean;
|
||||
kubernetesDashboards?: boolean;
|
||||
@@ -121,7 +120,6 @@ export interface FeatureToggles {
|
||||
lokiStructuredMetadata?: boolean;
|
||||
teamHttpHeaders?: boolean;
|
||||
cachingOptimizeSerializationMemoryUsage?: boolean;
|
||||
managedPluginsInstall?: boolean;
|
||||
prometheusPromQAIL?: boolean;
|
||||
prometheusCodeModeMetricNamesSearch?: boolean;
|
||||
addFieldFromCalculationStatFunctions?: boolean;
|
||||
@@ -260,4 +258,6 @@ export interface FeatureToggles {
|
||||
grafanaconThemes?: boolean;
|
||||
pluginsCDNSyncLoader?: boolean;
|
||||
alertingJiraIntegration?: boolean;
|
||||
alertingRuleVersionHistoryRestore?: boolean;
|
||||
newShareReportDrawer?: boolean;
|
||||
}
|
||||
@@ -32,6 +32,8 @@ export type PluginExtensionLink = PluginExtensionBase & {
|
||||
category?: string;
|
||||
};
|
||||
|
||||
export type PluginExtensionComponentMeta = Omit<PluginExtensionComponent, 'component'>;
|
||||
|
||||
export type PluginExtensionComponent<Props = {}> = PluginExtensionBase & {
|
||||
type: PluginExtensionTypes.component;
|
||||
component: React.ComponentType<Props>;
|
||||
@@ -193,7 +195,10 @@ export type PluginExtensionPanelContext = {
|
||||
data?: PanelData;
|
||||
};
|
||||
|
||||
export type PluginExtensionDataSourceConfigContext<JsonData extends DataSourceJsonData = DataSourceJsonData> = {
|
||||
export type PluginExtensionDataSourceConfigContext<
|
||||
JsonData extends DataSourceJsonData = DataSourceJsonData,
|
||||
SecureJsonData = {},
|
||||
> = {
|
||||
// The current datasource settings
|
||||
dataSource: DataSourceSettings<JsonData>;
|
||||
|
||||
@@ -209,6 +214,7 @@ export type PluginExtensionDataSourceConfigContext<JsonData extends DataSourceJs
|
||||
// Can be used to update the `jsonData` field on the datasource
|
||||
// (Only updates the form, it still needs to be saved by the user)
|
||||
setJsonData: (jsonData: JsonData) => void;
|
||||
setSecureJsonData: (secureJsonData: SecureJsonData) => void;
|
||||
};
|
||||
|
||||
export type PluginExtensionCommandPaletteContext = {};
|
||||
|
||||
@@ -8,5 +8,11 @@
|
||||
},
|
||||
"exclude": ["dist/**/*"],
|
||||
"extends": "@grafana/tsconfig",
|
||||
"include": ["src/**/*.ts*", "typings/jest", "../../public/app/types/jquery/*.ts", "../../public/app/types/*.d.ts"]
|
||||
"include": [
|
||||
"src/**/*.ts*",
|
||||
"typings/jest",
|
||||
"../../public/app/types/jquery/*.ts",
|
||||
"../../public/app/types/*.d.ts",
|
||||
"../grafana-ui/src/types/react-table-config.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
PluginExtensionLink,
|
||||
PluginExtensionComponent,
|
||||
PluginExtensionFunction,
|
||||
PluginExtensionComponentMeta,
|
||||
} from '@grafana/data';
|
||||
|
||||
import { isPluginExtensionComponent, isPluginExtensionLink } from './utils';
|
||||
@@ -42,7 +43,7 @@ export type UsePluginComponentResult<Props = {}> = {
|
||||
};
|
||||
|
||||
export type UsePluginComponentsResult<Props = {}> = {
|
||||
components: Array<React.ComponentType<Props>>;
|
||||
components: Array<React.ComponentType<Props> & { meta: PluginExtensionComponentMeta }>;
|
||||
isLoading: boolean;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -425,7 +425,7 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
apiRoute.Any("/plugin-proxy/:pluginId/*", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
||||
apiRoute.Any("/plugin-proxy/:pluginId", requestmeta.SetSLOGroup(requestmeta.SLOGroupHighSlow), authorize(ac.EvalPermission(pluginaccesscontrol.ActionAppAccess, pluginIDScope)), checkAppEnabled(hs.pluginStore, hs.PluginSettings), hs.ProxyPluginRequest)
|
||||
|
||||
if hs.Cfg.PluginAdminEnabled && (hs.Features.IsEnabledGlobally(featuremgmt.FlagManagedPluginsInstall) || !hs.Cfg.PluginAdminExternalManageEnabled) {
|
||||
if hs.Cfg.PluginAdminEnabled {
|
||||
apiRoute.Group("/plugins", func(pluginRoute routing.RouteRegister) {
|
||||
pluginRoute.Post("/:pluginId/install", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.InstallPlugin))
|
||||
pluginRoute.Post("/:pluginId/uninstall", authorizeInOrg(ac.UseGlobalOrSingleOrg(hs.Cfg), ac.EvalPermission(pluginaccesscontrol.ActionInstall)), routing.Wrap(hs.UninstallPlugin))
|
||||
|
||||
+2
-1
@@ -250,7 +250,8 @@ func (hs *HTTPServer) getThemeForIndexData(themePrefId string, themeURLParam str
|
||||
|
||||
if pref.IsValidThemeID(themePrefId) {
|
||||
theme := pref.GetThemeByID(themePrefId)
|
||||
if !theme.IsExtra || hs.Features.IsEnabledGlobally(featuremgmt.FlagExtraThemes) {
|
||||
// TODO refactor
|
||||
if !theme.IsExtra || hs.Features.IsEnabledGlobally(featuremgmt.FlagExtraThemes) || hs.Features.IsEnabledGlobally(featuremgmt.FlagGrafanaconThemes) {
|
||||
return theme
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func Test_PluginsInstallAndUninstall(t *testing.T) {
|
||||
preInstalledPlugin bool
|
||||
}
|
||||
tcs := []testCase{
|
||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: true},
|
||||
{expectedCode: http.StatusOK, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: true},
|
||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: true},
|
||||
{expectedCode: http.StatusNotFound, permissionOrg: globalOrg, permissions: canInstall, pluginAdminEnabled: false, pluginAdminExternalManageEnabled: false},
|
||||
{expectedCode: http.StatusForbidden, permissionOrg: globalOrg, permissions: cannotInstall, pluginAdminEnabled: true, pluginAdminExternalManageEnabled: false},
|
||||
|
||||
@@ -3,7 +3,7 @@ module github.com/grafana/grafana/pkg/apimachinery
|
||||
go 1.23.1
|
||||
|
||||
require (
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569 // @grafana/identity-access-team
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc // @grafana/identity-access-team
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c // @grafana/identity-access-team
|
||||
github.com/stretchr/testify v1.10.0
|
||||
k8s.io/apimachinery v0.32.1
|
||||
|
||||
@@ -32,8 +32,8 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569 h1:bQw6fdcxVdZ6xmZVhMtRgGEKIT1Zc6y+i1PWF8tMX4Q=
|
||||
github.com/grafana/authlib v0.0.0-20250206063954-bf4600a17569/go.mod h1:/gYfphsNu9v1qYWXxpv1NSvMEMSwvdf8qb8YlgwIRl8=
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc h1:qx8avmRi3wx+SuAn6UV0pKma1fJ0/4POUYNc+2rxZkg=
|
||||
github.com/grafana/authlib v0.0.0-20250217135712-106ec5baf6bc/go.mod h1:HamgITzYqErFp8qLwYM/JwwE3z6fXft0Z+5n0Dx1rUM=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c h1:b0sPDtt33uFdmvUJjSCld3kwE2E49dUvevuUDSJsEuo=
|
||||
github.com/grafana/authlib/types v0.0.0-20250120145936-5f0e28e7a87c/go.mod h1:qYjSd1tmJiuVoSICp7Py9/zD54O9uQQA3wuM6Gg4DFM=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
|
||||
@@ -21,10 +21,6 @@ var (
|
||||
Usage: "Specify number of tries before failing",
|
||||
Value: 1,
|
||||
}
|
||||
dryRunFlag = cli.BoolFlag{
|
||||
Name: "dry-run",
|
||||
Usage: "Only simulate actions",
|
||||
}
|
||||
tagFlag = cli.StringFlag{
|
||||
Name: "tag",
|
||||
Usage: "Grafana version tag",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/grafana/grafana/pkg/build"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -66,7 +67,7 @@ func main() {
|
||||
Name: "publish-metrics",
|
||||
Usage: "Publish a set of metrics from stdin",
|
||||
ArgsUsage: "<api-key>",
|
||||
Action: MaxArgCountWrapper(1, PublishMetrics),
|
||||
Action: util.MaxArgCountWrapper(1, PublishMetrics),
|
||||
},
|
||||
{
|
||||
Name: "verify-drone",
|
||||
@@ -178,7 +179,7 @@ func main() {
|
||||
Name: "fetch",
|
||||
Usage: "Fetch Grafana Docker images",
|
||||
ArgsUsage: "[version]",
|
||||
Action: MaxArgCountWrapper(1, FetchImages),
|
||||
Action: util.MaxArgCountWrapper(1, FetchImages),
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
},
|
||||
@@ -229,7 +230,7 @@ func main() {
|
||||
Flags: []cli.Flag{
|
||||
&editionFlag,
|
||||
&buildIDFlag,
|
||||
&dryRunFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "src-bucket",
|
||||
Value: "grafana-downloads",
|
||||
@@ -242,7 +243,7 @@ func main() {
|
||||
Usage: "Publish packages to GitHub releases",
|
||||
Action: PublishGithub,
|
||||
Flags: []cli.Flag{
|
||||
&dryRunFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "path",
|
||||
Usage: "Path to the asset to be published",
|
||||
@@ -267,7 +268,7 @@ func main() {
|
||||
Usage: "Publish image to AWS Marketplace releases",
|
||||
Action: PublishAwsMarketplace,
|
||||
Flags: []cli.Flag{
|
||||
&dryRunFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Release version (default from metadata)",
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
"github.com/aws/aws-sdk-go/service/marketplacecatalog"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -138,7 +139,7 @@ func setupPublishAwsMarketplaceTests(t *testing.T) *cli.App {
|
||||
testApp := cli.NewApp()
|
||||
testApp.Action = PublishAwsMarketplace
|
||||
testApp.Flags = []cli.Flag{
|
||||
&dryRunFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "version",
|
||||
Usage: "Release version (default from metadata)",
|
||||
@@ -171,9 +172,11 @@ type mockAwsMarketplaceDocker struct {
|
||||
func (m *mockAwsMarketplaceDocker) ImagePull(ctx context.Context, refStr string, options image.PullOptions) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader([]byte(""))), m.ImagePullError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceDocker) ImageTag(ctx context.Context, source string, target string) error {
|
||||
return m.ImageTagError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceDocker) ImagePush(ctx context.Context, image string, options image.PushOptions) (io.ReadCloser, error) {
|
||||
return io.NopCloser(bytes.NewReader([]byte(""))), m.ImagePushError
|
||||
}
|
||||
@@ -202,6 +205,7 @@ func (m *mockAwsMarketplaceCatalog) DescribeEntityWithContext(ctx context.Contex
|
||||
EntityIdentifier: aws.String("productid"),
|
||||
}, m.DescribeEntityWithContextError
|
||||
}
|
||||
|
||||
func (m *mockAwsMarketplaceCatalog) StartChangeSetWithContext(ctx context.Context, input *marketplacecatalog.StartChangeSetInput, opts ...request.Option) (*marketplacecatalog.StartChangeSetOutput, error) {
|
||||
return &marketplacecatalog.StartChangeSetOutput{}, m.StartChangeSetWithContextError
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-github/github"
|
||||
"github.com/grafana/grafana/pkg/build/cmd/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -158,7 +159,7 @@ func setupPublishGithubTests(t *testing.T) (*cli.App, string) {
|
||||
testApp := cli.NewApp()
|
||||
testApp.Action = PublishGithub
|
||||
testApp.Flags = []cli.Flag{
|
||||
&dryRunFlag,
|
||||
&util.DryRunFlag,
|
||||
&cli.StringFlag{
|
||||
Name: "path",
|
||||
Required: true,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package util
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
package util
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
|
||||
var DryRunFlag = cli.BoolFlag{
|
||||
Name: "dry-run",
|
||||
Usage: "Only simulate actions",
|
||||
}
|
||||
Whitespace-only changes.
@@ -91,7 +91,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
|
||||
}
|
||||
|
||||
// if searching for tags, get those instead of the dashboards or folders
|
||||
for facet, _ := range req.Facet {
|
||||
for facet := range req.Facet {
|
||||
if facet == resource.SEARCH_FIELD_TAGS {
|
||||
tags, err := c.dashboardStore.GetDashboardTags(ctx, &dashboards.GetDashboardTagsQuery{
|
||||
OrgID: user.GetOrgID(),
|
||||
@@ -102,7 +102,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
|
||||
list := &resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{},
|
||||
Facet: map[string]*resource.ResourceSearchResponse_Facet{
|
||||
"tags": &resource.ResourceSearchResponse_Facet{
|
||||
"tags": {
|
||||
Terms: []*resource.ResourceSearchResponse_TermFacet{},
|
||||
},
|
||||
},
|
||||
@@ -213,7 +213,7 @@ func (c *DashboardSearchClient) Search(ctx context.Context, req *resource.Resour
|
||||
Key: getResourceKey(&dashboards.DashboardSearchProjection{
|
||||
UID: dashboard.UID,
|
||||
}, req.Options.Key.Namespace),
|
||||
Cells: [][]byte{[]byte(dashboard.Title), []byte(dashboard.FolderUID), []byte{}},
|
||||
Cells: [][]byte{[]byte(dashboard.Title), []byte(dashboard.FolderUID), {}},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ func TestDashboardSearchClient_Search(t *testing.T) {
|
||||
require.Equal(t, &resource.ResourceSearchResponse{
|
||||
Results: &resource.ResourceTable{},
|
||||
Facet: map[string]*resource.ResourceSearchResponse_Facet{
|
||||
"tags": &resource.ResourceSearchResponse_Facet{
|
||||
"tags": {
|
||||
Terms: []*resource.ResourceSearchResponse_TermFacet{
|
||||
{
|
||||
Term: "tag1",
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigation"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
@@ -31,7 +31,7 @@ func ProvideRegistryServiceSink(
|
||||
restConfigProvider apiserver.RestConfigProvider,
|
||||
features featuremgmt.FeatureToggles,
|
||||
playlistAppProvider *playlist.PlaylistAppProvider,
|
||||
investigationAppProvider *investigation.InvestigationAppProvider,
|
||||
investigationAppProvider *investigations.InvestigationsAppProvider,
|
||||
advisorAppProvider *advisor.AdvisorAppProvider,
|
||||
) (*Service, error) {
|
||||
cfgWrapper := func(ctx context.Context) *rest.Config {
|
||||
@@ -47,11 +47,12 @@ func ProvideRegistryServiceSink(
|
||||
RestConfigGetter: cfgWrapper,
|
||||
APIRegistrar: registrar,
|
||||
}
|
||||
|
||||
logger := log.New("app-registry")
|
||||
var apiGroupRunner *runner.APIGroupRunner
|
||||
var err error
|
||||
providers := []app.Provider{playlistAppProvider}
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagInvestigationsBackend) {
|
||||
logger.Debug("Investigations backend is enabled")
|
||||
providers = append(providers, investigationAppProvider)
|
||||
}
|
||||
if features.IsEnabledGlobally(featuremgmt.FlagGrafanaAdvisor) {
|
||||
@@ -62,7 +63,7 @@ func ProvideRegistryServiceSink(
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Service{runner: apiGroupRunner, log: log.New("app-registry")}, nil
|
||||
return &Service{runner: apiGroupRunner, log: logger}, nil
|
||||
}
|
||||
|
||||
func (s *Service) Run(ctx context.Context) error {
|
||||
|
||||
+8
-8
@@ -1,28 +1,28 @@
|
||||
package investigation
|
||||
package investigations
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana-app-sdk/app"
|
||||
"github.com/grafana/grafana-app-sdk/simple"
|
||||
"github.com/grafana/grafana/apps/investigation/pkg/apis"
|
||||
investigationv1alpha1 "github.com/grafana/grafana/apps/investigation/pkg/apis/investigation/v1alpha1"
|
||||
investigationapp "github.com/grafana/grafana/apps/investigation/pkg/app"
|
||||
"github.com/grafana/grafana/apps/investigations/pkg/apis"
|
||||
investigationv0alpha1 "github.com/grafana/grafana/apps/investigations/pkg/apis/investigations/v0alpha1"
|
||||
investigationapp "github.com/grafana/grafana/apps/investigations/pkg/app"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder/runner"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type InvestigationAppProvider struct {
|
||||
type InvestigationsAppProvider struct {
|
||||
app.Provider
|
||||
cfg *setting.Cfg
|
||||
}
|
||||
|
||||
func RegisterApp(
|
||||
cfg *setting.Cfg,
|
||||
) *InvestigationAppProvider {
|
||||
provider := &InvestigationAppProvider{
|
||||
) *InvestigationsAppProvider {
|
||||
provider := &InvestigationsAppProvider{
|
||||
cfg: cfg,
|
||||
}
|
||||
appCfg := &runner.AppBuilderConfig{
|
||||
OpenAPIDefGetter: investigationv1alpha1.GetOpenAPIDefinitions,
|
||||
OpenAPIDefGetter: investigationv0alpha1.GetOpenAPIDefinitions,
|
||||
ManagedKinds: investigationapp.GetKinds(),
|
||||
}
|
||||
provider.Provider = simple.NewAppProvider(apis.LocalManifest(), appCfg, investigationapp.New)
|
||||
@@ -5,14 +5,14 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checkregistry"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/advisor"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigation"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/investigations"
|
||||
"github.com/grafana/grafana/pkg/registry/apps/playlist"
|
||||
)
|
||||
|
||||
var WireSet = wire.NewSet(
|
||||
ProvideRegistryServiceSink,
|
||||
playlist.RegisterApp,
|
||||
investigation.RegisterApp,
|
||||
investigations.RegisterApp,
|
||||
advisor.RegisterApp,
|
||||
checkregistry.ProvideService,
|
||||
wire.Bind(new(checkregistry.CheckService), new(*checkregistry.Service)),
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type Mode string
|
||||
|
||||
func (s Mode) IsValid() bool {
|
||||
switch s {
|
||||
case ModeGRPC, ModeInProc, ModeCloud:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
ModeCloud Mode = "cloud"
|
||||
ModeGRPC Mode = "grpc"
|
||||
ModeInProc Mode = "inproc"
|
||||
)
|
||||
|
||||
type Cfg struct {
|
||||
remoteAddress string
|
||||
listen bool
|
||||
mode Mode
|
||||
|
||||
token string
|
||||
tokenExchangeURL string
|
||||
tokenNamespace string
|
||||
|
||||
allowInsecure bool
|
||||
}
|
||||
|
||||
func ReadCfg(cfg *setting.Cfg) (*Cfg, error) {
|
||||
authzSection := cfg.SectionWithEnvOverrides("authorization")
|
||||
grpcClientAuthSection := cfg.SectionWithEnvOverrides("grpc_client_authentication")
|
||||
|
||||
mode := Mode(authzSection.Key("mode").MustString(string(ModeInProc)))
|
||||
if !mode.IsValid() {
|
||||
return nil, fmt.Errorf("authorization: invalid mode %q", mode)
|
||||
}
|
||||
|
||||
token := grpcClientAuthSection.Key("token").MustString("")
|
||||
tokenExchangeURL := grpcClientAuthSection.Key("token_exchange_url").MustString("")
|
||||
tokenNamespace := grpcClientAuthSection.Key("token_namespace").MustString("stacks-" + cfg.StackID)
|
||||
|
||||
// When running in cloud mode, the token and tokenExchangeURL are required.
|
||||
if mode == ModeCloud {
|
||||
if token == "" || tokenExchangeURL == "" {
|
||||
return nil, fmt.Errorf("authorization: missing token or tokenExchangeUrl")
|
||||
}
|
||||
}
|
||||
|
||||
return &Cfg{
|
||||
remoteAddress: authzSection.Key("remote_address").MustString(""),
|
||||
listen: authzSection.Key("listen").MustBool(false),
|
||||
mode: mode,
|
||||
token: token,
|
||||
tokenExchangeURL: tokenExchangeURL,
|
||||
tokenNamespace: tokenNamespace,
|
||||
allowInsecure: cfg.Env == setting.Dev,
|
||||
}, nil
|
||||
}
|
||||
+38
-106
@@ -7,7 +7,6 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/fullstorydev/grpchan"
|
||||
"github.com/fullstorydev/grpchan/inprocgrpc"
|
||||
grpcAuth "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/auth"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -26,7 +25,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
@@ -48,21 +46,18 @@ func ProvideAuthZClient(
|
||||
db db.DB,
|
||||
acService accesscontrol.Service,
|
||||
) (authlib.AccessClient, error) {
|
||||
authCfg, err := ReadCfg(cfg)
|
||||
authCfg, err := readAuthzClientSettings(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isRemoteServer := authCfg.mode == ModeCloud || authCfg.mode == ModeGRPC
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) && isRemoteServer {
|
||||
if !features.IsEnabledGlobally(featuremgmt.FlagAuthZGRPCServer) && authCfg.mode == clientModeCloud {
|
||||
return nil, errors.New("authZGRPCServer feature toggle is required for cloud and grpc mode")
|
||||
}
|
||||
|
||||
switch authCfg.mode {
|
||||
case ModeGRPC:
|
||||
return newGrpcLegacyClient(authCfg, tracer)
|
||||
case ModeCloud:
|
||||
return newCloudLegacyClient(authCfg, tracer)
|
||||
case clientModeCloud:
|
||||
return newRemoteRBACClient(authCfg, tracer)
|
||||
default:
|
||||
sql := legacysql.NewDatabaseProvider(db)
|
||||
|
||||
@@ -83,7 +78,18 @@ func ProvideAuthZClient(
|
||||
reg,
|
||||
cache.NewLocalCache(cache.Config{Expiry: 5 * time.Minute, CleanupInterval: 10 * time.Minute}),
|
||||
)
|
||||
return newInProcLegacyClient(server, tracer)
|
||||
|
||||
channel := &inprocgrpc.Channel{}
|
||||
channel.WithServerUnaryInterceptor(grpcAuth.UnaryServerInterceptor(func(ctx context.Context) (context.Context, error) {
|
||||
ctx = authlib.WithAuthInfo(ctx, authnlib.NewAccessTokenAuthInfo(authnlib.Claims[authnlib.AccessTokenClaims]{
|
||||
Rest: authnlib.AccessTokenClaims{
|
||||
Namespace: "*",
|
||||
},
|
||||
}))
|
||||
return ctx, nil
|
||||
}))
|
||||
authzv1.RegisterAuthzServiceServer(channel, server)
|
||||
return newRBACClient(channel, tracer), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,119 +102,46 @@ func ProvideStandaloneAuthZClient(
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
authCfg, err := ReadCfg(cfg)
|
||||
authCfg, err := readAuthzClientSettings(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if authCfg.mode == ModeGRPC {
|
||||
return newGrpcLegacyClient(authCfg, tracer)
|
||||
}
|
||||
return newCloudLegacyClient(authCfg, tracer)
|
||||
return newRemoteRBACClient(authCfg, tracer)
|
||||
}
|
||||
|
||||
func newInProcLegacyClient(server *rbac.Service, tracer tracing.Tracer) (authlib.AccessClient, error) {
|
||||
// For in-proc use-case authorize add fake service claims - it should be able to access every namespace, as there is only one
|
||||
staticAuth := func(ctx context.Context) (context.Context, error) {
|
||||
ctx = authlib.WithAuthInfo(ctx, authnlib.NewAccessTokenAuthInfo(authnlib.Claims[authnlib.AccessTokenClaims]{
|
||||
Rest: authnlib.AccessTokenClaims{
|
||||
Namespace: "*",
|
||||
},
|
||||
}))
|
||||
return ctx, nil
|
||||
func newRemoteRBACClient(clientCfg *authzClientSettings, tracer tracing.Tracer) (authlib.AccessClient, error) {
|
||||
tokenClient, err := authnlib.NewTokenExchangeClient(authnlib.TokenExchangeConfig{
|
||||
Token: clientCfg.token,
|
||||
TokenExchangeURL: clientCfg.tokenExchangeURL,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize token exchange client: %w", err)
|
||||
}
|
||||
|
||||
channel := &inprocgrpc.Channel{}
|
||||
channel.RegisterService(
|
||||
grpchan.InterceptServer(
|
||||
&authzv1.AuthzService_ServiceDesc,
|
||||
grpcAuth.UnaryServerInterceptor(staticAuth),
|
||||
grpcAuth.StreamServerInterceptor(staticAuth),
|
||||
conn, err := grpc.NewClient(
|
||||
clientCfg.remoteAddress,
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithPerRPCCredentials(
|
||||
newGRPCTokenAuth(authzServiceAudience, clientCfg.tokenNamespace, tokenClient),
|
||||
),
|
||||
server,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create authz client to remote server: %w", err)
|
||||
}
|
||||
|
||||
return newRBACClient(conn, tracer), nil
|
||||
}
|
||||
|
||||
func newRBACClient(conn grpc.ClientConnInterface, tracer tracing.Tracer) authlib.AccessClient {
|
||||
return authzlib.NewClient(
|
||||
&authzlib.ClientConfig{},
|
||||
authzlib.WithGrpcConnectionClientOption(channel),
|
||||
authzlib.WithDisableAccessTokenClientOption(),
|
||||
authzlib.WithTracerClientOption(tracer),
|
||||
authzlib.WithCacheClientOption(cache.NewLocalCache(cache.Config{
|
||||
Expiry: 30 * time.Second,
|
||||
CleanupInterval: 2 * time.Minute,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
func newGrpcLegacyClient(authCfg *Cfg, tracer tracing.Tracer) (authlib.AccessClient, error) {
|
||||
// This client interceptor is a noop, as we don't send an access token
|
||||
clientConfig := authnlib.GrpcClientConfig{}
|
||||
clientInterceptor, err := authnlib.NewGrpcClientInterceptor(
|
||||
&clientConfig,
|
||||
authnlib.WithDisableAccessTokenOption(),
|
||||
authnlib.WithTracerOption(tracer),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := authzlib.ClientConfig{RemoteAddress: authCfg.remoteAddress}
|
||||
client, err := authzlib.NewClient(&cfg,
|
||||
authzlib.WithGrpcDialOptionsClientOption(
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor),
|
||||
grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor),
|
||||
),
|
||||
authzlib.WithTracerClientOption(tracer),
|
||||
authzlib.WithCacheClientOption(cache.NewLocalCache(cache.Config{
|
||||
Expiry: 30 * time.Second,
|
||||
CleanupInterval: 2 * time.Minute,
|
||||
})),
|
||||
// TODO: remove this once access tokens are supported on-prem
|
||||
authzlib.WithDisableAccessTokenClientOption(),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func newCloudLegacyClient(authCfg *Cfg, tracer tracing.Tracer) (authlib.AccessClient, error) {
|
||||
grpcClientConfig := authnlib.GrpcClientConfig{
|
||||
TokenClientConfig: &authnlib.TokenExchangeConfig{
|
||||
Token: authCfg.token,
|
||||
TokenExchangeURL: authCfg.tokenExchangeURL,
|
||||
},
|
||||
TokenRequest: &authnlib.TokenExchangeRequest{
|
||||
Namespace: authCfg.tokenNamespace,
|
||||
Audiences: []string{authzServiceAudience},
|
||||
},
|
||||
}
|
||||
|
||||
clientInterceptor, err := authnlib.NewGrpcClientInterceptor(&grpcClientConfig, authnlib.WithTracerOption(tracer))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientCfg := authzlib.ClientConfig{RemoteAddress: authCfg.remoteAddress}
|
||||
client, err := authzlib.NewClient(&clientCfg,
|
||||
authzlib.WithGrpcDialOptionsClientOption(
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithUnaryInterceptor(clientInterceptor.UnaryClientInterceptor),
|
||||
grpc.WithStreamInterceptor(clientInterceptor.StreamClientInterceptor),
|
||||
),
|
||||
conn,
|
||||
authzlib.WithCacheClientOption(cache.NewLocalCache(cache.Config{
|
||||
Expiry: 30 * time.Second,
|
||||
CleanupInterval: 2 * time.Minute,
|
||||
})),
|
||||
authzlib.WithTracerClientOption(tracer),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func RegisterRBACAuthZService(
|
||||
@@ -251,7 +184,6 @@ func RegisterRBACAuthZService(
|
||||
|
||||
srv := handler.GetServer()
|
||||
authzv1.RegisterAuthzServiceServer(srv, server)
|
||||
authzextv1.RegisterAuthzExtentionServiceServer(srv, server)
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = tokenExhangeRoundTripper{}
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/rbac/store"
|
||||
"github.com/grafana/grafana/pkg/storage/legacysql"
|
||||
)
|
||||
@@ -38,7 +37,6 @@ const (
|
||||
|
||||
type Service struct {
|
||||
authzv1.UnimplementedAuthzServiceServer
|
||||
authzextv1.UnimplementedAuthzExtentionServiceServer
|
||||
|
||||
store store.Store
|
||||
folderStore store.FolderStore
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
type clientMode string
|
||||
|
||||
func (s clientMode) IsValid() bool {
|
||||
switch s {
|
||||
case clientModeInproc, clientModeCloud:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
clientModeCloud clientMode = "cloud"
|
||||
clientModeInproc clientMode = "inproc"
|
||||
)
|
||||
|
||||
type authzClientSettings struct {
|
||||
remoteAddress string
|
||||
mode clientMode
|
||||
|
||||
token string
|
||||
tokenExchangeURL string
|
||||
tokenNamespace string
|
||||
}
|
||||
|
||||
func readAuthzClientSettings(cfg *setting.Cfg) (*authzClientSettings, error) {
|
||||
authzSection := cfg.SectionWithEnvOverrides("authorization")
|
||||
grpcClientAuthSection := cfg.SectionWithEnvOverrides("grpc_client_authentication")
|
||||
|
||||
mode := clientMode(authzSection.Key("mode").MustString(string(clientModeInproc)))
|
||||
if !mode.IsValid() {
|
||||
return nil, fmt.Errorf("authorization: invalid mode %q", mode)
|
||||
}
|
||||
|
||||
s := &authzClientSettings{}
|
||||
s.mode = mode
|
||||
if s.mode == clientModeInproc {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
s.remoteAddress = authzSection.Key("remote_address").MustString("")
|
||||
s.token = grpcClientAuthSection.Key("token").MustString("")
|
||||
s.tokenNamespace = grpcClientAuthSection.Key("token_namespace").MustString("stacks-" + cfg.StackID)
|
||||
s.tokenExchangeURL = grpcClientAuthSection.Key("token_exchange_url").MustString("")
|
||||
|
||||
// When running in cloud mode, the token and tokenExchangeURL are required.
|
||||
if s.token == "" || s.tokenExchangeURL == "" {
|
||||
return nil, fmt.Errorf("authorization: missing token or tokenExchangeUrl")
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package authz
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/grafana/authlib/authn"
|
||||
)
|
||||
|
||||
func newGRPCTokenAuth(audience, namespace string, tc authn.TokenExchanger) *tokenAuth {
|
||||
return &tokenAuth{audience, namespace, tc}
|
||||
}
|
||||
|
||||
type tokenAuth struct {
|
||||
audience string
|
||||
namespace string
|
||||
tokenClient authn.TokenExchanger
|
||||
}
|
||||
|
||||
func (t *tokenAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
|
||||
token, err := t.tokenClient.Exchange(ctx, authn.TokenExchangeRequest{
|
||||
Namespace: t.namespace,
|
||||
Audiences: []string{t.audience},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]string{authn.DefaultAccessTokenMetadataKey: token.Token}, nil
|
||||
}
|
||||
|
||||
func (t *tokenAuth) RequireTransportSecurity() bool { return false }
|
||||
@@ -53,18 +53,13 @@ func ProvideZanzana(cfg *setting.Cfg, db db.DB, features featuremgmt.FeatureTogg
|
||||
if cfg.StackID == "" {
|
||||
return nil, fmt.Errorf("missing stack ID")
|
||||
}
|
||||
namespace := fmt.Sprintf("stacks-%s", cfg.StackID)
|
||||
|
||||
tokenAuthCred := &tokenAuth{
|
||||
cfg: cfg,
|
||||
namespace: namespace,
|
||||
tokenClient: tokenClient,
|
||||
}
|
||||
|
||||
dialOptions := []grpc.DialOption{
|
||||
// TODO: add TLS support
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithPerRPCCredentials(tokenAuthCred),
|
||||
grpc.WithPerRPCCredentials(
|
||||
newGRPCTokenAuth(authzServiceAudience, fmt.Sprintf("stacks-%s", cfg.StackID), tokenClient),
|
||||
),
|
||||
}
|
||||
|
||||
conn, err := grpc.NewClient(cfg.ZanzanaClient.Addr, dialOptions...)
|
||||
@@ -244,27 +239,3 @@ func (z *Zanzana) stopping(err error) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type tokenAuth struct {
|
||||
cfg *setting.Cfg
|
||||
namespace string
|
||||
tokenClient *authnlib.TokenExchangeClient
|
||||
}
|
||||
|
||||
func (t *tokenAuth) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
|
||||
token, err := t.tokenClient.Exchange(ctx, authnlib.TokenExchangeRequest{
|
||||
Namespace: t.namespace,
|
||||
Audiences: []string{authzServiceAudience},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]string{
|
||||
authnlib.DefaultAccessTokenMetadataKey: token.Token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *tokenAuth) RequireTransportSecurity() bool {
|
||||
return false
|
||||
}
|
||||
@@ -123,6 +123,7 @@ func (_m *FakeDashboardStore) CountInOrg(ctx context.Context, orgID int64) (int6
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// DeleteAllDashboards provides a mock function with given fields: ctx, orgID
|
||||
func (_m *FakeDashboardStore) DeleteAllDashboards(ctx context.Context, orgID int64) error {
|
||||
ret := _m.Called(ctx, orgID)
|
||||
|
||||
@@ -658,14 +658,6 @@ var (
|
||||
Owner: grafanaDatavizSquad,
|
||||
Expression: "true", // enabled by default
|
||||
},
|
||||
{
|
||||
Name: "transformationsVariableSupport",
|
||||
Description: "Allows using variables in transformations",
|
||||
FrontendOnly: true,
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
Owner: grafanaDatavizSquad,
|
||||
Expression: "true", // Enabled by default
|
||||
},
|
||||
{
|
||||
Name: "kubernetesPlaylists",
|
||||
Description: "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s",
|
||||
@@ -773,14 +765,6 @@ var (
|
||||
Owner: grafanaOperatorExperienceSquad,
|
||||
FrontendOnly: false,
|
||||
},
|
||||
{
|
||||
Name: "managedPluginsInstall",
|
||||
Description: "Install managed plugins directly from plugins catalog",
|
||||
Stage: FeatureStageGeneralAvailability,
|
||||
RequiresDevMode: false,
|
||||
Owner: grafanaPluginsPlatformSquad,
|
||||
Expression: "true", // enabled by default
|
||||
},
|
||||
{
|
||||
Name: "prometheusPromQAIL",
|
||||
Description: "Prometheus and AI/ML to assist users in creating a query",
|
||||
@@ -1817,6 +1801,23 @@ var (
|
||||
FrontendOnly: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
{
|
||||
Name: "alertingRuleVersionHistoryRestore",
|
||||
Description: "Enables the alert rule version history restore feature",
|
||||
FrontendOnly: true,
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaAlertingSquad,
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
{
|
||||
Name: "newShareReportDrawer",
|
||||
Description: "Enables the report creation drawer in a dashboard",
|
||||
Stage: FeatureStageExperimental,
|
||||
Owner: grafanaSharingSquad,
|
||||
HideFromAdminPage: true,
|
||||
HideFromDocs: true,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ panelMonitoring,GA,@grafana/dataviz-squad,false,false,true
|
||||
enableNativeHTTPHistogram,experimental,@grafana/grafana-backend-services-squad,false,true,false
|
||||
disableClassicHTTPHistogram,experimental,@grafana/grafana-backend-services-squad,false,true,false
|
||||
formatString,GA,@grafana/dataviz-squad,false,false,true
|
||||
transformationsVariableSupport,GA,@grafana/dataviz-squad,false,false,true
|
||||
kubernetesPlaylists,GA,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesSnapshots,experimental,@grafana/grafana-app-platform-squad,false,true,false
|
||||
kubernetesDashboards,experimental,@grafana/grafana-app-platform-squad,false,false,true
|
||||
@@ -102,7 +101,6 @@ recoveryThreshold,GA,@grafana/alerting-squad,false,true,false
|
||||
lokiStructuredMetadata,GA,@grafana/observability-logs,false,false,false
|
||||
teamHttpHeaders,preview,@grafana/identity-access-team,false,false,false
|
||||
cachingOptimizeSerializationMemoryUsage,experimental,@grafana/grafana-operator-experience-squad,false,false,false
|
||||
managedPluginsInstall,GA,@grafana/plugins-platform-backend,false,false,false
|
||||
prometheusPromQAIL,experimental,@grafana/oss-big-tent,false,false,true
|
||||
prometheusCodeModeMetricNamesSearch,experimental,@grafana/oss-big-tent,false,false,true
|
||||
addFieldFromCalculationStatFunctions,GA,@grafana/dataviz-squad,false,false,true
|
||||
@@ -241,3 +239,5 @@ newLogsPanel,experimental,@grafana/observability-logs,false,false,true
|
||||
grafanaconThemes,experimental,@grafana/grafana-frontend-platform,false,true,false
|
||||
pluginsCDNSyncLoader,experimental,@grafana/plugins-platform-backend,false,false,false
|
||||
alertingJiraIntegration,experimental,@grafana/alerting-squad,false,false,true
|
||||
alertingRuleVersionHistoryRestore,experimental,@grafana/alerting-squad,false,false,true
|
||||
newShareReportDrawer,experimental,@grafana/sharing-squad,false,false,false
|
||||
|
@@ -355,10 +355,6 @@ const (
|
||||
// Enable format string transformer
|
||||
FlagFormatString = "formatString"
|
||||
|
||||
// FlagTransformationsVariableSupport
|
||||
// Allows using variables in transformations
|
||||
FlagTransformationsVariableSupport = "transformationsVariableSupport"
|
||||
|
||||
// FlagKubernetesPlaylists
|
||||
// Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s
|
||||
FlagKubernetesPlaylists = "kubernetesPlaylists"
|
||||
@@ -419,10 +415,6 @@ const (
|
||||
// If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.
|
||||
FlagCachingOptimizeSerializationMemoryUsage = "cachingOptimizeSerializationMemoryUsage"
|
||||
|
||||
// FlagManagedPluginsInstall
|
||||
// Install managed plugins directly from plugins catalog
|
||||
FlagManagedPluginsInstall = "managedPluginsInstall"
|
||||
|
||||
// FlagPrometheusPromQAIL
|
||||
// Prometheus and AI/ML to assist users in creating a query
|
||||
FlagPrometheusPromQAIL = "prometheusPromQAIL"
|
||||
@@ -974,4 +966,12 @@ const (
|
||||
// FlagAlertingJiraIntegration
|
||||
// Enables the new Jira integration for contact points in cloud alert managers.
|
||||
FlagAlertingJiraIntegration = "alertingJiraIntegration"
|
||||
|
||||
// FlagAlertingRuleVersionHistoryRestore
|
||||
// Enables the alert rule version history restore feature
|
||||
FlagAlertingRuleVersionHistoryRestore = "alertingRuleVersionHistoryRestore"
|
||||
|
||||
// FlagNewShareReportDrawer
|
||||
// Enables the report creation drawer in a dashboard
|
||||
FlagNewShareReportDrawer = "newShareReportDrawer"
|
||||
)
|
||||
Loaded 100 of 187 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user