Merge remote-tracking branch 'origin/main' into secret-service/feature-branch
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
module bra
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/unknwon/bra
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module cog
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/grafana/cog/cmd/cli
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module cue
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool cuelang.org/go/cmd/cue
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module golangci-lint
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/golangci/golangci-lint/v2/cmd/golangci-lint
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module jb
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/jsonnet-bundler/jsonnet-bundler/cmd/jb
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module lefthook
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/evilmartians/lefthook
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module swagger
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
tool github.com/go-swagger/go-swagger/cmd/swagger
|
||||
|
||||
|
||||
+102
-102
@@ -25,7 +25,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build verify-drone
|
||||
@@ -75,7 +75,7 @@ steps:
|
||||
- go install github.com/bazelbuild/buildtools/buildifier@latest
|
||||
- buildifier --lint=warn -mode=check -r .
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: lint-starlark
|
||||
trigger:
|
||||
event:
|
||||
@@ -219,7 +219,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -228,21 +228,21 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apk add --update build-base shared-mime-info shared-mime-info-lang
|
||||
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -251,7 +251,7 @@ steps:
|
||||
| grep -o '\(.*\)/' | sort -u)
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend-integration
|
||||
trigger:
|
||||
event:
|
||||
@@ -306,7 +306,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $(/usr/bin/github-app-external-token) > /github-app/token
|
||||
@@ -351,16 +351,16 @@ steps:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- go run scripts/modowners/modowners.go check go.mod
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-modfile
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make swagger-validate
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-openapi-spec
|
||||
trigger:
|
||||
event:
|
||||
@@ -437,7 +437,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -447,7 +447,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -456,7 +456,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -507,7 +507,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-package
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -892,7 +892,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
@@ -906,7 +906,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -915,14 +915,14 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- dockerize -wait tcp://postgres:5432 -timeout 120s
|
||||
@@ -943,7 +943,7 @@ steps:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
POSTGRES_HOST: postgres
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: postgres-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mysql80:3306 -timeout 120s
|
||||
@@ -964,7 +964,7 @@ steps:
|
||||
environment:
|
||||
GRAFANA_TEST_DB: mysql
|
||||
MYSQL_HOST: mysql80
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: mysql-8.0-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379 -timeout 120s
|
||||
@@ -980,7 +980,7 @@ steps:
|
||||
- wait-for-redis
|
||||
environment:
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: redis-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://memcached:11211 -timeout 120s
|
||||
@@ -996,7 +996,7 @@ steps:
|
||||
- wait-for-memcached
|
||||
environment:
|
||||
MEMCACHED_HOSTS: memcached:11211
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: memcached-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
|
||||
@@ -1012,7 +1012,7 @@ steps:
|
||||
environment:
|
||||
AM_TENANT_ID: test
|
||||
AM_URL: http://mimir_backend:8080
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: remote-alertmanager-integration-tests
|
||||
trigger:
|
||||
event:
|
||||
@@ -1094,7 +1094,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
trigger:
|
||||
event:
|
||||
@@ -1215,7 +1215,7 @@ steps:
|
||||
&& return 1; fi
|
||||
depends_on:
|
||||
- clone-enterprise
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: swagger-gen
|
||||
trigger:
|
||||
event:
|
||||
@@ -1320,7 +1320,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -1331,7 +1331,7 @@ steps:
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on:
|
||||
- clone-enterprise
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -1341,14 +1341,14 @@ steps:
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on:
|
||||
- clone-enterprise
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -1356,7 +1356,7 @@ steps:
|
||||
- go test -v -run=^$ -benchmem -timeout=1h -count=8 -bench=. ${GO_PACKAGES}
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: sqlite-benchmark-integration-tests
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -1368,7 +1368,7 @@ steps:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
POSTGRES_HOST: postgres
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: postgres-benchmark-integration-tests
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -1379,7 +1379,7 @@ steps:
|
||||
environment:
|
||||
GRAFANA_TEST_DB: mysql
|
||||
MYSQL_HOST: mysql80
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: mysql-8.0-benchmark-integration-tests
|
||||
trigger:
|
||||
event:
|
||||
@@ -1451,7 +1451,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
trigger:
|
||||
branch: main
|
||||
@@ -1500,7 +1500,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -1509,21 +1509,21 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apk add --update build-base shared-mime-info shared-mime-info-lang
|
||||
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -1532,7 +1532,7 @@ steps:
|
||||
| grep -o '\(.*\)/' | sort -u)
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend-integration
|
||||
trigger:
|
||||
branch: main
|
||||
@@ -1577,22 +1577,22 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- go run scripts/modowners/modowners.go check go.mod
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-modfile
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make swagger-validate
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-openapi-spec
|
||||
- commands:
|
||||
- ./bin/build verify-drone
|
||||
@@ -1724,7 +1724,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- '# It is required that code generated from Thema/CUE be committed and in sync
|
||||
@@ -1734,7 +1734,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -1743,7 +1743,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -1793,7 +1793,7 @@ steps:
|
||||
environment:
|
||||
_EXPERIMENTAL_DAGGER_CLOUD_TOKEN:
|
||||
from_secret: dagger_token
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-package
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -2253,7 +2253,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
@@ -2267,7 +2267,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -2276,14 +2276,14 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- dockerize -wait tcp://postgres:5432 -timeout 120s
|
||||
@@ -2304,7 +2304,7 @@ steps:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
POSTGRES_HOST: postgres
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: postgres-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mysql80:3306 -timeout 120s
|
||||
@@ -2325,7 +2325,7 @@ steps:
|
||||
environment:
|
||||
GRAFANA_TEST_DB: mysql
|
||||
MYSQL_HOST: mysql80
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: mysql-8.0-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379 -timeout 120s
|
||||
@@ -2341,7 +2341,7 @@ steps:
|
||||
- wait-for-redis
|
||||
environment:
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: redis-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://memcached:11211 -timeout 120s
|
||||
@@ -2357,7 +2357,7 @@ steps:
|
||||
- wait-for-memcached
|
||||
environment:
|
||||
MEMCACHED_HOSTS: memcached:11211
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: memcached-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
|
||||
@@ -2373,7 +2373,7 @@ steps:
|
||||
environment:
|
||||
AM_TENANT_ID: test
|
||||
AM_URL: http://mimir_backend:8080
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: remote-alertmanager-integration-tests
|
||||
trigger:
|
||||
branch: main
|
||||
@@ -2641,7 +2641,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -2650,21 +2650,21 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apk add --update build-base shared-mime-info shared-mime-info-lang
|
||||
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -2673,7 +2673,7 @@ steps:
|
||||
| grep -o '\(.*\)/' | sort -u)
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend-integration
|
||||
trigger:
|
||||
branch:
|
||||
@@ -2716,22 +2716,22 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- go run scripts/modowners/modowners.go check go.mod
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-modfile
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make swagger-validate
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: validate-openapi-spec
|
||||
trigger:
|
||||
branch:
|
||||
@@ -2810,7 +2810,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- echo $DRONE_RUNNER_NAME
|
||||
@@ -2824,7 +2824,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -2833,14 +2833,14 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- dockerize -wait tcp://postgres:5432 -timeout 120s
|
||||
@@ -2861,7 +2861,7 @@ steps:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
POSTGRES_HOST: postgres
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: postgres-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mysql80:3306 -timeout 120s
|
||||
@@ -2882,7 +2882,7 @@ steps:
|
||||
environment:
|
||||
GRAFANA_TEST_DB: mysql
|
||||
MYSQL_HOST: mysql80
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: mysql-8.0-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379 -timeout 120s
|
||||
@@ -2898,7 +2898,7 @@ steps:
|
||||
- wait-for-redis
|
||||
environment:
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: redis-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://memcached:11211 -timeout 120s
|
||||
@@ -2914,7 +2914,7 @@ steps:
|
||||
- wait-for-memcached
|
||||
environment:
|
||||
MEMCACHED_HOSTS: memcached:11211
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: memcached-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
|
||||
@@ -2930,7 +2930,7 @@ steps:
|
||||
environment:
|
||||
AM_TENANT_ID: test
|
||||
AM_URL: http://mimir_backend:8080
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: remote-alertmanager-integration-tests
|
||||
trigger:
|
||||
branch:
|
||||
@@ -3030,7 +3030,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
@@ -3160,7 +3160,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts docker fetch --edition oss
|
||||
@@ -3301,7 +3301,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build artifacts packages --artifacts-editions=oss --tag $${DRONE_TAG} --src-bucket
|
||||
@@ -3393,7 +3393,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- yarn install --immutable || yarn install --immutable
|
||||
@@ -3493,7 +3493,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- depends_on:
|
||||
- compile-build-cmd
|
||||
@@ -3590,7 +3590,7 @@ steps:
|
||||
depends_on: []
|
||||
environment:
|
||||
CGO_ENABLED: 0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: compile-build-cmd
|
||||
- commands:
|
||||
- ./bin/build publish grafana-com --edition oss ${DRONE_TAG}
|
||||
@@ -3665,7 +3665,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -3742,7 +3742,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -3861,7 +3861,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -4000,7 +4000,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -4009,21 +4009,21 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- apk add --update build-base shared-mime-info shared-mime-info-lang
|
||||
- go list -f '{{.Dir}}/...' -m | xargs go test -short -covermode=atomic -timeout=5m
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend
|
||||
- commands:
|
||||
- apk add --update build-base
|
||||
@@ -4032,7 +4032,7 @@ steps:
|
||||
| grep -o '\(.*\)/' | sort -u)
|
||||
depends_on:
|
||||
- wire-install
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: test-backend-integration
|
||||
trigger:
|
||||
cron:
|
||||
@@ -4100,7 +4100,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -4246,7 +4246,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-publish
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -4376,7 +4376,7 @@ steps:
|
||||
STORYBOOK_DESTINATION:
|
||||
from_secret: rgm_storybook_destination
|
||||
UBUNTU_BASE: ubuntu:22.04
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: rgm-build
|
||||
pull: always
|
||||
volumes:
|
||||
@@ -4502,7 +4502,7 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-cue
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-cue
|
||||
- commands:
|
||||
- '# It is required that generated jsonnet is committed and in sync with its inputs.'
|
||||
@@ -4511,14 +4511,14 @@ steps:
|
||||
- apk add --update make
|
||||
- CODEGEN_VERIFY=1 make gen-jsonnet
|
||||
depends_on: []
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: verify-gen-jsonnet
|
||||
- commands:
|
||||
- apk add --update make
|
||||
- make gen-go
|
||||
depends_on:
|
||||
- verify-gen-cue
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: wire-install
|
||||
- commands:
|
||||
- dockerize -wait tcp://postgres:5432 -timeout 120s
|
||||
@@ -4539,7 +4539,7 @@ steps:
|
||||
GRAFANA_TEST_DB: postgres
|
||||
PGPASSWORD: grafanatest
|
||||
POSTGRES_HOST: postgres
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: postgres-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mysql80:3306 -timeout 120s
|
||||
@@ -4560,7 +4560,7 @@ steps:
|
||||
environment:
|
||||
GRAFANA_TEST_DB: mysql
|
||||
MYSQL_HOST: mysql80
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: mysql-8.0-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://redis:6379 -timeout 120s
|
||||
@@ -4576,7 +4576,7 @@ steps:
|
||||
- wait-for-redis
|
||||
environment:
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: redis-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://memcached:11211 -timeout 120s
|
||||
@@ -4592,7 +4592,7 @@ steps:
|
||||
- wait-for-memcached
|
||||
environment:
|
||||
MEMCACHED_HOSTS: memcached:11211
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: memcached-integration-tests
|
||||
- commands:
|
||||
- dockerize -wait tcp://mimir_backend:8080 -timeout 120s
|
||||
@@ -4608,7 +4608,7 @@ steps:
|
||||
environment:
|
||||
AM_TENANT_ID: test
|
||||
AM_URL: http://mimir_backend:8080
|
||||
image: golang:1.24.3-alpine
|
||||
image: golang:1.24.4-alpine
|
||||
name: remote-alertmanager-integration-tests
|
||||
trigger:
|
||||
event:
|
||||
@@ -4911,7 +4911,7 @@ steps:
|
||||
- commands:
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM docker:27-cli
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM alpine/git:2.40.1
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.3-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM golang:1.24.4-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22.11.0-alpine
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM node:22-bookworm
|
||||
- trivy --exit-code 0 --severity UNKNOWN,LOW,MEDIUM google/cloud-sdk:431.0.0
|
||||
@@ -4949,7 +4949,7 @@ steps:
|
||||
- commands:
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL docker:27-cli
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL alpine/git:2.40.1
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.3-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL golang:1.24.4-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22.11.0-alpine
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL node:22-bookworm
|
||||
- trivy --exit-code 1 --severity HIGH,CRITICAL google/cloud-sdk:431.0.0
|
||||
@@ -5206,6 +5206,6 @@ kind: secret
|
||||
name: gcr_credentials
|
||||
---
|
||||
kind: signature
|
||||
hmac: e430c51190ba46dbd426d13f47c13c275f4d05b6b1a26d1d9f67b5a8f09ecb41
|
||||
hmac: 7f318d682f431d61add0d60afcb9360f390217961709adeb88c34f11feec35b0
|
||||
|
||||
...
|
||||
|
||||
+18
-18
@@ -499,28 +499,28 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/features/canvas/ @grafana/dataviz-squad
|
||||
/public/app/features/geo/ @grafana/dataviz-squad
|
||||
/public/app/features/visualization/data-hover/ @grafana/dataviz-squad
|
||||
/public/app/features/commandPalette/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/commandPalette/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/connections/ @grafana/plugins-platform-frontend
|
||||
/public/app/features/correlations/ @grafana/datapro
|
||||
/public/app/features/dashboard/ @grafana/dashboards-squad
|
||||
/public/app/features/dashboard/components/TransformationsEditor/ @grafana/datapro
|
||||
/public/app/features/dashboard-scene/ @grafana/dashboards-squad
|
||||
/public/app/features/scopes/ @grafana/dashboards-squad
|
||||
/public/app/features/scopes/ @grafana/grafana-operator-experience-squad
|
||||
/public/app/features/datasources/ @grafana/plugins-platform-frontend
|
||||
/public/app/features/dimensions/ @grafana/dataviz-squad
|
||||
/public/app/features/dataframe-import/ @grafana/dataviz-squad
|
||||
/public/app/features/explore/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/expressions/ @grafana/grafana-datasources-core-services
|
||||
/public/app/features/folders/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/folders/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/inspector/ @grafana/dashboards-squad
|
||||
/public/app/features/invites/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/invites/ @grafana/sharing-squad
|
||||
/public/app/features/library-panels/ @grafana/dashboards-squad
|
||||
/public/app/features/logs/ @grafana/observability-logs
|
||||
/public/app/features/live/ @grafana/dashboards-squad
|
||||
/public/app/features/apiserver/ @grafana/grafana-app-platform-squad
|
||||
/public/app/features/manage-dashboards/ @grafana/dashboards-squad
|
||||
/public/app/features/notifications/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/org/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/notifications/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/org/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/panel/ @grafana/dashboards-squad
|
||||
/public/app/features/playlist/ @grafana/dashboards-squad
|
||||
/public/app/features/plugins/ @grafana/plugins-platform-frontend
|
||||
@@ -528,8 +528,8 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/features/runtime/ @ryantxu
|
||||
/public/app/features/query/ @grafana/dashboards-squad
|
||||
/public/app/features/sandbox/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/browse-dashboards/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/search/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/browse-dashboards/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/search/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/features/serviceaccounts/ @grafana/identity-squad
|
||||
/public/app/features/teams/ @grafana/access-squad
|
||||
/public/app/features/templating/ @grafana/dashboards-squad
|
||||
@@ -539,16 +539,16 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/features/users/ @grafana/access-squad
|
||||
/public/app/features/variables/ @grafana/dashboards-squad
|
||||
/public/app/features/preferences/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/bookmarks/ @grafana/grafana-frontend-platform
|
||||
/public/app/features/bookmarks/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/plugins/panel/alertlist/ @grafana/alerting-frontend
|
||||
/public/app/plugins/panel/annolist/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/annolist/ @grafana/dashboards-squad
|
||||
/public/app/plugins/panel/barchart/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/bargauge/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/dashlist/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/dashlist/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/plugins/panel/debug/ @ryantxu
|
||||
/public/app/plugins/panel/datagrid/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/gauge/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/gettingstarted/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/gettingstarted/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/plugins/panel/heatmap/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/histogram/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/logs/ @grafana/observability-logs
|
||||
@@ -567,12 +567,12 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/plugins/panel/canvas/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/candlestick/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/live/ @grafana/dashboards-squad
|
||||
/public/app/plugins/panel/news/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/news/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/stat/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/text/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/welcome/ @grafana/grafana-frontend-platform
|
||||
/public/app/plugins/panel/text/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/welcome/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/plugins/panel/xychart/ @grafana/dataviz-squad
|
||||
/public/app/routes/ @grafana/grafana-frontend-platform
|
||||
/public/app/routes/ @grafana/grafana-search-navigate-organise
|
||||
/public/app/store/ @grafana/grafana-frontend-platform
|
||||
/public/app/types/ @grafana/grafana-frontend-platform
|
||||
/public/app/types/alerting.ts @grafana/alerting-frontend
|
||||
@@ -600,7 +600,6 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/public/app/features/explore/NodeGraph/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/explore/FlameGraph/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/explore/TraceView/ @grafana/observability-traces-and-profiling
|
||||
/public/app/features/explore/QueryLibrary/ @grafana/grafana-frontend-platform
|
||||
|
||||
/public/api-merged.json @grafana/grafana-backend-group
|
||||
/public/api-enterprise-spec.json @grafana/grafana-backend-group
|
||||
@@ -642,7 +641,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/scripts/levitate-parse-json-report.js @grafana/plugins-platform-frontend
|
||||
/scripts/levitate-show-affected-plugins.js @grafana/plugins-platform-frontend
|
||||
/scripts/codemods/explicit-barrel-imports.cjs @grafana/frontend-ops
|
||||
/scripts/rtk-client-generator/ @grafana/grafana-frontend-platform
|
||||
/scripts/rtk-client-generator/ @grafana/grafana-search-navigate-organise
|
||||
|
||||
/scripts/**/generate-transformations* @grafana/datapro
|
||||
/scripts/webpack/ @grafana/frontend-ops
|
||||
@@ -689,6 +688,7 @@ playwright.config.ts @grafana/plugins-platform-frontend
|
||||
/pkg/services/dashboardsnapshots/ @grafana/sharing-squad
|
||||
/pkg/services/publicdashboards/ @grafana/sharing-squad
|
||||
/pkg/services/rendering/ @grafana/sharing-squad
|
||||
/public/app/features/explore/QueryLibrary/ @grafana/sharing-squad
|
||||
|
||||
# SSE - Server Side Expressions
|
||||
/pkg/expr/ @grafana/grafana-datasources-core-services
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
ARG BASE_IMAGE=alpine:3.21
|
||||
ARG JS_IMAGE=node:22-alpine
|
||||
ARG JS_PLATFORM=linux/amd64
|
||||
ARG GO_IMAGE=golang:1.24.3-alpine
|
||||
ARG GO_IMAGE=golang:1.24.4-alpine
|
||||
|
||||
# Default to building locally
|
||||
ARG GO_SRC=go-builder
|
||||
|
||||
@@ -9,7 +9,7 @@ include .bingo/Variables.mk
|
||||
include .citools/Variables.mk
|
||||
|
||||
GO = go
|
||||
GO_VERSION = 1.24.3
|
||||
GO_VERSION = 1.24.4
|
||||
GO_LINT_FILES ?= $(shell ./scripts/go-workspace/golangci-lint-includes.sh)
|
||||
GO_TEST_FILES ?= $(shell ./scripts/go-workspace/test-includes.sh)
|
||||
SH_FILES ?= $(shell find ./scripts -name *.sh)
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/advisor
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/authchecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/configchecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/datasourcecheck"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/instancechecks"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks/plugincheck"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/plugins/repo"
|
||||
@@ -81,6 +82,7 @@ func (s *Service) Checks() []checks.Check {
|
||||
),
|
||||
authchecks.New(s.ssoSettingsSvc),
|
||||
configchecks.New(s.cfg),
|
||||
instancechecks.New(s.cfg),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
var _ checks.Check = (*check)(nil)
|
||||
|
||||
type check struct {
|
||||
cfg *setting.Cfg
|
||||
isCloudInstance bool
|
||||
}
|
||||
|
||||
func New(cfg *setting.Cfg) checks.Check {
|
||||
return &check{
|
||||
cfg: cfg,
|
||||
isCloudInstance: cfg.StackID != "",
|
||||
}
|
||||
}
|
||||
|
||||
func (c *check) ID() string {
|
||||
return "instance"
|
||||
}
|
||||
|
||||
func (c *check) Name() string {
|
||||
return "instance attribute"
|
||||
}
|
||||
|
||||
func (c *check) Items(ctx context.Context) ([]any, error) {
|
||||
if c.isCloudInstance {
|
||||
return []any{
|
||||
pinnedVersion, // pinned version check
|
||||
}, nil
|
||||
}
|
||||
|
||||
return []any{
|
||||
outOfSupportVersion, // out of support version check
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *check) Item(ctx context.Context, id string) (any, error) {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
func (c *check) Init(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *check) Steps() []checks.Step {
|
||||
// If running in cloud, we need to check if the version is pinned
|
||||
if c.isCloudInstance {
|
||||
return []checks.Step{
|
||||
&pinnedVersionStep{
|
||||
BuildBranch: c.cfg.BuildBranch,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// If running in self-managed, we need to check if the version is out of support
|
||||
return []checks.Step{
|
||||
&outOfSupportVersionStep{
|
||||
GrafanaVersion: c.cfg.BuildVersion,
|
||||
BuildDate: time.Unix(c.cfg.BuildStamp, 0).UTC(),
|
||||
ghClient: github.NewClient(nil).Repositories,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheck_IsCloudInstance_Logic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stackID string
|
||||
expectedCloud bool
|
||||
expectedStepID string
|
||||
expectedItemID string
|
||||
}{
|
||||
{
|
||||
name: "empty stackID should run out of support check",
|
||||
stackID: "",
|
||||
expectedCloud: false,
|
||||
expectedStepID: outOfSupportVersion,
|
||||
expectedItemID: outOfSupportVersion,
|
||||
},
|
||||
{
|
||||
name: "non-empty stackID should run pinned version check",
|
||||
stackID: "12345",
|
||||
expectedCloud: true,
|
||||
expectedStepID: pinnedVersion,
|
||||
expectedItemID: pinnedVersion,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &setting.Cfg{
|
||||
StackID: tt.stackID,
|
||||
BuildBranch: "v10.0.0",
|
||||
BuildVersion: "10.0.0",
|
||||
BuildStamp: time.Now().Unix(),
|
||||
}
|
||||
|
||||
check := New(cfg).(*check)
|
||||
|
||||
// Verify isCloudInstance field is set correctly
|
||||
assert.Equal(t, tt.expectedCloud, check.isCloudInstance)
|
||||
|
||||
// Verify Steps() returns the correct step type
|
||||
steps := check.Steps()
|
||||
require.Len(t, steps, 1)
|
||||
assert.Equal(t, tt.expectedStepID, steps[0].ID())
|
||||
|
||||
// Verify Items() returns the correct item type
|
||||
items, err := check.Items(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Len(t, items, 1)
|
||||
assert.Equal(t, tt.expectedItemID, items[0])
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver/v3"
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
)
|
||||
|
||||
var _ checks.Step = &outOfSupportVersionStep{}
|
||||
|
||||
const (
|
||||
outOfSupportVersion = "out_of_support_version"
|
||||
)
|
||||
|
||||
type outOfSupportVersionStep struct {
|
||||
GrafanaVersion string
|
||||
BuildDate time.Time
|
||||
ghClient gitHubClient
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Title() string {
|
||||
return "Grafana version check"
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Description() string {
|
||||
return "Check if the current Grafana version is out of support."
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Resolution() string {
|
||||
return "Out of support versions will not receive security updates or bug fixes. " +
|
||||
"Upgrade to a more recent version. " +
|
||||
"<a href='https://grafana.com/docs/grafana/latest/upgrade-guide/when-to-upgrade/#what-to-know-about-version-support' target='_blank'>" +
|
||||
"Learn more about version support</a>."
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) ID() string {
|
||||
return outOfSupportVersion
|
||||
}
|
||||
|
||||
func (s *outOfSupportVersionStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
item, ok := it.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
if item != outOfSupportVersion {
|
||||
// Not interested in this item
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If the build date is less than 9 months old, it's supported
|
||||
if s.BuildDate.After(time.Now().AddDate(0, -9, 0)) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// If the build date is older than 15 months, it's not supported
|
||||
isOutOfSupport := false
|
||||
if s.BuildDate.Before(time.Now().AddDate(0, -15, 0)) {
|
||||
isOutOfSupport = true
|
||||
} else {
|
||||
// In other cases, we need to check if the version is out of support.
|
||||
// Minor versions are generally supported for 9 months but the last
|
||||
// minor version for a major version is supported for 15 months.
|
||||
// This is the case when we keep doing releases for old minor versions (e.g. 11.x when 12.x is out).
|
||||
// https://grafana.com/docs/grafana/latest/upgrade-guide/when-to-upgrade/#what-to-know-about-version-support
|
||||
|
||||
// Parse the current version using semver
|
||||
version, err := semver.NewVersion(s.GrafanaVersion)
|
||||
if err != nil {
|
||||
// Unable to parse the version so unable to check if it's out of support
|
||||
log.Error("Unable to parse the version", "version", s.GrafanaVersion, "error", err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// To verify if the current version is the latest minor version for this major version,
|
||||
// we try to get the version vX.Y+1.0 from GitHub
|
||||
nextMinorVersion := fmt.Sprintf("v%d.%d.0", version.Major(), version.Minor()+1)
|
||||
_, res, err := s.ghClient.GetReleaseByTag(ctx, "grafana", "grafana", nextMinorVersion)
|
||||
if err != nil && res.StatusCode != 404 {
|
||||
// Unable to get the release info so unable to check if it's out of support
|
||||
log.Error("Unable to get the release info", "version", s.GrafanaVersion, "error", err.Error())
|
||||
return nil, nil
|
||||
}
|
||||
latestMinor := false
|
||||
if res.StatusCode == 404 {
|
||||
// No next minor version found, so the current version is the latest minor version
|
||||
latestMinor = true
|
||||
}
|
||||
|
||||
// Calculate support end date
|
||||
supportMonths := 9
|
||||
if latestMinor {
|
||||
supportMonths = 15 // Extended support for last minor version
|
||||
}
|
||||
|
||||
supportEndDate := s.BuildDate.AddDate(0, supportMonths, 0)
|
||||
if time.Now().After(supportEndDate) {
|
||||
isOutOfSupport = true
|
||||
}
|
||||
}
|
||||
|
||||
if isOutOfSupport {
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityHigh,
|
||||
s.ID(),
|
||||
fmt.Sprintf("Grafana version %s is out of support", s.GrafanaVersion),
|
||||
outOfSupportVersion,
|
||||
[]advisor.CheckErrorLink{},
|
||||
)}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// gitHubClient is a mockable interface for the GitHub client
|
||||
type gitHubClient interface {
|
||||
GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*github.RepositoryRelease, *github.Response, error)
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-github/v70/github"
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestOutOfSupportVersionStep(t *testing.T) {
|
||||
now := time.Now()
|
||||
oldDate := now.AddDate(0, -16, 0) // 16 months ago
|
||||
recentDate := now.AddDate(0, -8, 0) // 8 months ago
|
||||
oldDateButSupportedIfLatestMinor := now.AddDate(0, -10, 0) // 10 months ago
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
step *outOfSupportVersionStep
|
||||
input any
|
||||
wantFailure bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should return error for invalid input type",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: now,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: 123, // invalid type
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "should return nil for non-out-of-support item",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: now,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: "other_item",
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for recent build date",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "11.5.0",
|
||||
BuildDate: recentDate,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for old build date",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDate,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for invalid version format",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "invalid-version",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for GitHub API error",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
errCode: http.StatusTooManyRequests,
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for non-latest minor version",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
release: &github.RepositoryRelease{},
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for latest minor version",
|
||||
step: &outOfSupportVersionStep{
|
||||
GrafanaVersion: "9.5.0",
|
||||
BuildDate: oldDateButSupportedIfLatestMinor,
|
||||
ghClient: &mockGitHubClient{
|
||||
errCode: http.StatusNotFound,
|
||||
},
|
||||
},
|
||||
input: outOfSupportVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
failures, err := tt.step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantFailure {
|
||||
require.Len(t, failures, 1)
|
||||
assert.Equal(t, advisor.CheckReportFailureSeverityHigh, failures[0].Severity)
|
||||
assert.Equal(t, outOfSupportVersion, failures[0].ItemID)
|
||||
assert.Equal(t, "Grafana version "+tt.step.GrafanaVersion+" is out of support", failures[0].Item)
|
||||
} else {
|
||||
assert.Empty(t, failures)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockGitHubClient struct {
|
||||
release *github.RepositoryRelease
|
||||
errCode int
|
||||
}
|
||||
|
||||
func (m *mockGitHubClient) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*github.RepositoryRelease, *github.Response, error) {
|
||||
if m.errCode != 0 {
|
||||
return nil, &github.Response{Response: &http.Response{StatusCode: m.errCode}}, assert.AnError
|
||||
}
|
||||
return m.release, &github.Response{Response: &http.Response{StatusCode: http.StatusOK}}, nil
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/advisor/pkg/app/checks"
|
||||
)
|
||||
|
||||
var _ checks.Step = &pinnedVersionStep{}
|
||||
|
||||
const (
|
||||
pinnedVersion = "pinned_version"
|
||||
)
|
||||
|
||||
type pinnedVersionStep struct {
|
||||
BuildBranch string
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Title() string {
|
||||
return "Grafana Cloud version check"
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Description() string {
|
||||
return "Check if the Grafana version is pinned."
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Resolution() string {
|
||||
return "You may miss out on security updates and bug fixes if you use a pinned version. " +
|
||||
"Contact your Grafana administrator and open a " +
|
||||
"<a href='https://grafana.com/profile/org#support' target=_blank>support ticket</a> to help you get unpinned."
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) ID() string {
|
||||
return pinnedVersion
|
||||
}
|
||||
|
||||
func (s *pinnedVersionStep) Run(ctx context.Context, log logging.Logger, _ *advisor.CheckSpec, it any) ([]advisor.CheckReportFailure, error) {
|
||||
item, ok := it.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid item type %T", it)
|
||||
}
|
||||
if item != pinnedVersion {
|
||||
// Not interested in this item
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if s.BuildBranch == "HEAD" {
|
||||
// Not a pinned version
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []advisor.CheckReportFailure{checks.NewCheckReportFailure(
|
||||
advisor.CheckReportFailureSeverityLow,
|
||||
s.ID(),
|
||||
"Grafana version is pinned",
|
||||
"pinned_version",
|
||||
[]advisor.CheckErrorLink{},
|
||||
)}, nil
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package instancechecks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana-app-sdk/logging"
|
||||
advisor "github.com/grafana/grafana/apps/advisor/pkg/apis/advisor/v0alpha1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPinnedVersionStep(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
step *pinnedVersionStep
|
||||
input any
|
||||
wantFailure bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "should return error for invalid input type",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "test-branch",
|
||||
},
|
||||
input: 123, // invalid type
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "should return nil for non-pinned version item",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "test-branch",
|
||||
},
|
||||
input: "other_item",
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return nil for HEAD build branch",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "HEAD",
|
||||
},
|
||||
input: pinnedVersion,
|
||||
wantFailure: false,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "should return failure for pinned version",
|
||||
step: &pinnedVersionStep{
|
||||
BuildBranch: "v9.5.0",
|
||||
},
|
||||
input: pinnedVersion,
|
||||
wantFailure: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
failures, err := tt.step.Run(context.Background(), logging.DefaultLogger, &advisor.CheckSpec{}, tt.input)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
|
||||
if tt.wantFailure {
|
||||
require.Len(t, failures, 1)
|
||||
assert.Equal(t, advisor.CheckReportFailureSeverityLow, failures[0].Severity)
|
||||
assert.Equal(t, "pinned_version", failures[0].ItemID)
|
||||
assert.Equal(t, "Grafana version is pinned", failures[0].Item)
|
||||
} else {
|
||||
assert.Empty(t, failures)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/alerting/notifications
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/dashboard
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.11.1
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/folder
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/iam
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/investigations
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/apps/playlist
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana-app-sdk v0.38.2
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module high-card
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require github.com/prometheus/client_golang v1.22.0
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module utf8-support
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.24.3
|
||||
FROM golang:1.24.4
|
||||
|
||||
ADD main.go /go/src/webhook/main.go
|
||||
|
||||
|
||||
+3
-1
@@ -59,7 +59,7 @@ The following tables list permissions associated with basic and fixed roles. Thi
|
||||
| Grafana Admin | `basic_grafana_admin` | fixed:roles:writer`<br>`fixed:users:writer`<br>`fixed:org.users:writer`<br>`fixed:ldap:writer`<br>`fixed:stats:reader`<br>`fixed:settings:writer`<br>`fixed:provisioning:writer`<br>`fixed:organization:maintainer`<br>`fixed:licensing:writer`<br>`fixed:plugins:maintainer`<br>`fixed:authentication.config:writer`<br>`fixed:migrationassistant:migrator` | Default [Grafana server administrator](/docs/grafana/<GRAFANA_VERSION>/administration/roles-and-permissions/#grafana-server-administrators) assignments. |
|
||||
| Admin | `basic_admin` | All roles assigned to Editor and `fixed:reports:writer` <br>`fixed:datasources:writer`<br>`fixed:organization:writer`<br>`fixed:datasources.permissions:writer`<br>`fixed:teams:writer`<br>`fixed:dashboards:writer`<br>`fixed:dashboards.permissions:writer`<br>`fixed:dashboards.public:writer`<br>`fixed:folders:writer`<br>`fixed:folders.permissions:writer`<br>`fixed:alerting:writer`<br>`fixed:apikeys:writer`<br>`fixed:alerting.provisioning.secrets:reader`<br>`fixed:alerting.provisioning:writer`<br>`fixed:datasources.caching:writer`<br>`fixed:plugins:writer`<br>`fixed:library.panels:writer` | Default [Grafana organization administrator](ref:rbac-basic-roles) assignments. |
|
||||
| Editor | `basic_editor` | All roles assigned to Viewer and `fixed:datasources:explorer` <br>`fixed:dashboards:creator`<br>`fixed:folders:creator`<br>`fixed:annotations:writer`<br>`fixed:alerting:writer`<br>`fixed:library.panels:creator`<br>`fixed:library.panels:general.writer`<br>`fixed:alerting.provisioning.status:writer` | Default [Editor](ref:rbac-basic-roles) assignments. |
|
||||
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
|
||||
| Viewer | `basic_viewer` | `fixed:datasources.id:reader`<br>`fixed:organization:reader`<br>`fixed:annotations:reader`<br>`fixed:annotations.dashboard:writer`<br>`fixed:alerting:reader`<br>`fixed:plugins.app:reader`<br>`fixed:dashboards.insights:reader`<br>`fixed:datasources.insights:reader`<br>`fixed:library.panels:general.reader`<br>`fixed:folders.general:reader`<br>`fixed:datasources.builtin:reader` | Default [Viewer](ref:rbac-basic-roles) assignments. |
|
||||
| No Basic Role | n/a | | Default [No Basic Role](ref:rbac-basic-roles) |
|
||||
|
||||
## Fixed role definitions
|
||||
@@ -103,6 +103,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
|
||||
| `fixed:datasources:explorer` | `fixed_qDzW9mzx9yM91T5Bi8dHUM2muTw` | `datasources:explore` | Enable the Explore feature. Data source permissions still apply, you can only query data sources for which you have query permissions. |
|
||||
| `fixed:datasources:reader` | `fixed_C2x8IxkiBc1KZVjyYH775T9jNMQ` | `datasources:read`<br>`datasources:query` | Read and query data sources. |
|
||||
| `fixed:datasources:writer` | `fixed_q8HXq8kjjA5IlHHgBJlKlUyaNik` | All permissions from `fixed:datasources:reader` and <br>`datasources:create`<br>`datasources:write`<br>`datasources:delete` | Read, query, create, delete, or update a data source. |
|
||||
| `fixed:datasources.builtin:reader` | `fixed_q8HXq8kjjA5IlHHgBJlKlUyaNik` | `datasources:read` and `datasources:query` scoped to `datasources:uid:grafana` | An internal role used to grant Viewers access to the builtin example data source in Grafana. |
|
||||
| `fixed:datasources.caching:reader` | `fixed_D2ddpGxJYlw0mbsTS1ek9fj0kj4` | `datasources.caching:read` | Read data source query caching settings. |
|
||||
| `fixed:datasources.caching:writer` | `fixed_JtFjHr7jd7hSqUYcktKvRvIOGRE` | `datasources.caching:read`<br>`datasources.caching:write` | Enable, disable, or update query caching settings. |
|
||||
| `fixed:datasources.id:reader` | `fixed_entg--fHmDqWY2-69N0ocawK0Os` | `datasources.id:read` | Read the ID of a data source based on its name. |
|
||||
@@ -112,6 +113,7 @@ To learn how to use the roles API to determine the role UUIDs, refer to [Manage
|
||||
| `fixed:folders:creator` | `fixed_gGLRbZGAGB6n9uECqSh_W382RlQ` | `folders:create` | Create folders in the root level. |
|
||||
| `fixed:folders:reader` | `fixed_yeW-5QPeo-i5PZUIUXMlAA97GnQ` | `folders:read`<br>`dashboards:read` | Read all folders and dashboards. |
|
||||
| `fixed:folders:writer` | `fixed_wJXLoTzgE7jVuz90dryYoiogL0o` | All permissions from `fixed:dashboards:writer` and <br>`folders:read`<br>`folders:write`<br>`folders:create`<br>`folders:delete`<br>`folders.permissions:read`<br>`folders.permissions:write` | Read, update, and delete all folders and dashboards. Create folders and subfolders. |
|
||||
| `fixed:folders.general:reader` | `fixed_rSASbkg8DvpG_gTX5s41d7uxRvI` | `folders:read` scoped to `folders:uid:general` | An internal role used to correctly display access to the folder tree for Viewer role. |
|
||||
| `fixed:folders.permissions:reader` | `fixed_E06l4cx0JFm47EeLBE4nmv3pnSo` | `folders.permissions:read` | Read all folder permissions. |
|
||||
| `fixed:folders.permissions:writer` | `fixed_3GAgpQ_hWG8o7-lwNb86_VB37eI` | All permissions from `fixed:folders.permissions:reader` and <br>`folders.permissions:write` | Read and update all folder permissions. |
|
||||
| `fixed:ldap:reader` | `fixed_lMcOPwSkxKY-qCK8NMJc5k6izLE` | `ldap.user:read`<br>`ldap.status:read` | Read the LDAP configuration and LDAP status information. |
|
||||
|
||||
+14
-1
@@ -229,7 +229,7 @@ To correct this, use the `--homepath` global option to specify the Grafana defau
|
||||
grafana cli --homepath "/usr/share/grafana" admin reset-admin-password <new password>
|
||||
```
|
||||
|
||||
If you have not lost the admin password, we recommend that you change the user password either in the User Preferences or in the Server Admin > User tab.
|
||||
If you have not lost the admin password, we recommend that you change the user password either in the User Preferences or in the **Server Admin > User** tab.
|
||||
|
||||
If you need to set the password in a script, then you can use the [Grafana User API](../developers/http_api/user/#change-password).
|
||||
|
||||
@@ -241,6 +241,19 @@ If you installed Grafana using Homebrew, you can reset the admin password using
|
||||
/opt/homebrew/opt/grafana/bin/grafana cli --config /opt/homebrew/etc/grafana/grafana.ini --homepath /opt/homebrew/opt/grafana/share/grafana --configOverrides cfg:default.paths.data=/opt/homebrew/var/lib/grafana admin reset-admin-password <new password>
|
||||
```
|
||||
|
||||
#### Reset admin password for Grafana deployed with Grafana Operator and using an external database
|
||||
|
||||
If you deploy Grafana with Grafana Operator and configure Grafana to use an external PostgreSQL or MySQL database, specify both the homepath and configuration file in your command:
|
||||
|
||||
```bash
|
||||
grafana cli --homepath /usr/share/grafana --config /etc/grafana/grafana.ini admin reset-admin-password <new password>
|
||||
```
|
||||
|
||||
If you don't do this, the CLI:
|
||||
|
||||
- Won't see the database connection information since it's in a directory outside of the Grafana homepath
|
||||
- Will configure the default SQLite database in `/var/lib/grafana` and reset that password instead of for your external database
|
||||
|
||||
### Migrate data and encrypt passwords
|
||||
|
||||
`data-migration` runs a script that migrates or cleans up data in your database.
|
||||
|
||||
@@ -84,7 +84,7 @@ In addition to Grafana, Grafana Labs also provides the following open source pro
|
||||
|
||||
**Grafana Beyla:** Grafana Beyla is an eBPF-based application auto-instrumentation tool for application observability. eBPF is used to automatically inspect application executables and the OS networking layer as well as capture basic trace spans related to web transactions and Rate-Errors-Duration (RED) metrics for Linux HTTP/S and gRPC services. All data capture occurs without any modifications to application code or configuration. For more information about Grafana Beyla, refer to [Grafana Beyla documentation](/docs/beyla/latest/).
|
||||
|
||||
**Grafana Alloy:** Grafana Alloy is a flexible, high performance, vendor-neutral distribution of the [OpenTelemetry][] (OTel) Collector.
|
||||
**Grafana Alloy:** Grafana Alloy is a flexible, high performance, vendor-neutral distribution of the [OpenTelemetry](https://opentelemetry.io/) (OTel) Collector.
|
||||
It's fully compatible with the most popular open source observability standards such as OpenTelemetry (OTel) and Prometheus.
|
||||
For more information about Grafana Alloy, refer to the [Grafana Alloy documentation](https://grafana.com/docs/alloy/latest/).
|
||||
|
||||
|
||||
@@ -164,10 +164,10 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Go 1.24.3
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.24.3
|
||||
go-version: 1.24.4
|
||||
|
||||
- name: Verify Go version
|
||||
run: go version
|
||||
@@ -208,7 +208,7 @@ This GitHub Action automates the deployment of a Grafana dashboard using the Fou
|
||||
The first few steps:
|
||||
|
||||
- Check out the repository to access the project code.
|
||||
- Install Go 1.24.3 using the `actions/setup-go` action.
|
||||
- Install Go 1.24.4 using the `actions/setup-go` action.
|
||||
- Verify Go is properly installed.
|
||||
|
||||
### 2. Download and install `grafanactl`
|
||||
|
||||
@@ -5,10 +5,6 @@ describe('Grouping panels', () => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
e2e.flows.revertAllChanges();
|
||||
});
|
||||
|
||||
/*
|
||||
* Rows
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
const PAGE_UNDER_TEST = 'edediimbjhdz4b/a-tall-dashboard';
|
||||
|
||||
describe('Dashboard Outline', () => {
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
});
|
||||
|
||||
it('can use dashboard outline', () => {
|
||||
e2e.flows.openDashboard({ uid: `${PAGE_UNDER_TEST}?orgId=1` });
|
||||
|
||||
e2e.flows.scenes.toggleEditMode();
|
||||
|
||||
e2e.components.PanelEditor.Outline.section().click();
|
||||
|
||||
// Should be able to click Variables item in outline to see add variable button
|
||||
e2e.components.PanelEditor.Outline.item('Variables').click();
|
||||
e2e.components.PanelEditor.ElementEditPane.addVariableButton().should('exist');
|
||||
|
||||
// Clicking a panel should scroll that panel in view
|
||||
cy.contains('Dashboard panel 48').should('not.exist');
|
||||
e2e.components.PanelEditor.Outline.item('Panel #48').click();
|
||||
cy.contains('Dashboard panel 48').should('exist');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,309 @@
|
||||
import { e2e } from '../utils';
|
||||
|
||||
describe('Dashboard', () => {
|
||||
beforeEach(() => {
|
||||
e2e.flows.login(Cypress.env('USERNAME'), Cypress.env('PASSWORD'));
|
||||
});
|
||||
|
||||
it('can switch to auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Switch to auto grid' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
const checkInputs = () => {
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('be.visible');
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns().should('be.visible');
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('be.visible');
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen().should('exist');
|
||||
};
|
||||
|
||||
checkInputs();
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
checkInputs();
|
||||
});
|
||||
|
||||
it('can change min column width in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set min column width' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
let firstStandardPanelTopOffset = 0;
|
||||
|
||||
// standard min column width will have 1 panel on a second row in edit mode
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
firstStandardPanelTopOffset = el.offset().top;
|
||||
});
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.last()
|
||||
.then((el) => {
|
||||
expect(el.offset().top).to.be.greaterThan(firstStandardPanelTopOffset);
|
||||
});
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-narrow]').click();
|
||||
|
||||
const checkOffset = () => {
|
||||
// narrow min column width will have all panels on the same row
|
||||
let narrowPanelTopOffset = 0;
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
narrowPanelTopOffset = el.offset().top;
|
||||
});
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.last()
|
||||
.then((el) => {
|
||||
expect(el.offset().top).to.eq(narrowPanelTopOffset);
|
||||
});
|
||||
};
|
||||
|
||||
checkOffset();
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('have.value', 'Narrow');
|
||||
|
||||
checkOffset();
|
||||
});
|
||||
|
||||
it('can change to custom min column width in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set custom min column width' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-custom]').click();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth()
|
||||
.should('be.visible')
|
||||
.clear()
|
||||
.type('900')
|
||||
.blur();
|
||||
|
||||
cy.wait(100); // cy too fast and executes next command before resizing is done
|
||||
|
||||
// // changing to 900 custom width to have each panel span the whole row to verify offset
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth().should('have.value', '900');
|
||||
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomMinColumnWidth().should('be.visible').click();
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('have.value', 'Standard');
|
||||
});
|
||||
|
||||
it('can change max columns in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set max columns' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-1]').click();
|
||||
|
||||
// changing to 1 max column to have each panel span the whole row to verify offset
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns().should('have.value', '1');
|
||||
|
||||
e2e.flows.scenes.verifyPanelsStackedVertically();
|
||||
});
|
||||
|
||||
it('can change row height in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set row height' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
let regularRowHeight = 0;
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
regularRowHeight = el.height();
|
||||
});
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-short]').click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
expect(el.height()).to.be.lessThan(regularRowHeight);
|
||||
});
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-tall]').click();
|
||||
|
||||
const checkHeight = () => {
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
expect(el.height()).to.be.greaterThan(regularRowHeight);
|
||||
});
|
||||
};
|
||||
|
||||
checkHeight();
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
checkHeight();
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('have.value', 'Tall');
|
||||
|
||||
checkHeight();
|
||||
});
|
||||
|
||||
it('can change to custom row height in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set custom row height' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
let regularRowHeight = 0;
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
regularRowHeight = el.height();
|
||||
});
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-custom]').click();
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight().clear().type('800').blur();
|
||||
cy.wait(100); // cy too fast and executes next command before resizing is done
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
const elHeight = el.height();
|
||||
expect(elHeight).be.closeTo(800, 5); // some flakyness and get 798 sometimes
|
||||
expect(elHeight).to.be.greaterThan(regularRowHeight);
|
||||
});
|
||||
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
expect(el.height()).be.closeTo(800, 5); // some flakyness and get 798 sometimes
|
||||
});
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight().should('have.value', '800');
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomRowHeight().should('be.visible').click();
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight().should('have.value', 'Standard');
|
||||
});
|
||||
|
||||
it('can change fill screen in auto grid layout', () => {
|
||||
e2e.flows.scenes.importV2Dashboard({ title: 'Set fill screen' });
|
||||
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel').should('have.length', 3);
|
||||
|
||||
e2e.components.OptionsGroup.toggle('grid-layout-category').click();
|
||||
|
||||
e2e.flows.scenes.selectAutoGridLayout();
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth().should('be.visible').click();
|
||||
cy.get('[id=combobox-option-narrow]').click();
|
||||
|
||||
let initialHeight = 0;
|
||||
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
initialHeight = el.height();
|
||||
});
|
||||
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen().click({ force: true });
|
||||
|
||||
const checkHeight = () => {
|
||||
e2e.components.Panels.Panel.title('New panel')
|
||||
.first()
|
||||
.then((el) => {
|
||||
expect(el.height()).to.be.greaterThan(initialHeight);
|
||||
});
|
||||
};
|
||||
|
||||
checkHeight();
|
||||
e2e.flows.scenes.saveDashboard();
|
||||
cy.reload();
|
||||
|
||||
checkHeight();
|
||||
e2e.components.NavToolbar.editDashboard.editButton().click();
|
||||
e2e.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen().should('be.checked');
|
||||
|
||||
checkHeight();
|
||||
});
|
||||
});
|
||||
@@ -12,6 +12,14 @@ export const selectRowsLayout = () => {
|
||||
clickGroupLayoutButton('Rows');
|
||||
};
|
||||
|
||||
export const selectCustomGridLayout = () => {
|
||||
clickGroupLayoutButton('Custom');
|
||||
};
|
||||
|
||||
export const selectAutoGridLayout = () => {
|
||||
clickGroupLayoutButton('Auto grid');
|
||||
};
|
||||
|
||||
const editPaneCopyOrDuplicate = (buttonLabel: string) => {
|
||||
e2e.components.EditPaneHeader.copyDropdown().click();
|
||||
cy.get('[role="menu"]').within(() => {
|
||||
|
||||
@@ -8,3 +8,4 @@ export * from './saveDashboard';
|
||||
export * from './importDashboard';
|
||||
export * from './editPaneActions';
|
||||
export * from './selectPanel';
|
||||
export * from './panelPosition';
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { e2e } from '../..';
|
||||
|
||||
export const verifyPanelsStackedVertically = () => {
|
||||
let initialOffset = 0;
|
||||
e2e.components.Panels.Panel.title('New panel').each((el) => {
|
||||
if (!initialOffset) {
|
||||
initialOffset = el.offset().top;
|
||||
} else {
|
||||
const elOffset = el.offset().top;
|
||||
expect(elOffset).to.be.greaterThan(initialOffset);
|
||||
initialOffset = elOffset;
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
buf.build/gen/go/parca-dev/parca/connectrpc/go v1.17.0-20240902100956-02fd72488966.1 // @grafana/observability-traces-and-profiling
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
// The `skip:golangci-lint` comment tag is used to exclude the package from the `golangci-lint` GitHub Action.
|
||||
// The module at the root of the repo (`.`) is excluded because ./pkg/... is included manually in the `golangci-lint` configuration.
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/hack
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require k8s.io/code-generator v0.33.1
|
||||
|
||||
|
||||
@@ -235,7 +235,6 @@
|
||||
"react-select-event": "5.5.1",
|
||||
"redux-mock-store": "1.5.5",
|
||||
"rimraf": "6.0.1",
|
||||
"rudder-sdk-js": "2.48.44",
|
||||
"sass": "1.83.4",
|
||||
"sass-loader": "16.0.4",
|
||||
"smtp-tester": "^2.1.0",
|
||||
|
||||
@@ -606,6 +606,32 @@ export const versionedComponents = {
|
||||
variableLabelInput: {
|
||||
'12.0.0': 'data-testid variable label input',
|
||||
},
|
||||
AutoGridLayout: {
|
||||
minColumnWidth: {
|
||||
'12.1.0': 'data-testid min column width selector',
|
||||
},
|
||||
customMinColumnWidth: {
|
||||
'12.1.0': 'data-testid custom min column width input',
|
||||
},
|
||||
clearCustomMinColumnWidth: {
|
||||
'12.1.0': 'data-testid clear custom min column width input',
|
||||
},
|
||||
maxColumns: {
|
||||
'12.1.0': 'data-testid max columns selector',
|
||||
},
|
||||
rowHeight: {
|
||||
'12.1.0': 'data-testid row height selector',
|
||||
},
|
||||
customRowHeight: {
|
||||
'12.1.0': 'data-testid custom row height input',
|
||||
},
|
||||
clearCustomRowHeight: {
|
||||
'12.1.0': 'data-testid clear custom row height input',
|
||||
},
|
||||
fillScreen: {
|
||||
'12.1.0': 'data-testid fill screen switch',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
PanelInspector: {
|
||||
|
||||
@@ -30,17 +30,20 @@ export async function loadPluginResources(id: string, language: string, loaders?
|
||||
}
|
||||
|
||||
// exported for testing
|
||||
export async function initDefaultI18nInstance() {
|
||||
export function initDefaultI18nInstance() {
|
||||
// If the resources are not an object, we need to initialize the plugin translations
|
||||
if (getI18nInstance().options?.resources && typeof getI18nInstance().options.resources === 'object') {
|
||||
return;
|
||||
}
|
||||
|
||||
await getI18nInstance().use(initReactI18next).init({
|
||||
const initPromise = getI18nInstance().use(initReactI18next).init({
|
||||
resources: {},
|
||||
returnEmptyString: false,
|
||||
lng: DEFAULT_LANGUAGE, // this should be the locale of the phrases in our source JSX
|
||||
});
|
||||
tFunc = getI18nInstance().t;
|
||||
transComponent = (props: TransProps) => <I18NextTrans shouldUnescape {...props} />;
|
||||
return initPromise;
|
||||
}
|
||||
|
||||
// exported for testing
|
||||
@@ -171,6 +174,7 @@ export function addResourceBundle(language: string, namespace: string, resources
|
||||
}
|
||||
|
||||
export const t: TFunction = (id: string, defaultMessage: string, values?: Record<string, unknown>) => {
|
||||
initDefaultI18nInstance();
|
||||
if (!tFunc) {
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
console.warn(
|
||||
@@ -189,10 +193,12 @@ export const t: TFunction = (id: string, defaultMessage: string, values?: Record
|
||||
};
|
||||
|
||||
export function useTranslate() {
|
||||
initDefaultI18nInstance();
|
||||
return { t };
|
||||
}
|
||||
|
||||
export function Trans(props: TransProps) {
|
||||
initDefaultI18nInstance();
|
||||
const Component = transComponent ?? I18NextTrans;
|
||||
return <Component shouldUnescape {...props} />;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLQuery } from '../../types';
|
||||
import { getColumnsWithIndices } from '../../utils/getColumnsWithIndices';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { GroupByRow } from './GroupByRow';
|
||||
@@ -15,6 +16,6 @@ interface SQLGroupByRowProps {
|
||||
|
||||
export function SQLGroupByRow({ fields, query, onQueryChange, db }: SQLGroupByRowProps) {
|
||||
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
|
||||
|
||||
return <GroupByRow columns={fields} sql={query.sql!} onSqlChange={onSqlChange} />;
|
||||
let columnsWithIndices: SelectableValue[] = getColumnsWithIndices(query, fields);
|
||||
return <GroupByRow columns={columnsWithIndices} sql={query.sql!} onSqlChange={onSqlChange} />;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { QueryWithDefaults } from '../../defaults';
|
||||
import { DB, SQLQuery } from '../../types';
|
||||
import { getColumnsWithIndices } from '../../utils/getColumnsWithIndices';
|
||||
import { useSqlChange } from '../../utils/useSqlChange';
|
||||
|
||||
import { OrderByRow } from './OrderByRow';
|
||||
@@ -15,26 +16,6 @@ type SQLOrderByRowProps = {
|
||||
|
||||
export function SQLOrderByRow({ fields, query, onQueryChange, db }: SQLOrderByRowProps) {
|
||||
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
|
||||
let columnsWithIndices: SelectableValue[] = [];
|
||||
|
||||
if (fields) {
|
||||
const options = query.sql?.columns?.map((c, i) => {
|
||||
const value = c.name ? `${c.name}(${c.parameters?.map((p) => p.name)})` : c.parameters?.map((p) => p.name);
|
||||
return {
|
||||
value,
|
||||
label: `${i + 1} - ${value}`,
|
||||
};
|
||||
});
|
||||
columnsWithIndices = [
|
||||
{
|
||||
value: '',
|
||||
label: 'Selected columns',
|
||||
options,
|
||||
expanded: true,
|
||||
},
|
||||
...fields,
|
||||
];
|
||||
}
|
||||
|
||||
let columnsWithIndices: SelectableValue[] = getColumnsWithIndices(query, fields);
|
||||
return <OrderByRow sql={query.sql!} onSqlChange={onSqlChange} columns={columnsWithIndices} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
|
||||
import { SQLQuery } from '../types';
|
||||
|
||||
export function getColumnsWithIndices(query: SQLQuery, fields: SelectableValue[]): SelectableValue[] {
|
||||
if (!fields || !query.sql?.columns) {
|
||||
return fields;
|
||||
}
|
||||
|
||||
const options = query.sql.columns.map((c, i) => {
|
||||
const value = c.name
|
||||
? `${c.name}(${c.parameters?.map((p) => p.name).join(', ')})`
|
||||
: c.parameters?.map((p) => p.name).join(', ');
|
||||
return {
|
||||
value,
|
||||
label: `${i + 1} - ${value}`,
|
||||
};
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
value: '',
|
||||
label: 'Selected columns',
|
||||
options,
|
||||
expanded: true,
|
||||
},
|
||||
...fields,
|
||||
];
|
||||
}
|
||||
@@ -105,6 +105,7 @@ export const getBodyStyles = (theme: GrafanaTheme2) => {
|
||||
|
||||
'&:disabled': {
|
||||
color: theme.colors.action.disabledText,
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
}),
|
||||
body: css({
|
||||
@@ -153,13 +154,18 @@ export const getBodyStyles = (theme: GrafanaTheme2) => {
|
||||
outline: 0,
|
||||
},
|
||||
|
||||
[`${hasActiveSelector}, .react-calendar__tile--active, .react-calendar__tile--active:hover`]: {
|
||||
[`${hasActiveSelector}, .react-calendar__tile--active`]: {
|
||||
color: theme.colors.primary.contrastText,
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
background: theme.colors.primary.main,
|
||||
border: '0px',
|
||||
},
|
||||
|
||||
'.react-calendar__tile:hover:not(.react-calendar__tile--active):not(.react-calendar__tile--rangeEnd):not(.react-calendar__tile--rangeStart)':
|
||||
{
|
||||
backgroundColor: theme.colors.action.hover,
|
||||
},
|
||||
|
||||
'.react-calendar__tile--rangeEnd, .react-calendar__tile--rangeStart': {
|
||||
padding: 0,
|
||||
border: '0px',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/aggregator
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/emicklei/go-restful/v3 v3.11.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/apimachinery
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/authlib v0.0.0-20250515162837-2f4a8263eabb // @grafana/identity-access-team
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/apis/secret
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/apiserver
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.7.0
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/build
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
// Override docker/docker to avoid:
|
||||
// go: github.com/drone-runners/drone-runner-docker@v1.8.2 requires
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/build/wire
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/google/go-cmp v0.7.0
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/codegen
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
cuelang.org/go v0.11.1
|
||||
|
||||
@@ -328,14 +328,6 @@ func buildGraphEdges(dp *simple.DirectedGraph, registry map[string]Node) error {
|
||||
for _, neededVar := range cmdNode.Command.NeedsVars() {
|
||||
neededNode, ok := registry[neededVar]
|
||||
if !ok {
|
||||
_, ok := cmdNode.Command.(*SQLCommand)
|
||||
// If the SSE is a SQL expression, and the node can't be found, it might be a CTE table name
|
||||
// CTEs are calculated during the evaluation of the SQL, so we won't have a node for them
|
||||
// So we `continue` in order to support CTE functionality
|
||||
// TODO: remove CTE table names from the list of table names during parsing of the SQL
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("unable to find dependent node '%v'", neededVar)
|
||||
}
|
||||
|
||||
|
||||
+19
-3
@@ -3,6 +3,7 @@ package sql
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dolthub/vitess/go/vt/sqlparser"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
@@ -10,7 +11,8 @@ import (
|
||||
|
||||
var logger = log.New("sql_expr")
|
||||
|
||||
// TablesList returns a list of tables for the sql statement
|
||||
// TablesList returns a list of tables for the sql statement excluding
|
||||
// CTEs and the 'dual' table. The list is sorted alphabetically.
|
||||
func TablesList(rawSQL string) ([]string, error) {
|
||||
stmt, err := sqlparser.Parse(rawSQL)
|
||||
if err != nil {
|
||||
@@ -19,10 +21,18 @@ func TablesList(rawSQL string) ([]string, error) {
|
||||
}
|
||||
|
||||
tables := make(map[string]struct{})
|
||||
cteNames := make(map[string]struct{})
|
||||
|
||||
walkSubtree := func(node sqlparser.SQLNode) error {
|
||||
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
|
||||
switch v := node.(type) {
|
||||
case *sqlparser.CommonTableExpr:
|
||||
// Track CTE name from the As field
|
||||
cteName := v.As.String()
|
||||
if cteName != "" {
|
||||
cteNames[strings.ToLower(cteName)] = struct{}{}
|
||||
}
|
||||
|
||||
case *sqlparser.AliasedTableExpr:
|
||||
if tableName, ok := v.Expr.(sqlparser.TableName); ok {
|
||||
tables[tableName.Name.String()] = struct{}{}
|
||||
@@ -49,9 +59,15 @@ func TablesList(rawSQL string) ([]string, error) {
|
||||
// Remove 'dual' table if it exists
|
||||
// This is a special table in MySQL that always returns a single row with a single column
|
||||
// See: https://dev.mysql.com/doc/refman/5.7/en/select.html#:~:text=You%20are%20permitted%20to%20specify%20DUAL%20as%20a%20dummy%20table%20name%20in%20situations%20where%20no%20tables%20are%20referenced
|
||||
if table != "dual" {
|
||||
result = append(result, table)
|
||||
if table == "dual" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip CTEs
|
||||
if _, ok := cteNames[strings.ToLower(table)]; ok {
|
||||
continue
|
||||
}
|
||||
result = append(result, table)
|
||||
}
|
||||
|
||||
sort.Strings(result)
|
||||
|
||||
@@ -105,7 +105,7 @@ func TestTablesList(t *testing.T) {
|
||||
)
|
||||
SELECT name, price
|
||||
FROM top_products;`,
|
||||
expected: []string{"products", "top_products"},
|
||||
expected: []string{"products"},
|
||||
},
|
||||
{
|
||||
name: "with quote",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/plugins/codegen
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
replace github.com/grafana/grafana/pkg/codegen => ../../codegen
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/promlib
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require (
|
||||
github.com/grafana/dskit v0.0.0-20250317084829-9cdd36a91f10
|
||||
|
||||
@@ -167,14 +167,8 @@ func (p *queryParser) parseRequest(ctx context.Context, input *query.QueryDataRe
|
||||
q, ok := queryRefIDs[refId]
|
||||
|
||||
if !ok {
|
||||
_, isSQLCMD := target.Command.(*expr.SQLCommand)
|
||||
if isSQLCMD {
|
||||
continue
|
||||
} else {
|
||||
target, ok = expressions[refId]
|
||||
if !ok {
|
||||
return rsp, makeDependencyError(exp.RefID, refId)
|
||||
}
|
||||
if target, ok = expressions[refId]; !ok {
|
||||
return rsp, makeDependencyError(exp.RefID, refId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -201,6 +201,46 @@ func TestSqlInputs(t *testing.T) {
|
||||
require.Equal(t, parsedRequestInfo.SqlInputs["B"], struct{}{})
|
||||
}
|
||||
|
||||
func TestSqlCTE(t *testing.T) {
|
||||
parser := newQueryParser(
|
||||
expr.NewExpressionQueryReader(featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions)),
|
||||
nil,
|
||||
tracing.InitializeTracerForTest(),
|
||||
log.NewNopLogger(),
|
||||
)
|
||||
|
||||
parsedRequestInfo, err := parser.parseRequest(context.Background(), &query.QueryDataRequest{
|
||||
QueryDataRequest: data.QueryDataRequest{
|
||||
Queries: []data.DataQuery{
|
||||
data.NewDataQuery(map[string]any{
|
||||
"refId": "A",
|
||||
"datasource": &data.DataSourceRef{
|
||||
Type: "prometheus",
|
||||
UID: "local-prom",
|
||||
},
|
||||
}),
|
||||
data.NewDataQuery(map[string]any{
|
||||
"refId": "B",
|
||||
"datasource": &data.DataSourceRef{
|
||||
Type: "__expr__",
|
||||
UID: "__expr__",
|
||||
},
|
||||
"type": "sql",
|
||||
"expression": `WITH CTE AS (
|
||||
SELECT
|
||||
Month
|
||||
FROM A
|
||||
)
|
||||
|
||||
SELECT * FROM CTE`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, parsedRequestInfo.SqlInputs["B"], struct{}{})
|
||||
}
|
||||
|
||||
func TestGrafanaDS(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
parser := newQueryParser(expr.NewExpressionQueryReader(featuremgmt.WithFeatures()),
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
module github.com/grafana/grafana/pkg/semconv
|
||||
|
||||
go 1.24.3
|
||||
go 1.24.4
|
||||
|
||||
require go.opentelemetry.io/otel v1.35.0
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const (
|
||||
grafanaDashboardsSquad codeowner = "@grafana/dashboards-squad"
|
||||
grafanaDatavizSquad codeowner = "@grafana/dataviz-squad"
|
||||
grafanaFrontendPlatformSquad codeowner = "@grafana/grafana-frontend-platform"
|
||||
grafanaFrontendSearchNavOrganise codeowner = "@grafana/grafana-search-navigate-organise"
|
||||
grafanaBackendServicesSquad codeowner = "@grafana/grafana-backend-services-squad"
|
||||
grafanaSearchAndStorageSquad codeowner = "@grafana/search-and-storage"
|
||||
grafanaPluginsPlatformSquad codeowner = "@grafana/plugins-platform-backend"
|
||||
|
||||
@@ -780,7 +780,14 @@ func (b *backend) getHistory(ctx context.Context, req *resourcepb.ListRequest, c
|
||||
listReq.MinRV = latestDeletedRV + 1
|
||||
}
|
||||
|
||||
rows, err := dbutil.QueryRows(ctx, tx, sqlResourceHistoryGet, listReq)
|
||||
var rows db.Rows
|
||||
if listReq.Trash {
|
||||
// unlike history, trash will not return an object if an object of the same name is live
|
||||
// (i.e. in the resource table)
|
||||
rows, err = dbutil.QueryRows(ctx, tx, sqlResourceTrash, listReq)
|
||||
} else {
|
||||
rows, err = dbutil.QueryRows(ctx, tx, sqlResourceHistoryGet, listReq)
|
||||
}
|
||||
if rows != nil {
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
|
||||
@@ -15,9 +15,6 @@ WHERE 1 = 1
|
||||
{{ if .Key.Name }}
|
||||
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||
{{ end }}
|
||||
{{ if .Trash }}
|
||||
AND {{ .Ident "action" }} = 3
|
||||
{{ end }}
|
||||
{{ if (gt .StartRV 0) }}
|
||||
{{ if .SortAscending }}
|
||||
AND {{ .Ident "resource_version" }} > {{ .Arg .StartRV }}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
SELECT
|
||||
h.{{ .Ident "guid" }},
|
||||
h.{{ .Ident "resource_version" }},
|
||||
h.{{ .Ident "namespace" }},
|
||||
h.{{ .Ident "group" }},
|
||||
h.{{ .Ident "resource" }},
|
||||
h.{{ .Ident "name" }},
|
||||
h.{{ .Ident "folder" }},
|
||||
h.{{ .Ident "value" }}
|
||||
FROM {{ .Ident "resource_history" }} h
|
||||
INNER JOIN (
|
||||
SELECT {{ .Ident "name" }}, MAX({{ .Ident "resource_version" }}) as max_rv
|
||||
FROM {{ .Ident "resource_history" }}
|
||||
WHERE 1 = 1
|
||||
AND {{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||
AND {{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||
AND {{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||
{{ if .Key.Name }}
|
||||
AND {{ .Ident "name" }} = {{ .Arg .Key.Name }}
|
||||
{{ end }}
|
||||
AND {{ .Ident "action" }} = 3
|
||||
{{ if (gt .StartRV 0) }}
|
||||
{{ if .SortAscending }}
|
||||
AND {{ .Ident "resource_version" }} > {{ .Arg .StartRV }}
|
||||
{{ else }}
|
||||
AND {{ .Ident "resource_version" }} < {{ .Arg .StartRV }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if (gt .MinRV 0) }}
|
||||
AND {{ .Ident "resource_version" }} >= {{ .Arg .MinRV }}
|
||||
{{ end }}
|
||||
{{ if (gt .ExactRV 0) }}
|
||||
AND {{ .Ident "resource_version" }} = {{ .Arg .ExactRV }}
|
||||
{{ end }}
|
||||
GROUP BY {{ .Ident "name" }}
|
||||
) max_versions ON h.{{ .Ident "name" }} = max_versions.{{ .Ident "name" }}
|
||||
AND h.{{ .Ident "resource_version" }} = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.{{ .Ident "namespace" }} = {{ .Arg .Key.Namespace }}
|
||||
AND h.{{ .Ident "group" }} = {{ .Arg .Key.Group }}
|
||||
AND h.{{ .Ident "resource" }} = {{ .Arg .Key.Resource }}
|
||||
AND h.{{ .Ident "action" }} = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM {{ .Ident "resource" }} r
|
||||
WHERE r.{{ .Ident "namespace" }} = h.{{ .Ident "namespace" }}
|
||||
AND r.{{ .Ident "group" }} = h.{{ .Ident "group" }}
|
||||
AND r.{{ .Ident "resource" }} = h.{{ .Ident "resource" }}
|
||||
AND r.{{ .Ident "name" }} = h.{{ .Ident "name" }}
|
||||
)
|
||||
{{ if .SortAscending }}
|
||||
ORDER BY h.{{ .Ident "resource_version" }} ASC
|
||||
{{ else }}
|
||||
ORDER BY h.{{ .Ident "resource_version" }} DESC
|
||||
{{ end }}
|
||||
@@ -46,6 +46,7 @@ var (
|
||||
sqlResourceHistoryGet = mustTemplate("resource_history_get.sql")
|
||||
sqlResourceHistoryDelete = mustTemplate("resource_history_delete.sql")
|
||||
sqlResourceHistoryPrune = mustTemplate("resource_history_prune.sql")
|
||||
sqlResourceTrash = mustTemplate("resource_trash.sql")
|
||||
sqlResourceInsertFromHistory = mustTemplate("resource_insert_from_history.sql")
|
||||
|
||||
// sqlResourceLabelsInsert = mustTemplate("resource_labels_insert.sql")
|
||||
|
||||
@@ -245,6 +245,9 @@ func TestUnifiedStorageQueries(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
sqlResourceTrash: {
|
||||
{
|
||||
Name: "read trash",
|
||||
Data: &sqlGetHistoryRequest{
|
||||
|
||||
Vendored
Executable
+35
@@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h.`guid`,
|
||||
h.`resource_version`,
|
||||
h.`namespace`,
|
||||
h.`group`,
|
||||
h.`resource`,
|
||||
h.`name`,
|
||||
h.`folder`,
|
||||
h.`value`
|
||||
FROM `resource_history` h
|
||||
INNER JOIN (
|
||||
SELECT `name`, MAX(`resource_version`) as max_rv
|
||||
FROM `resource_history`
|
||||
WHERE 1 = 1
|
||||
AND `namespace` = 'nn'
|
||||
AND `group` = 'gg'
|
||||
AND `resource` = 'rr'
|
||||
AND `action` = 3
|
||||
AND `resource_version` < 123456
|
||||
GROUP BY `name`
|
||||
) max_versions ON h.`name` = max_versions.`name`
|
||||
AND h.`resource_version` = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.`namespace` = 'nn'
|
||||
AND h.`group` = 'gg'
|
||||
AND h.`resource` = 'rr'
|
||||
AND h.`action` = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `resource` r
|
||||
WHERE r.`namespace` = h.`namespace`
|
||||
AND r.`group` = h.`group`
|
||||
AND r.`resource` = h.`resource`
|
||||
AND r.`name` = h.`name`
|
||||
)
|
||||
ORDER BY h.`resource_version` DESC
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h.`guid`,
|
||||
h.`resource_version`,
|
||||
h.`namespace`,
|
||||
h.`group`,
|
||||
h.`resource`,
|
||||
h.`name`,
|
||||
h.`folder`,
|
||||
h.`value`
|
||||
FROM `resource_history` h
|
||||
INNER JOIN (
|
||||
SELECT `name`, MAX(`resource_version`) as max_rv
|
||||
FROM `resource_history`
|
||||
WHERE 1 = 1
|
||||
AND `namespace` = 'nn'
|
||||
AND `group` = 'gg'
|
||||
AND `resource` = 'rr'
|
||||
AND `action` = 3
|
||||
GROUP BY `name`
|
||||
) max_versions ON h.`name` = max_versions.`name`
|
||||
AND h.`resource_version` = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h.`namespace` = 'nn'
|
||||
AND h.`group` = 'gg'
|
||||
AND h.`resource` = 'rr'
|
||||
AND h.`action` = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `resource` r
|
||||
WHERE r.`namespace` = h.`namespace`
|
||||
AND r.`group` = h.`group`
|
||||
AND r.`resource` = h.`resource`
|
||||
AND r.`name` = h.`name`
|
||||
)
|
||||
ORDER BY h.`resource_version` DESC
|
||||
Vendored
Executable
+35
@@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
AND "resource_version" < 123456
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
||||
Vendored
Executable
+35
@@ -0,0 +1,35 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
AND "resource_version" < 123456
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
SELECT
|
||||
h."guid",
|
||||
h."resource_version",
|
||||
h."namespace",
|
||||
h."group",
|
||||
h."resource",
|
||||
h."name",
|
||||
h."folder",
|
||||
h."value"
|
||||
FROM "resource_history" h
|
||||
INNER JOIN (
|
||||
SELECT "name", MAX("resource_version") as max_rv
|
||||
FROM "resource_history"
|
||||
WHERE 1 = 1
|
||||
AND "namespace" = 'nn'
|
||||
AND "group" = 'gg'
|
||||
AND "resource" = 'rr'
|
||||
AND "action" = 3
|
||||
GROUP BY "name"
|
||||
) max_versions ON h."name" = max_versions."name"
|
||||
AND h."resource_version" = max_versions.max_rv
|
||||
WHERE 1 = 1
|
||||
AND h."namespace" = 'nn'
|
||||
AND h."group" = 'gg'
|
||||
AND h."resource" = 'rr'
|
||||
AND h."action" = 3
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "resource" r
|
||||
WHERE r."namespace" = h."namespace"
|
||||
AND r."group" = h."group"
|
||||
AND r."resource" = h."resource"
|
||||
AND r."name" = h."name"
|
||||
)
|
||||
ORDER BY h."resource_version" DESC
|
||||
@@ -35,6 +35,7 @@ const (
|
||||
TestGetResourceStats = "get resource stats"
|
||||
TestListHistory = "list history"
|
||||
TestListHistoryErrorReporting = "list history error reporting"
|
||||
TestListTrash = "list trash"
|
||||
TestCreateNewResource = "create new resource"
|
||||
)
|
||||
|
||||
@@ -79,6 +80,7 @@ func RunStorageBackendTest(t *testing.T, newBackend NewBackendFunc, opts *TestOp
|
||||
{TestGetResourceStats, runTestIntegrationBackendGetResourceStats},
|
||||
{TestListHistory, runTestIntegrationBackendListHistory},
|
||||
{TestListHistoryErrorReporting, runTestIntegrationBackendListHistoryErrorReporting},
|
||||
{TestListTrash, runTestIntegrationBackendTrash},
|
||||
{TestCreateNewResource, runTestIntegrationBackendCreateNewResource},
|
||||
}
|
||||
|
||||
@@ -1128,3 +1130,95 @@ func newServer(t *testing.T, b resource.StorageBackend) resource.ResourceServer
|
||||
|
||||
return server
|
||||
}
|
||||
|
||||
func runTestIntegrationBackendTrash(t *testing.T, backend resource.StorageBackend, nsPrefix string) {
|
||||
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
|
||||
server := newServer(t, backend)
|
||||
ns := nsPrefix + "-ns-trash"
|
||||
|
||||
// item1 deleted with multiple history events
|
||||
rv1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv1, int64(0))
|
||||
rvDelete1, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete1, rv1)
|
||||
rvDelete2, err := writeEvent(ctx, backend, "item1", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete2, rvDelete1)
|
||||
|
||||
// item2 deleted and recreated, should not be returned in trash
|
||||
rv2, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv2, int64(0))
|
||||
rvDelete3, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_DELETED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rvDelete3, rv2)
|
||||
rv3, err := writeEvent(ctx, backend, "item2", resourcepb.WatchEvent_ADDED, WithNamespace(ns))
|
||||
require.NoError(t, err)
|
||||
require.Greater(t, rv3, int64(0))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
request *resourcepb.ListRequest
|
||||
expectedVersions []int64
|
||||
expectedValues []string
|
||||
minExpectedHeadRV int64
|
||||
expectedContinueRV int64
|
||||
expectedSortAsc bool
|
||||
}{
|
||||
{
|
||||
name: "returns the latest delete event",
|
||||
request: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_TRASH,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "item1",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVersions: []int64{rvDelete2},
|
||||
expectedValues: []string{"item1 DELETED"},
|
||||
minExpectedHeadRV: rvDelete2,
|
||||
expectedContinueRV: rvDelete2,
|
||||
expectedSortAsc: false,
|
||||
},
|
||||
{
|
||||
name: "does not return a version in the resource table",
|
||||
request: &resourcepb.ListRequest{
|
||||
Source: resourcepb.ListRequest_TRASH,
|
||||
Options: &resourcepb.ListOptions{
|
||||
Key: &resourcepb.ResourceKey{
|
||||
Namespace: ns,
|
||||
Group: "group",
|
||||
Resource: "resource",
|
||||
Name: "item2",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedVersions: []int64{},
|
||||
expectedValues: []string{},
|
||||
minExpectedHeadRV: rv3,
|
||||
expectedContinueRV: rv3,
|
||||
expectedSortAsc: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
res, err := server.List(ctx, tc.request)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, res.Error)
|
||||
expectedItemCount := len(tc.expectedVersions)
|
||||
require.Len(t, res.Items, expectedItemCount)
|
||||
for i := 0; i < expectedItemCount; i++ {
|
||||
require.Equal(t, tc.expectedVersions[i], res.Items[i].ResourceVersion)
|
||||
require.Contains(t, string(res.Items[i].Value), tc.expectedValues[i])
|
||||
}
|
||||
require.GreaterOrEqual(t, res.ResourceVersion, tc.minExpectedHeadRV)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ var (
|
||||
)
|
||||
|
||||
type ProfilingClient interface {
|
||||
ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, http.Header, error)
|
||||
LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, http.Header, error)
|
||||
LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, http.Header, error)
|
||||
GetSeries(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, http.Header, error)
|
||||
GetProfile(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error)
|
||||
GetSpanProfile(ctx context.Context, profileTypeID string, labelSelector string, spanSelector []string, start int64, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error)
|
||||
ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, error)
|
||||
LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, error)
|
||||
LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, error)
|
||||
GetSeries(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, error)
|
||||
GetProfile(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, maxNodes *int64) (*ProfileResponse, error)
|
||||
GetSpanProfile(ctx context.Context, profileTypeID string, labelSelector string, spanSelector []string, start int64, end int64, maxNodes *int64) (*ProfileResponse, error)
|
||||
}
|
||||
|
||||
// PyroscopeDatasource is a datasource for querying application performance profiles.
|
||||
@@ -109,7 +109,7 @@ func (d *PyroscopeDatasource) profileTypes(ctx context.Context, req *backend.Cal
|
||||
}
|
||||
}
|
||||
|
||||
types, respHeaders, err := d.client.ProfileTypes(ctx, start, end)
|
||||
types, err := d.client.ProfileTypes(ctx, start, end)
|
||||
if err != nil {
|
||||
ctxLogger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
@@ -119,7 +119,7 @@ func (d *PyroscopeDatasource) profileTypes(ctx context.Context, req *backend.Cal
|
||||
ctxLogger.Error("Failed to marshal response", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
}
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: bodyData, Headers: respHeaders, Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: bodyData, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send response", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
@@ -146,7 +146,7 @@ func (d *PyroscopeDatasource) labelNames(ctx context.Context, req *backend.CallR
|
||||
return fmt.Errorf("failed parsing label selector: %v", err)
|
||||
}
|
||||
|
||||
labelNames, respHeaders, err := d.client.LabelNames(ctx, labelSelector, start, end)
|
||||
labelNames, err := d.client.LabelNames(ctx, labelSelector, start, end)
|
||||
if err != nil {
|
||||
ctxLogger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
return fmt.Errorf("error calling LabelNames: %v", err)
|
||||
@@ -167,7 +167,7 @@ func (d *PyroscopeDatasource) labelNames(ctx context.Context, req *backend.CallR
|
||||
ctxLogger.Error("Failed to marshal response", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
}
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: jsonResponse, Headers: respHeaders, Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: jsonResponse, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send response", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
@@ -195,7 +195,7 @@ func (d *PyroscopeDatasource) labelValues(ctx context.Context, req *backend.Call
|
||||
end, _ := strconv.ParseInt(query.Get("end"), 10, 64)
|
||||
label := query.Get("label")
|
||||
|
||||
res, respHeaders, err := d.client.LabelValues(ctx, label, query.Get("query"), start, end)
|
||||
res, err := d.client.LabelValues(ctx, label, query.Get("query"), start, end)
|
||||
if err != nil {
|
||||
ctxLogger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
return fmt.Errorf("error calling LabelValues: %v", err)
|
||||
@@ -207,7 +207,7 @@ func (d *PyroscopeDatasource) labelValues(ctx context.Context, req *backend.Call
|
||||
return err
|
||||
}
|
||||
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: respHeaders, Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send response", "error", err, "function", logEntrypoint())
|
||||
return err
|
||||
@@ -255,7 +255,7 @@ func (d *PyroscopeDatasource) CheckHealth(ctx context.Context, _ *backend.CheckH
|
||||
// request succeeded or failed, we set the window to be small.
|
||||
start := time.Unix(1, 0).UnixMilli()
|
||||
end := time.Unix(4, 0).UnixMilli()
|
||||
if _, _, err := d.client.ProfileTypes(ctx, start, end); err != nil {
|
||||
if _, err := d.client.ProfileTypes(ctx, start, end); err != nil {
|
||||
status = backend.HealthStatusError
|
||||
message = err.Error()
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func NewPyroscopeClient(httpClient *http.Client, url string) *PyroscopeClient {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, http.Header, error) {
|
||||
func (c *PyroscopeClient) ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.ProfileTypes")
|
||||
defer span.End()
|
||||
res, err := c.connectClient.ProfileTypes(ctx, connect.NewRequest(&querierv1.ProfileTypesRequest{
|
||||
@@ -82,11 +82,11 @@ func (c *PyroscopeClient) ProfileTypes(ctx context.Context, start int64, end int
|
||||
logger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
if res.Msg.ProfileTypes == nil {
|
||||
// Let's make sure we send at least empty array if we don't have any types
|
||||
return []*ProfileType{}, nil, nil
|
||||
return []*ProfileType{}, nil
|
||||
} else {
|
||||
pTypes := make([]*ProfileType, len(res.Msg.ProfileTypes))
|
||||
for i, pType := range res.Msg.ProfileTypes {
|
||||
@@ -95,11 +95,11 @@ func (c *PyroscopeClient) ProfileTypes(ctx context.Context, start int64, end int
|
||||
Label: pType.Name + " - " + pType.SampleType,
|
||||
}
|
||||
}
|
||||
return pTypes, res.Header(), nil
|
||||
return pTypes, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, http.Header, error) {
|
||||
func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, labelSelector string, start int64, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.GetSeries", trace.WithAttributes(attribute.String("profileTypeID", profileTypeID), attribute.String("labelSelector", labelSelector)))
|
||||
defer span.End()
|
||||
req := connect.NewRequest(&querierv1.SelectSeriesRequest{
|
||||
@@ -117,7 +117,7 @@ func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, l
|
||||
logger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
series := make([]*Series, len(resp.Msg.Series))
|
||||
@@ -152,10 +152,10 @@ func (c *PyroscopeClient) GetSeries(ctx context.Context, profileTypeID string, l
|
||||
Series: series,
|
||||
Units: getUnits(profileTypeID),
|
||||
Label: parts[1],
|
||||
}, resp.Header(), nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSelector string, start, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error) {
|
||||
func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSelector string, start, end int64, maxNodes *int64) (*ProfileResponse, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.GetProfile", trace.WithAttributes(attribute.String("profileTypeID", profileTypeID), attribute.String("labelSelector", labelSelector)))
|
||||
defer span.End()
|
||||
req := &connect.Request[querierv1.SelectMergeStacktracesRequest]{
|
||||
@@ -173,18 +173,18 @@ func (c *PyroscopeClient) GetProfile(ctx context.Context, profileTypeID, labelSe
|
||||
logger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Msg.Flamegraph == nil {
|
||||
// Not an error, can happen when querying data oout of range.
|
||||
return nil, nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return profileQuery(resp.Msg.Flamegraph, profileTypeID), resp.Header(), nil
|
||||
return profileQuery(ctx, err, span, resp.Msg.Flamegraph, profileTypeID)
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) GetSpanProfile(ctx context.Context, profileTypeID, labelSelector string, spanSelector []string, start, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error) {
|
||||
func (c *PyroscopeClient) GetSpanProfile(ctx context.Context, profileTypeID, labelSelector string, spanSelector []string, start, end int64, maxNodes *int64) (*ProfileResponse, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.GetSpanProfile", trace.WithAttributes(attribute.String("profileTypeID", profileTypeID), attribute.String("labelSelector", labelSelector), attribute.String("spanSelector", strings.Join(spanSelector, ","))))
|
||||
defer span.End()
|
||||
req := &connect.Request[querierv1.SelectMergeSpanProfileRequest]{
|
||||
@@ -202,18 +202,18 @@ func (c *PyroscopeClient) GetSpanProfile(ctx context.Context, profileTypeID, lab
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.Msg.Flamegraph == nil {
|
||||
// Not an error, can happen when querying data oout of range.
|
||||
return nil, nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return profileQuery(resp.Msg.Flamegraph, profileTypeID), resp.Header(), nil
|
||||
return profileQuery(ctx, err, span, resp.Msg.Flamegraph, profileTypeID)
|
||||
}
|
||||
|
||||
func profileQuery(flamegraph *querierv1.FlameGraph, profileTypeID string) *ProfileResponse {
|
||||
func profileQuery(ctx context.Context, err error, span trace.Span, flamegraph *querierv1.FlameGraph, profileTypeID string) (*ProfileResponse, error) {
|
||||
levels := make([]*Level, len(flamegraph.Levels))
|
||||
for i, level := range flamegraph.Levels {
|
||||
levels[i] = &Level{
|
||||
@@ -229,7 +229,7 @@ func profileQuery(flamegraph *querierv1.FlameGraph, profileTypeID string) *Profi
|
||||
MaxSelf: flamegraph.MaxSelf,
|
||||
},
|
||||
Units: getUnits(profileTypeID),
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getUnits(profileTypeID string) string {
|
||||
@@ -244,7 +244,7 @@ func getUnits(profileTypeID string) string {
|
||||
return unit
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, http.Header, error) {
|
||||
func (c *PyroscopeClient) LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.LabelNames")
|
||||
defer span.End()
|
||||
resp, err := c.connectClient.LabelNames(ctx, connect.NewRequest(&typesv1.LabelNamesRequest{
|
||||
@@ -256,11 +256,11 @@ func (c *PyroscopeClient) LabelNames(ctx context.Context, labelSelector string,
|
||||
logger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, fmt.Errorf("error sending LabelNames request %v", err)
|
||||
return nil, fmt.Errorf("error sending LabelNames request %v", err)
|
||||
}
|
||||
|
||||
if resp.Msg.Names == nil {
|
||||
return []string{}, nil, nil
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
var filtered []string
|
||||
@@ -270,10 +270,10 @@ func (c *PyroscopeClient) LabelNames(ctx context.Context, labelSelector string,
|
||||
}
|
||||
}
|
||||
|
||||
return filtered, resp.Header(), nil
|
||||
return filtered, nil
|
||||
}
|
||||
|
||||
func (c *PyroscopeClient) LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, http.Header, error) {
|
||||
func (c *PyroscopeClient) LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, error) {
|
||||
ctx, span := tracing.DefaultTracer().Start(ctx, "datasource.pyroscope.LabelValues")
|
||||
defer span.End()
|
||||
resp, err := c.connectClient.LabelValues(ctx, connect.NewRequest(&typesv1.LabelValuesRequest{
|
||||
@@ -286,12 +286,12 @@ func (c *PyroscopeClient) LabelValues(ctx context.Context, label string, labelSe
|
||||
logger.Error("Received error from client", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return nil, nil, err
|
||||
return nil, err
|
||||
}
|
||||
if resp.Msg.Names == nil {
|
||||
return []string{}, nil, nil
|
||||
return []string{}, nil
|
||||
}
|
||||
return resp.Msg.Names, resp.Header(), nil
|
||||
return resp.Msg.Names, nil
|
||||
}
|
||||
|
||||
func isPrivateLabel(label string) bool {
|
||||
|
||||
@@ -19,7 +19,7 @@ func Test_PyroscopeClient(t *testing.T) {
|
||||
|
||||
t.Run("GetSeries", func(t *testing.T) {
|
||||
limit := int64(42)
|
||||
resp, _, err := client.GetSeries(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, []string{}, &limit, 15)
|
||||
resp, err := client.GetSeries(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, []string{}, &limit, 15)
|
||||
require.Nil(t, err)
|
||||
|
||||
series := &SeriesResponse{
|
||||
@@ -34,7 +34,7 @@ func Test_PyroscopeClient(t *testing.T) {
|
||||
|
||||
t.Run("GetProfile", func(t *testing.T) {
|
||||
maxNodes := int64(-1)
|
||||
resp, _, err := client.GetProfile(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, &maxNodes)
|
||||
resp, err := client.GetProfile(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, &maxNodes)
|
||||
require.Nil(t, err)
|
||||
|
||||
series := &ProfileResponse{
|
||||
@@ -56,7 +56,7 @@ func Test_PyroscopeClient(t *testing.T) {
|
||||
t.Run("GetProfile with empty response", func(t *testing.T) {
|
||||
connectClient.SendEmptyProfileResponse = true
|
||||
maxNodes := int64(-1)
|
||||
resp, _, err := client.GetProfile(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, &maxNodes)
|
||||
resp, err := client.GetProfile(context.Background(), "memory:alloc_objects:count:space:bytes", "{}", 0, 100, &maxNodes)
|
||||
require.Nil(t, err)
|
||||
// Mainly ensuring this does not panic like before
|
||||
require.Nil(t, resp)
|
||||
|
||||
@@ -77,7 +77,7 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
}
|
||||
}
|
||||
logger.Debug("Sending SelectSeriesRequest", "queryModel", qm, "function", logEntrypoint())
|
||||
seriesResp, _, err := d.client.GetSeries(
|
||||
seriesResp, err := d.client.GetSeries(
|
||||
gCtx,
|
||||
profileTypeId,
|
||||
labelSelector,
|
||||
@@ -114,7 +114,7 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
var profileResp *ProfileResponse
|
||||
if len(qm.SpanSelector) > 0 {
|
||||
logger.Debug("Calling GetSpanProfile", "queryModel", qm, "function", logEntrypoint())
|
||||
prof, _, err := d.client.GetSpanProfile(gCtx, profileTypeId, labelSelector, qm.SpanSelector, query.TimeRange.From.UnixMilli(), query.TimeRange.To.UnixMilli(), qm.MaxNodes)
|
||||
prof, err := d.client.GetSpanProfile(gCtx, profileTypeId, labelSelector, qm.SpanSelector, query.TimeRange.From.UnixMilli(), query.TimeRange.To.UnixMilli(), qm.MaxNodes)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
@@ -124,7 +124,7 @@ func (d *PyroscopeDatasource) query(ctx context.Context, pCtx backend.PluginCont
|
||||
profileResp = prof
|
||||
} else {
|
||||
logger.Debug("Calling GetProfile", "queryModel", qm, "function", logEntrypoint())
|
||||
prof, _, err := d.client.GetProfile(gCtx, profileTypeId, labelSelector, query.TimeRange.From.UnixMilli(), query.TimeRange.To.UnixMilli(), qm.MaxNodes)
|
||||
prof, err := d.client.GetProfile(gCtx, profileTypeId, labelSelector, query.TimeRange.From.UnixMilli(), query.TimeRange.To.UnixMilli(), qm.MaxNodes)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
|
||||
@@ -2,7 +2,6 @@ package pyroscope
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -419,7 +418,7 @@ type FakeClient struct {
|
||||
Args []any
|
||||
}
|
||||
|
||||
func (f *FakeClient) ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, http.Header, error) {
|
||||
func (f *FakeClient) ProfileTypes(ctx context.Context, start int64, end int64) ([]*ProfileType, error) {
|
||||
return []*ProfileType{
|
||||
{
|
||||
ID: "type:1",
|
||||
@@ -429,18 +428,18 @@ func (f *FakeClient) ProfileTypes(ctx context.Context, start int64, end int64) (
|
||||
ID: "type:2",
|
||||
Label: "memory",
|
||||
},
|
||||
}, http.Header{}, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *FakeClient) LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, http.Header, error) {
|
||||
func (f *FakeClient) LabelValues(ctx context.Context, label string, labelSelector string, start int64, end int64) ([]string, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (f *FakeClient) LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, http.Header, error) {
|
||||
func (f *FakeClient) LabelNames(ctx context.Context, labelSelector string, start int64, end int64) ([]string, error) {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (f *FakeClient) GetProfile(ctx context.Context, profileTypeID, labelSelector string, start, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error) {
|
||||
func (f *FakeClient) GetProfile(ctx context.Context, profileTypeID, labelSelector string, start, end int64, maxNodes *int64) (*ProfileResponse, error) {
|
||||
return &ProfileResponse{
|
||||
Flamebearer: &Flamebearer{
|
||||
Names: []string{"foo", "bar", "baz"},
|
||||
@@ -453,10 +452,10 @@ func (f *FakeClient) GetProfile(ctx context.Context, profileTypeID, labelSelecto
|
||||
MaxSelf: 56,
|
||||
},
|
||||
Units: "count",
|
||||
}, http.Header{}, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *FakeClient) GetSpanProfile(ctx context.Context, profileTypeID, labelSelector string, spanSelector []string, start, end int64, maxNodes *int64) (*ProfileResponse, http.Header, error) {
|
||||
func (f *FakeClient) GetSpanProfile(ctx context.Context, profileTypeID, labelSelector string, spanSelector []string, start, end int64, maxNodes *int64) (*ProfileResponse, error) {
|
||||
return &ProfileResponse{
|
||||
Flamebearer: &Flamebearer{
|
||||
Names: []string{"foo", "bar", "baz"},
|
||||
@@ -469,10 +468,10 @@ func (f *FakeClient) GetSpanProfile(ctx context.Context, profileTypeID, labelSel
|
||||
MaxSelf: 56,
|
||||
},
|
||||
Units: "count",
|
||||
}, http.Header{}, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *FakeClient) GetSeries(ctx context.Context, profileTypeID, labelSelector string, start, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, http.Header, error) {
|
||||
func (f *FakeClient) GetSeries(ctx context.Context, profileTypeID, labelSelector string, start, end int64, groupBy []string, limit *int64, step float64) (*SeriesResponse, error) {
|
||||
f.Args = []any{profileTypeID, labelSelector, start, end, groupBy, step}
|
||||
return &SeriesResponse{
|
||||
Series: []*Series{
|
||||
@@ -483,5 +482,5 @@ func (f *FakeClient) GetSeries(ctx context.Context, profileTypeID, labelSelector
|
||||
},
|
||||
Units: "count",
|
||||
Label: "test",
|
||||
}, http.Header{}, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (d *ParcaDatasource) callProfileTypes(ctx context.Context, req *backend.Cal
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: res.Header(), Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send data to Parca", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
@@ -98,7 +98,7 @@ func (d *ParcaDatasource) callLabelNames(ctx context.Context, req *backend.CallR
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: res.Header(), Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send data to Parca", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
@@ -142,7 +142,7 @@ func (d *ParcaDatasource) callLabelValues(ctx context.Context, req *backend.Call
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
return err
|
||||
}
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Headers: res.Header(), Status: 200})
|
||||
err = sender.Send(&backend.CallResourceResponse{Body: data, Status: 200})
|
||||
if err != nil {
|
||||
ctxLogger.Error("Failed to send data to Parca", "error", err, "function", logEntrypoint())
|
||||
span.RecordError(err)
|
||||
|
||||
@@ -347,48 +347,59 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({
|
||||
pointsFilter = (u, seriesIdx, show, gaps) => {
|
||||
let filtered = [];
|
||||
|
||||
let series = u.series[seriesIdx];
|
||||
|
||||
if (!show && gaps && gaps.length) {
|
||||
const [firstIdx, lastIdx] = series.idxs!;
|
||||
const xData = u.data[0];
|
||||
if (!show) {
|
||||
const yData = u.data[seriesIdx];
|
||||
const firstPos = Math.round(u.valToPos(xData[firstIdx], 'x', true));
|
||||
const lastPos = Math.round(u.valToPos(xData[lastIdx], 'x', true));
|
||||
|
||||
if (gaps[0][0] === firstPos) {
|
||||
filtered.push(firstIdx);
|
||||
}
|
||||
if (gaps && gaps.length) {
|
||||
const firstIdx = u.posToIdx(gaps[0][0], true);
|
||||
|
||||
// show single points between consecutive gaps that share end/start
|
||||
for (let i = 0; i < gaps.length; i++) {
|
||||
let thisGap = gaps[i];
|
||||
let nextGap = gaps[i + 1];
|
||||
if (yData[firstIdx - 1] == null) {
|
||||
filtered.push(firstIdx);
|
||||
}
|
||||
|
||||
if (nextGap && thisGap[1] === nextGap[0]) {
|
||||
// approx when data density is > 1pt/px, since gap start/end pixels are rounded
|
||||
let approxIdx = u.posToIdx(thisGap[1], true);
|
||||
// show single points between consecutive gaps that share end/start
|
||||
for (let i = 0; i < gaps.length; i++) {
|
||||
let thisGap = gaps[i];
|
||||
let nextGap = gaps[i + 1];
|
||||
|
||||
if (yData[approxIdx] == null) {
|
||||
// scan left/right alternating to find closest index with non-null value
|
||||
for (let j = 1; j < 100; j++) {
|
||||
if (yData[approxIdx + j] != null) {
|
||||
approxIdx += j;
|
||||
break;
|
||||
}
|
||||
if (yData[approxIdx - j] != null) {
|
||||
approxIdx -= j;
|
||||
break;
|
||||
if (nextGap && thisGap[1] === nextGap[0]) {
|
||||
// approx when data density is > 1pt/px, since gap start/end pixels are rounded
|
||||
let approxIdx = u.posToIdx(thisGap[1], true);
|
||||
|
||||
if (yData[approxIdx] == null) {
|
||||
// scan left/right alternating to find closest index with non-null value
|
||||
for (let j = 1; j < 100; j++) {
|
||||
if (yData[approxIdx + j] != null) {
|
||||
approxIdx += j;
|
||||
break;
|
||||
}
|
||||
if (yData[approxIdx - j] != null) {
|
||||
approxIdx -= j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filtered.push(approxIdx);
|
||||
filtered.push(approxIdx);
|
||||
}
|
||||
}
|
||||
|
||||
const lastIdx = u.posToIdx(gaps[gaps.length - 1][1], true);
|
||||
|
||||
if (yData[lastIdx + 1] == null) {
|
||||
filtered.push(lastIdx);
|
||||
}
|
||||
}
|
||||
|
||||
if (gaps[gaps.length - 1][1] === lastPos) {
|
||||
filtered.push(lastIdx);
|
||||
// single point surrounded by nulls
|
||||
else {
|
||||
// meh heuristic
|
||||
if (yData[0] == null && yData[yData.length - 1] == null) {
|
||||
for (let i = 0; i < yData.length; i++) {
|
||||
if (yData[i] != null) {
|
||||
filtered.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import type { apiOptions, identify, load, page, track } from 'rudder-sdk-js'; // SDK is loaded dynamically from config, so we only import types from the SDK package
|
||||
|
||||
import { BuildInfo, CurrentUserDTO } from '@grafana/data';
|
||||
import {
|
||||
EchoBackend,
|
||||
@@ -12,11 +10,19 @@ import {
|
||||
|
||||
import { loadScript } from '../../utils';
|
||||
|
||||
type Properties = Record<string, string | boolean | number>;
|
||||
|
||||
interface RudderstackAPIOptions {
|
||||
Intercom?: {
|
||||
user_hash: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Rudderstack {
|
||||
identify: typeof identify;
|
||||
load: typeof load;
|
||||
page: typeof page;
|
||||
track: typeof track;
|
||||
identify: (identifier: string, traits: Properties, options?: RudderstackAPIOptions) => void;
|
||||
load: (writeKey: string, dataPlaneURL: string, options: { configUrl?: string; destSDKBaseURL?: string }) => void;
|
||||
page: () => void;
|
||||
track: (eventName: string, properties?: Properties) => void;
|
||||
}
|
||||
|
||||
declare global {
|
||||
@@ -76,7 +82,7 @@ export class RudderstackBackend implements EchoBackend<PageviewEchoEvent, Rudder
|
||||
|
||||
if (options.user) {
|
||||
const { identifier, intercomIdentifier } = options.user.analytics;
|
||||
const apiOptions: apiOptions = {};
|
||||
const apiOptions: RudderstackAPIOptions = {};
|
||||
|
||||
if (intercomIdentifier) {
|
||||
apiOptions.Intercom = {
|
||||
|
||||
+15
-1
@@ -3,6 +3,7 @@ import { capitalize } from 'lodash';
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import { GrafanaTheme2 } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useTranslate } from '@grafana/i18n';
|
||||
import { t } from '@grafana/i18n/internal';
|
||||
import { Button, Combobox, ComboboxOption, Field, InlineSwitch, Input, Stack, useStyles2 } from '@grafana/ui';
|
||||
@@ -109,6 +110,7 @@ function GridLayoutColumns({ layoutManager }: { layoutManager: AutoGridLayoutMan
|
||||
options={minWidthOptions}
|
||||
value={columnWidth}
|
||||
onChange={onNamedMinWidthChanged}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.minColumnWidth}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
@@ -120,6 +122,7 @@ function GridLayoutColumns({ layoutManager }: { layoutManager: AutoGridLayoutMan
|
||||
min={50}
|
||||
max={2000}
|
||||
invalid={customMinWidthError}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customMinColumnWidth}
|
||||
suffix={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -127,6 +130,7 @@ function GridLayoutColumns({ layoutManager }: { layoutManager: AutoGridLayoutMan
|
||||
icon="times"
|
||||
tooltip={t('dashboard.auto-grid.options.min-width-custom-clear', 'Back to standard min column width')}
|
||||
onClick={onClearCustomMinWidth}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomMinColumnWidth}
|
||||
>
|
||||
{t('dashboard.auto-grid.options.custom-min-width.clear', 'Clear')}
|
||||
</Button>
|
||||
@@ -141,6 +145,7 @@ function GridLayoutColumns({ layoutManager }: { layoutManager: AutoGridLayoutMan
|
||||
value={String(maxColumnCount)}
|
||||
onChange={({ value }) => layoutManager.onMaxColumnCountChanged(parseInt(value, 10))}
|
||||
width={6.5}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.maxColumns}
|
||||
/>
|
||||
</Field>
|
||||
</Stack>
|
||||
@@ -217,7 +222,13 @@ function GridLayoutRows({ layoutManager }: { layoutManager: AutoGridLayoutManage
|
||||
className={styles.wideSelector}
|
||||
>
|
||||
{isStandardHeight ? (
|
||||
<Combobox id="min-height" options={minWidthOptions} value={rowHeight} onChange={onNamedMinHeightChanged} />
|
||||
<Combobox
|
||||
id="min-height"
|
||||
options={minWidthOptions}
|
||||
value={rowHeight}
|
||||
onChange={onNamedMinHeightChanged}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.rowHeight}
|
||||
/>
|
||||
) : (
|
||||
<Input
|
||||
id="min-height"
|
||||
@@ -228,6 +239,7 @@ function GridLayoutRows({ layoutManager }: { layoutManager: AutoGridLayoutManage
|
||||
min={50}
|
||||
max={2000}
|
||||
invalid={customMinWidthError}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.customRowHeight}
|
||||
suffix={
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -235,6 +247,7 @@ function GridLayoutRows({ layoutManager }: { layoutManager: AutoGridLayoutManage
|
||||
icon="times"
|
||||
tooltip={t('dashboard.auto-grid.options.min-width-custom-clear', 'Back to standard min column width')}
|
||||
onClick={onClearCustomRowHeight}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.clearCustomRowHeight}
|
||||
>
|
||||
{t('dashboard.auto-grid.options.custom-min-height.clear', 'Clear')}
|
||||
</Button>
|
||||
@@ -247,6 +260,7 @@ function GridLayoutRows({ layoutManager }: { layoutManager: AutoGridLayoutManage
|
||||
id="fill-screen-toggle"
|
||||
value={fillScreen}
|
||||
onChange={() => layoutManager.onFillScreenChanged(!fillScreen)}
|
||||
data-testid={selectors.components.PanelEditor.ElementEditPane.AutoGridLayout.fillScreen}
|
||||
/>
|
||||
</Field>
|
||||
</Stack>
|
||||
|
||||
+42
@@ -273,6 +273,48 @@ describe('LokiQueryBuilderOptions', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Step validation', () => {
|
||||
it('considers empty step as valid', async () => {
|
||||
setup({ expr: 'rate({foo="bar"}[5m]' });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.queryByText(/Invalid step/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('considers variable step that exists in the datasource as valid', async () => {
|
||||
const datasource = createLokiDatasource();
|
||||
datasource.getVariables = jest.fn().mockReturnValue(['$interval']);
|
||||
setup({ expr: 'rate({foo="bar"}[5m]', step: '$interval' }, undefined, { datasource });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.queryByText(/Invalid step/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('considers variable step that does not exist in the datasource as invalid', async () => {
|
||||
const datasource = createLokiDatasource();
|
||||
datasource.getVariables = jest.fn().mockReturnValue(['$interval']);
|
||||
setup({ expr: 'rate({foo="bar"}[5m]', step: '$custom' }, undefined, { datasource });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.getByText(/Invalid step/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('considers valid duration step as valid', async () => {
|
||||
setup({ expr: 'rate({foo="bar"}[5m]', step: '1m' });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.queryByText(/Invalid step/)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('considers invalid step as invalid', async () => {
|
||||
setup({ expr: 'rate({foo="bar"}[5m]', step: 'invalid' });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.getByText(/Invalid step/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('considers non-duration number as invalid', async () => {
|
||||
setup({ expr: 'rate({foo="bar"}[5m]', step: '123' });
|
||||
await userEvent.click(screen.getByRole('button', { name: /Options/ }));
|
||||
expect(screen.getByText(/Invalid step/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setup(queryOverrides: Partial<LokiQuery> = {}, onChange = jest.fn(), propOverrides: Partial<Props> = {}) {
|
||||
|
||||
+12
-2
@@ -138,8 +138,18 @@ export const LokiQueryBuilderOptions = React.memo<Props>(
|
||||
if (!query.step) {
|
||||
return true;
|
||||
}
|
||||
return typeof query.step === 'string' && isValidGrafanaDuration(query.step) && !isNaN(parseInt(query.step, 10));
|
||||
}, [query.step]);
|
||||
|
||||
if (typeof query.step === 'string') {
|
||||
// If we use a variable as step, we consider it valid
|
||||
if (datasource.getVariables().includes(query.step)) {
|
||||
return true;
|
||||
}
|
||||
// Check if the step is a valid Grafana duration
|
||||
return isValidGrafanaDuration(query.step) && !isNaN(parseInt(query.step, 10));
|
||||
}
|
||||
|
||||
return false;
|
||||
}, [query.step, datasource]);
|
||||
|
||||
return (
|
||||
<EditorRow>
|
||||
|
||||
@@ -2,9 +2,9 @@ import { css } from '@emotion/css';
|
||||
import { defaults } from 'lodash';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, QueryEditorProps } from '@grafana/data';
|
||||
import { CoreApp, GrafanaTheme2, QueryEditorProps } from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import { Button, InlineLabel, useStyles2 } from '@grafana/ui';
|
||||
import { Alert, Button, Icon, InlineLabel, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { TempoDatasource } from '../datasource';
|
||||
import { defaultQuery, MyDataSourceOptions, TempoQuery } from '../types';
|
||||
@@ -28,8 +28,26 @@ export function QueryEditor(props: Props) {
|
||||
return genQuery === query.query || genQuery === '{}';
|
||||
});
|
||||
|
||||
const alertingWarning = (
|
||||
<Alert title="Tempo metrics is an experimental feature" severity="warning">
|
||||
Please note that TraceQL metrics is an experimental feature and should not be used in production. Read more about
|
||||
it in{' '}
|
||||
<a
|
||||
className={css({ textDecoration: 'underline' })}
|
||||
href="https://grafana.com/docs/tempo/latest/operations/traceql-metrics/"
|
||||
target="_blank"
|
||||
>
|
||||
documentation
|
||||
<Icon name="external-link-alt" />
|
||||
</a>
|
||||
.
|
||||
</Alert>
|
||||
);
|
||||
const inAlerting = props.app === CoreApp.UnifiedAlerting || props.app === CoreApp.CloudAlerting;
|
||||
|
||||
return (
|
||||
<>
|
||||
{inAlerting && alertingWarning}
|
||||
<InlineLabel>
|
||||
Build complex queries using TraceQL to select a list of traces.{' '}
|
||||
<a rel="noreferrer" target="_blank" href="https://grafana.com/docs/tempo/latest/traceql/">
|
||||
|
||||
@@ -4465,6 +4465,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Vybrat ",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5283,7 +5284,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "zpráva",
|
||||
"body-dashboard-drastically-changed": "Protože používáte nové funkce nástěnky, které jsou podporované pouze v novém formátu schématu nástěnky Grafana, nástěnka bude uložena v novém formátu. Ujistěte se, jestli chcete provést tuto akci nebo chcete uložit nástěnku jako novou kopii.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Zrušit"
|
||||
},
|
||||
@@ -5299,7 +5300,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "Funkce dynamické nástěnky panelu je experimentální a nemá plnou funkčnost srovnatelnou se současným chováním nástěnek. Je založena na novém formátu schématu, který nepodporuje panely knihovny. To znamená, že při exportu nástěnky pro použití v jiné instanci nebudeme zahrnovat panely knihoven. Jejich podpora je naplánována v rámci <2>životního cyklu</2> funkce.",
|
||||
"schema-v2-library-panels-export-title": "Schéma nástěnky V2 zatím nepodporuje export panelů knihoven pro použití v jiné instanci",
|
||||
"title-dashboard-drastically-changed": "Nástěnka se podstatně změnila"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Nastaví aktuální obnovení jako nové výchozí",
|
||||
@@ -7767,6 +7768,8 @@
|
||||
},
|
||||
"enable-highlighting": "Povolit zvýraznění",
|
||||
"escape-newlines": "Opravit nesprávně uniklé sekvence nového řádku a záložek v řádcích protokolu",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Skrýt časová razítka",
|
||||
"hide-unique-labels": "Skrýt jedinečné štítky",
|
||||
"newest-first": "Seřazeno od nejnovějších protokolů – kliknutím zobrazíte nejstarší protokoly jako první",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Auswählen",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "Nachricht",
|
||||
"body-dashboard-drastically-changed": "Da Sie neue Dashboard-Funktionen nutzen, die nur im neuen Dashboard-Schema-Format von Grafana unterstützt werden, wird das Dashboard im neuen Format gespeichert. Bitte prüfen Sie, ob Sie diese Aktion wirklich ausführen möchten, oder ob Sie das Dashboard lieber als neue Kopie speichern möchten.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Abbrechen"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "Die dynamische Dashboard-Funktionalität ist experimentell und weist keine vollständige Funktionsparität mit dem aktuellen Dashboard-Verhalten auf. Sie basiert auf einem neuen Schemaformat, das keine Bibliotheks-Panels unterstützt. Das bedeutet, dass wir bei einem Export des Dashboards für die Nutzung in einer anderen Instanz keine Bibliotheks-Panels einbeziehen. Wir planen, sie im weiteren Verlauf des <2>Lebenszyklus</2> der Funktion zu unterstützen.",
|
||||
"schema-v2-library-panels-export-title": "Das Dashboard-Schema V2 unterstützt noch keinen Export von Bibliotheks-Panels für die Verwendung in einer anderen Instanz",
|
||||
"title-dashboard-drastically-changed": "Dashboard erheblich verändert"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Macht die aktuelle Aktualisierung zum neuen Standard",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Markierung aktivieren",
|
||||
"escape-newlines": "Falsch dargestellte Zeilenumbrüche und Tab-Sequenzen in Log-Zeilen korrigieren",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Zeitstempel ausblenden",
|
||||
"hide-unique-labels": "Eindeutige Labels ausblenden",
|
||||
"newest-first": "Sortiert nach neuesten Logs zuerst – klicken Sie, um die ältesten zuerst anzuzeigen",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Elegir",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "mensaje",
|
||||
"body-dashboard-drastically-changed": "Como estás utilizando nuevas funciones de dashboards que solo son compatibles con el nuevo formato de esquema de dashboards de Grafana, el dashboard se guardará en el nuevo formato. Asegúrate de que quieres realizar esta acción o prefieres guardar el dashboard como una nueva copia.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "La funcionalidad del dashboard dinámico es experimental y no tiene una paridad de características completa con el comportamiento actual de los dashboards. Se basa en un nuevo formato de esquema, que no admite paneles de librería. Esto significa que al exportar el dashboard para usarlo en otra instancia, no se incluirán paneles de librería. Tenemos la intención de admitirlos a medida que avanzamos en el <2>ciclo de vida</2> de la función.",
|
||||
"schema-v2-library-panels-export-title": "El esquema de dashboard V2 aún no admite la exportación de paneles de librería para su uso en otra instancia",
|
||||
"title-dashboard-drastically-changed": "El dashboard ha cambiado drásticamente"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Hará que la actualización actual sea la nueva predeterminada",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Activar resaltado",
|
||||
"escape-newlines": "Corregir las secuencias de tabulación y de nueva línea que escaparon incorrectamente en las líneas de log",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Ocultar marcas de tiempo",
|
||||
"hide-unique-labels": "Ocultar etiquetas únicas",
|
||||
"newest-first": "Ordenado por los logs más nuevos primero: haga clic para mostrar los más antiguos primero",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Choisir",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "message",
|
||||
"body-dashboard-drastically-changed": "Étant donné que vous utilisez de nouvelles fonctionnalités de tableau de bord uniquement prises en charge par le nouveau format de schéma de tableau de bord Grafana, le tableau de bord sera enregistré dans le nouveau format. Veuillez vous assurer que vous souhaitez effectuer cette action ou que vous préférez enregistrer le tableau de bord en tant que nouvelle copie.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Annuler"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "La fonctionnalité de tableau de bord dynamique est expérimentale et n’a pas une parité complète de fonctionnalités avec le comportement actuel des tableaux de bord. Elle est basée sur un nouveau format de schéma, qui ne prend pas en charge les panneaux de bibliothèque. Cela signifie que lors de l’exportation du tableau de bord pour l’utiliser dans une autre instance, nous n’inclurons pas les panneaux de la bibliothèque. Nous avons l’intention de les prendre en charge au fur et à mesure de notre progression dans le <2>cycle de vie</2> de la fonctionnalité.",
|
||||
"schema-v2-library-panels-export-title": "Le schéma de tableau de bord V2 ne prend pas encore en charge l’exportation de panneaux de bibliothèque à utiliser dans une autre instance",
|
||||
"title-dashboard-drastically-changed": "Le tableau de bord a radicalement changé"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "L’actualisation actuelle deviendra la nouvelle valeur par défaut",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Activer la surbrillance",
|
||||
"escape-newlines": "Correction des séquences de sauts de ligne et de tabulations incorrectement échappées dans les lignes du journal",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Masquer les horodatages",
|
||||
"hide-unique-labels": "Masquer les étiquettes uniques",
|
||||
"newest-first": "Trié par les journaux les plus récents en premier - Cliquez pour afficher les plus anciens en premier",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Kiválasztás",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "üzenet",
|
||||
"body-dashboard-drastically-changed": "Mivel olyan új irányítópult-funkciókat használ, amelyeket csak a Grafana új irányítópult-sémaformátuma támogat, az irányítópultot az új formátumban menti a rendszer. Győződjön meg róla, hogy végre kívánja hajtani ezt a műveletet, vagy inkább új példányként menti az irányítópultot.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Mégse"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "A dinamikus irányítópult funkcionalitása kísérleti jellegű, és nem rendelkezik teljes funkcionalitással a jelenlegi irányítópultok viselkedéséhez képest. Új sémaformátumon alapul, amely nem támogatja a könyvtárpaneleket. Ez azt jelenti, hogy amikor az irányítópultot egy másik példányban való használathoz exportálja, nem fogja tartalmazni a könyvtárpaneleket. Támogatni kívánjuk őket a funkció <2>életciklusának</2> előrehaladtával.",
|
||||
"schema-v2-library-panels-export-title": "A Dashboard Schema V2 még nem támogatja a könyvtárpanelek exportálását egy másik példányban való használathoz",
|
||||
"title-dashboard-drastically-changed": "Irányítópult nagymértékben megváltozott"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Az aktuális frissítés lesz az új alapértelmezés",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Kiemelés engedélyezése",
|
||||
"escape-newlines": "Helytelenül értelmezett újsor- és tabulátorkarakteres szekvenciák javítása a naplósorokban",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Időbélyegek elrejtése",
|
||||
"hide-unique-labels": "Egyedi címkék elrejtése",
|
||||
"newest-first": "Rendezés a legújabb naplók szerint – kattintson, hogy a legrégebbi naplók jelenjenek meg elsőként",
|
||||
|
||||
@@ -4408,6 +4408,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Pilih",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5223,7 +5224,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "pesan",
|
||||
"body-dashboard-drastically-changed": "Anda menggunakan fitur dasbor baru yang hanya didukung pada format skema dasbor Grafana baru, jadi dasbor akan disimpan dalam format baru. Pastikan Anda ingin melakukan tindakan ini atau Anda lebih suka menyimpan dasbor sebagai salinan baru.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Batalkan"
|
||||
},
|
||||
@@ -5239,7 +5240,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "Fungsionalitas dasbor dinamis bersifat eksperimen, dan tidak memiliki paritas fitur penuh dengan perilaku dasbor saat ini. Ini didasarkan pada format skema baru, yang tidak mendukung panel pustaka. Ini berarti ketika mengekspor dasbor untuk menggunakannya di instans lain, kami tidak akan menyertakan panel pustaka. Kami bermaksud untuk mendukung seiring fitur <2>siklus pengembangan</2> fitur.",
|
||||
"schema-v2-library-panels-export-title": "Skema Dasbor V2 belum mendukung ekspor panel pustaka untuk digunakan di instans lain",
|
||||
"title-dashboard-drastically-changed": "Dasbor berubah drastis"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Akan menjadikan muat ulang saat ini sebagai default baru",
|
||||
@@ -7689,6 +7690,8 @@
|
||||
},
|
||||
"enable-highlighting": "Aktifkan penyorotan",
|
||||
"escape-newlines": "Perbaiki escape sequence baris baru dan tab yang salah di baris log",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Sembunyikan stempel waktu",
|
||||
"hide-unique-labels": "Sembunyikan label unik",
|
||||
"newest-first": "Diurutkan berdasarkan log terbaru lebih dulu - Klik untuk menampilkan yang paling lama lebih dulu",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Scegli",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "messaggio",
|
||||
"body-dashboard-drastically-changed": "Dato che stai utilizzando nuove funzionalità della dashboard supportate solo nel nuovo formato grafico della dashboard Grafana, la dashboard verrà salvata nel nuovo formato. Assicurati di voler eseguire questa azione o di voler salvare la dashboard come nuova copia.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Annulla"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "La funzionalità della dashboard dinamica è in fase sperimentale e non ha una parità di funzionalità completa con il comportamento attuale delle dashboard. Si basa su un nuovo formato di schema, che non supporta i pannelli della libreria. Ciò significa che quando si esporta la dashboard per utilizzarla in un'altra istanza, non includeremo i pannelli della libreria. Intendiamo supportarli man mano che progrediamo nel <2>ciclo di vita</2> della funzionalità.",
|
||||
"schema-v2-library-panels-export-title": "Schema Dashboard V2 non supporta ancora l'esportazione di pannelli della libreria da utilizzare in un'altra istanza",
|
||||
"title-dashboard-drastically-changed": "La dashboard è cambiata radicalmente"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Imposterà l'aggiornamento corrente come nuovo valore predefinito",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Abilita evidenziazione",
|
||||
"escape-newlines": "Correggi le sequenze di nuove righe e tabulazioni non corrette nelle righe del registro",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Nascondi marca temporale",
|
||||
"hide-unique-labels": "Nascondi etichette univoche",
|
||||
"newest-first": "Ordine: prima i registri più recenti – Fai clic per mostrare prima i meno recenti",
|
||||
|
||||
@@ -4408,6 +4408,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "選択する",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5223,7 +5224,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "メッセージ",
|
||||
"body-dashboard-drastically-changed": "新しいGrafanaダッシュボードスキーマ形式でのみサポートされている新たなダッシュボード機能を使用しているため、ダッシュボードは新形式で保存されます。この操作を実行するか、ダッシュボードを新しいコピーとして保存するかを選択してください。",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "キャンセル"
|
||||
},
|
||||
@@ -5239,7 +5240,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "動的ダッシュボード機能は実験的なもので、現在のダッシュボード動作と完全な機能互換性はありません。この機能は、ライブラリパネルをサポートしていない新しいスキーマ形式に基づいています。そのため、別のインスタンスで使用する目的でダッシュボードをエクスポートした場合、ライブラリパネルは含まれません。機能の<2>ライフサイクル</2>が進むにつれ、これらをサポートする予定です。",
|
||||
"schema-v2-library-panels-export-title": "現在ダッシュボードスキーマV2では、別のインスタンスでの使用目的でのライブラリパネルのエクスポートをサポートしていません",
|
||||
"title-dashboard-drastically-changed": "ダッシュボードが大幅に変更されました"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "現在の更新を新しいデフォルトに設定",
|
||||
@@ -7689,6 +7690,8 @@
|
||||
},
|
||||
"enable-highlighting": "ハイライトを有効にする",
|
||||
"escape-newlines": "ログ行で誤ってエスケープされた改行とタブシーケンスを修正",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "タイムスタンプを非表示",
|
||||
"hide-unique-labels": "一意のラベルを非表示",
|
||||
"newest-first": "最新のログ順に並び替え - クリックして最も古いログを最初に表示",
|
||||
|
||||
@@ -4408,6 +4408,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "선택",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5223,7 +5224,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "메시지",
|
||||
"body-dashboard-drastically-changed": "새로운 Grafana 대시보드 스키마 형식에서만 지원되는 새 대시보드 기능을 사용하고 있기 때문에 대시보드가 새 형식으로 저장됩니다. 이 동작을 수행할지 아니면 대시보드를 새 복사본으로 저장할지 확인하세요.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "취소"
|
||||
},
|
||||
@@ -5239,7 +5240,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "동적 대시보드 기능은 실험 단계이며 현재 대시보드 동작과 모든 기능이 동일하지는 않습니다. 이 기능은 라이브러리 패널을 지원하지 않는 새로운 스키마 형식을 기반으로 합니다. 즉, 다른 인스턴스에서 사용하기 위해 대시보드를 내보낼 때 라이브러리 패널이 포함되지 않는다는 것을 의미합니다. Grafana는 기능 <2>수명 주기</2>가 진행됨에 따라 이를 지원할 계획입니다.",
|
||||
"schema-v2-library-panels-export-title": "대시보드 스키마 V2는 아직 다른 인스턴스에서 사용할 라이브러리 패널 내보내기를 지원하지 않습니다",
|
||||
"title-dashboard-drastically-changed": "대시보드가 대폭 변경됨"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "현재 새로 고침을 새로운 기본값으로 설정합니다",
|
||||
@@ -7689,6 +7690,8 @@
|
||||
},
|
||||
"enable-highlighting": "강조 표시 활성화",
|
||||
"escape-newlines": "로그 줄에서 잘못된 줄 바꿈 및 탭 시퀀스 수정",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "타임스탬프 숨기기",
|
||||
"hide-unique-labels": "고유 라벨 숨기기",
|
||||
"newest-first": "최신 로그순으로 정렬 - 클릭하여 오래된 로그순으로 표시",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Kies",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "bericht",
|
||||
"body-dashboard-drastically-changed": "Omdat je nieuwe dashboardfuncties gebruikt die alleen worden ondersteund in de nieuwe indeling van het Grafana-dashboardschema, wordt het dashboard opgeslagen in de nieuwe indeling. Beslis of je deze actie wilt uitvoeren of dat je het dashboard liever als een nieuwe kopie opslaat.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Annuleren"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "De dynamische dashboardfunctionaliteit is experimenteel en heeft geen volledige functiepariteit met het huidige gedrag van dashboards. Het is gebaseerd op een nieuw schemaformaat, dat geen bibliotheekpanelen ondersteunt. Dit betekent dat we bij het exporteren van het dashboard om het in een andere instantie te gebruiken, geen bibliotheekpanelen opnemen. We zijn van plan om ze te ondersteunen naarmate we verder komen in de <2>levenscyclus</2> van de functie.",
|
||||
"schema-v2-library-panels-export-title": "Dashboardschema V2 biedt nog geen ondersteuning voor het exporteren van bibliotheekpanelen die in een andere instantie kunnen worden gebruikt",
|
||||
"title-dashboard-drastically-changed": "Dashboard is drastisch gewijzigd"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Maakt de huidige vernieuwing de nieuwe standaard",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Markeren inschakelen",
|
||||
"escape-newlines": "Corrigeer onjuist escaped nieuwe lijn en tabbladsequenties in logregels",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Tijdstempels verbergen",
|
||||
"hide-unique-labels": "Unieke labels verbergen",
|
||||
"newest-first": "Gesorteerd op nieuwste logboeken eerst - klik om oudste eerst weer te geven",
|
||||
|
||||
@@ -4465,6 +4465,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Wybierz",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5283,7 +5284,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "Wiadomość",
|
||||
"body-dashboard-drastically-changed": "Ponieważ używasz nowych funkcji obsługiwanych tylko w nowym formacie schematu pulpitu Grafany, pulpit zostanie zapisany w nowym formacie. Zdecyduj, czy chcesz wykonać tę czynność, czy wolisz zapisać pulpit jako nową kopię.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Anuluj"
|
||||
},
|
||||
@@ -5299,7 +5300,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "Funkcja pulpitu dynamicznego jest eksperymentalna i nie odpowiada w pełni obecnemu sposobowi działania pulpitów. Opiera się na nowym formacie schematu, który nie obsługuje paneli biblioteki. Oznacza to, że przy eksporcie pulpitu, aby użyć go w innej instancji, nie uwzględnimy paneli biblioteki. Zamierzamy dodać obsługę tych paneli w miarę <2>rozwoju tej funkcji</2>.",
|
||||
"schema-v2-library-panels-export-title": "Schemat pulpitu w wersji 2 nie obsługuje jeszcze eksportowania paneli biblioteki do użytku w innej instancji",
|
||||
"title-dashboard-drastically-changed": "Panel znacznie się zmienił"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Bieżące odświeżenie stanie się nową wartością domyślną",
|
||||
@@ -7767,6 +7768,8 @@
|
||||
},
|
||||
"enable-highlighting": "Włącz wyróżnianie",
|
||||
"escape-newlines": "Napraw nieprawidłowe sekwencje znaków nowego wiersza i tabulacji we wpisach logów",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Ukryj znaczniki czasu",
|
||||
"hide-unique-labels": "Ukryj unikalne etykiety",
|
||||
"newest-first": "Sortowanie od najnowszych wpisów dziennika – kliknij, aby wyświetlić najpierw najstarsze",
|
||||
|
||||
@@ -4427,6 +4427,7 @@
|
||||
}
|
||||
},
|
||||
"repeat-row-select2": {
|
||||
"placeholder": "Escolher",
|
||||
"variable-options": {
|
||||
"label": {
|
||||
"disable-repeating": "",
|
||||
@@ -5243,7 +5244,7 @@
|
||||
},
|
||||
"save-dashboard-form": {
|
||||
"aria-label-message": "mensagem",
|
||||
"body-dashboard-drastically-changed": "Como você está usando novos recursos de painéis compatíveis apenas com o novo formato de esquema de painel da Grafana, o painel será salvo no novo formato. Certifique-se de que você deseja executar esta ação ou prefere salvar o painel como uma nova cópia.",
|
||||
"body-dashboard-drastically-changed": "",
|
||||
"cancel-button": {
|
||||
"cancel": "Cancelar"
|
||||
},
|
||||
@@ -5259,7 +5260,7 @@
|
||||
},
|
||||
"schema-v2-library-panels-export": "A funcionalidade de painel dinâmico está em testes e não possui todos os recursos presentes no comportamento atual dos painéis. Ele se baseia em um novo formato de esquema, que não aceita painéis da biblioteca. Isso significa que, ao exportar o painel para usá-lo em outra instância, não incluiremos painéis da biblioteca. Pretendemos aceitá-los à medida que avançamos no <2>ciclo de vida</2> do recurso.",
|
||||
"schema-v2-library-panels-export-title": "O esquema de painel V2 ainda não aceita a exportação de painéis da biblioteca para uso em outra instância",
|
||||
"title-dashboard-drastically-changed": "O painel foi alterado drasticamente"
|
||||
"title-dashboard-drastically-changed": ""
|
||||
},
|
||||
"save-dashboard-form-common-options": {
|
||||
"save-refresh-description-current-refresh-default": "Fará com que a atualização atual seja o novo padrão",
|
||||
@@ -7715,6 +7716,8 @@
|
||||
},
|
||||
"enable-highlighting": "Ativar destaque",
|
||||
"escape-newlines": "Corrigir sequências de tabulação e quebra de linha adicionadas incorretamente por escape nas linhas de log",
|
||||
"font-size-default": "",
|
||||
"font-size-small": "",
|
||||
"hide-timestamps": "Ocultar data e hora",
|
||||
"hide-unique-labels": "Ocultar rótulos exclusivos",
|
||||
"newest-first": "Organizado por logs mais recentes primeiro: clique para exibir os mais antigos primeiro",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user