Compare commits

...

19 Commits

Author SHA1 Message Date
Galen 2f01b2f52e chore: upgrade dep package to fix types/runtime conflicts 2025-11-07 09:27:54 -06:00
Leon Sorokin f62d7cf019 Merge branch 'main' into leeoniya/lazy-react-query-builder 2025-11-07 07:54:07 -06:00
Alexa Vargas e5ed003fb2 Dashboard Library: Add new "suggestedDashboards" feature toggle (#113591) 2025-11-07 13:38:59 +00:00
Jo 176b0f8b48 IAM: Refactor user org hooks to use MutateRequest API (#113392)
* update with mutation hooks

* add missing delete mutation
2025-11-07 14:36:53 +01:00
Juan Cabanas 33390a1483 LibraryPanels: Improve getAllLibraryElements filter performance (#113544) 2025-11-07 10:16:41 -03:00
Gabriel MABILLE e90759e5af grafana-iam: enable dual writing for resource permissions (#112793)
* `grafana-iam`: enable dual writing for resource permissions

Co-authored-by: jguer <joao.guerreiro@grafana.com>

* copy paste mistake

* Reduce complexity

* nits to make the code easy to review

* Forgot to check the error

---------

Co-authored-by: jguer <joao.guerreiro@grafana.com>
2025-11-07 13:50:40 +01:00
Alex Khomenko 8cb5f5646a Provisioning: Fix miscellaneous issues with setting and displaying sync status (#113529)
* Provisioning: Preserve in progress job data

* Refactor code and cover more situations

* Fix linting

* Fix issue with remove path operation for started time

* Cleanup

* prettier

---------

Co-authored-by: Roberto Jimenez Sanchez <roberto.jimenez@grafana.com>
2025-11-07 12:27:25 +01:00
Seunghun Shin c784de6ef5 Alerting: Add compressed periodic save for alert instances (#111803)
What is this feature?

This PR implements compressed periodic save for alert state storage, providing a more efficient alternative to regular periodic saves by grouping alert instances by rule UID and storing them using protobuf and snappy compression. When enabled via the state_compressed_periodic_save_enabled configuration option, the system groups alert instances by their alert rule, compresses each group using protobuf serialization and snappy compression, and processes all rules within a single database transaction at specified intervals instead of syncing after every alert evaluation cycle.

Why do we need this feature?

During discussions in PR #111357, we identified the need for a compressed approach to periodic alert state storage that could further reduce database load beyond the jitter mechanism. While the jitter feature distributes database operations over time, this compressed periodic save approach reduces the frequency of database operations by batching alert state updates at explicitly declared intervals rather than syncing after every alert evaluation cycle.
This approach provides several key benefits:

- Reduced Database Frequency: Instead of frequent sync operations tied to alert evaluation cycles, updates occur only at configured intervals
- Storage Efficiency: Rule-based grouping with protobuf and snappy compression significantly reduces storage requirements

The compressed periodic save complements the existing jitter mechanism by providing an alternative strategy focused on reducing overall database interaction frequency while maintaining data integrity through compression and batching.

Who is this feature for?

- Platform/Infrastructure teams managing large-scale Grafana deployments with high alert cardinality
- Organizations looking to optimize storage costs and database performance for alerting workloads
- Production environments with 1000+ alert rules where database write frequency is a concern
2025-11-07 11:51:48 +01:00
Jean-Philippe Quéméner 589435b7c2 fix(unified-storage): resource server tracing (#113582) 2025-11-07 11:51:32 +01:00
Gilles De Mey b4d2d1eaf5 Alerting: Fix width of the code editor for Alertmanager configurations (#113541)
fix width of the code editor for Alertmanager configurations
2025-11-07 11:15:18 +01:00
Tobias Skarhed 36e28963d3 Scopes: Script for setting up gdev scope resources (#113448)
* Script for setting up gdev scope objects

* Script for setting up gdev scope objects

* Format

* Update codeowners

* Do a feature flag check

* Formatting

* Remove FF check, because creation is explicit anyways

* Formatting
2025-11-07 10:56:16 +01:00
Ida Štambuk 942b847952 CloudWatch: Add anomaly command to language support, add documentation for anomaly queries (#113311) 2025-11-07 09:54:24 +00:00
Elliot Kirk 488423abfc Icons: add hand pointer icon (#113255)
add hand pointer icon
2025-11-07 09:53:42 +00:00
Roberto Jiménez Sánchez f75c853b90 Provisioning: Update slog-gokit to v0.1.5 to fix data race (#113455)
* Use fork of slog-gokit to fix data race

Replace github.com/tjhop/slog-gokit with fork that includes fix for data race in handler.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Update workspace

* Bump github.com/tjhop/slog-gokit to v0.1.5

* Update go.sum

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-07 09:47:53 +00:00
Ida Štambuk 4bbbd19049 CloudWatch: Make match exact toggle false by default (#113314) 2025-11-07 10:30:24 +01:00
Nathan Vērzemnieks f4b23253b1 DataSources: Update SDKs in support of auth service (#112101)
* DataSources: Update SDKs for auth service

* Fix deprecated methods & types for new arrow-go version
2025-11-07 10:15:27 +01:00
Erik Sundell 06e1c83276 Chore: Bump plugin-e2e (#113578)
* bump plugin-e2e

* use plugin-e2e selector

* update lock file
2025-11-07 10:11:05 +01:00
Leon Sorokin 52821bd72c Chore: Lazy-load react-awesome-query-builder 2025-11-06 18:45:45 -06:00
Moustafa Baiou 54041155bd fix import path for annotation app 2025-11-06 19:33:12 -05:00
67 changed files with 1970 additions and 784 deletions
+1
View File
@@ -227,6 +227,7 @@
/devenv/datasources.yaml @grafana/grafana-backend-group
/devenv/datasources_docker.yaml @grafana/grafana-backend-group
/devenv/dev-dashboards-without-uid/ @grafana/dashboards-squad
/devenv/scopes/ @grafana/grafana-operator-experience-squad
/devenv/dev-dashboards/annotations @grafana/dataviz-squad
/devenv/dev-dashboards/migrations @grafana/dataviz-squad
+10 -18
View File
@@ -68,13 +68,13 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/at-wat/mqtt-go v0.19.4 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2 v1.38.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect
github.com/aws/aws-sdk-go-v2 v1.39.1 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
github.com/aws/smithy-go v1.23.1 // indirect
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
@@ -91,7 +91,6 @@ require (
github.com/cloudflare/circl v1.6.1 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/diegoholiveira/jsonlogic/v3 v3.7.4 // indirect
@@ -114,7 +113,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.24.0 // indirect
@@ -159,7 +158,7 @@ require (
github.com/grafana/authlib v0.0.0-20250930082137-a40e2c2b094f // indirect
github.com/grafana/dataplane/sdata v0.0.9 // indirect
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect
github.com/grafana/grafana-aws-sdk v1.2.0 // indirect
github.com/grafana/grafana-aws-sdk v1.3.0 // indirect
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 // indirect
github.com/grafana/grafana/apps/plugins v0.0.0 // indirect
github.com/grafana/grafana/apps/provisioning v0.0.0 // indirect
@@ -199,7 +198,6 @@ require (
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lestrrat-go/strftime v1.0.4 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magefile/mage v1.15.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattetti/filebuffer v1.0.1 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
@@ -252,7 +250,6 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
@@ -265,11 +262,7 @@ require (
github.com/stretchr/objx v0.5.2 // indirect
github.com/tetratelabs/wazero v1.8.2 // indirect
github.com/thomaspoignant/go-feature-flag v1.42.0 // indirect
github.com/tjhop/slog-gokit v0.1.3 // indirect
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect
github.com/urfave/cli v1.22.17 // indirect
github.com/tjhop/slog-gokit v0.1.5 // indirect
github.com/woodsbury/decimal128 v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
@@ -319,7 +312,6 @@ require (
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/mail.v2 v2.3.1 // indirect
+28 -60
View File
@@ -173,42 +173,42 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8=
github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
github.com/aws/aws-sdk-go-v2 v1.39.1 h1:fWZhGAwVRK/fAN2tmt7ilH4PPAE11rDj7HytrmbZ2FE=
github.com/aws/aws-sdk-go-v2 v1.39.1/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
github.com/aws/aws-sdk-go-v2/config v1.31.2 h1:NOaSZpVGEH2Np/c1toSeW0jooNl+9ALmsUTZ8YvkJR0=
github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 h1:AmmvNEYrru7sYNJnp3pf57lGbiarX4T9qU/6AZ9SucU=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY=
github.com/aws/aws-sdk-go-v2/config v1.31.10 h1:7LllDZAegXU3yk41mwM6KcPu0wmjKGQB1bg99bNdQm4=
github.com/aws/aws-sdk-go-v2/config v1.31.10/go.mod h1:Ge6gzXPjqu4v0oHvgAwvGzYcK921GU0hQM25WF/Kl+8=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 h1:TxkI7QI+sFkTItN/6cJuMZEIVMFXeu2dI1ZffkXngKI=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14/go.mod h1:12x4Uw/vijC11XkctTjy92TNCQ+UnNJkT7fzX0Yd93E=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 h1:gLD09eaJUdiszm7vd1btiQUYE0Hj+0I2b8AS+75z9AY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8/go.mod h1:4RW3oMPt1POR74qVOC4SbubxAwdP4pCT0nSw3jycOU4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 h1:cTXRdLkpBanlDwISl+5chq5ui1d1YWg4PWMR9c3kXyw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84/go.mod h1:kwSy5X7tfIHN39uucmjQVs2LvDdXEjQucgQQEqCggEo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 h1:6bgAZgRyT4RoFWhxS+aoGMFyE0cD1bSzFnEEi4bFPGI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8/go.mod h1:KcGkXFVU8U28qS4KvLEcPxytPZPBcRawaH2Pf/0jptE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 h1:HhJYoES3zOz34yWEpGENqJvRVPqpmJyR3+AFg9ybhdY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8/go.mod h1:JnA+hPWeYAVbDssp83tv+ysAG8lTfLVXvSsyKg/7xNA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 h1:GMYy2EOWfzdP3wfVAGXBNKY5vK4K8vMET4sYOYltmqs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36/go.mod h1:gDhdAV6wL3PmPqBhiPbnlS447GoWs8HTTOYef9/9Inw=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 h1:nAP2GYbfh8dd2zGZqFRSMlq+/F6cMPBUuCsGAMkN074=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4/go.mod h1:LT10DsiGjLWh4GbjInf9LQejkYEhBgBCjLG5+lvk4EE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 h1:M6JI2aGFEzYxsF6CXIuRBnkge9Wf9a2xU39rNeXgu10=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8/go.mod h1:Fw+MyTwlwjFsSTE31mH211Np+CUslml8mzc0AFEG09s=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 h1:qcLWgdhq45sDM9na4cvXax9dyLitn8EYBRl8Ak4XtG4=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17/go.mod h1:M+jkjBFZ2J6DJrjMv2+vkBbuht6kxJYtJiwoVgX4p4U=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 h1:0reDqfEN+tB+sozj2r92Bep8MEwBZgtAXTND1Kk9OXg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 h1:pd9G9HQaM6UZAZh19pYOkpKSQkyQQ9ftnl/LttQOcGI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 h1:FTdEN9dtWPB0EOURNtDPmwGp6GGvMqRJCAihkSl/1No=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4/go.mod h1:mYubxV9Ff42fZH4kexj43gFPhgc/LyC7KqvUKt1watc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 h1:I7ghctfGXrscr7r1Ga/mDqSJKm7Fkpl5Mwq79Z+rZqU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0/go.mod h1:Zo9id81XP6jbayIFWNuDpA6lMBWhsVy+3ou2jLa4JnA=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B0f17JdflleJRNR4=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0=
@@ -334,10 +334,7 @@ github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03V
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8=
github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg=
@@ -456,8 +453,8 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -663,9 +660,6 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -689,8 +683,8 @@ github.com/grafana/grafana-app-sdk v0.48.1 h1:bKJadWH18WCpJ+Zk8AezRFXCcZgGredRv+
github.com/grafana/grafana-app-sdk v0.48.1/go.mod h1:5LljCz+wvmGfkQ8ZKTOfserhtXNEF0cSFthoWShvN6c=
github.com/grafana/grafana-app-sdk/logging v0.48.1 h1:veM0X5LAPyN3KsDLglWjIofndbGuf7MqnrDuDN+F/Ng=
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-aws-sdk v1.2.0 h1:LLR4/g91WBuCRwm2cbWfCREq565+GxIFe08nqqIcIuw=
github.com/grafana/grafana-aws-sdk v1.2.0/go.mod h1:bBo7qOmM3f61vO+2JxTolNUph1l2TmtzmWcU9/Im+8A=
github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM=
github.com/grafana/grafana-aws-sdk v1.3.0/go.mod h1:VGycF0JkCGKND2O5je1ucOqPJ0ZNhZYzV3c2bNBAaGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 h1:FFcEA01tW+SmuJIuDbHOdgUBL+d7DPrZ2N4zwzPhfGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1/go.mod h1:Oi4anANlCuTCc66jCyqIzfVbgLXFll8Wja+Y4vfANlc=
github.com/grafana/grafana-plugin-sdk-go v0.281.0 h1:V8dGyatzcOLQeivFhBV2JWMwTSZH/clDnpfKG9p3dTA=
@@ -833,9 +827,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 h1:SwcnSwBR7X/5EHJQlXBockkJVIMRVt5yKaesBPMtyZQ=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6/go.mod h1:WrYiIuiXUMIvTDAQw97C+9l0CnBmCcvosPjN3XDqS/o=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
@@ -882,8 +873,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/madflojo/testcerts v1.4.0 h1:I09gN0C1ly9IgeVNcAqKk8RAKIJTe3QnFrrPBDyvzN4=
github.com/madflojo/testcerts v1.4.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
@@ -1115,8 +1104,6 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sagikazarmark/crypt v0.6.0/go.mod h1:U8+INwJo3nBv1m6A/8OBXAq7Jnpspk5AxSgDyEQcea8=
@@ -1132,7 +1119,6 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg=
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -1141,10 +1127,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
@@ -1189,7 +1171,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -1202,8 +1183,8 @@ github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574
github.com/thomaspoignant/go-feature-flag v1.42.0 h1:C7embmOTzaLyRki+OoU2RvtVjJE9IrvgBA2C1mRN1lc=
github.com/thomaspoignant/go-feature-flag v1.42.0/go.mod h1:y0QiWH7chHWhGATb/+XqwAwErORmPSH2MUsQlCmmWlM=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tjhop/slog-gokit v0.1.3 h1:6SdexP3UIeg93KLFeiM1Wp1caRwdTLgsD/THxBUy1+o=
github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs=
github.com/tjhop/slog-gokit v0.1.5 h1:ayloIUi5EK2QYB8eY4DOPO95/mRtMW42lUkp3quJohc=
github.com/tjhop/slog-gokit v0.1.5/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
@@ -1213,16 +1194,6 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI=
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a h1:vcrhXnj9g9PIE+cmZgaPSwOyJ8MAQTRmsgGrB0x5rF4=
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
@@ -1528,7 +1499,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1892,8 +1862,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+14 -22
View File
@@ -100,24 +100,24 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/at-wat/mqtt-go v0.19.4 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2 v1.38.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.39.1 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
github.com/aws/smithy-go v1.23.1 // indirect
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
@@ -151,7 +151,6 @@ require (
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dennwc/varint v1.0.0 // indirect
github.com/dgraph-io/badger/v4 v4.7.0 // indirect
@@ -182,7 +181,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logfmt/logfmt v0.6.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.24.0 // indirect
@@ -235,7 +234,7 @@ require (
github.com/grafana/authlib/types v0.0.0-20250926065801-df98203cff37 // indirect
github.com/grafana/dataplane/sdata v0.0.9 // indirect
github.com/grafana/dskit v0.0.0-20250908063411-6b6da59b5cc4 // indirect
github.com/grafana/grafana-aws-sdk v1.2.0 // indirect
github.com/grafana/grafana-aws-sdk v1.3.0 // indirect
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 // indirect
github.com/grafana/grafana-plugin-sdk-go v0.281.0 // indirect
github.com/grafana/grafana/apps/dashboard v0.0.0 // indirect
@@ -295,7 +294,6 @@ require (
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/lestrrat-go/strftime v1.0.4 // indirect
github.com/lib/pq v1.10.9 // indirect
github.com/magefile/mage v1.15.0 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mattbaird/jsonpatch v0.0.0-20240118010651-0ba75a80ca38 // indirect
github.com/mattetti/filebuffer v1.0.1 // indirect
@@ -361,7 +359,6 @@ require (
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/cors v1.11.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sethvargo/go-retry v0.3.0 // indirect
@@ -382,13 +379,9 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tetratelabs/wazero v1.8.2 // indirect
github.com/thomaspoignant/go-feature-flag v1.42.0 // indirect
github.com/tjhop/slog-gokit v0.1.3 // indirect
github.com/tjhop/slog-gokit v0.1.5 // indirect
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a // indirect
github.com/urfave/cli v1.22.17 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/woodsbury/decimal128 v1.3.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
@@ -455,7 +448,6 @@ require (
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/mail.v2 v2.3.1 // indirect
+30 -57
View File
@@ -237,22 +237,22 @@ github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8=
github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
github.com/aws/aws-sdk-go-v2 v1.39.1 h1:fWZhGAwVRK/fAN2tmt7ilH4PPAE11rDj7HytrmbZ2FE=
github.com/aws/aws-sdk-go-v2 v1.39.1/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
github.com/aws/aws-sdk-go-v2/config v1.31.2 h1:NOaSZpVGEH2Np/c1toSeW0jooNl+9ALmsUTZ8YvkJR0=
github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 h1:AmmvNEYrru7sYNJnp3pf57lGbiarX4T9qU/6AZ9SucU=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY=
github.com/aws/aws-sdk-go-v2/config v1.31.10 h1:7LllDZAegXU3yk41mwM6KcPu0wmjKGQB1bg99bNdQm4=
github.com/aws/aws-sdk-go-v2/config v1.31.10/go.mod h1:Ge6gzXPjqu4v0oHvgAwvGzYcK921GU0hQM25WF/Kl+8=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 h1:TxkI7QI+sFkTItN/6cJuMZEIVMFXeu2dI1ZffkXngKI=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14/go.mod h1:12x4Uw/vijC11XkctTjy92TNCQ+UnNJkT7fzX0Yd93E=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 h1:gLD09eaJUdiszm7vd1btiQUYE0Hj+0I2b8AS+75z9AY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8/go.mod h1:4RW3oMPt1POR74qVOC4SbubxAwdP4pCT0nSw3jycOU4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 h1:cTXRdLkpBanlDwISl+5chq5ui1d1YWg4PWMR9c3kXyw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84/go.mod h1:kwSy5X7tfIHN39uucmjQVs2LvDdXEjQucgQQEqCggEo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 h1:6bgAZgRyT4RoFWhxS+aoGMFyE0cD1bSzFnEEi4bFPGI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8/go.mod h1:KcGkXFVU8U28qS4KvLEcPxytPZPBcRawaH2Pf/0jptE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 h1:HhJYoES3zOz34yWEpGENqJvRVPqpmJyR3+AFg9ybhdY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8/go.mod h1:JnA+hPWeYAVbDssp83tv+ysAG8lTfLVXvSsyKg/7xNA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 h1:GMYy2EOWfzdP3wfVAGXBNKY5vK4K8vMET4sYOYltmqs=
@@ -263,12 +263,12 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.51.0 h1:e5cbPZYTIY2nUEFie
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.51.0/go.mod h1:UseIHRfrm7PqeZo6fcTb6FUCXzCnh1KJbQbmOfxArGM=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2 h1:IfMb3Ar8xEaWjgH/zeVHYD8izwJdQgRP5mKCTDt4GNk=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2/go.mod h1:35jGWx7ECvCwTsApqicFYzZ7JFEnBc6oHUuOQ3xIS54=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 h1:nAP2GYbfh8dd2zGZqFRSMlq+/F6cMPBUuCsGAMkN074=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4/go.mod h1:LT10DsiGjLWh4GbjInf9LQejkYEhBgBCjLG5+lvk4EE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 h1:M6JI2aGFEzYxsF6CXIuRBnkge9Wf9a2xU39rNeXgu10=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8/go.mod h1:Fw+MyTwlwjFsSTE31mH211Np+CUslml8mzc0AFEG09s=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 h1:qcLWgdhq45sDM9na4cvXax9dyLitn8EYBRl8Ak4XtG4=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17/go.mod h1:M+jkjBFZ2J6DJrjMv2+vkBbuht6kxJYtJiwoVgX4p4U=
github.com/aws/aws-sdk-go-v2/service/kms v1.41.2 h1:zJeUxFP7+XP52u23vrp4zMcVhShTWbNO8dHV6xCSvFo=
@@ -279,12 +279,12 @@ github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.26.6 h1:Pwbxovp
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.26.6/go.mod h1:Z4xLt5mXspLKjBV92i165wAJ/3T6TIv4n7RtIS8pWV0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 h1:0reDqfEN+tB+sozj2r92Bep8MEwBZgtAXTND1Kk9OXg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 h1:pd9G9HQaM6UZAZh19pYOkpKSQkyQQ9ftnl/LttQOcGI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 h1:FTdEN9dtWPB0EOURNtDPmwGp6GGvMqRJCAihkSl/1No=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4/go.mod h1:mYubxV9Ff42fZH4kexj43gFPhgc/LyC7KqvUKt1watc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 h1:I7ghctfGXrscr7r1Ga/mDqSJKm7Fkpl5Mwq79Z+rZqU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0/go.mod h1:Zo9id81XP6jbayIFWNuDpA6lMBWhsVy+3ou2jLa4JnA=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B0f17JdflleJRNR4=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s=
@@ -444,8 +444,8 @@ github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
@@ -595,8 +595,8 @@ github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXg
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -826,9 +826,6 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -858,8 +855,8 @@ github.com/grafana/grafana-app-sdk v0.48.1 h1:bKJadWH18WCpJ+Zk8AezRFXCcZgGredRv+
github.com/grafana/grafana-app-sdk v0.48.1/go.mod h1:5LljCz+wvmGfkQ8ZKTOfserhtXNEF0cSFthoWShvN6c=
github.com/grafana/grafana-app-sdk/logging v0.48.1 h1:veM0X5LAPyN3KsDLglWjIofndbGuf7MqnrDuDN+F/Ng=
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-aws-sdk v1.2.0 h1:LLR4/g91WBuCRwm2cbWfCREq565+GxIFe08nqqIcIuw=
github.com/grafana/grafana-aws-sdk v1.2.0/go.mod h1:bBo7qOmM3f61vO+2JxTolNUph1l2TmtzmWcU9/Im+8A=
github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM=
github.com/grafana/grafana-aws-sdk v1.3.0/go.mod h1:VGycF0JkCGKND2O5je1ucOqPJ0ZNhZYzV3c2bNBAaGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 h1:FFcEA01tW+SmuJIuDbHOdgUBL+d7DPrZ2N4zwzPhfGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1/go.mod h1:Oi4anANlCuTCc66jCyqIzfVbgLXFll8Wja+Y4vfANlc=
github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 h1:JOzchPgptwJdruYoed7x28lFDwhzs7kssResYsnC0iI=
@@ -1067,9 +1064,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6 h1:SwcnSwBR7X/5EHJQlXBockkJVIMRVt5yKaesBPMtyZQ=
github.com/jszwedko/go-datemath v0.1.1-0.20230526204004-640a500621d6/go.mod h1:WrYiIuiXUMIvTDAQw97C+9l0CnBmCcvosPjN3XDqS/o=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
@@ -1124,8 +1118,6 @@ github.com/m3db/prometheus_remote_client_golang v0.4.4 h1:DsAIjVKoCp7Ym35tAOFL1O
github.com/m3db/prometheus_remote_client_golang v0.4.4/go.mod h1:wHfVbA3eAK6dQvKjCkHhusWYegCk3bDGkA15zymSHdc=
github.com/madflojo/testcerts v1.4.0 h1:I09gN0C1ly9IgeVNcAqKk8RAKIJTe3QnFrrPBDyvzN4=
github.com/madflojo/testcerts v1.4.0/go.mod h1:MW8sh39gLnkKh4K0Nc55AyHEDl9l/FBLDUsQhpmkuo0=
github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
@@ -1426,8 +1418,8 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys=
github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -1458,7 +1450,6 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg=
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
@@ -1467,11 +1458,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304 h1:Jpy1PXuP99tXNrhbq2BaPz9B+jNAvH1JPQQpG/9GCXY=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
@@ -1526,7 +1512,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@@ -1541,8 +1526,8 @@ github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574
github.com/thomaspoignant/go-feature-flag v1.42.0 h1:C7embmOTzaLyRki+OoU2RvtVjJE9IrvgBA2C1mRN1lc=
github.com/thomaspoignant/go-feature-flag v1.42.0/go.mod h1:y0QiWH7chHWhGATb/+XqwAwErORmPSH2MUsQlCmmWlM=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tjhop/slog-gokit v0.1.3 h1:6SdexP3UIeg93KLFeiM1Wp1caRwdTLgsD/THxBUy1+o=
github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs=
github.com/tjhop/slog-gokit v0.1.5 h1:ayloIUi5EK2QYB8eY4DOPO95/mRtMW42lUkp3quJohc=
github.com/tjhop/slog-gokit v0.1.5/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
@@ -1559,16 +1544,7 @@ github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8 h1:aVGB3YnaS/JNfOW3tiHIlmNmTDg618va+eT0mVomgyI=
github.com/unknwon/bra v0.0.0-20200517080246-1e3013ecaff8/go.mod h1:fVle4kNr08ydeohzYafr20oZzbAkhQT39gKK/pFQ5M4=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/unknwon/log v0.0.0-20150304194804-e617c87089d3/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a h1:vcrhXnj9g9PIE+cmZgaPSwOyJ8MAQTRmsgGrB0x5rF4=
github.com/unknwon/log v0.0.0-20200308114134-929b1006e34a/go.mod h1:1xEUf2abjfP92w2GZTV+GgaRxXErwRXcClbUwrNJffU=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
@@ -1916,7 +1892,6 @@ golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191020152052-9984515f0562/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2293,8 +2268,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7 h1:XNNYLJHt73EyYiCZi6+xjupS9CpvmiDgjPTAjrBlQbo=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+140
View File
@@ -0,0 +1,140 @@
# Scopes Provisioning Script
This script generates Scopes, ScopeNodes, and ScopeNavigations for Grafana development environments.
## Usage
### Create resources
```bash
# From devenv directory
./setup.sh scopes
# Or run directly
cd scopes
go run scopes.go
```
### Delete all gdev-prefixed resources
```bash
# From devenv directory
./setup.sh undev
# Or run directly
cd scopes
go run scopes.go -clean
```
**Note about caching**: The `/find/scope_navigations` endpoint used by the UI caches ScopeNavigation results for 15 minutes. After running cleanup, deleted resources may still appear in the UI until the cache expires. The resources are actually deleted (you can verify by checking the `/scopenavigations` list endpoint), but the UI will refresh after ~15 minutes or after restarting Grafana.
Doing an `Empty Cache and Hard Reload` will also help.
## Configuration
The script reads from `scopes-config.yaml` by default. You can specify a different config file:
```bash
go run scopes.go -config=my-config.yaml
```
### Configuration Format
The configuration file uses YAML format with a natural tree structure. The indentation itself represents the hierarchy:
- **scopes**: Map of scope definitions (key is the scope name)
- **tree**: Tree structure of scope nodes where the YAML structure defines parent-child relationships
- **navigations**: Map of scope navigations linking URLs to scopes (key is the navigation name)
Example:
```yaml
scopes:
app1:
title: Application 1
filters:
- key: app
operator: equals
value: app1
tree:
environments:
title: Environments
nodeType: container
children:
production:
title: Production
nodeType: container
children:
app1-prod:
title: Application 1
nodeType: leaf
linkId: app1
linkType: scope
navigations:
# Link to a dashboard
app1-nav:
url: /d/86Js1xRmk
scope: app1
# Link to another dashboard
app2-nav:
url: /d/GlAqcPgmz
scope: app2
# Custom URLs
explore-nav:
url: /explore
scope: app1
```
### Tree Structure
The tree structure uses YAML's natural indentation to represent hierarchy:
- **Key**: Unique identifier for the node (will be prefixed with "gdev-")
- **title**: Display title
- **nodeType**: Either "container" (can have children) or "leaf" (selectable scope)
- **linkId**: References a scope name (if nodeType is "leaf")
- **linkType**: Usually "scope"
- **children**: Map of child nodes (nested structure follows YAML indentation)
### Node Types
- **container**: A category/grouping node that can contain other nodes
- **leaf**: A selectable node that links to a scope
### Navigations
Navigations link URLs to scopes. The `url` field should contain the full URL path (e.g., `/d/abc123` for dashboards or `/explore` for other pages).
To find dashboard UIDs from gdev dashboards:
```bash
# Find UIDs of all gdev dashboards
find devenv/dev-dashboards -name "*.json" -exec sh -c 'echo "{}:" && jq -r ".uid // .dashboard.uid // \"NO_UID\"" {}' \;
# Or for a specific dashboard
jq -r ".uid // .dashboard.uid" devenv/dev-dashboards/all-panels.json
```
## Environment Variables
- `GRAFANA_URL`: Grafana URL (default: http://localhost:3000)
- `GRAFANA_NAMESPACE`: Namespace (default: default)
- `GRAFANA_USER`: Grafana username (default: admin)
- `GRAFANA_PASSWORD`: Grafana password (default: admin)
## Command Line Flags
- `-url`: Grafana URL
- `-namespace`: Namespace
- `-config`: Config file path (default: scopes-config.yaml)
- `-user`: Grafana username
- `-password`: Grafana password
- `-clean`: Delete all gdev-prefixed resources
## Prefix
All resources are automatically prefixed with "gdev-" to avoid conflicts with production data.
+84
View File
@@ -0,0 +1,84 @@
scopes:
app1:
title: Application 1
filters:
- key: app
operator: equals
value: app1
app2:
title: Application 2
filters:
- key: app
operator: equals
value: app2
cluster1:
title: Cluster 1
filters:
- key: cluster
operator: equals
value: cluster1
tree:
gdev-scopes:
title: gdev-scopes
nodeType: container
children:
production:
title: Production
nodeType: container
children:
app1-prod:
title: Application 1
nodeType: leaf
linkId: app1
linkType: scope
app2-prod:
title: Application 2
nodeType: leaf
linkId: app2
linkType: scope
test-cases:
title: Test cases
nodeType: container
disableMultiSelect: true
children:
test-case-1:
title: Test case 1
nodeType: leaf
linkId: test-case-1
linkType: scope
test-case-2:
title: Test case 2
nodeType: leaf
linkId: test-case-2
linkType: scope
clusters:
title: Clusters
nodeType: container
linkId: cluster1
linkType: scope
children:
cluster1-node:
title: Cluster 1
nodeType: leaf
linkId: cluster1
linkType: scope
navigations:
# Example: Link to a dashboard
app1-nav:
url: /d/86Js1xRmk
scope: app1
# Example: Link to a dashboard with full URL (already has /d/)
app2-nav:
url: /d/GlAqcPgmz
scope: app2
# Example: Custom URL path
custom-nav:
url: /explore
scope: app1
+433
View File
@@ -0,0 +1,433 @@
//go:build ignore
// +build ignore
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"os"
"strings"
"gopkg.in/yaml.v3"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/grafana/grafana/apps/scope/pkg/apis/scope/v0alpha1"
)
const (
prefix = "gdev"
apiVersion = "scope.grafana.app/v0alpha1"
defaultURL = "http://localhost:3000"
defaultUser = "admin"
)
var (
grafanaURL = flag.String("url", getEnv("GRAFANA_URL", defaultURL), "Grafana URL")
namespace = flag.String("namespace", getEnv("GRAFANA_NAMESPACE", "default"), "Namespace")
configFile = flag.String("config", "scopes-config.yaml", "Config file path")
user = flag.String("user", getEnv("GRAFANA_USER", defaultUser), "Grafana username")
password = flag.String("password", getEnv("GRAFANA_PASSWORD", "admin"), "Grafana password")
cleanupFlag = flag.Bool("clean", false, "Delete all gdev-prefixed resources")
)
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
type Config struct {
Scopes map[string]ScopeConfig `yaml:"scopes"`
Tree map[string]TreeNode `yaml:"tree"`
Navigations map[string]NavigationConfig `yaml:"navigations"`
}
// ScopeConfig is used for YAML parsing - converts to v0alpha1.ScopeSpec
type ScopeConfig struct {
Title string `yaml:"title"`
Filters []ScopeFilterConfig `yaml:"filters"`
}
// ScopeFilterConfig is used for YAML parsing - converts to v0alpha1.ScopeFilter
type ScopeFilterConfig struct {
Key string `yaml:"key"`
Value string `yaml:"value"`
Values []string `yaml:"values,omitempty"`
Operator string `yaml:"operator"`
}
// TreeNode is used for YAML parsing - converts to v0alpha1.ScopeNodeSpec
type TreeNode struct {
Title string `yaml:"title"`
NodeType string `yaml:"nodeType"`
LinkID string `yaml:"linkId,omitempty"`
LinkType string `yaml:"linkType,omitempty"`
Children map[string]TreeNode `yaml:"children,omitempty"`
}
type NavigationConfig struct {
URL string `yaml:"url"` // URL path (e.g., /d/abc123 or /explore)
Scope string `yaml:"scope"`
}
// Helper function to convert ScopeFilterConfig to v0alpha1.ScopeFilter
func convertFilter(cfg ScopeFilterConfig) v0alpha1.ScopeFilter {
filter := v0alpha1.ScopeFilter{
Key: cfg.Key,
Value: cfg.Value,
Values: cfg.Values,
Operator: v0alpha1.FilterOperator(cfg.Operator),
}
return filter
}
// Helper function to convert ScopeConfig to v0alpha1.ScopeSpec
func convertScopeSpec(cfg ScopeConfig) v0alpha1.ScopeSpec {
filters := make([]v0alpha1.ScopeFilter, len(cfg.Filters))
for i, f := range cfg.Filters {
filters[i] = convertFilter(f)
}
return v0alpha1.ScopeSpec{
Title: cfg.Title,
Filters: filters,
}
}
type Client struct {
baseURL string
namespace string
httpClient *http.Client
auth string
}
func NewClient(baseURL, namespace, user, password string) *Client {
return &Client{
baseURL: baseURL,
namespace: namespace,
httpClient: &http.Client{},
auth: basicAuth(user, password),
}
}
func basicAuth(username, password string) string {
return fmt.Sprintf("%s:%s", username, password)
}
func (c *Client) makeRequest(method, endpoint string, body []byte) error {
url := fmt.Sprintf("%s/apis/%s/namespaces/%s%s", c.baseURL, apiVersion, c.namespace, endpoint)
var req *http.Request
var err error
if body != nil {
req, err = http.NewRequest(method, url, bytes.NewBuffer(body))
} else {
req, err = http.NewRequest(method, url, nil)
}
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(strings.Split(c.auth, ":")[0], strings.Split(c.auth, ":")[1])
resp, err := c.httpClient.Do(req)
if err != nil {
return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
bodyBytes, _ := io.ReadAll(resp.Body)
// For DELETE requests, 404 is acceptable (resource already deleted)
if resp.StatusCode == 404 {
return nil
}
return fmt.Errorf("API request failed: HTTP %d - %s", resp.StatusCode, string(bodyBytes))
}
return nil
}
func (c *Client) createScope(name string, cfg ScopeConfig) error {
prefixedName := prefix + "-" + name
spec := convertScopeSpec(cfg)
resource := v0alpha1.Scope{
TypeMeta: metav1.TypeMeta{
APIVersion: apiVersion,
Kind: "Scope",
},
ObjectMeta: metav1.ObjectMeta{
Name: prefixedName,
},
Spec: spec,
}
body, err := json.Marshal(resource)
if err != nil {
return fmt.Errorf("failed to marshal scope: %w", err)
}
fmt.Printf("✓ Creating scope: %s\n", prefixedName)
return c.makeRequest("POST", "/scopes", body)
}
func (c *Client) createScopeNode(name string, node TreeNode, parentName string) error {
prefixedName := prefix + "-" + name
prefixedParent := ""
prefixedLinkID := ""
if parentName != "" {
prefixedParent = prefix + "-" + parentName
}
if node.LinkID != "" {
prefixedLinkID = prefix + "-" + node.LinkID
}
nodeType := v0alpha1.NodeType(node.NodeType)
if nodeType == "" {
nodeType = v0alpha1.NodeTypeContainer
}
linkType := v0alpha1.LinkType(node.LinkType)
if linkType == "" {
linkType = v0alpha1.LinkTypeScope
}
spec := v0alpha1.ScopeNodeSpec{
Title: node.Title,
NodeType: nodeType,
DisableMultiSelect: false,
}
if prefixedParent != "" {
spec.ParentName = prefixedParent
}
if prefixedLinkID != "" {
spec.LinkID = prefixedLinkID
spec.LinkType = linkType
}
resource := v0alpha1.ScopeNode{
TypeMeta: metav1.TypeMeta{
APIVersion: apiVersion,
Kind: "ScopeNode",
},
ObjectMeta: metav1.ObjectMeta{
Name: prefixedName,
},
Spec: spec,
}
body, err := json.Marshal(resource)
if err != nil {
return fmt.Errorf("failed to marshal scope node: %w", err)
}
fmt.Printf("✓ Creating scope node: %s\n", prefixedName)
return c.makeRequest("POST", "/scopenodes", body)
}
func (c *Client) createScopeNavigation(name string, nav NavigationConfig) error {
prefixedName := prefix + "-" + name
prefixedScope := prefix + "-" + nav.Scope
if nav.URL == "" {
return fmt.Errorf("navigation %s must have 'url' specified", name)
}
spec := v0alpha1.ScopeNavigationSpec{
URL: nav.URL,
Scope: prefixedScope,
}
resource := v0alpha1.ScopeNavigation{
TypeMeta: metav1.TypeMeta{
APIVersion: apiVersion,
Kind: "ScopeNavigation",
},
ObjectMeta: metav1.ObjectMeta{
Name: prefixedName,
},
Spec: spec,
}
body, err := json.Marshal(resource)
if err != nil {
return fmt.Errorf("failed to marshal scope navigation: %w", err)
}
fmt.Printf("✓ Creating scope navigation: %s\n", prefixedName)
return c.makeRequest("POST", "/scopenavigations", body)
}
func (c *Client) createTreeNodes(children map[string]TreeNode, parentName string) error {
for name, node := range children {
// Build full node name by appending to parent name
// This makes it easy to see the tree path from the node name
fullNodeName := name
if parentName != "" {
fullNodeName = parentName + "-" + name
}
// parentName here is the full parent name (already includes full path)
err := c.createScopeNode(fullNodeName, node, parentName)
if err != nil {
return err
}
if len(node.Children) > 0 {
// Pass fullNodeName as parent for children (will be prefixed with "gdev-" in createScopeNode)
if err := c.createTreeNodes(node.Children, fullNodeName); err != nil {
return err
}
}
}
return nil
}
func (c *Client) deleteResources() {
fmt.Println("Deleting all gdev-prefixed resources...")
// Delete scopes (silently handle errors if endpoints aren't available)
c.deleteResourceType("/scopes", "scope")
// Delete scope nodes
c.deleteResourceType("/scopenodes", "scope node")
// Delete scope navigations
c.deleteResourceType("/scopenavigations", "scope navigation")
fmt.Println("✓ Cleanup complete")
}
func (c *Client) deleteResourceType(endpoint, resourceType string) {
url := fmt.Sprintf("%s/apis/%s/namespaces/%s%s", c.baseURL, apiVersion, c.namespace, endpoint)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
// Silently skip if we can't create request
return
}
req.Header.Set("Content-Type", "application/json")
req.SetBasicAuth(strings.Split(c.auth, ":")[0], strings.Split(c.auth, ":")[1])
resp, err := c.httpClient.Do(req)
if err != nil {
// Silently skip if endpoint isn't available
return
}
defer resp.Body.Close()
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
// Silently skip if endpoint returns error (might not be available)
return
}
var listResponse struct {
Items []struct {
Metadata struct {
Name string `json:"name"`
} `json:"metadata"`
} `json:"items"`
}
bodyBytes, _ := io.ReadAll(resp.Body)
if err := json.Unmarshal(bodyBytes, &listResponse); err != nil {
// Silently skip if we can't decode response
return
}
if len(listResponse.Items) == 0 {
return
}
deletedCount := 0
for _, item := range listResponse.Items {
if strings.HasPrefix(item.Metadata.Name, prefix+"-") {
fmt.Printf(" Deleting %s: %s\n", resourceType, item.Metadata.Name)
deleteURL := fmt.Sprintf("%s/%s", endpoint, item.Metadata.Name)
if err := c.makeRequest("DELETE", deleteURL, nil); err != nil {
// Silently skip deletion errors
} else {
deletedCount++
}
}
}
}
func main() {
flag.Parse()
client := NewClient(*grafanaURL, *namespace, *user, *password)
if *cleanupFlag {
// Cleanup should be silent if endpoints aren't available
client.deleteResources()
return
}
configData, err := os.ReadFile(*configFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading config file: %v\n", err)
os.Exit(1)
}
var config Config
if err := yaml.Unmarshal(configData, &config); err != nil {
fmt.Fprintf(os.Stderr, "Error parsing config file: %v\n", err)
os.Exit(1)
}
fmt.Printf("Loading configuration from: %s\n", *configFile)
fmt.Printf("Grafana URL: %s\n", *grafanaURL)
fmt.Printf("Namespace: %s\n", *namespace)
fmt.Printf("Prefix: %s\n\n", prefix)
// Create scopes
fmt.Println("Creating scopes...")
for name, scope := range config.Scopes {
if err := client.createScope(name, scope); err != nil {
fmt.Fprintf(os.Stderr, "Error creating scope %s: %v\n", name, err)
os.Exit(1)
}
}
fmt.Println()
// Create scope nodes (tree structure)
if len(config.Tree) > 0 {
fmt.Println("Creating scope nodes...")
if err := client.createTreeNodes(config.Tree, ""); err != nil {
fmt.Fprintf(os.Stderr, "Error creating scope nodes: %v\n", err)
os.Exit(1)
}
fmt.Println()
}
// Create scope navigations
if len(config.Navigations) > 0 {
fmt.Println("Creating scope navigations...")
for name, nav := range config.Navigations {
if err := client.createScopeNavigation(name, nav); err != nil {
fmt.Fprintf(os.Stderr, "Error creating scope navigation %s: %v\n", name, err)
os.Exit(1)
}
}
fmt.Println()
}
fmt.Println("✓ All resources created successfully!")
}
+16
View File
@@ -19,6 +19,13 @@ bulkFolders() {
ln -s -f ../../../devenv/bulk-folders/bulk-folders.yaml ../conf/provisioning/dashboards/bulk-folders.yaml
}
scopes() {
echo -e "\xE2\x9C\x94 Setting up scopes, scope nodes, and scope navigations"
cd scopes
go run scopes.go
cd ..
}
requiresJsonnet() {
if ! type "jsonnet" > /dev/null; then
echo "you need you install jsonnet to run this script"
@@ -49,6 +56,12 @@ undev() {
rm -rf bulk-folders/Bulk\ Folder*
echo -e " \xE2\x9C\x94 Reverting bulk-folders provisioning"
# Removing scopes, scope nodes, and scope navigations
cd scopes
go run scopes.go -clean
cd ..
echo -e " \xE2\x9C\x94 Deleting scopes, scope nodes, and scope navigations"
# Removing the symlinks
rm -f ../conf/provisioning/dashboards/custom.yaml
rm -f ../conf/provisioning/dashboards/bulk-folders.yaml
@@ -63,6 +76,7 @@ usage() {
echo " bulk-dashboards - provision 400 dashboards"
echo " bulk-folders [folders] [dashboards] - provision many folders with dashboards"
echo " bulk-folders - provision 200 folders with 3 dashboards in each"
echo " scopes - provision scopes, scope nodes, and scope navigations"
echo " no args - provision core datasources and dev dashboards"
echo " undev - removes any provisioning done by the setup.sh"
}
@@ -80,6 +94,8 @@ main() {
bulkDashboard
elif [[ $cmd == "bulk-folders" ]]; then
bulkFolders "$arg1"
elif [[ $cmd == "scopes" ]]; then
scopes
elif [[ $cmd == "undev" ]]; then
undev
else
@@ -68,7 +68,21 @@ You can change this behavior by disabling the `alertingSaveStateCompressed` feat
You can also reduce database load by writing states periodically instead of after every evaluation.
To save state periodically:
There are two approaches for periodic state saving:
#### Compressed periodic saves
You can combine compressed alert state storage with periodic saves by enabling both `alertingSaveStateCompressed` and `alertingSaveStatePeriodic` feature toggles together.
This approach groups all alert instances by rule UID and compresses them together for efficient storage.
When both feature toggles are enabled, Grafana will save compressed alert states at the interval specified by `state_periodic_save_interval`. Note that in compressed mode, the `state_periodic_save_batch_size` setting is ignored as the system groups instances by rule UID rather than by batch size.
#### Batch-based periodic saves
Alternatively, you can use batch-based periodic saves without compression:
This approach processes individual alert instances in batches of a specified size.
1. Enable the `alertingSaveStatePeriodic` feature toggle.
1. Disable the `alertingSaveStateCompressed` feature toggle.
@@ -77,7 +91,7 @@ By default, it saves the states every 5 minutes to the database and on each shut
can also be configured using the `state_periodic_save_interval` configuration flag. During this process, Grafana deletes all existing alert instances from the database and then writes the entire current set of instances back in batches in a single transaction.
Configure the size of each batch using the `state_periodic_save_batch_size` configuration option.
#### Jitter for periodic saves
##### Jitter for batch-based periodic saves
To further distribute database load, you can enable jitter for periodic state saves by setting `state_periodic_save_jitter_enabled = true`. When jitter is enabled, instead of saving all batches simultaneously, Grafana spreads the batch writes across a calculated time window of 85% of the save interval.
@@ -250,6 +250,19 @@ You can query CloudWatch Logs using three supported query language options:
1. Select a region.
1. Select **CloudWatch Logs** from the query type drop-down.
1. Select the Logs Mode depending on whether you would like to query CloudWatch Logs Insights or Log Anomalies
**Log Anomalies**
[Anomaly detection](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/LogsAnomalyDetection.html) uses machine-learning and pattern recognition to establish baselines of typical log content.
The Log Anomalies query editor fetches the list of anomalies detected in your CloudWatch service. In order to query log anomalies in the editor, a log anomaly detector must be created in the AWS CloudWatch console first.
The log trend cell shows the number of occurrences of the pattern over the selected query time range.
The table shows 50 log anomalies at a time. If you would like to narrow down the list, you can filter anomalies by their ARN and suppressed state.
In addition to this, you can use the Logs Insights QL editor and the `anomaly` command together with the `patterns` command to define and display log anomalies in real time. See the [CloudWatch Logs Insights](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/LogsAnomalyDetection-Insights.html) documentation for more info.
**Logs Insights**
1. Select the query language you would like to use in the **Query Language** drop-down.
1. Click **Select log groups** and choose up to 20 log groups to query.
1. Use the main input area to write your logs query. Amazon CloudWatch only supports a subset of OpenSearch SQL and PPL commands. To find out more about the syntax supported, consult [Amazon CloudWatch Logs documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/CWL_AnalyzeLogData_Languages.html)
@@ -258,7 +271,7 @@ You can query CloudWatch Logs using three supported query language options:
You must specify the region and log groups when querying with **Logs Insights QL** and **OpenSearch PPL**. **OpenSearch SQL** doesn't require log group selection. However, selecting log groups simplifies query writing by populating syntax suggestions with discovered log group fields.
{{< /admonition >}}
Click **CloudWatch Logs Insights** to interactively view, search, and analyze your log data in the CloudWatch Logs Insights console. If you're not logged in to the CloudWatch console, the link forwards you to the login page.
Click **View in CloudWatch console** to interactively view, search, and analyze your log data in the CloudWatch Logs Insights console. If you're not logged in to the CloudWatch console, the link forwards you to the login page.
### Query Log groups with OpenSearch SQL
@@ -38,7 +38,7 @@ test.describe(
formatExpectError('Could not locate header elements in table panel')
).toContainText(['col1', 'col2']);
await expect(
panelEditPage.panel.locator.getByRole('gridcell'),
panelEditPage.panel.data,
formatExpectError('Could not locate headers in table panel')
).toContainText(['val1', 'val2', 'val3', 'val4']);
});
@@ -58,7 +58,7 @@ test.describe(
formatExpectError('Could not locate header elements in table panel')
).toContainText(['col1', 'col2']);
await expect(
panelEditPage.panel.locator.getByRole('gridcell'),
panelEditPage.panel.data,
formatExpectError('Could not locate data elements in table panel')
).toContainText(['val1', 'val2', 'val3', 'val4']);
});
+16 -14
View File
@@ -32,7 +32,7 @@ require (
github.com/apache/arrow-go/v18 v18.4.1 // @grafana/plugins-platform-backend
github.com/armon/go-radix v1.0.0 // @grafana/grafana-app-platform-squad
github.com/aws/aws-sdk-go v1.55.7 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2 v1.38.1 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2 v1.39.1 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.45.3 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.51.0 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2 // @grafana/aws-datasources
@@ -63,7 +63,7 @@ require (
github.com/go-jose/go-jose/v4 v4.1.2 // @grafana/identity-access-team
github.com/go-kit/log v0.2.1 // @grafana/grafana-backend-group
github.com/go-ldap/ldap/v3 v3.4.4 // @grafana/identity-access-team
github.com/go-logfmt/logfmt v0.6.0 // @grafana/oss-big-tent
github.com/go-logfmt/logfmt v0.6.1 // @grafana/oss-big-tent
github.com/go-openapi/loads v0.23.1 // @grafana/alerting-backend
github.com/go-openapi/runtime v0.28.0 // @grafana/alerting-backend
github.com/go-openapi/strfmt v0.24.0 // @grafana/alerting-backend
@@ -98,7 +98,7 @@ require (
github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend
github.com/grafana/grafana-app-sdk v0.48.1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-app-sdk/logging v0.48.1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-aws-sdk v1.2.0 // @grafana/aws-datasources
github.com/grafana/grafana-aws-sdk v1.3.0 // @grafana/aws-datasources
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 // @grafana/partner-datasources
github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-google-sdk-go v0.4.2 // @grafana/partner-datasources
@@ -172,7 +172,7 @@ require (
github.com/stretchr/testify v1.11.1 // @grafana/grafana-backend-group
github.com/testcontainers/testcontainers-go v0.36.0 //@grafana/grafana-app-platform-squad
github.com/thomaspoignant/go-feature-flag v1.42.0 // @grafana/grafana-backend-group
github.com/tjhop/slog-gokit v0.1.3 // @grafana/grafana-app-platform-squad
github.com/tjhop/slog-gokit v0.1.5 // @grafana/grafana-app-platform-squad
github.com/ua-parser/uap-go v0.0.0-20250213224047-9c035f085b90 // @grafana/grafana-backend-group
github.com/urfave/cli v1.22.17 // indirect; @grafana/grafana-backend-group
github.com/urfave/cli/v2 v2.27.7 // @grafana/grafana-backend-group
@@ -237,6 +237,7 @@ require (
github.com/grafana/grafana/apps/alerting/alertenrichment v0.0.0 // @grafana/alerting-backend
github.com/grafana/grafana/apps/alerting/notifications v0.0.0 // @grafana/alerting-backend
github.com/grafana/grafana/apps/alerting/rules v0.0.0 // @grafana/alerting-backend
github.com/grafana/grafana/apps/annotation v0.0.0 // @grafana/grafana-backend-services-squad
github.com/grafana/grafana/apps/correlations v0.0.0 // @grafana/datapro
github.com/grafana/grafana/apps/dashboard v0.0.0 // @grafana/grafana-app-platform-squad @grafana/dashboards-squad
github.com/grafana/grafana/apps/example v0.0.0-20251027162426-edef69fdc82b // @grafana/grafana-app-platform-squad
@@ -268,6 +269,7 @@ replace (
github.com/grafana/grafana/apps/alerting/alertenrichment => ./apps/alerting/alertenrichment
github.com/grafana/grafana/apps/alerting/notifications => ./apps/alerting/notifications
github.com/grafana/grafana/apps/alerting/rules => ./apps/alerting/rules
github.com/grafana/grafana/apps/annotation => ./apps/annotation
github.com/grafana/grafana/apps/correlations => ./apps/correlations
github.com/grafana/grafana/apps/dashboard => ./apps/dashboard
github.com/grafana/grafana/apps/folder => ./apps/folder
@@ -332,23 +334,23 @@ require (
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/at-wat/mqtt-go v0.19.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.10 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.41.2 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 // indirect
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df // indirect
+28 -28
View File
@@ -846,22 +846,22 @@ github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8=
github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
github.com/aws/aws-sdk-go-v2 v1.39.1 h1:fWZhGAwVRK/fAN2tmt7ilH4PPAE11rDj7HytrmbZ2FE=
github.com/aws/aws-sdk-go-v2 v1.39.1/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 h1:12SpdwU8Djs+YGklkinSSlcrPyj3H4VifVsKf78KbwA=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11/go.mod h1:dd+Lkp6YmMryke+qxW/VnKyhMBDTYP41Q2Bb+6gNZgY=
github.com/aws/aws-sdk-go-v2/config v1.31.2 h1:NOaSZpVGEH2Np/c1toSeW0jooNl+9ALmsUTZ8YvkJR0=
github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6 h1:AmmvNEYrru7sYNJnp3pf57lGbiarX4T9qU/6AZ9SucU=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY=
github.com/aws/aws-sdk-go-v2/config v1.31.10 h1:7LllDZAegXU3yk41mwM6KcPu0wmjKGQB1bg99bNdQm4=
github.com/aws/aws-sdk-go-v2/config v1.31.10/go.mod h1:Ge6gzXPjqu4v0oHvgAwvGzYcK921GU0hQM25WF/Kl+8=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14 h1:TxkI7QI+sFkTItN/6cJuMZEIVMFXeu2dI1ZffkXngKI=
github.com/aws/aws-sdk-go-v2/credentials v1.18.14/go.mod h1:12x4Uw/vijC11XkctTjy92TNCQ+UnNJkT7fzX0Yd93E=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8 h1:gLD09eaJUdiszm7vd1btiQUYE0Hj+0I2b8AS+75z9AY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.8/go.mod h1:4RW3oMPt1POR74qVOC4SbubxAwdP4pCT0nSw3jycOU4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84 h1:cTXRdLkpBanlDwISl+5chq5ui1d1YWg4PWMR9c3kXyw=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.84/go.mod h1:kwSy5X7tfIHN39uucmjQVs2LvDdXEjQucgQQEqCggEo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8 h1:6bgAZgRyT4RoFWhxS+aoGMFyE0cD1bSzFnEEi4bFPGI=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.8/go.mod h1:KcGkXFVU8U28qS4KvLEcPxytPZPBcRawaH2Pf/0jptE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8 h1:HhJYoES3zOz34yWEpGENqJvRVPqpmJyR3+AFg9ybhdY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.8/go.mod h1:JnA+hPWeYAVbDssp83tv+ysAG8lTfLVXvSsyKg/7xNA=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 h1:GMYy2EOWfzdP3wfVAGXBNKY5vK4K8vMET4sYOYltmqs=
@@ -872,12 +872,12 @@ github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.51.0 h1:e5cbPZYTIY2nUEFie
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.51.0/go.mod h1:UseIHRfrm7PqeZo6fcTb6FUCXzCnh1KJbQbmOfxArGM=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2 h1:IfMb3Ar8xEaWjgH/zeVHYD8izwJdQgRP5mKCTDt4GNk=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.225.2/go.mod h1:35jGWx7ECvCwTsApqicFYzZ7JFEnBc6oHUuOQ3xIS54=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 h1:nAP2GYbfh8dd2zGZqFRSMlq+/F6cMPBUuCsGAMkN074=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4/go.mod h1:LT10DsiGjLWh4GbjInf9LQejkYEhBgBCjLG5+lvk4EE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8 h1:M6JI2aGFEzYxsF6CXIuRBnkge9Wf9a2xU39rNeXgu10=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.8/go.mod h1:Fw+MyTwlwjFsSTE31mH211Np+CUslml8mzc0AFEG09s=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 h1:qcLWgdhq45sDM9na4cvXax9dyLitn8EYBRl8Ak4XtG4=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17/go.mod h1:M+jkjBFZ2J6DJrjMv2+vkBbuht6kxJYtJiwoVgX4p4U=
github.com/aws/aws-sdk-go-v2/service/kms v1.41.2 h1:zJeUxFP7+XP52u23vrp4zMcVhShTWbNO8dHV6xCSvFo=
@@ -888,12 +888,12 @@ github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.26.6 h1:Pwbxovp
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.26.6/go.mod h1:Z4xLt5mXspLKjBV92i165wAJ/3T6TIv4n7RtIS8pWV0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0 h1:0reDqfEN+tB+sozj2r92Bep8MEwBZgtAXTND1Kk9OXg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.84.0/go.mod h1:kUklwasNoCn5YpyAqC/97r6dzTA1SRKJfKq16SXeoDU=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 h1:pd9G9HQaM6UZAZh19pYOkpKSQkyQQ9ftnl/LttQOcGI=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4 h1:FTdEN9dtWPB0EOURNtDPmwGp6GGvMqRJCAihkSl/1No=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.4/go.mod h1:mYubxV9Ff42fZH4kexj43gFPhgc/LyC7KqvUKt1watc=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0 h1:I7ghctfGXrscr7r1Ga/mDqSJKm7Fkpl5Mwq79Z+rZqU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.0/go.mod h1:Zo9id81XP6jbayIFWNuDpA6lMBWhsVy+3ou2jLa4JnA=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5 h1:+LVB0xBqEgjQoqr9bGZbRzvg212B0f17JdflleJRNR4=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.5/go.mod h1:xoaxeqnnUaZjPjaICgIy5B+MHCSb/ZSOn4MvkFNOUA0=
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s=
@@ -1248,8 +1248,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -1637,8 +1637,8 @@ github.com/grafana/grafana-app-sdk v0.48.1 h1:bKJadWH18WCpJ+Zk8AezRFXCcZgGredRv+
github.com/grafana/grafana-app-sdk v0.48.1/go.mod h1:5LljCz+wvmGfkQ8ZKTOfserhtXNEF0cSFthoWShvN6c=
github.com/grafana/grafana-app-sdk/logging v0.48.1 h1:veM0X5LAPyN3KsDLglWjIofndbGuf7MqnrDuDN+F/Ng=
github.com/grafana/grafana-app-sdk/logging v0.48.1/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-aws-sdk v1.2.0 h1:LLR4/g91WBuCRwm2cbWfCREq565+GxIFe08nqqIcIuw=
github.com/grafana/grafana-aws-sdk v1.2.0/go.mod h1:bBo7qOmM3f61vO+2JxTolNUph1l2TmtzmWcU9/Im+8A=
github.com/grafana/grafana-aws-sdk v1.3.0 h1:/bfJzP93rCel1GbWoRSq0oUo424MZXt8jAp2BK9w8tM=
github.com/grafana/grafana-aws-sdk v1.3.0/go.mod h1:VGycF0JkCGKND2O5je1ucOqPJ0ZNhZYzV3c2bNBAaGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1 h1:FFcEA01tW+SmuJIuDbHOdgUBL+d7DPrZ2N4zwzPhfGk=
github.com/grafana/grafana-azure-sdk-go/v2 v2.3.1/go.mod h1:Oi4anANlCuTCc66jCyqIzfVbgLXFll8Wja+Y4vfANlc=
github.com/grafana/grafana-cloud-migration-snapshot v1.9.0 h1:JOzchPgptwJdruYoed7x28lFDwhzs7kssResYsnC0iI=
@@ -2499,8 +2499,8 @@ github.com/thomaspoignant/go-feature-flag v1.42.0 h1:C7embmOTzaLyRki+OoU2RvtVjJE
github.com/thomaspoignant/go-feature-flag v1.42.0/go.mod h1:y0QiWH7chHWhGATb/+XqwAwErORmPSH2MUsQlCmmWlM=
github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tjhop/slog-gokit v0.1.3 h1:6SdexP3UIeg93KLFeiM1Wp1caRwdTLgsD/THxBUy1+o=
github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs=
github.com/tjhop/slog-gokit v0.1.5 h1:ayloIUi5EK2QYB8eY4DOPO95/mRtMW42lUkp3quJohc=
github.com/tjhop/slog-gokit v0.1.5/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
+83 -3
View File
@@ -336,6 +336,8 @@ github.com/MicahParks/keyfunc/v2 v2.1.0/go.mod h1:rW42fi+xgLJ2FRRXAfNx9ZA8WpD4Oe
github.com/Microsoft/go-winio v0.4.21/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/hcsshim v0.11.5/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
github.com/MissingRoberto/slog-gokit v0.0.0-20251105092822-783f72952ce4 h1:gTtFbl79tuZSeJuSO7kXSbmXSvKSa/PoUXda1tuz0O8=
github.com/MissingRoberto/slog-gokit v0.0.0-20251105092822-783f72952ce4/go.mod h1:yA48zAHvV+Sg4z4VRyeFyFUNNXd3JY5Zg84u3USICq0=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
@@ -408,44 +410,75 @@ github.com/aws/aws-lambda-go v1.47.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7Rfg
github.com/aws/aws-msk-iam-sasl-signer-go v1.0.1 h1:nMp7diZObd4XEVUR0pEvn7/E13JIgManMX79Q6quV6E=
github.com/aws/aws-msk-iam-sasl-signer-go v1.0.1/go.mod h1:MVYeeOhILFFemC/XlYTClvBjYZrg/EPd3ts885KrNTI=
github.com/aws/aws-sdk-go-v2 v1.36.5/go.mod h1:EYrzvCCN9CMUTa5+6lf6MM4tq3Zjp8UhSGR/cBsjai0=
github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg=
github.com/aws/aws-sdk-go-v2/config v1.29.17/go.mod h1:9P4wwACpbeXs9Pm9w1QTh6BwWwJjwYvJ1iCt5QbCXh8=
github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes=
github.com/aws/aws-sdk-go-v2/credentials v1.17.70/go.mod h1:M+lWhhmomVGgtuPOhO85u4pEa3SmssPTdcYpP/5J/xc=
github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.5 h1:oUEqVqonG3xuarrsze1KVJ30KagNYDemikTbdu8KlN8=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.19.5/go.mod h1:VNM08cHlOsIbSHRqb6D/M2L4kKXfJv3A2/f0GNbOQSc=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.7.87 h1:oDPArGgCrG/4aTi86ij3S2PB59XXkTSKYVNQlmqRHXQ=
github.com/aws/aws-sdk-go-v2/feature/dynamodb/expression v1.7.87/go.mod h1:ZeQC4gVarhdcWeM1c90DyBLaBCNhEeAbKUXwVI/byvw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32/go.mod h1:h4Sg6FQdexC1yYG9RDnOvLbW1a/P986++/Y/a+GyEM8=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.69/go.mod h1:GJj8mmO6YT6EqgduWocwhMoxTLFitkhIrK+owzrYL2I=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36/go.mod h1:Q1lnJArKRXkenyog6+Y+zr7WDpk4e6XlR6gs20bbeNo=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36/go.mod h1:UdyGa7Q91id/sdyHPwth+043HhmP6yP9MBHgbZM0xo8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.44.0 h1:A99gjqZDbdhjtjJVZrmVzVKO2+p3MSg35bDWtbMQVxw=
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.44.0/go.mod h1:mWB0GE1bqcVSvpW7OtFA0sKuHk52+IqtnsYU2jUfYAs=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.26.0 h1:0wOCTKrmwkyC8Bk76hYH/B4IJn5MGt6gMkSXc0A2uyc=
github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.26.0/go.mod h1:He/RikglWUczbkV+fkdpcV/3GdL/rTRNVy7VaUiezMo=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4/go.mod h1:/xFi9KtvBXP97ppCz1TAEvU1Uf66qvid89rbem3wCzQ=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.17 h1:x187MqiHwBGjMGAed8Y8K1VGuCtFvQvXb24r+bwmSdo=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.10.17/go.mod h1:mC9qMbA6e1pwEq6X3zDGtZRXMG2YaElJkbJlMVHLs5I=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17/go.mod h1:ygpklyoaypuyDvOM5ujWGrYWpAK3h7ugnmKCU/76Ys4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
github.com/aws/aws-sdk-go-v2/service/kinesis v1.33.0 h1:JPXkrQk5OS/+Q81fKH97Ll/Vmmy0p9vwHhxw+V+tVjg=
github.com/aws/aws-sdk-go-v2/service/kinesis v1.33.0/go.mod h1:dJngkoVMrq0K7QvRkdRZYM4NUp6cdWa2GBdpm8zoY8U=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3 h1:UPTdlTOwWUX49fVi7cymEN6hDqCwe3LNv1vi7TXUutk=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.3/go.mod h1:gjDP16zn+WWalyaUqwCCioQ8gU8lzttCCc9jYsiQI/8=
github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.78.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4 h1:NgRFYyFpiMD62y4VPXh4DosPFbZd4vdMVBWKk0VmWXc=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.4/go.mod h1:TKKN7IQoM7uTnyuFm9bm9cw5P//ZYTl4m3htBWQ1G/c=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.2 h1:vlYXbindmagyVA3RS2SPd47eKZ00GZZQcr+etTviHtc=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.2/go.mod h1:yGhDiLKguA3iFJYxbrQkQiNzuy+ddxesSZYWVeeEH5Q=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7 h1:d+mnMa4JbJlooSbYQfrJpit/YINaB30JEVgrhtjZneA=
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.35.7/go.mod h1:1X1NotbcGHH7PCQJ98PsExSxsJj/VWzz8MfFz43+02M=
github.com/aws/aws-sdk-go-v2/service/sns v1.31.3 h1:eSTEdxkfle2G98FE+Xl3db/XAXXVTJPNQo9K/Ar8oAI=
github.com/aws/aws-sdk-go-v2/service/sns v1.31.3/go.mod h1:1dn0delSO3J69THuty5iwP0US2Glt0mx2qBBlI13pvw=
github.com/aws/aws-sdk-go-v2/service/sns v1.34.2 h1:PajtbJ/5bEo6iUAIGMYnK8ljqg2F1h4mMCGh1acjN30=
github.com/aws/aws-sdk-go-v2/service/sns v1.34.2/go.mod h1:PJtxxMdj747j8DeZENRTTYAz/lx/pADn/U0k7YNNiUY=
github.com/aws/aws-sdk-go-v2/service/sns v1.34.7 h1:OBuZE9Wt8h2imuRktu+WfjiTGrnYdCIJg8IX92aalHE=
github.com/aws/aws-sdk-go-v2/service/sns v1.34.7/go.mod h1:4WYoZAhHt+dWYpoOQUgkUKfuQbE6Gg/hW4oXE0pKS9U=
github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3 h1:Vjqy5BZCOIsn4Pj8xzyqgGmsSqzz7y/WXbN3RgOoVrc=
github.com/aws/aws-sdk-go-v2/service/sqs v1.34.3/go.mod h1:L0enV3GCRd5iG9B64W35C4/hwsCB00Ib+DKVGTadKHI=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.3 h1:j5BchjfDoS7K26vPdyJlyxBIIBGDflq3qjjJKBDlbcI=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.3/go.mod h1:Bar4MrRxeqdn6XIh8JGfiXuFRmyrrsZNTJotxEJmWW0=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8 h1:80dpSqWMwx2dAm30Ib7J6ucz1ZHfiv5OCRwN/EnCOXQ=
github.com/aws/aws-sdk-go-v2/service/sqs v1.38.8/go.mod h1:IzNt/udsXlETCdvBOL0nmyMe2t9cGmXmZgsdoZGYYhI=
github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4 h1:hgSBvRT7JEWx2+vEGI9/Ld5rZtl7M5lu8PqdvOmbRHw=
github.com/aws/aws-sdk-go-v2/service/ssm v1.52.4/go.mod h1:v7NIzEFIHBiicOMaMTuEmbnzGnqW0d+6ulNALul6fYE=
github.com/aws/aws-sdk-go-v2/service/ssm v1.58.0 h1:zQz6Q5uaC8s9734DV9UDAm2q1TEEfOvEejDBSulOapI=
github.com/aws/aws-sdk-go-v2/service/ssm v1.58.0/go.mod h1:PUWUl5MDiYNQkUHN9Pyd9kgtA/YhbxnSnHP+yQqzrM8=
github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1 h1:OwMzNDe5VVTXD4kGmeK/FtqAITiV8Mw4TCa8IyNO0as=
github.com/aws/aws-sdk-go-v2/service/ssm v1.60.1/go.mod h1:IyVabkWrs8SNdOEZLyFFcW9bUltV4G6OQS0s6H20PHg=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5/go.mod h1:b7SiVprpU+iGazDUqvRSLf5XmCdn+JtT1on7uNL6Ipc=
github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3/go.mod h1:vq/GQR1gOFLquZMSrxUK/cpvKCNVYibNyJ1m7JrU88E=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM=
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0/go.mod h1:7ph2tGpfQvwzgistp2+zga9f+bCjlQJPkPUmMgDSD7w=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo=
github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw=
github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/awslabs/aws-lambda-go-api-proxy v0.16.2 h1:CJyGEyO1CIwOnXTU40urf0mchf6t3voxpvUDikOU9LY=
github.com/awslabs/aws-lambda-go-api-proxy v0.16.2/go.mod h1:vxxjwBHe/KbgFeNlAP/Tvp4SsVRL3WQamcWRxqVh0z0=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -550,7 +583,6 @@ github.com/couchbase/ghistogram v0.1.0 h1:b95QcQTCzjTUocDXp/uMgSNQi8oj1tGwnJ4bOD
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/moss v0.2.0 h1:VCYrMzFwEryyhRSeI+/b3tRBSeTpi/8gn5Kf6dxqn+o=
github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
@@ -706,6 +738,7 @@ github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
@@ -805,10 +838,9 @@ github.com/grafana/dskit v0.0.0-20250818234656-8ff9c6532e85/go.mod h1:kImsvJ1xnm
github.com/grafana/go-gelf/v2 v2.0.1 h1:BOChP0h/jLeD+7F9mL7tq10xVkDG15he3T1zHuQaWak=
github.com/grafana/go-gelf/v2 v2.0.1/go.mod h1:lexHie0xzYGwCgiRGcvZ723bSNyNI8ZRD4s0CLobh90=
github.com/grafana/go-mysql-server v0.20.1-0.20251027172658-317a8d46ffa4/go.mod h1:EeYR0apo+8j2Dyxmn2ghkPlirO2S5mT1xHBrA+Efys8=
github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0=
github.com/grafana/grafana-app-sdk/logging v0.40.2/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/gomemcache v0.0.0-20250228145437-da7b95fd2ac1/go.mod h1:j/s0jkda4UXTemDs7Pgw/vMT06alWc42CHisvYac0qw=
github.com/grafana/grafana-app-sdk v0.40.1/go.mod h1:4P8h7VB6KcDjX9bAoBQc6IP8iNylxe6bSXLR9gA39gM=
github.com/grafana/grafana-app-sdk v0.40.2/go.mod h1:BbNXPNki3mtbkWxYqJsyA1Cj9AShSyaY33z8WkyfVv0=
github.com/grafana/grafana-app-sdk v0.41.0 h1:SYHN3U7B1myRKY3UZZDkFsue9TDmAOap0UrQVTqtYBU=
github.com/grafana/grafana-app-sdk v0.41.0/go.mod h1:Wg/3vEZfok1hhIWiHaaJm+FwkosfO98o8KbeLFEnZpY=
github.com/grafana/grafana-app-sdk v0.46.0/go.mod h1:LCTrqR1SwBS13XGVYveBmM7giJDDjzuXK+M9VzPuPWc=
@@ -818,6 +850,7 @@ github.com/grafana/grafana-app-sdk/logging v0.39.0 h1:3GgN5+dUZYqq74Q+GT9/ET+yo+
github.com/grafana/grafana-app-sdk/logging v0.39.0/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
github.com/grafana/grafana-app-sdk/logging v0.39.1/go.mod h1:WhDENSnaGHtyVVwZGVnAR7YLvh2xlLDYR3D7E6h7XVk=
github.com/grafana/grafana-app-sdk/logging v0.40.0/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-app-sdk/logging v0.40.2/go.mod h1:otUD9XpJD7A5sCLb8mcs9hIXGdeV6lnhzVwe747g4RU=
github.com/grafana/grafana-app-sdk/logging v0.43.0/go.mod h1:0xrjKSGY5z+NLGuGsXQpxiCHR4Smu79i/CbAfdkaB1M=
github.com/grafana/grafana-app-sdk/logging v0.43.1/go.mod h1:0xrjKSGY5z+NLGuGsXQpxiCHR4Smu79i/CbAfdkaB1M=
github.com/grafana/grafana-app-sdk/logging v0.43.2/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
@@ -825,10 +858,56 @@ github.com/grafana/grafana-app-sdk/logging v0.45.0/go.mod h1:Gh/nBWnspK3oDNWtiM5
github.com/grafana/grafana-app-sdk/logging v0.46.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/logging v0.48.0 h1:xolkQxBlA2LQF4hprKIAeu+zUem1DigYZ6XC1TOhFJE=
github.com/grafana/grafana-app-sdk/logging v0.48.0/go.mod h1:Gh/nBWnspK3oDNWtiM5qUF/fardHzOIEez+SPI3JeHA=
github.com/grafana/grafana-app-sdk/plugin v0.41.0 h1:ShUvGpAVzM3UxcsfwS6l/lwW4ytDeTbCQXf8w2P8Yp8=
github.com/grafana/grafana-app-sdk/plugin v0.41.0/go.mod h1:YIhimVfAqtOp3kdhxOanaSZjypVKh/bYxf9wfFfhDm0=
github.com/grafana/grafana-aws-sdk v0.38.2 h1:TzQD0OpWsNjtldi5G5TLDlBRk8OyDf+B5ujcoAu4Dp0=
github.com/grafana/grafana-aws-sdk v0.38.2/go.mod h1:j3vi+cXYHEFqjhBGrI6/lw1TNM+dl0Y3f0cSnDOPy+s=
github.com/grafana/grafana-aws-sdk v1.0.2 h1:98eBuHYFmgvH0xO9kKf4RBsEsgQRp8EOA/9yhDIpkss=
github.com/grafana/grafana-aws-sdk v1.0.2/go.mod h1:hO7q7yWV+t6dmiyJjMa3IbuYnYkBua+G/IAlOPVIYKE=
github.com/grafana/grafana-aws-sdk v1.1.0/go.mod h1:7e+47EdHynteYWGoT5Ere9KeOXQObsk8F0vkOLQ1tz8=
github.com/grafana/grafana-aws-sdk v1.2.0/go.mod h1:bBo7qOmM3f61vO+2JxTolNUph1l2TmtzmWcU9/Im+8A=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo=
github.com/grafana/grafana-azure-sdk-go/v2 v2.2.0/go.mod h1:H9sVh9A4yg5egMGZeh0mifxT1Q/uqwKe1LBjBJU6pN8=
github.com/grafana/grafana-plugin-sdk-go v0.263.0/go.mod h1:U43Cnrj/9DNYyvFcNdeUWNjMXTKNB0jcTcQGpWKd2gw=
github.com/grafana/grafana-plugin-sdk-go v0.267.0/go.mod h1:OuwS4c/JYgn0rr/w5zhJBpLo4gKm/vw15RsfpYAvK9Q=
github.com/grafana/grafana-plugin-sdk-go v0.269.1/go.mod h1:yv2KbO4mlr9WuDK2f+2gHAMTwwLmLuqaEnrPXTRU+OI=
github.com/grafana/grafana-plugin-sdk-go v0.275.0/go.mod h1:mO9LJqdXDh5JpO/xIdPAeg5LdThgQ06Y/SLpXDWKw2c=
github.com/grafana/grafana-plugin-sdk-go v0.277.0/go.mod h1:mAUWg68w5+1f5TLDqagIr8sWr1RT9h7ufJl5NMcWJAU=
github.com/grafana/grafana-plugin-sdk-go v0.278.0/go.mod h1:+8NXT/XUJ/89GV6FxGQ366NZ3nU+cAXDMd0OUESF9H4=
github.com/grafana/grafana-plugin-sdk-go v0.279.0/go.mod h1:/7oGN6Z7DGTGaLHhgIYrRr6Wvmdsb3BLw5hL4Kbjy88=
github.com/grafana/grafana-plugin-sdk-go v0.280.0/go.mod h1:Z15Wiq3c4I0tzHYrLYpOqrO8u3+2RJ+HN2Q9uiZTILA=
github.com/grafana/grafana/apps/advisor v0.0.0-20250123151950-b066a6313173/go.mod h1:goSDiy3jtC2cp8wjpPZdUHRENcoSUHae1/Px/MDfddA=
github.com/grafana/grafana/apps/advisor v0.0.0-20250220154326-6e5de80ef295/go.mod h1:9I1dKV3Dqr0NPR9Af0WJGxOytp5/6W3JLiNChOz8r+c=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250121113133-e747350fee2d/go.mod h1:AvleS6icyPmcBjihtx5jYEvdzLmHGBp66NuE0AMR57A=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250416173722-ec17e0e4ce03/go.mod h1:oemrhKvFxxc5m32xKHPxInEHAObH0/hPPyHUiBUZ1Cc=
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250506052906-7a2fc797fb4a/go.mod h1:VkX53kBiqIMHBoGgeEDJnzm5Nwcmv/726tuZuT5SvJY=
github.com/grafana/grafana/apps/alerting/rules v0.0.0-20250731223157-26b18dda3364/go.mod h1:wi4njPm5mJ8IpK13h57be8sWoxOhqr1UQOwmXhRM9Gk=
github.com/grafana/grafana/apps/dashboard v0.0.0-20250616135341-59c2f154336b/go.mod h1:OIlvNnUufYDhBXa4xK4CyzPI2C69ZJkHy5+aFDyPtXw=
github.com/grafana/grafana/apps/dashboard v0.0.0-20250616145019-8d27f12428cb/go.mod h1:OIlvNnUufYDhBXa4xK4CyzPI2C69ZJkHy5+aFDyPtXw=
github.com/grafana/grafana/apps/dashboard v0.0.0-20250627191313-2f1a6ae1712b/go.mod h1:eR8wca74ADgxBrvX0uNpdB1qnPaGx/KhCm4Xj8oqHfQ=
github.com/grafana/grafana/apps/investigation v0.0.0-20250121113133-e747350fee2d/go.mod h1:HQprw3MmiYj5OUV9CZnkwA1FKDZBmYACuAB3oDvUOmI=
github.com/grafana/grafana/apps/playlist v0.0.0-20250121113133-e747350fee2d/go.mod h1:DjJe5osrW/BKrzN9hAAOSElNWutj1bcriExa7iDP7kA=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805113453-4b17c24d67ff h1:JDT0Mcfpi3c525xzeli+v5dR9pf5HhdFjr8djRdhs10=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805113453-4b17c24d67ff/go.mod h1:NQlHMO5fHhjexw71wVjv522532NRvFg5F4tcjUEktjs=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805120145-0c5a00302924 h1:uGXX6gCF1q2ytIL0w1X3UAKgF/UZ7eDDAgOaSqLOeW8=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805120145-0c5a00302924/go.mod h1:NQlHMO5fHhjexw71wVjv522532NRvFg5F4tcjUEktjs=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805123034-066163d71001 h1:y2AHkdji2I+zXv8rsSC8OjWEzJJjqW5OlmCsZR5+RuU=
github.com/grafana/grafana/apps/preferences v0.0.0-20250805123034-066163d71001/go.mod h1:NQlHMO5fHhjexw71wVjv522532NRvFg5F4tcjUEktjs=
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250121113133-e747350fee2d/go.mod h1:1sq0guad+G4SUTlBgx7SXfhnzy7D86K/LcVOtiQCiMA=
github.com/grafana/grafana/pkg/semconv v0.0.0-20250121113133-e747350fee2d/go.mod h1:tfLnBpPYgwrBMRz4EXqPCZJyCjEG4Ev37FSlXnocJ2c=
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250121113133-e747350fee2d/go.mod h1:CXpwZ3Mkw6xVlGKc0SqUxqXCP3Uv182q6qAQnLaLxRg=
github.com/grafana/grafana/pkg/storage/unified/apistore v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:xrKQcxQxz+IUF90ybtfENFeEXtlj9nAsX/3Fw0KEIeQ=
github.com/grafana/nanogit v0.0.0-20250616082354-5e94194d02ed h1:59JF1WhHLT+lNX89Tm1OzOEySMVMASAhaPbsRjtp8Kc=
github.com/grafana/nanogit v0.0.0-20250616082354-5e94194d02ed/go.mod h1:OIAAKNgG5fpuJQRNO1lUSj9nc18Xl3O7M8fjIlBO1cI=
github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5 h1:MAQ2B0cu0V1S91ZjVa7NomNZFjaR2SmdtvdwhqBtyhU=
github.com/grafana/nanogit v0.0.0-20250619160700-ebf70d342aa5/go.mod h1:tN93IZUaAmnSWgL0IgnKdLv6DNeIhTJGvl1wvQMrWco=
github.com/grafana/nanogit v0.0.0-20250723104447-68f58f5ecec0/go.mod h1:ToqLjIdvV3AZQa3K6e5m9hy/nsGaUByc2dWQlctB9iA=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3 h1:6D2gGAwyQBElSrp3E+9lSr7k8gLuP3Aiy20rweLWeBw=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20240930132144-b5e64e81e8d3/go.mod h1:YeND+6FDA7OuFgDzYODN8kfPhXLCehcpxe4T9mdnpCY=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975 h1:4/BZkGObFWZf4cLbE2Vqg/1VTz67Q0AJ7LHspWLKJoQ=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250331083058-4563aec7a975/go.mod h1:FGdGvhI40Dq+CTQaSzK9evuve774cgOUdGfVO04OXkw=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36 h1:AjZ58JRw1ZieFH/SdsddF5BXtsDKt5kSrKNPWrzYz3Y=
github.com/grafana/prometheus-alertmanager v0.25.1-0.20250604130045-92c8f6389b36/go.mod h1:O/QP1BCm0HHIzbKvgMzqb5sSyH88rzkFk84F4TfJjBU=
github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU=
github.com/grafana/sqlds/v4 v4.2.4/go.mod h1:BQRjUG8rOqrBI4NAaeoWrIMuoNgfi8bdhCJ+5cgEfLU=
github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 h1:bjh0PVYSVVFxzINqPFYJmAmJNrWPgnVjuSdYJGHmtFU=
@@ -1346,6 +1425,7 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tjhop/slog-gokit v0.1.3/go.mod h1:Bbu5v2748qpAWH7k6gse/kw3076IJf6owJmh7yArmJs=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
+3 -2
View File
@@ -92,7 +92,7 @@
"@emotion/eslint-plugin": "11.12.0",
"@grafana/eslint-config": "8.2.0",
"@grafana/eslint-plugin": "link:./packages/grafana-eslint-rules",
"@grafana/plugin-e2e": "2.1.7",
"@grafana/plugin-e2e": "^3.0.1",
"@grafana/test-utils": "workspace:*",
"@manypkg/get-packages": "^3.0.0",
"@npmcli/package-json": "^6.0.0",
@@ -322,7 +322,8 @@
"@react-aria/focus": "3.21.2",
"@react-aria/overlays": "3.30.0",
"@react-aria/utils": "3.31.0",
"@react-awesome-query-builder/ui": "6.6.15",
"@react-awesome-query-builder/core": "^6.7.0-alpha.0",
"@react-awesome-query-builder/ui": "^6.7.0-alpha.0",
"@reduxjs/toolkit": "2.9.0",
"@visx/event": "3.12.0",
"@visx/gradient": "3.12.0",
+5 -1
View File
@@ -565,10 +565,14 @@ export interface FeatureToggles {
*/
queryLibrary?: boolean;
/**
* Enable suggested dashboards when creating new dashboards
* Enable dashboard library experiments that are production ready
*/
dashboardLibrary?: boolean;
/**
* Enable suggested dashboards when creating new dashboards
*/
suggestedDashboards?: boolean;
/**
* Sets the logs table as default visualisation in logs explore
*/
logsExploreTableDefaultVisualization?: boolean;
+1
View File
@@ -162,6 +162,7 @@ export const availableIconsIndex = {
globe: true,
grafana: true,
'graph-bar': true,
'hand-pointer': true,
heart: true,
'heart-rate': true,
'heart-break': true,
@@ -247,7 +247,7 @@ export interface CloudWatchLogsQuery extends common.DataQuery {
*/
logGroups?: Array<LogGroup>;
/**
* Whether a query is a Logs Insights or Logs Anomalies query
* Whether a query is a Logs Insights or Log Anomalies query
*/
logsMode?: LogsMode;
/**
@@ -275,7 +275,7 @@ export const defaultCloudWatchLogsQuery: Partial<CloudWatchLogsQuery> = {
};
/**
* Shape of a Cloudwatch Logs Anomalies query
* Shape of a Cloudwatch Log Anomalies query
*/
export interface CloudWatchLogsAnomaliesQuery extends common.DataQuery {
/**
@@ -284,7 +284,7 @@ export interface CloudWatchLogsAnomaliesQuery extends common.DataQuery {
anomalyDetectionARN?: string;
id: string;
/**
* Whether a query is a Logs Insights or Logs Anomalies query
* Whether a query is a Logs Insights or Log Anomalies query
*/
logsMode?: LogsMode;
/**
+2 -1
View File
@@ -22,7 +22,8 @@
"@grafana/plugin-ui": "^0.10.10",
"@grafana/runtime": "12.4.0-pre",
"@grafana/ui": "12.4.0-pre",
"@react-awesome-query-builder/ui": "6.6.15",
"@react-awesome-query-builder/core": "^6.7.0-alpha.0",
"@react-awesome-query-builder/ui": "^6.7.0-alpha.0",
"immutable": "5.1.4",
"lodash": "4.17.21",
"react": "18.3.1",
@@ -1,3 +1,4 @@
import React, { Suspense } from 'react';
import { useAsync } from 'react-use';
import { SelectableValue, TypedVariableModel } from '@grafana/data';
@@ -7,8 +8,9 @@ import { QueryWithDefaults } from '../../defaults';
import { DB, SQLExpression, SQLQuery, SQLSelectableValue } from '../../types';
import { useSqlChange } from '../../utils/useSqlChange';
import { Config } from './AwesomeQueryBuilder';
import { WhereRow } from './WhereRow';
import type { Config } from './AwesomeQueryBuilder';
const LazyWhereRow = React.lazy(() => import(/* webpackChunkName: "sql-editor-where-row" */ './WhereRow'));
interface WhereRowProps {
query: QueryWithDefaults;
@@ -25,19 +27,21 @@ export function SQLWhereRow({ query, fields, onQueryChange, db }: WhereRowProps)
const { onSqlChange } = useSqlChange({ query, onQueryChange, db });
return (
<WhereRow
// TODO: fix key that's used to force clean render or SQLWhereRow - otherwise it doesn't render operators correctly
key={JSON.stringify(state.value)}
config={{ fields: state.value || {} }}
sql={query.sql!}
onSqlChange={(val: SQLExpression) => {
const templateVars = getTemplateSrv().getVariables();
<Suspense>
<LazyWhereRow
// TODO: fix key that's used to force clean render or SQLWhereRow - otherwise it doesn't render operators correctly
key={JSON.stringify(state.value)}
config={{ fields: state.value || {} }}
sql={query.sql!}
onSqlChange={(val: SQLExpression) => {
const templateVars = getTemplateSrv().getVariables();
removeQuotesForMultiVariables(val, templateVars);
removeQuotesForMultiVariables(val, templateVars);
onSqlChange(val);
}}
/>
onSqlChange(val);
}}
/>
</Suspense>
);
}
@@ -90,3 +90,5 @@ injectGlobal`
display: none;
}
`;
export default WhereRow;
+1 -1
View File
@@ -1,4 +1,4 @@
import { JsonTree } from '@react-awesome-query-builder/ui';
import type { JsonTree } from '@react-awesome-query-builder/ui';
import {
DataFrame,
+45 -9
View File
@@ -350,23 +350,59 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
}
//nolint:staticcheck // not yet migrated to OpenFeature
if b.features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthzResourcePermissionApis) {
resourcePermissionStore, err := NewLocalStore(iamv0.ResourcePermissionInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.resourcePermissionsStorage)
if err != nil {
if err := b.UpdateResourcePermissionsAPIGroup(apiGroupInfo, opts, storage, b.enableDualWriter, enableZanzanaSync); err != nil {
return err
}
if enableZanzanaSync {
b.logger.Info("Enabling AfterCreate, BeginUpdate, and AfterDelete hooks for ResourcePermission to sync to Zanzana")
resourcePermissionStore.AfterCreate = b.AfterResourcePermissionCreate
resourcePermissionStore.BeginUpdate = b.BeginResourcePermissionUpdate
resourcePermissionStore.AfterDelete = b.AfterResourcePermissionDelete
}
storage[iamv0.ResourcePermissionInfo.StoragePath()] = resourcePermissionStore
}
apiGroupInfo.VersionedResourcesStorageMap[legacyiamv0.VERSION] = storage
return nil
}
func (b *IdentityAccessManagementAPIBuilder) UpdateResourcePermissionsAPIGroup(
apiGroupInfo *genericapiserver.APIGroupInfo,
opts builder.APIGroupOptions,
storage map[string]rest.Storage,
enableDualWriter bool,
enableZanzanaSync bool,
) error {
var store rest.Storage
// Create the legacy store first
legacyStore, err := NewLocalStore(iamv0.ResourcePermissionInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.resourcePermissionsStorage)
if err != nil {
return err
}
// Register the hooks for Zanzana sync
// FIXME: The hooks are registered on the legacy store
// Once we fully migrate to unified storage, we can move these hooks to the unified store
if enableZanzanaSync {
b.logger.Info("Enabling AfterCreate, BeginUpdate, and AfterDelete hooks for ResourcePermission to sync to Zanzana")
legacyStore.AfterCreate = b.AfterResourcePermissionCreate
legacyStore.BeginUpdate = b.BeginResourcePermissionUpdate
legacyStore.AfterDelete = b.AfterResourcePermissionDelete
}
// Set the default store to the legacy store
store = legacyStore
if enableDualWriter {
// Create the dual write store (UniStore + LegacyStore)
uniStore, err := grafanaregistry.NewRegistryStore(apiGroupInfo.Scheme, iamv0.ResourcePermissionInfo, opts.OptsGetter)
if err != nil {
return err
}
store, err = opts.DualWriteBuilder(iamv0.ResourcePermissionInfo.GroupResource(), legacyStore, uniStore)
if err != nil {
return err
}
}
storage[iamv0.ResourcePermissionInfo.StoragePath()] = store
return nil
}
func (b *IdentityAccessManagementAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return func(rc common.ReferenceCallback) map[string]common.OpenAPIDefinition {
dst := legacyiamv0.GetOpenAPIDefinitions(rc)
@@ -16,8 +16,9 @@ import (
type FakeZanzanaClient struct {
zanzana.Client
writeCallback func(context.Context, *v1.WriteRequest) error
readCallback func(context.Context, *v1.ReadRequest) (*v1.ReadResponse, error)
writeCallback func(context.Context, *v1.WriteRequest) error
readCallback func(context.Context, *v1.ReadRequest) (*v1.ReadResponse, error)
mutateCallback func(context.Context, *v1.MutateRequest) error
}
// Read implements zanzana.Client.
@@ -33,6 +34,14 @@ func (f *FakeZanzanaClient) Write(ctx context.Context, req *v1.WriteRequest) err
return f.writeCallback(ctx, req)
}
// Mutate implements zanzana.Client.
func (f *FakeZanzanaClient) Mutate(ctx context.Context, req *v1.MutateRequest) error {
if f.mutateCallback != nil {
return f.mutateCallback(ctx, req)
}
return nil
}
func requireTuplesMatch(t *testing.T, actual []*v1.TupleKey, expected []*v1.TupleKey, msgAndArgs ...interface{}) {
t.Helper()
for _, exp := range expected {
+68 -133
View File
@@ -10,27 +10,8 @@ import (
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
v1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana"
)
// createUserBasicRoleTuple creates a tuple for a user's basic role assignment
func createUserBasicRoleTuple(userUID, orgRole string) *v1.TupleKey {
if orgRole == "" {
return nil
}
basicRole := zanzana.TranslateBasicRole(orgRole)
if basicRole == "" {
return nil
}
return &v1.TupleKey{
User: zanzana.NewTupleEntry(zanzana.TypeUser, userUID, ""),
Relation: zanzana.RelationAssignee,
Object: zanzana.NewTupleEntry(zanzana.TypeRole, basicRole, ""),
}
}
// AfterUserCreate is a post-create hook that writes the user's basic role assignment to Zanzana (openFGA)
func (b *IdentityAccessManagementAPIBuilder) AfterUserCreate(obj runtime.Object, _ *metav1.CreateOptions) {
if b.zClient == nil {
@@ -43,24 +24,24 @@ func (b *IdentityAccessManagementAPIBuilder) AfterUserCreate(obj runtime.Object,
return
}
resourceType := "user"
operation := "create"
// Skip if user has no role assigned
if user.Spec.Role == "" {
b.logger.Debug("user has no role assigned, skipping basic role sync",
"namespace", user.Namespace,
"userUID", user.Name,
"name", user.Name,
)
return
}
resourceType := "user"
operation := "create"
// Grab a ticket to write to Zanzana
wait := time.Now()
b.zTickets <- true
hooksWaitHistogram.WithLabelValues(resourceType, operation).Observe(time.Since(wait).Seconds())
go func(u *iamv0.User) {
go func(namespace, subjectName, role, resourceType, operation string) {
start := time.Now()
status := "success"
@@ -70,44 +51,38 @@ func (b *IdentityAccessManagementAPIBuilder) AfterUserCreate(obj runtime.Object,
hooksOperationCounter.WithLabelValues(resourceType, operation, status).Inc()
}()
tuple := createUserBasicRoleTuple(u.Name, u.Spec.Role)
if tuple == nil {
b.logger.Warn("failed to create user basic role tuple",
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
)
status = "failure"
return
}
b.logger.Debug("writing user basic role to zanzana",
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
"namespace", namespace,
"name", subjectName,
"role", role,
)
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
defer cancel()
err := b.zClient.Write(ctx, &v1.WriteRequest{
Namespace: u.Namespace,
Writes: &v1.WriteRequestWrites{
TupleKeys: []*v1.TupleKey{tuple},
err := b.zClient.Mutate(ctx, &v1.MutateRequest{
Namespace: namespace,
Operations: []*v1.MutateOperation{
{
Operation: &v1.MutateOperation_UpdateUserOrgRole{
UpdateUserOrgRole: &v1.UpdateUserOrgRoleOperation{User: subjectName, Role: role},
},
},
},
})
if err != nil {
status = "failure"
b.logger.Error("failed to write user basic role to zanzana",
"err", err,
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
"namespace", namespace,
"name", subjectName,
"role", role,
)
} else {
hooksTuplesCounter.WithLabelValues(resourceType, operation, "write").Inc()
}
}(user.DeepCopy())
}(user.Namespace, user.Name, user.Spec.Role, resourceType, operation)
}
// BeginUserUpdate is a pre-update hook that gets called on user updates
@@ -142,7 +117,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginUserUpdate(ctx context.Context
b.zTickets <- true
hooksWaitHistogram.WithLabelValues("user", "update").Observe(time.Since(wait).Seconds())
go func(old, new *iamv0.User) {
go func(namespace, subjectName, oldRole, newRole string) {
start := time.Now()
status := "success"
@@ -153,72 +128,40 @@ func (b *IdentityAccessManagementAPIBuilder) BeginUserUpdate(ctx context.Context
}()
b.logger.Debug("updating user basic role in zanzana",
"namespace", new.Namespace,
"userUID", new.Name,
"oldRole", old.Spec.Role,
"newRole", new.Spec.Role,
"namespace", namespace,
"name", subjectName,
"oldRole", oldRole,
"newRole", newRole,
)
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
defer cancel()
req := &v1.WriteRequest{
Namespace: new.Namespace,
err := b.zClient.Mutate(ctx, &v1.MutateRequest{
Namespace: namespace,
Operations: []*v1.MutateOperation{
{
Operation: &v1.MutateOperation_UpdateUserOrgRole{
UpdateUserOrgRole: &v1.UpdateUserOrgRoleOperation{User: subjectName, Role: newRole},
},
}, {
Operation: &v1.MutateOperation_DeleteUserOrgRole{
DeleteUserOrgRole: &v1.DeleteUserOrgRoleOperation{User: subjectName, Role: oldRole},
},
},
},
})
if err != nil {
status = "failure"
b.logger.Error("failed to update user basic role in zanzana",
"err", err,
"namespace", namespace,
"name", subjectName,
"role", newRole,
"oldRole", oldRole,
)
}
// Delete old role tuple if it existed
if old.Spec.Role != "" {
oldTuple := createUserBasicRoleTuple(old.Name, old.Spec.Role)
if oldTuple != nil {
deleteTuple := tupleToTupleKeyWithoutCondition(oldTuple)
req.Deletes = &v1.WriteRequestDeletes{
TupleKeys: []*v1.TupleKeyWithoutCondition{deleteTuple},
}
b.logger.Debug("deleting old user basic role from zanzana",
"namespace", new.Namespace,
"userUID", new.Name,
"role", old.Spec.Role,
)
}
}
// Write new role tuple if it exists
if new.Spec.Role != "" {
newTuple := createUserBasicRoleTuple(new.Name, new.Spec.Role)
if newTuple != nil {
req.Writes = &v1.WriteRequestWrites{
TupleKeys: []*v1.TupleKey{newTuple},
}
b.logger.Debug("writing new user basic role to zanzana",
"namespace", new.Namespace,
"userUID", new.Name,
"role", new.Spec.Role,
)
}
}
// Only make the request if there are deletes or writes
if (req.Deletes != nil && len(req.Deletes.TupleKeys) > 0) || (req.Writes != nil && len(req.Writes.TupleKeys) > 0) {
err := b.zClient.Write(ctx, req)
if err != nil {
status = "failure"
b.logger.Error("failed to update user basic role in zanzana",
"err", err,
"namespace", new.Namespace,
"userUID", new.Name,
)
} else {
if req.Deletes != nil && len(req.Deletes.TupleKeys) > 0 {
hooksTuplesCounter.WithLabelValues("user", "update", "delete").Inc()
}
if req.Writes != nil && len(req.Writes.TupleKeys) > 0 {
hooksTuplesCounter.WithLabelValues("user", "update", "write").Inc()
}
}
} else {
b.logger.Debug("no tuples to update in zanzana", "namespace", new.Namespace)
}
}(oldUser.DeepCopy(), newUser.DeepCopy())
}(oldUser.Namespace, oldUser.Name, oldUser.Spec.Role, newUser.Spec.Role)
}, nil
}
@@ -241,7 +184,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterUserDelete(obj runtime.Object,
if user.Spec.Role == "" {
b.logger.Debug("user had no role assigned, skipping basic role sync",
"namespace", user.Namespace,
"userUID", user.Name,
"name", user.Name,
)
return
}
@@ -250,7 +193,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterUserDelete(obj runtime.Object,
b.zTickets <- true
hooksWaitHistogram.WithLabelValues(resourceType, operation).Observe(time.Since(wait).Seconds())
go func(u *iamv0.User) {
go func(namespace, subjectName, role string) {
start := time.Now()
status := "success"
@@ -260,44 +203,36 @@ func (b *IdentityAccessManagementAPIBuilder) AfterUserDelete(obj runtime.Object,
hooksOperationCounter.WithLabelValues(resourceType, operation, status).Inc()
}()
tuple := createUserBasicRoleTuple(u.Name, u.Spec.Role)
if tuple == nil {
b.logger.Warn("failed to create user basic role tuple for deletion",
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
)
status = "failure"
return
}
deleteTuple := tupleToTupleKeyWithoutCondition(tuple)
b.logger.Debug("deleting user basic role from zanzana",
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
"namespace", namespace,
"name", subjectName,
"role", role,
)
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
defer cancel()
err := b.zClient.Write(ctx, &v1.WriteRequest{
Namespace: u.Namespace,
Deletes: &v1.WriteRequestDeletes{
TupleKeys: []*v1.TupleKeyWithoutCondition{deleteTuple},
err := b.zClient.Mutate(ctx, &v1.MutateRequest{
Namespace: namespace,
Operations: []*v1.MutateOperation{
{
Operation: &v1.MutateOperation_DeleteUserOrgRole{
DeleteUserOrgRole: &v1.DeleteUserOrgRoleOperation{User: subjectName, Role: role},
},
},
},
})
if err != nil {
status = "failure"
b.logger.Error("failed to delete user basic role from zanzana",
"err", err,
"namespace", u.Namespace,
"userUID", u.Name,
"role", u.Spec.Role,
"namespace", namespace,
"name", subjectName,
"role", role,
)
} else {
hooksTuplesCounter.WithLabelValues(resourceType, operation, "delete").Inc()
}
}(user.DeepCopy())
}(user.Namespace, user.Name, user.Spec.Role)
}
+96 -136
View File
@@ -33,21 +33,22 @@ func TestAfterUserCreate(t *testing.T) {
},
}
testAdminRole := func(ctx context.Context, req *v1.WriteRequest) error {
testAdminRole := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.NotNil(t, req.Writes)
require.Len(t, req.Writes.TupleKeys, 1)
require.Equal(t, "org-1", req.Namespace)
require.Len(t, req.Operations, 1)
tuple := req.Writes.TupleKeys[0]
require.Equal(t, "user:df2p421det1q8c", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_admin", tuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
updateOp := op.GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "df2p421det1q8c", updateOp.User)
require.Equal(t, "Admin", updateOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testAdminRole}
b.zClient = &FakeZanzanaClient{mutateCallback: testAdminRole}
b.AfterUserCreate(&user, nil)
wg.Wait()
})
@@ -64,21 +65,22 @@ func TestAfterUserCreate(t *testing.T) {
},
}
testEditorRole := func(ctx context.Context, req *v1.WriteRequest) error {
testEditorRole := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.NotNil(t, req.Writes)
require.Len(t, req.Writes.TupleKeys, 1)
require.Equal(t, "org-2", req.Namespace)
require.Len(t, req.Operations, 1)
tuple := req.Writes.TupleKeys[0]
require.Equal(t, "user:user123", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_editor", tuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
updateOp := op.GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "user123", updateOp.User)
require.Equal(t, "Editor", updateOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testEditorRole}
b.zClient = &FakeZanzanaClient{mutateCallback: testEditorRole}
b.AfterUserCreate(&user, nil)
wg.Wait()
})
@@ -95,21 +97,22 @@ func TestAfterUserCreate(t *testing.T) {
},
}
testViewerRole := func(ctx context.Context, req *v1.WriteRequest) error {
testViewerRole := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.NotNil(t, req.Writes)
require.Len(t, req.Writes.TupleKeys, 1)
require.Equal(t, "org-3", req.Namespace)
require.Len(t, req.Operations, 1)
tuple := req.Writes.TupleKeys[0]
require.Equal(t, "user:viewer456", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_viewer", tuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
updateOp := op.GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "viewer456", updateOp.User)
require.Equal(t, "Viewer", updateOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testViewerRole}
b.zClient = &FakeZanzanaClient{mutateCallback: testViewerRole}
b.AfterUserCreate(&user, nil)
wg.Wait()
})
@@ -184,31 +187,28 @@ func TestBeginUserUpdate(t *testing.T) {
},
}
testRoleChange := func(ctx context.Context, req *v1.WriteRequest) error {
testRoleChange := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-1", req.Namespace)
require.Len(t, req.Operations, 2)
// Should delete old role
require.NotNil(t, req.Deletes)
require.Len(t, req.Deletes.TupleKeys, 1)
deleteTuple := req.Deletes.TupleKeys[0]
require.Equal(t, "user:testuser", deleteTuple.User)
require.Equal(t, "assignee", deleteTuple.Relation)
require.Equal(t, "role:basic_viewer", deleteTuple.Object)
// First operation should be UpdateUserOrgRole with new role
updateOp := req.Operations[0].GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "testuser", updateOp.User)
require.Equal(t, "Admin", updateOp.Role)
// Should write new role
require.NotNil(t, req.Writes)
require.Len(t, req.Writes.TupleKeys, 1)
writeTuple := req.Writes.TupleKeys[0]
require.Equal(t, "user:testuser", writeTuple.User)
require.Equal(t, "assignee", writeTuple.Relation)
require.Equal(t, "role:basic_admin", writeTuple.Object)
// Second operation should be DeleteUserOrgRole with old role
deleteOp := req.Operations[1].GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "testuser", deleteOp.User)
require.Equal(t, "Viewer", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testRoleChange}
b.zClient = &FakeZanzanaClient{mutateCallback: testRoleChange}
finishFunc, err := b.BeginUserUpdate(context.Background(), &newUser, &oldUser, nil)
require.NoError(t, err)
@@ -218,7 +218,7 @@ func TestBeginUserUpdate(t *testing.T) {
wg.Wait()
})
t.Run("should delete old role when new role is empty", func(t *testing.T) {
t.Run("should update role when new role is empty", func(t *testing.T) {
wg.Add(1)
oldUser := iamv0.User{
ObjectMeta: metav1.ObjectMeta{
@@ -240,26 +240,28 @@ func TestBeginUserUpdate(t *testing.T) {
},
}
testRemoveRole := func(ctx context.Context, req *v1.WriteRequest) error {
testRemoveRole := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-2", req.Namespace)
require.Len(t, req.Operations, 2)
// Should delete old role
require.NotNil(t, req.Deletes)
require.Len(t, req.Deletes.TupleKeys, 1)
deleteTuple := req.Deletes.TupleKeys[0]
require.Equal(t, "user:testuser2", deleteTuple.User)
require.Equal(t, "assignee", deleteTuple.Relation)
require.Equal(t, "role:basic_editor", deleteTuple.Object)
// First operation should be UpdateUserOrgRole with empty role
updateOp := req.Operations[0].GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "testuser2", updateOp.User)
require.Equal(t, "", updateOp.Role)
// Should not write new role
require.Nil(t, req.Writes)
// Second operation should be DeleteUserOrgRole with old role
deleteOp := req.Operations[1].GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "testuser2", deleteOp.User)
require.Equal(t, "Editor", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testRemoveRole}
b.zClient = &FakeZanzanaClient{mutateCallback: testRemoveRole}
finishFunc, err := b.BeginUserUpdate(context.Background(), &newUser, &oldUser, nil)
require.NoError(t, err)
@@ -291,26 +293,28 @@ func TestBeginUserUpdate(t *testing.T) {
},
}
testAddRole := func(ctx context.Context, req *v1.WriteRequest) error {
testAddRole := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-3", req.Namespace)
require.Len(t, req.Operations, 2)
// Should not delete old role (was empty)
require.Nil(t, req.Deletes)
// First operation should be UpdateUserOrgRole with new role
updateOp := req.Operations[0].GetUpdateUserOrgRole()
require.NotNil(t, updateOp)
require.Equal(t, "testuser3", updateOp.User)
require.Equal(t, "Admin", updateOp.Role)
// Should write new role
require.NotNil(t, req.Writes)
require.Len(t, req.Writes.TupleKeys, 1)
writeTuple := req.Writes.TupleKeys[0]
require.Equal(t, "user:testuser3", writeTuple.User)
require.Equal(t, "assignee", writeTuple.Relation)
require.Equal(t, "role:basic_admin", writeTuple.Object)
// Second operation should be DeleteUserOrgRole with empty old role
deleteOp := req.Operations[1].GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "testuser3", deleteOp.User)
require.Equal(t, "", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testAddRole}
b.zClient = &FakeZanzanaClient{mutateCallback: testAddRole}
finishFunc, err := b.BeginUserUpdate(context.Background(), &newUser, &oldUser, nil)
require.NoError(t, err)
@@ -368,12 +372,12 @@ func TestBeginUserUpdate(t *testing.T) {
}
callCount := 0
testNoCall := func(ctx context.Context, req *v1.WriteRequest) error {
testNoCall := func(ctx context.Context, req *v1.MutateRequest) error {
callCount++
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testNoCall}
b.zClient = &FakeZanzanaClient{mutateCallback: testNoCall}
finishFunc, err := b.BeginUserUpdate(context.Background(), &newUser, &oldUser, nil)
require.NoError(t, err)
@@ -437,25 +441,23 @@ func TestAfterUserDelete(t *testing.T) {
},
}
testDeleteAdmin := func(ctx context.Context, req *v1.WriteRequest) error {
testDeleteAdmin := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-1", req.Namespace)
require.Len(t, req.Operations, 1)
// Should have deletes but no writes
require.NotNil(t, req.Deletes)
require.Len(t, req.Deletes.TupleKeys, 1)
require.Nil(t, req.Writes)
deleteTuple := req.Deletes.TupleKeys[0]
require.Equal(t, "user:df2p421det1q8c", deleteTuple.User)
require.Equal(t, "assignee", deleteTuple.Relation)
require.Equal(t, "role:basic_admin", deleteTuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
deleteOp := op.GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "df2p421det1q8c", deleteOp.User)
require.Equal(t, "Admin", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDeleteAdmin}
b.zClient = &FakeZanzanaClient{mutateCallback: testDeleteAdmin}
b.AfterUserDelete(&user, nil)
wg.Wait()
})
@@ -472,22 +474,23 @@ func TestAfterUserDelete(t *testing.T) {
},
}
testDeleteEditor := func(ctx context.Context, req *v1.WriteRequest) error {
testDeleteEditor := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-2", req.Namespace)
require.Len(t, req.Operations, 1)
require.NotNil(t, req.Deletes)
require.Len(t, req.Deletes.TupleKeys, 1)
deleteTuple := req.Deletes.TupleKeys[0]
require.Equal(t, "user:editor123", deleteTuple.User)
require.Equal(t, "assignee", deleteTuple.Relation)
require.Equal(t, "role:basic_editor", deleteTuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
deleteOp := op.GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "editor123", deleteOp.User)
require.Equal(t, "Editor", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDeleteEditor}
b.zClient = &FakeZanzanaClient{mutateCallback: testDeleteEditor}
b.AfterUserDelete(&user, nil)
wg.Wait()
})
@@ -504,22 +507,23 @@ func TestAfterUserDelete(t *testing.T) {
},
}
testDeleteViewer := func(ctx context.Context, req *v1.WriteRequest) error {
testDeleteViewer := func(ctx context.Context, req *v1.MutateRequest) error {
defer wg.Done()
require.NotNil(t, req)
require.Equal(t, "org-3", req.Namespace)
require.Len(t, req.Operations, 1)
require.NotNil(t, req.Deletes)
require.Len(t, req.Deletes.TupleKeys, 1)
deleteTuple := req.Deletes.TupleKeys[0]
require.Equal(t, "user:viewer456", deleteTuple.User)
require.Equal(t, "assignee", deleteTuple.Relation)
require.Equal(t, "role:basic_viewer", deleteTuple.Object)
op := req.Operations[0]
require.NotNil(t, op)
deleteOp := op.GetDeleteUserOrgRole()
require.NotNil(t, deleteOp)
require.Equal(t, "viewer456", deleteOp.User)
require.Equal(t, "Viewer", deleteOp.Role)
return nil
}
b.zClient = &FakeZanzanaClient{writeCallback: testDeleteViewer}
b.zClient = &FakeZanzanaClient{mutateCallback: testDeleteViewer}
b.AfterUserDelete(&user, nil)
wg.Wait()
})
@@ -563,47 +567,3 @@ func TestAfterUserDelete(t *testing.T) {
// If we get here without panic, the test passes
})
}
func TestCreateUserBasicRoleTuple(t *testing.T) {
t.Run("should create tuple for Admin role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user123", "Admin")
require.NotNil(t, tuple)
require.Equal(t, "user:user123", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_admin", tuple.Object)
})
t.Run("should create tuple for Editor role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user456", "Editor")
require.NotNil(t, tuple)
require.Equal(t, "user:user456", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_editor", tuple.Object)
})
t.Run("should create tuple for Viewer role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user789", "Viewer")
require.NotNil(t, tuple)
require.Equal(t, "user:user789", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_viewer", tuple.Object)
})
t.Run("should create tuple for None role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user000", "None")
require.NotNil(t, tuple)
require.Equal(t, "user:user000", tuple.User)
require.Equal(t, "assignee", tuple.Relation)
require.Equal(t, "role:basic_none", tuple.Object)
})
t.Run("should return nil for empty role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user123", "")
require.Nil(t, tuple)
})
t.Run("should return nil for invalid role", func(t *testing.T) {
tuple := createUserBasicRoleTuple("user123", "InvalidRole")
require.Nil(t, tuple)
})
}
@@ -404,32 +404,47 @@ func (rc *RepositoryController) addSyncJob(ctx context.Context, obj *provisionin
return nil
}
func (rc *RepositoryController) determineSyncStatus(obj *provisioning.Repository, syncOptions *provisioning.SyncJobOptions, healthStatus provisioning.HealthStatus) *provisioning.SyncStatus {
func (rc *RepositoryController) determineSyncStatusOps(obj *provisioning.Repository, syncOptions *provisioning.SyncJobOptions, healthStatus provisioning.HealthStatus) []map[string]interface{} {
const unhealthyMessage = "Repository is unhealthy"
hasUnhealthyMessage := len(obj.Status.Sync.Message) > 0 && obj.Status.Sync.Message[0] == unhealthyMessage
var patchOperations []map[string]interface{}
switch {
case syncOptions != nil:
return &provisioning.SyncStatus{
State: provisioning.JobStatePending,
LastRef: obj.Status.Sync.LastRef,
Started: time.Now().UnixMilli(),
}
// We will try to trigger a new sync job if we have sync options
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync/state",
"value": provisioning.JobStatePending,
})
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync/started",
"value": int64(0),
})
case healthStatus.Healthy && hasUnhealthyMessage: // if the repository is healthy and the message is set, clear it
// FIXME: is this the clearest way to do this? Should we introduce another status or way of way of handling more
// specific errors?
return &provisioning.SyncStatus{
LastRef: obj.Status.Sync.LastRef,
}
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync/message",
"value": []string{},
})
case !healthStatus.Healthy && !hasUnhealthyMessage: // if the repository is unhealthy and the message is not already set, set it
return &provisioning.SyncStatus{
State: provisioning.JobStateError,
Message: []string{unhealthyMessage},
LastRef: obj.Status.Sync.LastRef,
}
default:
return nil
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync/state",
"value": provisioning.JobStateError,
})
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync/message",
"value": []string{unhealthyMessage},
})
}
return patchOperations
}
//nolint:gocyclo
@@ -509,13 +524,7 @@ func (rc *RepositoryController) process(item *queueItem) error {
// determine the sync strategy and sync status to apply
syncOptions := rc.determineSyncStrategy(ctx, obj, repo, shouldResync, healthStatus)
if syncStatus := rc.determineSyncStatus(obj, syncOptions, healthStatus); syncStatus != nil {
patchOperations = append(patchOperations, map[string]interface{}{
"op": "replace",
"path": "/status/sync",
"value": syncStatus,
})
}
patchOperations = append(patchOperations, rc.determineSyncStatusOps(obj, syncOptions, healthStatus)...)
// Apply all patch operations
if len(patchOperations) > 0 {
@@ -525,6 +534,8 @@ func (rc *RepositoryController) process(item *queueItem) error {
}
}
// QUESTION: should we trigger the sync job after we have applied all patch operations or before?
// Is there are risk of race condition here?
// Trigger sync job after we have applied all patch operations
if syncOptions != nil {
if err := rc.addSyncJob(ctx, obj, syncOptions); err != nil {
+22 -14
View File
@@ -132,28 +132,36 @@ func (c *jobsConnector) Connect(
}
spec.Repository = name
// If a sync job is being created, we should update its status to pending.
job, err := c.jobs.GetJobQueue().Insert(ctx, cfg.Namespace, spec)
if err != nil {
responder.Error(err)
return
}
// For pull jobs update the sync status
// patch the sync status 'state' to 'pending', and reset the 'started' field, leaving other fields unchanged.
// Intentionally maintain the previous job name until the jobs is picked up.
if spec.Pull != nil {
err = c.statusPatcherProvider.GetStatusPatcher().Patch(ctx, cfg, map[string]interface{}{
"op": "replace",
"path": "/status/sync",
"value": &provisioning.SyncStatus{
State: provisioning.JobStatePending,
LastRef: cfg.Status.Sync.LastRef,
Started: time.Now().UnixMilli(),
err = c.statusPatcherProvider.GetStatusPatcher().Patch(ctx, cfg,
map[string]interface{}{
"op": "replace",
"path": "/status/sync/state",
"value": provisioning.JobStatePending,
},
})
map[string]interface{}{
// Use "replace" instead of "remove" since "remove" fails if the path does not exist (RFC 6902).
// "started" field uses "omitempty", so it may be missing in the JSON.
"op": "replace",
"path": "/status/sync/started",
"value": int64(0),
},
)
if err != nil {
responder.Error(err)
return
}
}
job, err := c.jobs.GetJobQueue().Insert(ctx, cfg.Namespace, spec)
if err != nil {
responder.Error(err)
return
}
responder.Object(http.StatusAccepted, job)
}), 30*time.Second), nil
}
@@ -110,25 +110,35 @@ func (r *SyncWorker) Process(ctx context.Context, repo repository.Repository, jo
}
syncStatus := job.Status.ToSyncStatus(job.Name)
// Preserve last ref as we use replace operation
// Preserve last ref
lastRef := repo.Config().Status.Sync.LastRef
syncStatus.LastRef = lastRef
if syncStatus.State == "" {
syncStatus.State = provisioning.JobStateWorking
}
// Ensure the sync state is set to 'working' if not already set or still pending.
// FIXME: This should not be needed as the progress recorder should have set it to 'working' by now.
syncStatus.State = provisioning.JobStateWorking
// Update sync status at start using JSON patch
// Update sync status at start using granular JSON patch operations
// Only patch fields that are actually being set to avoid overwriting with zero values
patchOperations := []map[string]interface{}{
{
"op": "replace",
"path": "/status/sync",
"value": syncStatus,
"path": "/status/sync/state",
"value": syncStatus.State,
},
{
"op": "replace",
"path": "/status/sync/job",
"value": syncStatus.JobID,
},
{
"op": "replace",
"path": "/status/sync/started",
"value": syncStatus.Started,
},
}
progress.SetMessage(ctx, "update sync status at start")
statusCtx, statusSpan := r.tracer.Start(ctx, "provisioning.sync.update_start_status")
if err := r.patchStatus(statusCtx, cfg, patchOperations...); err != nil {
statusSpan.End()
@@ -174,14 +184,13 @@ func (r *SyncWorker) Process(ctx context.Context, repo repository.Repository, jo
}
syncSpan.End()
// Create sync status and set hash if successful
if syncStatus.State == provisioning.JobStateSuccess {
if syncStatus.State != provisioning.JobStateError {
syncStatus.LastRef = currentRef
} else {
// Preserve the original lastRef on error
syncStatus.LastRef = lastRef
}
// Update final status using JSON patch
progress.SetMessage(ctx, "update status and stats")
patchOperations = []map[string]interface{}{
{
@@ -115,17 +115,18 @@ func TestSyncWorker_Process(t *testing.T) {
rw.MockRepository.On("Config").Return(repoConfig)
pr.On("SetMessage", mock.Anything, "update sync status at start").Return()
rpf.On("Execute", mock.Anything, repoConfig, mock.MatchedBy(func(patch map[string]interface{}) bool {
if patch["op"] != "replace" || patch["path"] != "/status/sync" {
return false
}
if patch["value"].(provisioning.SyncStatus).LastRef != "existing-ref" || patch["value"].(provisioning.SyncStatus).JobID != "test-job" {
return false
}
return true
})).Return(errors.New("failed to patch status"))
// Expect granular patches for state, job, and started fields
rpf.On("Execute", mock.Anything, repoConfig,
mock.MatchedBy(func(patch map[string]interface{}) bool {
return patch["op"] == "replace" && patch["path"] == "/status/sync/state"
}),
mock.MatchedBy(func(patch map[string]interface{}) bool {
return patch["op"] == "replace" && patch["path"] == "/status/sync/job"
}),
mock.MatchedBy(func(patch map[string]interface{}) bool {
return patch["op"] == "replace" && patch["path"] == "/status/sync/started"
}),
).Return(errors.New("failed to patch status"))
},
expectedError: "update repo with job status at start: failed to patch status",
},
@@ -151,9 +152,9 @@ func TestSyncWorker_Process(t *testing.T) {
// Storage is migrated
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
// Initial status update succeeds
// Initial status update succeeds - expect granular patches
pr.On("SetMessage", mock.Anything, "update sync status at start").Return()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything).Return(nil).Once()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Repository resources creation fails
rrf.On("Client", mock.Anything, mock.Anything).Return(nil, errors.New("failed to create repository resources client"))
@@ -188,9 +189,9 @@ func TestSyncWorker_Process(t *testing.T) {
// Storage is migrated
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
// Initial status update succeeds
// Initial status update succeeds - expect granular patches
pr.On("SetMessage", mock.Anything, "update sync status at start").Return()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything).Return(nil).Once()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Repository resources creation succeeds
rrf.On("Client", mock.Anything, mock.Anything).Return(&resources.MockRepositoryResources{}, nil)
@@ -224,9 +225,9 @@ func TestSyncWorker_Process(t *testing.T) {
// Storage is migrated
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
// Initial status update
// Initial status update - expect granular patches
pr.On("SetMessage", mock.Anything, "update sync status at start").Return()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything).Return(nil)
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Setup resources and clients
mockRepoResources := resources.NewMockRepositoryResources(t)
@@ -254,7 +255,7 @@ func TestSyncWorker_Process(t *testing.T) {
}
syncStatus := patch["value"].(provisioning.SyncStatus)
return syncStatus.LastRef == "new-ref" && syncStatus.State == provisioning.JobStateSuccess
})).Return(nil)
})).Return(nil).Once()
},
expectedError: "",
},
@@ -277,9 +278,9 @@ func TestSyncWorker_Process(t *testing.T) {
// Storage is migrated
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
// Initial status update
// Initial status update - expect granular patches
pr.On("SetMessage", mock.Anything, "update sync status at start").Return()
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything).Return(nil)
rpf.On("Execute", mock.Anything, repoConfig, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Setup resources and clients
mockRepoResources := resources.NewMockRepositoryResources(t)
@@ -308,7 +309,7 @@ func TestSyncWorker_Process(t *testing.T) {
patch["path"] == "/status/sync" &&
syncStatus.LastRef == "existing-ref" && // LastRef should not change on failure
syncStatus.State == provisioning.JobStateError
})).Return(nil)
})).Return(nil).Once()
},
expectedError: "sync operation failed",
},
@@ -334,7 +335,9 @@ func TestSyncWorker_Process(t *testing.T) {
pr.On("SetMessage", mock.Anything, mock.Anything).Return()
pr.On("StrictMaxErrors", 20).Return()
pr.On("Complete", mock.Anything, mock.Anything).Return(provisioning.JobStatus{State: provisioning.JobStateSuccess})
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything).Return(nil)
// Initial patch with granular updates, final patch with full sync status
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
s.On("Sync", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return("new-ref", nil)
},
expectedError: "",
@@ -355,10 +358,13 @@ func TestSyncWorker_Process(t *testing.T) {
mockRepoResources.On("Stats", mock.Anything).Return(nil, nil)
rrf.On("Client", mock.Anything, mock.Anything).Return(mockRepoResources, nil)
// Verify only sync status is patched
// Initial patch with granular updates
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Verify only sync status is patched for final update
rpf.On("Execute", mock.Anything, mock.Anything, mock.MatchedBy(func(patch map[string]interface{}) bool {
return patch["path"] == "/status/sync"
})).Return(nil)
})).Return(nil).Once()
// Simple mocks for other calls
mockClients := resources.NewMockResourceClients(t)
@@ -381,7 +387,8 @@ func TestSyncWorker_Process(t *testing.T) {
}
rw.MockRepository.On("Config").Return(repoConfig)
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Initial patch with granular updates
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
mockRepoResources := resources.NewMockRepositoryResources(t)
stats := &provisioning.ResourceStats{
@@ -468,10 +475,13 @@ func TestSyncWorker_Process(t *testing.T) {
mockRepoResources.On("Stats", mock.Anything).Return(stats, nil)
rrf.On("Client", mock.Anything, mock.Anything).Return(mockRepoResources, nil)
// Initial patch with granular updates
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Verify only sync status is patched (multiple stats should be ignored)
rpf.On("Execute", mock.Anything, mock.Anything, mock.MatchedBy(func(patch map[string]interface{}) bool {
return patch["path"] == "/status/sync"
})).Return(nil)
})).Return(nil).Once()
// Simple mocks for other calls
mockClients := resources.NewMockResourceClients(t)
@@ -495,8 +505,8 @@ func TestSyncWorker_Process(t *testing.T) {
rw.MockRepository.On("Config").Return(repoConfig)
ds.On("ReadFromUnified", mock.Anything, mock.Anything).Return(true, nil).Twice()
// Initial status patch succeeds
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Initial status patch succeeds - expect granular patches
rpf.On("Execute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil).Once()
// Setup resources and clients
mockRepoResources := resources.NewMockRepositoryResources(t)
+7
View File
@@ -971,6 +971,13 @@ var (
},
{
Name: "dashboardLibrary",
Description: "Enable dashboard library experiments that are production ready",
Stage: FeatureStageExperimental,
Owner: grafanaSharingSquad,
FrontendOnly: false,
},
{
Name: "suggestedDashboards",
Description: "Enable suggested dashboards when creating new dashboards",
Stage: FeatureStageExperimental,
Owner: grafanaSharingSquad,
+1
View File
@@ -127,6 +127,7 @@ disableNumericMetricsSortingInExpressions,experimental,@grafana/oss-big-tent,fal
grafanaManagedRecordingRules,experimental,@grafana/alerting-squad,false,false,false
queryLibrary,preview,@grafana/sharing-squad,false,false,false
dashboardLibrary,experimental,@grafana/sharing-squad,false,false,false
suggestedDashboards,experimental,@grafana/sharing-squad,false,false,false
logsExploreTableDefaultVisualization,experimental,@grafana/observability-logs,false,false,true
alertingListViewV2,privatePreview,@grafana/alerting-squad,false,false,true
alertingDisableSendAlertsExternal,experimental,@grafana/alerting-squad,false,false,false
1 Name Stage Owner requiresDevMode RequiresRestart FrontendOnly
127 grafanaManagedRecordingRules experimental @grafana/alerting-squad false false false
128 queryLibrary preview @grafana/sharing-squad false false false
129 dashboardLibrary experimental @grafana/sharing-squad false false false
130 suggestedDashboards experimental @grafana/sharing-squad false false false
131 logsExploreTableDefaultVisualization experimental @grafana/observability-logs false false true
132 alertingListViewV2 privatePreview @grafana/alerting-squad false false true
133 alertingDisableSendAlertsExternal experimental @grafana/alerting-squad false false false
+5 -1
View File
@@ -516,9 +516,13 @@ const (
FlagQueryLibrary = "queryLibrary"
// FlagDashboardLibrary
// Enable suggested dashboards when creating new dashboards
// Enable dashboard library experiments that are production ready
FlagDashboardLibrary = "dashboardLibrary"
// FlagSuggestedDashboards
// Enable suggested dashboards when creating new dashboards
FlagSuggestedDashboards = "suggestedDashboards"
// FlagLogsExploreTableDefaultVisualization
// Sets the logs table as default visualisation in logs explore
FlagLogsExploreTableDefaultVisualization = "logsExploreTableDefaultVisualization"
+15 -3
View File
@@ -1082,14 +1082,14 @@
{
"metadata": {
"name": "dashboardLibrary",
"resourceVersion": "1760051989635",
"resourceVersion": "1762521182817",
"creationTimestamp": "2025-09-26T16:02:12Z",
"annotations": {
"grafana.app/updatedTimestamp": "2025-10-09 23:19:49.635811 +0000 UTC"
"grafana.app/updatedTimestamp": "2025-11-07 13:13:02.817210943 +0000 UTC"
}
},
"spec": {
"description": "Enable suggested dashboards when creating new dashboards",
"description": "Enable dashboard library experiments that are production ready",
"stage": "experimental",
"codeowner": "@grafana/sharing-squad"
}
@@ -3779,6 +3779,18 @@
"codeowner": "@grafana/search-and-storage"
}
},
{
"metadata": {
"name": "suggestedDashboards",
"resourceVersion": "1762521182817",
"creationTimestamp": "2025-11-07T13:13:02Z"
},
"spec": {
"description": "Enable suggested dashboards when creating new dashboards",
"stage": "experimental",
"codeowner": "@grafana/sharing-squad"
}
},
{
"metadata": {
"name": "tableNextGen",
+24 -15
View File
@@ -473,17 +473,22 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
if err != nil {
return err
}
// Every signed in user can see the general folder. The general folder might have "general" or the empty string as its UID.
var folderUIDS = []string{"general", ""}
folderMap := map[string]string{}
// Using a map for O(1) lookup instead of O(n) slice iteration
folderUIDSet := make(map[string]bool, len(fs)+2)
folderUIDSet["general"] = true
folderUIDSet[""] = true
folderMap := make(map[string]string, len(fs))
for _, f := range fs {
folderUIDS = append(folderUIDS, f.UID)
folderUIDSet[f.UID] = true
folderMap[f.UID] = f.Title
}
// if the user is not an admin, we need to filter out elements that are not in folders the user can see
for _, element := range elements {
if !signedInUser.HasRole(org.RoleAdmin) {
if !contains(folderUIDS, element.FolderUID) {
if !folderUIDSet[element.FolderUID] {
continue
}
}
@@ -522,10 +527,11 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
})
}
var libraryElements []model.LibraryElement
var libraryElements []model.LibraryElementWithMeta
countBuilder := db.SQLBuilder{}
if folderFilter.includeGeneralFolder {
countBuilder.Write(selectLibraryElementDTOWithMeta)
countBuilder.Write(", '' as folder_uid ")
countBuilder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
countBuilder.Write(` WHERE le.org_id=? AND le.folder_id=0`, signedInUser.GetOrgID())
writeKindSQL(query, &countBuilder)
@@ -537,6 +543,7 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
countBuilder.Write(" ")
}
countBuilder.Write(selectLibraryElementDTOWithMeta)
countBuilder.Write(", le.folder_uid as folder_uid ")
countBuilder.Write(getFromLibraryElementDTOWithMeta(l.SQLStore.GetDialect()))
countBuilder.Write(` WHERE le.org_id=? AND le.folder_id<>0`, signedInUser.GetOrgID())
writeKindSQL(query, &countBuilder)
@@ -550,8 +557,19 @@ func (l *LibraryElementService) getAllLibraryElements(c context.Context, signedI
return err
}
// Apply the same folder permission filtering to the count for non-admin users
totalCount := int64(len(libraryElements))
if !signedInUser.HasRole(org.RoleAdmin) {
totalCount = 0
for _, element := range libraryElements {
if folderUIDSet[element.FolderUID] {
totalCount++
}
}
}
result = model.LibraryElementSearchResult{
TotalCount: int64(len(libraryElements)),
TotalCount: totalCount,
Elements: retDTOs,
Page: query.Page,
PerPage: query.PerPage,
@@ -878,15 +896,6 @@ func (l *LibraryElementService) deleteLibraryElementsInFolderUID(c context.Conte
})
}
func contains(slice []string, element string) bool {
for _, item := range slice {
if item == element {
return true
}
}
return false
}
func getFoldersWithMatchingTitles(c context.Context, l *LibraryElementService, signedInUser identity.Requester, query model.SearchLibraryElementsQuery) ([]string, error) {
if len(strings.TrimSpace(query.SearchString)) <= 0 {
return nil, nil
@@ -1370,4 +1370,161 @@ func TestIntegration_GetAllLibraryElements(t *testing.T) {
require.NotEmpty(t, element.UID, "Should have a UID")
require.Equal(t, int64(0), element.Meta.ConnectedDashboards, "Should have no connected dashboards")
})
// Non-admin user permission tests
scenarioWithPanel(t, "When a non-admin user has folders but none of the library elements are in those folders, it should return empty result",
func(t *testing.T, sc scenarioContext) {
// Create library panels in the scenario folder
// nolint:staticcheck
command := getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Text - Library Panel2")
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
// Create a different folder that the non-admin user has access to (but has no panels)
differentFolder := &folder.Folder{
ID: 2,
OrgID: 1,
UID: "uid_for_DifferentFolder",
Title: "DifferentFolder",
}
// Change user to non-admin and set their accessible folders to only the different folder
// This simulates a user who can see a folder but that folder doesn't contain any of the library elements
sc.reqContext.OrgRole = org.RoleViewer
sc.folderSvc.ExpectedFolders = []*folder.Folder{differentFolder}
sc.folderSvc.AddFolder(differentFolder)
resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result libraryElementsSearch
err := json.Unmarshal(resp.Body(), &result)
require.NoError(t, err)
// TotalCount should be 0 for non-admin users since they can't access the folders with panels
require.Equal(t, int64(0), result.Result.TotalCount, "TotalCount should be 0 since user has no access to folders with panels")
require.Equal(t, 0, len(result.Result.Elements), "Elements should be empty since user has no access to folders with panels")
require.Equal(t, 1, result.Result.Page, "Should be on page 1")
require.Equal(t, 100, result.Result.PerPage, "Should have perPage 100")
})
scenarioWithPanel(t, "When a non-admin user has folders and some library elements are in those folders, it should return only accessible elements",
func(t *testing.T, sc scenarioContext) {
// Create a second folder that the non-admin user will have access to
accessibleFolder := &folder.Folder{
ID: 2,
OrgID: 1,
UID: "uid_for_AccessibleFolder",
Title: "AccessibleFolder",
}
// Create a library panel in the accessible folder (need to add it to fake service first)
sc.folderSvc.ExpectedFolder = accessibleFolder
sc.folderSvc.AddFolder(accessibleFolder)
// nolint:staticcheck
command := getCreatePanelCommand(accessibleFolder.ID, accessibleFolder.UID, "Accessible Panel")
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
// Create another panel in a folder the user won't have access to
inaccessibleFolder := &folder.Folder{
ID: 3,
OrgID: 1,
UID: "uid_for_InaccessibleFolder",
Title: "InaccessibleFolder",
}
sc.folderSvc.ExpectedFolder = inaccessibleFolder
sc.folderSvc.AddFolder(inaccessibleFolder)
// nolint:staticcheck
command = getCreatePanelCommand(inaccessibleFolder.ID, inaccessibleFolder.UID, "Inaccessible Panel")
sc.reqContext.Req.Body = mockRequestBody(command)
resp = sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
// Change user to non-admin and set their accessible folders to only the accessible folder and scenario folder
// This will filter out the inaccessible folder
sc.reqContext.OrgRole = org.RoleViewer
sc.folderSvc.ExpectedFolders = []*folder.Folder{sc.folder, accessibleFolder}
resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result libraryElementsSearch
err := json.Unmarshal(resp.Body(), &result)
require.NoError(t, err)
// TotalCount should match the number of accessible elements (2) for non-admin users
require.Equal(t, int64(2), result.Result.TotalCount, "TotalCount should be 2 (only accessible panels)")
require.Equal(t, 2, len(result.Result.Elements), "Elements should contain only 2 accessible panels")
require.Equal(t, 1, result.Result.Page, "Should be on page 1")
require.Equal(t, 100, result.Result.PerPage, "Should have perPage 100")
// Verify the returned panels are from accessible folders only
folderUIDs := make(map[string]bool)
for _, element := range result.Result.Elements {
folderUIDs[element.FolderUID] = true
require.Contains(t, []string{sc.folder.UID, accessibleFolder.UID}, element.FolderUID, "Element should be in accessible folder")
require.NotEqual(t, inaccessibleFolder.UID, element.FolderUID, "Element should not be from inaccessible folder")
}
require.True(t, folderUIDs[sc.folder.UID], "Should include panel from scenario folder")
require.True(t, folderUIDs[accessibleFolder.UID], "Should include panel from accessible folder")
})
scenarioWithPanel(t, "When a non-admin user has access to all folders containing library elements, it should return all elements",
func(t *testing.T, sc scenarioContext) {
// Create a second folder that the non-admin user will have access to
folder2 := &folder.Folder{
ID: 2,
OrgID: 1,
UID: "uid_for_Folder2",
Title: "Folder2",
}
sc.folderSvc.ExpectedFolder = folder2
sc.folderSvc.AddFolder(folder2)
// Create a library panel in folder2
// nolint:staticcheck
command := getCreatePanelCommand(folder2.ID, folder2.UID, "Panel in Folder2")
sc.reqContext.Req.Body = mockRequestBody(command)
resp := sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
// Create another panel in the original scenario folder
sc.folderSvc.ExpectedFolder = sc.folder
// nolint:staticcheck
command = getCreatePanelCommand(sc.folder.ID, sc.folder.UID, "Panel in ScenarioFolder")
sc.reqContext.Req.Body = mockRequestBody(command)
resp = sc.service.createHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
// Change user to non-admin and set their accessible folders to include all folders with panels
sc.reqContext.OrgRole = org.RoleViewer
sc.folderSvc.ExpectedFolders = []*folder.Folder{sc.folder, folder2}
resp = sc.service.getAllHandler(sc.reqContext)
require.Equal(t, 200, resp.Status())
var result libraryElementsSearch
err := json.Unmarshal(resp.Body(), &result)
require.NoError(t, err)
// Should return all 3 panels (1 from initial setup + 2 created in this test)
require.Equal(t, int64(3), result.Result.TotalCount, "Should return all 3 panels")
require.Equal(t, 3, len(result.Result.Elements), "Should have 3 elements")
require.Equal(t, 1, result.Result.Page, "Should be on page 1")
require.Equal(t, 100, result.Result.PerPage, "Should have perPage 100")
// Verify all panels are from the accessible folders
folderUIDs := make(map[string]int)
for _, element := range result.Result.Elements {
folderUIDs[element.FolderUID]++
require.Contains(t, []string{sc.folder.UID, folder2.UID}, element.FolderUID, "All elements should be in accessible folders")
require.Equal(t, int64(model.PanelElement), element.Kind, "Should be a panel element")
require.Equal(t, "text", element.Type, "Should be text panel")
}
require.Equal(t, 2, folderUIDs[sc.folder.UID], "Should have 2 panels in scenario folder")
require.Equal(t, 1, folderUIDs[folder2.UID], "Should have 1 panel in folder2")
})
}
+9 -8
View File
@@ -504,13 +504,6 @@ func initInstanceStore(sqlStore db.DB, logger log.Logger, featureToggles feature
if featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingSaveStateCompressed) {
logger.Info("Using protobuf-based alert instance store")
instanceStore = protoInstanceStore
// If FlagAlertingSaveStateCompressed is enabled, ProtoInstanceDBStore is used,
// which functions differently from InstanceDBStore. FlagAlertingSaveStatePeriodic is
// not applicable to ProtoInstanceDBStore, so a warning is logged if it is set.
//nolint:staticcheck // not yet migrated to OpenFeature
if featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingSaveStatePeriodic) {
logger.Warn("alertingSaveStatePeriodic is not used when alertingSaveStateCompressed feature flag enabled")
}
} else {
logger.Info("Using simple database alert instance store")
instanceStore = simpleInstanceStore
@@ -525,7 +518,15 @@ func initStatePersister(uaCfg setting.UnifiedAlertingSettings, cfg state.Manager
//nolint:staticcheck // not yet migrated to OpenFeature
if featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingSaveStateCompressed) {
logger.Info("Using rule state persister")
statePersister = state.NewSyncRuleStatePersisiter(logger, cfg)
if featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingSaveStatePeriodic) {
logger.Info("Compressed storage with periodic save enabled")
ticker := clock.New().Ticker(cfg.StatePeriodicSaveInterval)
statePersister = state.NewSyncRuleStatePersisiter(logger, ticker, cfg)
} else {
logger.Info("Compressed storage FullSync disabled")
statePersister = state.NewSyncRuleStatePersisiter(logger, nil, cfg)
}
} else if featureToggles.IsEnabledGlobally(featuremgmt.FlagAlertingSaveStatePeriodic) {
logger.Info("Using periodic state persister")
ticker := clock.New().Ticker(uaCfg.StatePeriodicSaveInterval)
+3 -1
View File
@@ -436,7 +436,9 @@ func TestInitStatePersister(t *testing.T) {
ua := setting.UnifiedAlertingSettings{
StatePeriodicSaveInterval: 1 * time.Minute,
}
cfg := state.ManagerCfg{}
cfg := state.ManagerCfg{
StatePeriodicSaveInterval: 1 * time.Minute,
}
tests := []struct {
name string
+4
View File
@@ -8,6 +8,10 @@ import (
history_model "github.com/grafana/grafana/pkg/services/ngalert/state/historian/model"
)
type AlertInstancesProvider interface {
GetAlertInstances() []models.AlertInstance
}
// InstanceStore represents the ability to fetch and write alert instances.
type InstanceStore interface {
InstanceReader
@@ -12,10 +12,6 @@ import (
"github.com/grafana/grafana/pkg/services/ngalert/models"
)
type AlertInstancesProvider interface {
GetAlertInstances() []models.AlertInstance
}
type AsyncStatePersister struct {
log log.Logger
batchSize int
@@ -4,6 +4,7 @@ import (
"context"
"time"
"github.com/benbjohnson/clock"
"go.opentelemetry.io/otel/trace"
"github.com/grafana/grafana/pkg/infra/log"
@@ -11,22 +12,63 @@ import (
)
type SyncRuleStatePersister struct {
log log.Logger
store InstanceStore
log log.Logger
store InstanceStore
ticker *clock.Ticker
}
func NewSyncRuleStatePersisiter(log log.Logger, cfg ManagerCfg) StatePersister {
func NewSyncRuleStatePersisiter(log log.Logger, ticker *clock.Ticker, cfg ManagerCfg) StatePersister {
return &SyncRuleStatePersister{
log: log,
store: cfg.InstanceStore,
log: log,
store: cfg.InstanceStore,
ticker: ticker,
}
}
func (a *SyncRuleStatePersister) Async(_ context.Context, _ AlertInstancesProvider) {
a.log.Debug("Async: No-Op")
func (a *SyncRuleStatePersister) Async(ctx context.Context, instancesProvider AlertInstancesProvider) {
if a.ticker == nil {
return
}
for {
select {
case <-a.ticker.C:
if err := a.fullSync(ctx, instancesProvider); err != nil {
a.log.Error("Failed to do a full compressed state sync to database", "err", err)
}
case <-ctx.Done():
a.log.Info("Scheduler is shutting down, doing a final state sync.")
if err := a.fullSync(context.Background(), instancesProvider); err != nil {
a.log.Error("Failed to do a full compressed state sync to database", "err", err)
}
a.ticker.Stop()
a.log.Info("Compressed state async worker is shut down.")
return
}
}
}
func (a *SyncRuleStatePersister) fullSync(ctx context.Context, instancesProvider AlertInstancesProvider) error {
startTime := time.Now()
a.log.Debug("Full compressed state sync start")
instances := instancesProvider.GetAlertInstances()
// batchSize is set to 0 because compressed storage groups instances by ruleUID, not by batch size
err := a.store.FullSync(ctx, instances, 0, nil)
if err != nil {
a.log.Error("Full compressed state sync failed", "duration", time.Since(startTime), "instances", len(instances))
return err
}
a.log.Debug("Full compressed state sync done", "duration", time.Since(startTime), "instances", len(instances))
return nil
}
func (a *SyncRuleStatePersister) Sync(ctx context.Context, span trace.Span, ruleKey models.AlertRuleKeyWithGroup, states StateTransitions) {
if a.ticker != nil {
a.log.Debug("Skip immediate save, using periodic save instead")
return
}
if a.store == nil || len(states) == 0 {
return
}
@@ -9,6 +9,7 @@ import (
"time"
"github.com/golang/snappy"
"github.com/grafana/grafana/pkg/services/sqlstore"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb"
@@ -98,28 +99,13 @@ func (st ProtoInstanceDBStore) SaveAlertInstancesForRule(ctx context.Context, ke
logger := st.Logger.FromContext(ctx)
logger.Debug("SaveAlertInstancesForRule called", "rule_uid", key.UID, "org_id", key.OrgID, "instances", len(instances))
alert_instances_proto := make([]*pb.AlertInstance, len(instances))
for i, instance := range instances {
alert_instances_proto[i] = alertInstanceModelToProto(instance)
}
compressedAlertInstances, err := compressAlertInstances(alert_instances_proto)
compressedAlertInstances, err := convertAndCompressAlertInstances(instances)
if err != nil {
return fmt.Errorf("failed to compress alert instances: %w", err)
}
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
params := []any{key.OrgID, key.UID, compressedAlertInstances, time.Now()}
upsertSQL := st.SQLStore.GetDialect().UpsertSQL(
"alert_rule_state",
[]string{"org_id", "rule_uid"},
[]string{"org_id", "rule_uid", "data", "updated_at"},
)
_, err = sess.SQL(upsertSQL, params...).Query()
return err
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
return st.upsertCompressedAlertInstances(sess, key.OrgID, key.UID, compressedAlertInstances, time.Now())
})
}
@@ -134,9 +120,68 @@ func (st ProtoInstanceDBStore) DeleteAlertInstancesByRule(ctx context.Context, k
}
func (st ProtoInstanceDBStore) FullSync(ctx context.Context, instances []models.AlertInstance, batchSize int, jitterFunc func(int) time.Duration) error {
if len(instances) == 0 {
return nil
}
logger := st.Logger.FromContext(ctx)
logger.Error("FullSync called and not implemented")
return errors.New("fullsync is not implemented for proto instance database store")
logger.Debug("FullSync called", "total_instances", len(instances))
ruleGroups := make(map[models.AlertRuleKeyWithGroup][]models.AlertInstance)
for _, instance := range instances {
ruleKey := models.AlertRuleKeyWithGroup{
AlertRuleKey: models.AlertRuleKey{
OrgID: instance.RuleOrgID,
UID: instance.RuleUID,
},
RuleGroup: "",
}
ruleGroups[ruleKey] = append(ruleGroups[ruleKey], instance)
}
type preparedRule struct {
ruleKey models.AlertRuleKeyWithGroup
compressedData []byte
}
preparedRules := make([]preparedRule, 0, len(ruleGroups))
for ruleKey, ruleInstances := range ruleGroups {
// Convert and compress instances
compressedAlertInstances, err := convertAndCompressAlertInstances(ruleInstances)
if err != nil {
logger.Error("Failed to compress instances for rule", "rule_uid", ruleKey.UID, "error", err)
continue
}
preparedRules = append(preparedRules, preparedRule{
ruleKey: ruleKey,
compressedData: compressedAlertInstances,
})
logger.Debug("Prepared rule for sync", "rule_uid", ruleKey.UID, "org_id", ruleKey.OrgID, "instances", len(ruleInstances))
}
return st.SQLStore.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error {
syncTimestamp := time.Now()
logger.Debug("Starting FullSync transaction", "rules_count", len(preparedRules), "timestamp", syncTimestamp)
// First we delete all records from the table
if _, err := sess.Exec("DELETE FROM alert_rule_state"); err != nil {
return fmt.Errorf("failed to delete alert_rule_state: %w", err)
}
for i, prepared := range preparedRules {
logger.Debug("Executing UPSERT for rule", "rule_uid", prepared.ruleKey.UID, "org_id", prepared.ruleKey.OrgID, "rule_index", i+1, "total_rules", len(preparedRules))
// Execute UPSERT with pre-compressed data using helper method
if err := st.upsertCompressedAlertInstances(sess, prepared.ruleKey.OrgID, prepared.ruleKey.UID, prepared.compressedData, syncTimestamp); err != nil {
return fmt.Errorf("failed to save instances for rule %s: %w", prepared.ruleKey.UID, err)
}
}
logger.Debug("FullSync transaction completed successfully", "rules_synced", len(preparedRules))
return nil
})
}
func alertInstanceModelToProto(modelInstance models.AlertInstance) *pb.AlertInstance {
@@ -155,6 +200,30 @@ func alertInstanceModelToProto(modelInstance models.AlertInstance) *pb.AlertInst
}
}
// convertAndCompressAlertInstances converts model instances to protobuf and compresses them
func convertAndCompressAlertInstances(instances []models.AlertInstance) ([]byte, error) {
alertInstancesProto := make([]*pb.AlertInstance, len(instances))
for i, instance := range instances {
alertInstancesProto[i] = alertInstanceModelToProto(instance)
}
return compressAlertInstances(alertInstancesProto)
}
// upsertCompressedAlertInstances performs upsert operation for compressed alert instances
func (st ProtoInstanceDBStore) upsertCompressedAlertInstances(sess *sqlstore.DBSession, orgID int64, ruleUID string, compressedData []byte, timestamp time.Time) error {
upsertSQL := st.SQLStore.GetDialect().UpsertSQL(
"alert_rule_state",
[]string{"org_id", "rule_uid"},
[]string{"org_id", "rule_uid", "data", "updated_at"},
)
params := []any{orgID, ruleUID, compressedData, timestamp}
_, err := sess.SQL(upsertSQL, params...).Query()
return err
}
func compressAlertInstances(instances []*pb.AlertInstance) ([]byte, error) {
mProto, err := proto.Marshal(&pb.AlertInstances{Instances: instances})
if err != nil {
@@ -174,6 +174,166 @@ func TestCompressAndDecompressAlertInstances(t *testing.T) {
require.EqualExportedValues(t, alertInstances[1], decompressedInstances[1])
}
func TestConvertAndCompressAlertInstances(t *testing.T) {
now := time.Now()
modelInstances := []models.AlertInstance{
{
AlertInstanceKey: models.AlertInstanceKey{
RuleUID: "rule-uid-1",
RuleOrgID: 1,
LabelsHash: "hash-1",
},
Labels: map[string]string{"label-1": "value-1"},
CurrentState: models.InstanceStateFiring,
CurrentStateSince: now,
CurrentStateEnd: now.Add(time.Hour),
CurrentReason: "reason-1",
LastEvalTime: now.Add(-time.Minute),
LastSentAt: &now,
FiredAt: &now,
ResolvedAt: nil,
ResultFingerprint: "fingerprint-1",
},
{
AlertInstanceKey: models.AlertInstanceKey{
RuleUID: "rule-uid-1",
RuleOrgID: 1,
LabelsHash: "hash-2",
},
Labels: map[string]string{"label-2": "value-2"},
CurrentState: models.InstanceStateNormal,
CurrentStateSince: now,
CurrentStateEnd: now.Add(time.Hour),
CurrentReason: "reason-2",
LastEvalTime: now.Add(-time.Minute),
LastSentAt: nil,
FiredAt: nil,
ResolvedAt: &now,
ResultFingerprint: "fingerprint-2",
},
}
compressedData, err := convertAndCompressAlertInstances(modelInstances)
require.NoError(t, err)
require.NotEmpty(t, compressedData)
// Verify we can decompress and get back the same data
decompressedInstances, err := decompressAlertInstances(compressedData)
require.NoError(t, err)
require.Len(t, decompressedInstances, 2)
// Convert back to model to compare
for i, protoInstance := range decompressedInstances {
modelInstance := alertInstanceProtoToModel("rule-uid-1", 1, protoInstance)
require.Equal(t, modelInstances[i].Labels, modelInstance.Labels)
require.Equal(t, modelInstances[i].CurrentState, modelInstance.CurrentState)
require.Equal(t, modelInstances[i].LabelsHash, modelInstance.LabelsHash)
require.Equal(t, modelInstances[i].ResultFingerprint, modelInstance.ResultFingerprint)
}
}
func TestConvertAndCompressAlertInstances_EmptyInput(t *testing.T) {
emptyInstances := []models.AlertInstance{}
compressedData, err := convertAndCompressAlertInstances(emptyInstances)
require.NoError(t, err)
decompressedInstances, err := decompressAlertInstances(compressedData)
require.NoError(t, err)
require.Empty(t, decompressedInstances)
}
func TestFullSyncGroupingLogic(t *testing.T) {
now := time.Now()
// Test instances from multiple rules to verify grouping logic
instances := []models.AlertInstance{
{
AlertInstanceKey: models.AlertInstanceKey{
RuleUID: "rule-1",
RuleOrgID: 1,
LabelsHash: "hash-1-1",
},
Labels: models.InstanceLabels{"rule1": "instance1"},
CurrentState: models.InstanceStateFiring,
CurrentStateSince: now,
CurrentStateEnd: now.Add(time.Hour),
CurrentReason: "test reason 1",
LastEvalTime: now.Add(-time.Minute),
ResultFingerprint: "fingerprint-1-1",
},
{
AlertInstanceKey: models.AlertInstanceKey{
RuleUID: "rule-1",
RuleOrgID: 1,
LabelsHash: "hash-1-2",
},
Labels: models.InstanceLabels{"rule1": "instance2"},
CurrentState: models.InstanceStateNormal,
CurrentStateSince: now,
CurrentStateEnd: now.Add(time.Hour),
CurrentReason: "test reason 2",
LastEvalTime: now.Add(-time.Minute),
ResultFingerprint: "fingerprint-1-2",
},
{
AlertInstanceKey: models.AlertInstanceKey{
RuleUID: "rule-2",
RuleOrgID: 1,
LabelsHash: "hash-2-1",
},
Labels: models.InstanceLabels{"rule2": "instance1"},
CurrentState: models.InstanceStatePending,
CurrentStateSince: now,
CurrentStateEnd: now.Add(time.Hour),
CurrentReason: "test reason 3",
LastEvalTime: now.Add(-time.Minute),
ResultFingerprint: "fingerprint-2-1",
},
}
// Test the grouping logic that FullSync uses internally
ruleGroups := make(map[models.AlertRuleKeyWithGroup][]models.AlertInstance)
for _, instance := range instances {
ruleKey := models.AlertRuleKeyWithGroup{
AlertRuleKey: models.AlertRuleKey{
OrgID: instance.RuleOrgID,
UID: instance.RuleUID,
},
RuleGroup: "",
}
ruleGroups[ruleKey] = append(ruleGroups[ruleKey], instance)
}
// Verify grouping worked correctly
require.Len(t, ruleGroups, 2, "Should have 2 rule groups")
rule1Key := models.AlertRuleKeyWithGroup{
AlertRuleKey: models.AlertRuleKey{OrgID: 1, UID: "rule-1"},
RuleGroup: "",
}
rule2Key := models.AlertRuleKeyWithGroup{
AlertRuleKey: models.AlertRuleKey{OrgID: 1, UID: "rule-2"},
RuleGroup: "",
}
require.Len(t, ruleGroups[rule1Key], 2, "Rule 1 should have 2 instances")
require.Len(t, ruleGroups[rule2Key], 1, "Rule 2 should have 1 instance")
// Test compression for each group
for ruleKey, ruleInstances := range ruleGroups {
compressedData, err := convertAndCompressAlertInstances(ruleInstances)
require.NoError(t, err, "Compression should succeed for rule %s", ruleKey.UID)
require.NotEmpty(t, compressedData, "Compressed data should not be empty for rule %s", ruleKey.UID)
// Verify decompression works
decompressedInstances, err := decompressAlertInstances(compressedData)
require.NoError(t, err, "Decompression should succeed for rule %s", ruleKey.UID)
require.Len(t, decompressedInstances, len(ruleInstances), "Should have same number of instances after decompression for rule %s", ruleKey.UID)
}
}
func toProtoTimestampPtr(tm *time.Time) *timestamppb.Timestamp {
if tm == nil {
return nil
+1 -3
View File
@@ -114,9 +114,7 @@ func (w *parquetWriter) Close() error {
// writes the current buffer to parquet and re-inits the arrow buffer
func (w *parquetWriter) flush() error {
w.logger.Info("flush", "count", w.rv.Len())
//TODO: fix deprecation warning
//nolint:staticcheck
rec := array.NewRecord(w.schema, []arrow.Array{
rec := array.NewRecordBatch(w.schema, []arrow.Array{
w.rv.NewArray(),
w.namespace.NewArray(),
w.group.NewArray(),
+1 -1
View File
@@ -110,7 +110,7 @@ func NewBulkSettings(md metadata.MD) (BulkSettings, error) {
// All requests must be to the same NAMESPACE/GROUP/RESOURCE
func (s *server) BulkProcess(stream resourcepb.BulkStore_BulkProcessServer) error {
ctx := stream.Context()
ctx, span := s.tracer.Start(ctx, "resource.server.BulkProcess")
ctx, span := tracer.Start(ctx, "resource.server.BulkProcess")
defer span.End()
sendAndClose := func(rsp *resourcepb.BulkResponse) error {
+6 -13
View File
@@ -127,11 +127,8 @@ type SearchBackend interface {
GetOpenIndexes() []NamespacedResource
}
const tracingPrexfixSearch = "unified_search."
// This supports indexing+search regardless of implementation
type searchSupport struct {
tracer trace.Tracer
log *slog.Logger
storage StorageBackend
search SearchBackend
@@ -163,14 +160,11 @@ var (
_ resourcepb.ManagedObjectIndexServer = (*searchSupport)(nil)
)
func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.AccessClient, blob BlobSupport, tracer trace.Tracer, indexMetrics *BleveIndexMetrics, ownsIndexFn func(key NamespacedResource) (bool, error)) (support *searchSupport, err error) {
func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.AccessClient, blob BlobSupport, indexMetrics *BleveIndexMetrics, ownsIndexFn func(key NamespacedResource) (bool, error)) (support *searchSupport, err error) {
// No backend search support
if opts.Backend == nil {
return nil, nil
}
if tracer == nil {
return nil, fmt.Errorf("missing tracer")
}
if opts.InitWorkerThreads < 1 {
opts.InitWorkerThreads = 1
@@ -188,7 +182,6 @@ func newSearchSupport(opts SearchOptions, storage StorageBackend, access types.A
support = &searchSupport{
access: access,
tracer: tracer,
storage: storage,
search: opts.Backend,
log: slog.Default().With("logger", "resource-search"),
@@ -341,7 +334,7 @@ func (s *searchSupport) CountManagedObjects(ctx context.Context, req *resourcepb
// Search implements ResourceIndexServer.
func (s *searchSupport) Search(ctx context.Context, req *resourcepb.ResourceSearchRequest) (*resourcepb.ResourceSearchResponse, error) {
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Search")
ctx, span := tracer.Start(ctx, "resource.searchSupport.Search")
defer span.End()
if req.Options.Key.Namespace == "" || req.Options.Key.Group == "" || req.Options.Key.Resource == "" {
@@ -499,7 +492,7 @@ func (s *searchSupport) buildIndexes(ctx context.Context) (int, error) {
func (s *searchSupport) init(ctx context.Context) error {
origCtx := ctx
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Init")
ctx, span := tracer.Start(ctx, "resource.searchSupport.init")
defer span.End()
start := time.Now().Unix()
@@ -632,7 +625,7 @@ func (s *searchSupport) runIndexRebuilder(ctx context.Context) {
}
func (s *searchSupport) rebuildIndex(ctx context.Context, req rebuildRequest) {
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"RebuildIndex")
ctx, span := tracer.Start(ctx, "resource.searchSupport.rebuildIndex")
defer span.End()
l := s.log.With("namespace", req.Namespace, "group", req.Group, "resource", req.Resource)
@@ -731,7 +724,7 @@ func (s *searchSupport) getOrCreateIndex(ctx context.Context, key NamespacedReso
return nil, fmt.Errorf("search is not configured properly (missing unifiedStorageSearch feature toggle?)")
}
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"GetOrCreateIndex")
ctx, span := tracer.Start(ctx, "resource.searchSupport.getOrCreateIndex")
defer span.End()
span.SetAttributes(
attribute.String("namespace", key.Namespace),
@@ -808,7 +801,7 @@ func (s *searchSupport) getOrCreateIndex(ctx context.Context, key NamespacedReso
}
func (s *searchSupport) build(ctx context.Context, nsr NamespacedResource, size int64, indexBuildReason string, rebuild bool) (ResourceIndex, error) {
ctx, span := s.tracer.Start(ctx, tracingPrexfixSearch+"Build")
ctx, span := tracer.Start(ctx, "resource.searchSupport.build")
defer span.End()
span.SetAttributes(
+5 -6
View File
@@ -13,7 +13,6 @@ import (
"github.com/grafana/authlib/types"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace/noop"
dashboardv1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
@@ -211,7 +210,7 @@ func TestSearchGetOrCreateIndex(t *testing.T) {
InitMinCount: 1, // set min count to default for this test
}
support, err := newSearchSupport(opts, storage, nil, nil, noop.NewTracerProvider().Tracer("test"), nil, nil)
support, err := newSearchSupport(opts, storage, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, support)
@@ -267,7 +266,7 @@ func TestSearchGetOrCreateIndexWithIndexUpdate(t *testing.T) {
}
// Enable searchAfterWrite
support, err := newSearchSupport(opts, storage, nil, nil, noop.NewTracerProvider().Tracer("test"), nil, nil)
support, err := newSearchSupport(opts, storage, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, support)
@@ -316,7 +315,7 @@ func TestSearchGetOrCreateIndexWithCancellation(t *testing.T) {
InitMinCount: 1, // set min count to default for this test
}
support, err := newSearchSupport(opts, storage, nil, nil, noop.NewTracerProvider().Tracer("test"), nil, nil)
support, err := newSearchSupport(opts, storage, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, support)
@@ -594,7 +593,7 @@ func TestFindIndexesForRebuild(t *testing.T) {
MinBuildVersion: semver.MustParse("5.5.5"),
}
support, err := newSearchSupport(opts, storage, nil, nil, noop.NewTracerProvider().Tracer("test"), nil, nil)
support, err := newSearchSupport(opts, storage, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, support)
@@ -665,7 +664,7 @@ func TestRebuildIndexes(t *testing.T) {
Resources: supplier,
}
support, err := newSearchSupport(opts, storage, nil, nil, noop.NewTracerProvider().Tracer("test"), nil, nil)
support, err := newSearchSupport(opts, storage, nil, nil, nil, nil)
require.NoError(t, err)
require.NotNil(t, support)
+10 -18
View File
@@ -14,8 +14,7 @@ import (
"github.com/Masterminds/semver"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"go.opentelemetry.io/otel/trace"
"go.opentelemetry.io/otel/trace/noop"
"go.opentelemetry.io/otel"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -30,6 +29,8 @@ import (
"github.com/grafana/grafana/pkg/util/scheduler"
)
var tracer = otel.Tracer("github.com/grafana/grafana/pkg/storage/unified/resource")
// ResourceServer implements all gRPC services
type ResourceServer interface {
resourcepb.ResourceStoreServer
@@ -210,9 +211,6 @@ type SearchOptions struct {
}
type ResourceServerOptions struct {
// OTel tracer
Tracer trace.Tracer
// Real storage backend
Backend StorageBackend
@@ -259,10 +257,6 @@ type ResourceServerOptions struct {
}
func NewResourceServer(opts ResourceServerOptions) (*server, error) {
if opts.Tracer == nil {
opts.Tracer = noop.NewTracerProvider().Tracer("resource-server")
}
if opts.Backend == nil {
return nil, fmt.Errorf("missing Backend implementation")
}
@@ -314,8 +308,8 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
}
blobstore, err = NewCDKBlobSupport(ctx, CDKBlobSupportOptions{
Tracer: opts.Tracer,
Bucket: NewInstrumentedBucket(bucket, opts.Reg, opts.Tracer),
Tracer: tracer,
Bucket: NewInstrumentedBucket(bucket, opts.Reg, tracer),
})
if err != nil {
return nil, err
@@ -331,7 +325,6 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
// Make this cancelable
ctx, cancel := context.WithCancel(context.Background())
s := &server{
tracer: opts.Tracer,
log: logger,
backend: opts.Backend,
blob: blobstore,
@@ -355,7 +348,7 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
if opts.Search.Resources != nil {
var err error
s.search, err = newSearchSupport(opts.Search, s.backend, s.access, s.blob, opts.Tracer, opts.IndexMetrics, opts.OwnsIndexFn)
s.search, err = newSearchSupport(opts.Search, s.backend, s.access, s.blob, opts.IndexMetrics, opts.OwnsIndexFn)
if err != nil {
return nil, err
}
@@ -373,7 +366,6 @@ func NewResourceServer(opts ResourceServerOptions) (*server, error) {
var _ ResourceServer = &server{}
type server struct {
tracer trace.Tracer
log *slog.Logger
backend StorageBackend
blob BlobSupport
@@ -651,7 +643,7 @@ func (s *server) checkFolderMovePermissions(ctx context.Context, user claims.Aut
}
func (s *server) Create(ctx context.Context, req *resourcepb.CreateRequest) (*resourcepb.CreateResponse, error) {
ctx, span := s.tracer.Start(ctx, "storage_server.Create")
ctx, span := tracer.Start(ctx, "resource.server.Create")
defer span.End()
if r := verifyRequestKey(req.Key); r != nil {
@@ -738,7 +730,7 @@ func (s *server) sleepAfterSuccessfulWriteOperation(res responseWithErrorResult,
}
func (s *server) Update(ctx context.Context, req *resourcepb.UpdateRequest) (*resourcepb.UpdateResponse, error) {
ctx, span := s.tracer.Start(ctx, "storage_server.Update")
ctx, span := tracer.Start(ctx, "resource.server.Update")
defer span.End()
rsp := &resourcepb.UpdateResponse{}
@@ -812,7 +804,7 @@ func (s *server) update(ctx context.Context, user claims.AuthInfo, req *resource
}
func (s *server) Delete(ctx context.Context, req *resourcepb.DeleteRequest) (*resourcepb.DeleteResponse, error) {
ctx, span := s.tracer.Start(ctx, "storage_server.Delete")
ctx, span := tracer.Start(ctx, "resource.server.Delete")
defer span.End()
rsp := &resourcepb.DeleteResponse{}
@@ -983,7 +975,7 @@ func (s *server) read(ctx context.Context, user claims.AuthInfo, req *resourcepb
}
func (s *server) List(ctx context.Context, req *resourcepb.ListRequest) (*resourcepb.ListResponse, error) {
ctx, span := s.tracer.Start(ctx, "storage_server.List")
ctx, span := tracer.Start(ctx, "resource.server.List")
defer span.End()
// The history + trash queries do not yet support additional filters
-1
View File
@@ -62,7 +62,6 @@ func NewResourceServer(opts ServerOptions) (resource.ResourceServer, error) {
}
serverOptions := resource.ResourceServerOptions{
Tracer: opts.Tracer,
Blob: resource.BlobConfig{
URL: apiserverCfg.Key("blob_url").MustString(""),
},
+3 -3
View File
@@ -304,7 +304,7 @@ const (
type CloudWatchLogsQuery struct {
// Whether a query is a Metrics, Logs, or Annotations query
QueryMode CloudWatchQueryMode `json:"queryMode"`
// Whether a query is a Logs Insights or Logs Anomalies query
// Whether a query is a Logs Insights or Log Anomalies query
LogsMode *LogsMode `json:"logsMode,omitempty"`
Id string `json:"id"`
// AWS region to query for the logs
@@ -356,14 +356,14 @@ func NewLogGroup() *LogGroup {
return &LogGroup{}
}
// Shape of a Cloudwatch Logs Anomalies query
// Shape of a Cloudwatch Log Anomalies query
type CloudWatchLogsAnomaliesQuery struct {
Id string `json:"id"`
// AWS region to query for the logs
Region string `json:"region"`
// Whether a query is a Metrics, Logs or Annotations query
QueryMode *CloudWatchQueryMode `json:"queryMode,omitempty"`
// Whether a query is a Logs Insights or Logs Anomalies query
// Whether a query is a Logs Insights or Log Anomalies query
LogsMode *LogsMode `json:"logsMode,omitempty"`
// Filter to return only anomalies that are 'SUPPRESSED', 'UNSUPPRESSED', or 'ALL' (default)
SuppressionState *string `json:"suppressionState,omitempty"`
+1 -3
View File
@@ -26,9 +26,7 @@ const rowLimit = 1_000_000
type recordReader interface {
Next() bool
Schema() *arrow.Schema
//TODO: fix deprecation warning
//nolint:staticcheck
Record() arrow.Record
Record() arrow.RecordBatch
Err() error
}
+8 -24
View File
@@ -77,12 +77,8 @@ func TestNewQueryDataResponse(t *testing.T) {
arr = append(arr, tarr)
}
//TODO: fix deprecation warning
//nolint:staticcheck
record := array.NewRecord(schema, arr, -1)
//TODO: fix deprecation warning
//nolint:staticcheck
records := []arrow.Record{record}
record := array.NewRecordBatch(schema, arr, -1)
records := []arrow.RecordBatch{record}
reader, err := array.NewRecordReader(schema, records)
assert.NoError(t, err)
@@ -206,12 +202,8 @@ func TestNewQueryDataResponse_Error(t *testing.T) {
)
assert.NoError(t, err)
//TODO: fix deprecation warning
//nolint:staticcheck
record := array.NewRecord(schema, []arrow.Array{i64s, f64s}, -1)
//TODO: fix deprecation warning
//nolint:staticcheck
records := []arrow.Record{record}
record := array.NewRecordBatch(schema, []arrow.Array{i64s, f64s}, -1)
records := []arrow.RecordBatch{record}
reader, err := array.NewRecordReader(schema, records)
assert.NoError(t, err)
@@ -255,12 +247,8 @@ func TestNewQueryDataResponse_WideTable(t *testing.T) {
)
assert.NoError(t, err)
//TODO: fix deprecation warning
//nolint:staticcheck
record := array.NewRecord(schema, []arrow.Array{times, strs, i64s}, -1)
//TODO: fix deprecation warning
//nolint:staticcheck
records := []arrow.Record{record}
record := array.NewRecordBatch(schema, []arrow.Array{times, strs, i64s}, -1)
records := []arrow.RecordBatch{record}
reader, err := array.NewRecordReader(schema, records)
assert.NoError(t, err)
@@ -534,12 +522,8 @@ func TestCustomMetadata(t *testing.T) {
)
assert.NoError(t, err)
//TODO: fix deprecation warning
//nolint:staticcheck
record := array.NewRecord(schema, []arrow.Array{i64s}, -1)
//TODO: fix deprecation warning
//nolint:staticcheck
records := []arrow.Record{record}
record := array.NewRecordBatch(schema, []arrow.Array{i64s}, -1)
records := []arrow.RecordBatch{record}
reader, err := array.NewRecordReader(schema, records)
assert.NoError(t, err)
+2 -7
View File
@@ -196,9 +196,7 @@ func arrowToNestedSetDataFrame(flamegraph *v1alpha1.FlamegraphArrow) (*data.Fram
defer arrowReader.Release()
arrowReader.Next()
//TODO: fix deprecation warning
//nolint:staticcheck
rec := arrowReader.Record()
rec := arrowReader.RecordBatch()
fi, err := newFlamegraphIterator(rec)
if err != nil {
@@ -238,10 +236,7 @@ type flamegraphIterator struct {
addressBuilder *bytes.Buffer
}
// TODO: fix deprecation warning
//
//nolint:staticcheck
func newFlamegraphIterator(rec arrow.Record) (*flamegraphIterator, error) {
func newFlamegraphIterator(rec arrow.RecordBatch) (*flamegraphIterator, error) {
schema := rec.Schema()
columnChildren := rec.Column(schema.FieldIndices(FlamegraphFieldChildren)[0]).(*array.List)
+1 -3
View File
@@ -278,9 +278,7 @@ func flamegraphResponse() *connect.Response[v1alpha1.QueryResponse] {
builderFlat.Append(columns.flat[i])
}
//TODO: fix deprecation warning
//nolint:staticcheck
record := array.NewRecord(
record := array.NewRecordBatch(
arrow.NewSchema(fields, nil),
[]arrow.Array{
builderLocationAddress.NewArray(),
@@ -1,4 +1,4 @@
import { BooleanFieldSettings } from '@react-awesome-query-builder/ui';
import type { BooleanFieldSettings } from '@react-awesome-query-builder/ui';
import {
FieldConfigPropertyItem,
@@ -165,11 +165,11 @@ export default function AlertmanagerConfig({ alertmanagerName, onDismiss, onSave
{isLoadingSuccessful && (
<div className={styles.content}>
<AutoSizer>
{({ height, width }) => (
<AutoSizer disableWidth>
{({ height }) => (
<CodeEditor
language="json"
width={width}
width="100%"
height={height}
showLineNumbers={true}
monacoOptions={{
@@ -1,4 +1,4 @@
import { css } from '@emotion/css';
import { css, cx } from '@emotion/css';
import { t, Trans } from '@grafana/i18n';
import { Badge, Card, Grid, Text, TextLink, useStyles2 } from '@grafana/ui';
@@ -17,6 +17,8 @@ export function RepositoryPullStatusCard({ repo }: { repo: Repository }) {
const statusColor = getStatusColor(status?.sync.state);
const statusIcon = getStatusIcon(status?.sync.state);
const isWorking = status?.sync.state === 'working' || status?.sync.state === 'pending';
const { url: lastCommitUrl, hasUrl } = getRepoCommitUrl(repo.spec, status?.sync.lastRef);
return (
@@ -42,45 +44,45 @@ export function RepositoryPullStatusCard({ repo }: { repo: Repository }) {
<Text variant="body">{status?.sync.job ?? 'N/A'}</Text>
</div>
{/* Last Ref */}
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.last-ref">Last Ref:</Trans>
</Text>
<div className={styles.spanTwo}>
{hasUrl && lastCommitUrl ? (
<TextLink href={lastCommitUrl} external>
<div className={cx(styles.historicalData, { [styles.historicalDataOverlay]: isWorking })}>
{/* Last Ref */}
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.last-ref">Last Ref:</Trans>
</Text>
<div className={styles.spanTwo}>
{hasUrl && lastCommitUrl ? (
<TextLink href={lastCommitUrl} external>
{status?.sync.lastRef
? status.sync.lastRef.substring(0, 7)
: t('provisioning.repository-overview.not-available', 'N/A')}
</TextLink>
) : (
<Text variant="body">
{status?.sync.lastRef
? status.sync.lastRef.substring(0, 7)
: t('provisioning.repository-overview.not-available', 'N/A')}
</Text>
</TextLink>
) : (
<Text variant="body">
{status?.sync.lastRef
? status.sync.lastRef.substring(0, 7)
: t('provisioning.repository-overview.not-available', 'N/A')}
</Text>
)}
</div>
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.finished">Last successful pull:</Trans>
</Text>
<div className={styles.spanTwo}>
<Text variant="body">{formatTimestamp(status?.sync.finished)}</Text>
</div>
{!!status?.sync?.message?.length && (
<>
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.messages">Messages:</Trans>
</Text>
<div className={styles.spanTwo}>
<MessageList messages={status.sync.message} variant="body" />
</div>
</>
)}
</div>
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.finished">Last successful pull:</Trans>
</Text>
<div className={styles.spanTwo}>
<Text variant="body">{formatTimestamp(status?.sync.finished)}</Text>
</div>
{!!status?.sync?.message?.length && (
<>
<Text color="secondary">
<Trans i18nKey="provisioning.repository-overview.messages">Messages:</Trans>
</Text>
<div className={styles.spanTwo}>
<MessageList messages={status.sync.message} variant="body" />
</div>
</>
)}
</Grid>
</Card.Description>
<Card.Actions>
@@ -95,5 +97,13 @@ const getStyles = () => {
spanTwo: css({
gridColumn: 'span 2',
}),
historicalData: css({
gridColumn: '1 / -1',
display: 'grid',
gridTemplateColumns: 'subgrid',
}),
historicalDataOverlay: css({
opacity: 0.6,
}),
};
};
@@ -25,7 +25,7 @@ const logsQueryLanguageOptions: Array<SelectableValue<LogsQueryLanguage>> = [
const logsModeOptions: Array<SelectableValue<LogsMode>> = [
{ label: 'Logs Insights', value: LogsMode.Insights },
{ label: 'Logs Anomalies', value: LogsMode.Anomalies },
{ label: 'Log Anomalies', value: LogsMode.Anomalies },
];
export const CloudWatchLogsQueryEditor = memo(function CloudWatchLogsQueryEditor(props: Props) {
@@ -155,7 +155,7 @@ composableKinds: DataQuery: {
// Whether a query is a Metrics, Logs, or Annotations query
queryMode: #CloudWatchQueryMode
// Whether a query is a Logs Insights or Logs Anomalies query
// Whether a query is a Logs Insights or Log Anomalies query
logsMode?: #LogsMode
id: string
// AWS region to query for the logs
@@ -173,7 +173,7 @@ composableKinds: DataQuery: {
queryLanguage?: #LogsQueryLanguage
} @cuetsy(kind="interface")
// Shape of a Cloudwatch Logs Anomalies query
// Shape of a Cloudwatch Log Anomalies query
#CloudWatchLogsAnomaliesQuery: {
common.DataQuery
id: string
@@ -181,7 +181,7 @@ composableKinds: DataQuery: {
region: string
// Whether a query is a Metrics, Logs or Annotations query
queryMode?: #CloudWatchQueryMode
// Whether a query is a Logs Insights or Logs Anomalies query
// Whether a query is a Logs Insights or Log Anomalies query
logsMode?: #LogsMode
// Filter to return only anomalies that are 'SUPPRESSED', 'UNSUPPRESSED', or 'ALL' (default)
suppressionState?: string
+3 -3
View File
@@ -245,7 +245,7 @@ export interface CloudWatchLogsQuery extends common.DataQuery {
*/
logGroups?: Array<LogGroup>;
/**
* Whether a query is a Logs Insights or Logs Anomalies query
* Whether a query is a Logs Insights or Log Anomalies query
*/
logsMode?: LogsMode;
/**
@@ -273,7 +273,7 @@ export const defaultCloudWatchLogsQuery: Partial<CloudWatchLogsQuery> = {
};
/**
* Shape of a Cloudwatch Logs Anomalies query
* Shape of a Cloudwatch Log Anomalies query
*/
export interface CloudWatchLogsAnomaliesQuery extends common.DataQuery {
/**
@@ -282,7 +282,7 @@ export interface CloudWatchLogsAnomaliesQuery extends common.DataQuery {
anomalyDetectionARN?: string;
id: string;
/**
* Whether a query is a Logs Insights or Logs Anomalies query
* Whether a query is a Logs Insights or Log Anomalies query
*/
logsMode?: LogsMode;
/**
@@ -461,7 +461,7 @@ describe('datasource', () => {
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).metricEditorMode).toEqual(
MetricEditorMode.Builder
);
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).matchExact).toEqual(true);
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).matchExact).toEqual(false);
});
it('should set default values from logs query', () => {
const defaultLogGroups = [{ name: 'logName', arn: 'logARN' }];
@@ -24,7 +24,7 @@ export const DEFAULT_METRICS_QUERY: Omit<CloudWatchMetricsQuery, 'refId'> = {
metricEditorMode: MetricEditorMode.Builder,
sql: undefined,
sqlExpression: '',
matchExact: true,
matchExact: false,
};
export const DEFAULT_ANNOTATIONS_QUERY: Omit<CloudWatchAnnotationQuery, 'refId'> = {
@@ -17,7 +17,8 @@ export const SORT = 'sort';
export const LIMIT = 'limit';
export const PARSE = 'parse';
export const DEDUP = 'dedup';
export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP, DIFF];
export const ANOMALY = 'anomaly';
export const LOGS_COMMANDS = [DISPLAY, FIELDS, FILTER, PATTERN, STATS, SORT, LIMIT, PARSE, DEDUP, DIFF, ANOMALY];
export const LOGS_LOGIC_OPERATORS = ['and', 'or', 'not'];
@@ -823,7 +823,7 @@ const stopQueryResponseStub = {
const anomaliesQueryResponse: DataQueryResponse = {
data: [
{
name: 'Logs anomalies',
name: 'Log anomalies',
refId: 'A',
meta: {
preferredVisualisationType: 'table',
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 128C256 119.2 263.2 112 272 112C280.8 112 288 119.2 288 128L288 264C288 274.3 294.6 283.5 304.4 286.8C314.2 290.1 325 286.7 331.2 278.5C334.2 274.6 338.8 272.1 344 272.1C352.8 272.1 360 279.3 360 288.1C360 298.4 366.6 307.6 376.4 310.9C386.2 314.2 397 310.8 403.2 302.6C406.2 298.7 410.8 296.2 416 296.2C423.8 296.2 430.3 301.8 431.7 309.2C433.3 317.4 439 324.3 446.8 327.2C454.6 330.1 463.5 328.8 470.1 323.6C472.8 321.5 476.2 320.2 480 320.2C488.8 320.2 496 327.4 496 336.2L496 456.2C496 496 463.8 528.2 424 528.2L307.4 528.2C270 528.2 235 509.5 214.2 478.3L146.7 376.9C141.8 369.5 143.8 359.6 151.1 354.7C158.4 349.8 168.4 351.8 173.3 359.1L212 417.2C217.9 426 228.8 429.9 238.9 426.9C249 423.9 255.9 414.5 255.9 403.9L256 128zM272 64C236.7 64 208 92.7 208 128L208 325.7C187.2 302 151.5 296.8 124.5 314.7C95.1 334.4 87.1 374.1 106.8 403.5L174.3 504.8C204 549.3 253.9 576 307.4 576L424 576C490.3 576 544 522.3 544 456L544 336C544 300.7 515.3 272 480 272C475.5 272 471.2 272.5 467 273.3C455.3 257.9 436.8 248 416 248C409.1 248 402.5 249.1 396.3 251.1C384.7 234.7 365.6 224 344 224C341.3 224 338.6 224.2 336 224.5L336 128C336 92.7 307.3 64 272 64zM320 368C320 359.2 312.8 352 304 352C295.2 352 288 359.2 288 368L288 464C288 472.8 295.2 480 304 480C312.8 480 320 472.8 320 464L320 368zM368 352C359.2 352 352 359.2 352 368L352 464C352 472.8 359.2 480 368 480C376.8 480 384 472.8 384 464L384 368C384 359.2 376.8 352 368 352zM448 368C448 359.2 440.8 352 432 352C423.2 352 416 359.2 416 368L416 464C416 472.8 423.2 480 432 480C440.8 480 448 472.8 448 464L448 368z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+66 -11
View File
@@ -3397,17 +3397,17 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/plugin-e2e@npm:2.1.7":
version: 2.1.7
resolution: "@grafana/plugin-e2e@npm:2.1.7"
"@grafana/plugin-e2e@npm:^3.0.1":
version: 3.0.1
resolution: "@grafana/plugin-e2e@npm:3.0.1"
dependencies:
"@grafana/e2e-selectors": "npm:^12.1.0-254610"
"@grafana/e2e-selectors": "npm:^12.2.0-255920"
semver: "npm:^7.5.4"
uuid: "npm:^11.0.2"
uuid: "npm:^13.0.0"
yaml: "npm:^2.3.4"
peerDependencies:
"@playwright/test": ^1.52.0
checksum: 10/4e14bda1c6304c89a1ffca972dda18d3ab1fad59ee30e6735112d75f7c5c40856091e2e17f46451aec5261b6368f67c245edb112072d98c9067723f4ca2c71c3
checksum: 10/1cd95c1e4365f93e884d7ae4d77961e038cd4fcc235e313dd9f96bd0e21090c5aa26a765bedf45cc13cc0dc913da79007172fd9680088b356ef485672ce34681
languageName: node
linkType: hard
@@ -3655,7 +3655,8 @@ __metadata:
"@grafana/plugin-ui": "npm:^0.10.10"
"@grafana/runtime": "npm:12.4.0-pre"
"@grafana/ui": "npm:12.4.0-pre"
"@react-awesome-query-builder/ui": "npm:6.6.15"
"@react-awesome-query-builder/core": "npm:^6.7.0-alpha.0"
"@react-awesome-query-builder/ui": "npm:^6.7.0-alpha.0"
"@testing-library/dom": "npm:10.4.1"
"@testing-library/jest-dom": "npm:^6.1.2"
"@testing-library/react": "npm:16.3.0"
@@ -6936,7 +6937,24 @@ __metadata:
languageName: node
linkType: hard
"@react-awesome-query-builder/ui@npm:6.6.15, @react-awesome-query-builder/ui@npm:^6.6.4":
"@react-awesome-query-builder/core@npm:^6.7.0-alpha.0":
version: 6.7.0-alpha.0
resolution: "@react-awesome-query-builder/core@npm:6.7.0-alpha.0"
dependencies:
"@babel/runtime": "npm:^7.27.0"
clone: "npm:^2.1.2"
i18next: "npm:^23.11.5"
immutable: "npm:^4.3.6"
json-logic-js: "npm:^2.0.2"
lodash: "npm:^4.17.21"
moment: "npm:^2.30.1"
spel2js: "npm:^0.2.8"
sqlstring: "npm:^2.3.3"
checksum: 10/2039cc283204567864a01ee49b44fae52e7ddc5f14cdf9e5e5f0728c483a7afa0d4c05d364a4e50413045d2dd0f5c713508dadc34e0f98ad371fcd5cfde86862
languageName: node
linkType: hard
"@react-awesome-query-builder/ui@npm:^6.6.4":
version: 6.6.15
resolution: "@react-awesome-query-builder/ui@npm:6.6.15"
dependencies:
@@ -6954,6 +6972,26 @@ __metadata:
languageName: node
linkType: hard
"@react-awesome-query-builder/ui@npm:^6.7.0-alpha.0":
version: 6.7.0-alpha.0
resolution: "@react-awesome-query-builder/ui@npm:6.7.0-alpha.0"
dependencies:
"@babel/runtime": "npm:^7.27.0"
"@react-awesome-query-builder/core": "npm:^6.7.0-alpha.0"
chroma-js: "npm:^3.1.2"
classnames: "npm:^2.5.1"
lodash: "npm:^4.17.21"
prop-types: "npm:^15.8.1"
react-number-format: "npm:^5.0.0"
react-redux: "npm:^8.1.3"
redux: "npm:^4.2.1"
peerDependencies:
react: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
react-dom: ^16.8.4 || ^17.0.1 || ^18.0.0 || ^19.0.0
checksum: 10/1f39a807672a94af219c58638f67b5cf9b0e43523c0c5718a12cba677e86d1b669c5715fe8920448139d35bc17b73b168cd581fd4d5bbbf0aaeec1780c58bceb
languageName: node
linkType: hard
"@react-stately/flags@npm:^3.1.2":
version: 3.1.2
resolution: "@react-stately/flags@npm:3.1.2"
@@ -13410,6 +13448,13 @@ __metadata:
languageName: node
linkType: hard
"chroma-js@npm:^3.1.2":
version: 3.1.2
resolution: "chroma-js@npm:3.1.2"
checksum: 10/ea09b27d04b477a8fdc3314bfa6dc8de05feab47999ff1acdeba6cf3c32c76338baa5b4562d949d33d8adeca57754e5ea591c15b9985d65583c8771e7e07c2ef
languageName: node
linkType: hard
"chrome-remote-interface@npm:0.33.3":
version: 0.33.3
resolution: "chrome-remote-interface@npm:0.33.3"
@@ -18861,7 +18906,7 @@ __metadata:
"@grafana/llm": "npm:0.22.1"
"@grafana/monaco-logql": "npm:^0.0.8"
"@grafana/o11y-ds-frontend": "workspace:*"
"@grafana/plugin-e2e": "npm:2.1.7"
"@grafana/plugin-e2e": "npm:^3.0.1"
"@grafana/plugin-ui": "npm:^0.10.10"
"@grafana/prometheus": "workspace:*"
"@grafana/runtime": "workspace:*"
@@ -18896,7 +18941,8 @@ __metadata:
"@react-aria/focus": "npm:3.21.2"
"@react-aria/overlays": "npm:3.30.0"
"@react-aria/utils": "npm:3.31.0"
"@react-awesome-query-builder/ui": "npm:6.6.15"
"@react-awesome-query-builder/core": "npm:^6.7.0-alpha.0"
"@react-awesome-query-builder/ui": "npm:^6.7.0-alpha.0"
"@react-types/button": "npm:3.13.0"
"@react-types/menu": "npm:3.10.3"
"@react-types/overlays": "npm:3.9.0"
@@ -32996,7 +33042,7 @@ __metadata:
languageName: node
linkType: hard
"uuid@npm:11.1.0, uuid@npm:^11.0.0, uuid@npm:^11.0.2, uuid@npm:^11.0.5":
"uuid@npm:11.1.0, uuid@npm:^11.0.0, uuid@npm:^11.0.5":
version: 11.1.0
resolution: "uuid@npm:11.1.0"
bin:
@@ -33014,6 +33060,15 @@ __metadata:
languageName: node
linkType: hard
"uuid@npm:^13.0.0":
version: 13.0.0
resolution: "uuid@npm:13.0.0"
bin:
uuid: dist-node/bin/uuid
checksum: 10/2742b24d1e00257e60612572e4d28679423469998cafbaf1fe9f1482e3edf9c40754b31bfdb3d08d71b29239f227a304588f75210b3b48f2609f0673f1feccef
languageName: node
linkType: hard
"uuid@npm:^8.3.2":
version: 8.3.2
resolution: "uuid@npm:8.3.2"