Compare commits

..

30 Commits

Author SHA1 Message Date
Kevin Minehart
a909c838b5 [release-12.0.5] sync packaging w/ main (#111495)
sync packaging w/ main
2025-09-23 14:22:39 -05:00
Kevin Minehart
8e1063cf0a update missing npm publish scripts
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Build Release Packages / publish-dockerhub (push) Has been cancelled
Build Release Packages / Publish NPM canaries (push) Has been cancelled
Build Release Packages / notify-pr (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
2025-09-23 10:29:55 -05:00
Kevin Minehart
26c1134326 update release-npm and validate script 2025-09-23 09:52:46 -05:00
grafana-delivery-bot[bot]
31c4188746 [release-12.0.5] search: Force index IDX_dashboard_title when searching dashboards (#111418)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Build Release Packages / publish-dockerhub (push) Has been cancelled
Build Release Packages / Publish NPM canaries (push) Has been cancelled
Build Release Packages / notify-pr (push) Has been cancelled
search: Force index IDX_dashboard_title when searching dashboards (#110595)


(cherry picked from commit 726c7ba71b)

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
Co-authored-by: maicon <maiconscosta@gmail.com>
2025-09-19 16:47:23 -03:00
grafana-delivery-bot[bot]
da3717b315 [release-12.0.5] Page limit config for dashboards with visible annotations (#111377)
* Page limit config for dashboards with visible annotations (#110911)

* Page limit config for dashboards with visible annotations

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

---------

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
(cherry picked from commit 77fa3333e4)

* Add missing parameter to TestIntegrationAuthorize

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>

---------

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
Co-authored-by: maicon <maiconscosta@gmail.com>
2025-09-19 15:29:49 -03:00
grafana-delivery-bot[bot]
3c845fd337 [release-12.0.5] Add permission role_id action index (#111409)
Add permission role_id action index (#110125)

* Add permission role_id action index



* Drop permission role_id index



---------


(cherry picked from commit 99fc606e17)

Signed-off-by: Maicon Costa <maiconscosta@gmail.com>
Co-authored-by: maicon <maiconscosta@gmail.com>
2025-09-19 15:29:18 -03:00
Josh Hunt
af54287df1 [release-12.0.5] CI: Backport release-npm.yml (#111393)
backport release-npm.yml from main
2025-09-19 18:32:03 +01:00
Nathan Vērzemnieks
560be84dc8 [release-12.0.5] Backport aws-sdk-go-v2 update and subsequent fixes (#111243)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Build Release Packages / publish-dockerhub (push) Has been cancelled
Build Release Packages / Publish NPM canaries (push) Has been cancelled
Build Release Packages / notify-pr (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Trivy Scan / trivy-scan (push) Has been cancelled
* CloudWatch: Backport aws-sdk-go-v2 update from external plugin (#107136)

(cherry picked from a18ea34688)

* Datasources: Update grafana-aws-sdk for new sigv4 middleware and aws-sdk-go v1 removal (#107522)

(cherry picked from commit 66d9a33cc9)

* CloudWatch: Fix proxy transport issue (#107807)

(cherry picked from c3eeb1fcd9)

* CloudWatch: Fix http client handling + assume role bug (#107893)

(cherry picked from commit f34a9fc0c2)

* CloudWatch: Use default region when query region is unset (#109089)

(cherry picked from commit 5f4097a159)

* CloudWatch: Fix handling region for legacy alerts (#109217)

(cherry picked from commit 2bf9aea8ef)

* Update go.mod owners

---------

Co-authored-by: Isabella Siu <Isabella.siu@grafana.com>
2025-09-19 12:46:40 +02:00
grafana-delivery-bot[bot]
6b87718cde [release-12.0.5] CI: Fix NPM workflow inputs (#111348)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Build Release Packages / publish-dockerhub (push) Has been cancelled
Build Release Packages / Publish NPM canaries (push) Has been cancelled
Build Release Packages / notify-pr (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Fix referring to inputs (#111345)

(cherry picked from commit 1d6c1da94f)

Co-authored-by: Josh Hunt <joshhunt@users.noreply.github.com>
2025-09-18 22:28:05 +00:00
Kevin Minehart
0e620047b5 [release-12.0.5] backport bump-version.yml and release-build.yml (#111344) 2025-09-18 21:57:42 +00:00
grafana-delivery-bot[bot]
c0e87eeb98 [release-12.0.5] Chore: Improve short url redirection (#111180)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Chore: Improve short url redirection (#111162)

Improve short url redirection

(cherry picked from commit 81fe57478f)

Co-authored-by: Misi <mgyongyosi@users.noreply.github.com>
2025-09-16 15:44:11 +02:00
Misi
550a339cab [release-12.0.5] Fix: Fix redirection after login when Grafana is served from subpath (#111156)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Fix: Fix redirection after login when Grafana is served from subpath (#110889)

Fix short link (/goto) redirection when Grafana is served from subpath

(cherry picked from commit ccc87a03f0)
2025-09-16 09:35:23 +02:00
John Troy
27a095a971 [release-12.0.5] LDAP: Restore test user mapping functionality (#111130)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
LDAP: Restore test user mapping functionality (#110841)

* Migrate LdapPage from connect() to React-Redux hooks

* Convert LDAP debug page into a drawer and hook it into settings

* prettier

* Use the Text component and make the input and button look like they do on the main settings page.

* Bring back isLoading and put in a LoadingPlaceholder

* i18n-extract

* rejigger

* linter fix

(cherry picked from commit 585b53bc7d)
2025-09-15 15:28:45 -04:00
Will Browne
394f5d8ce4 Plugins: StaticFS should implement FSRemover (#110706) (#110945)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Trivy Scan / trivy-scan (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
* Plugins: StaticFS should implement FSRemover (#110706)

make staticfs implement fs removal interface

* fix test

* fix build

* undo test remove
2025-09-11 14:44:34 +01:00
Alex Spencer
323a4a24fa [release-12.0.5] CI: pin dagger version to match go.mod (#110643)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
CI: pin dagger version to match go.mod (#110638)

* pin dagger version to match go.mod

* set in e2e too

(cherry picked from commit 4810e51743)

Co-authored-by: Kevin Minehart <5140827+kminehart@users.noreply.github.com>
2025-09-05 12:45:39 +02:00
Jack Westbrook
cd1a52454f [release-12.0.5] Fix: Prevent Rollup from treeshaking NPM packages (#110523)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Fix: Prevent Rollup from treeshaking NPM packages (#108567)

fix(packages): prevent rollup from treeshaking libraries

(cherry picked from commit 15d9df93f9)
2025-09-04 09:08:05 +02:00
William Wernert
58dd6d242a Alerting: Bump alerting package to include change to NewTLSClient (#110528)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Trivy Scan / trivy-scan (push) Has been cancelled
Bump alerting package to include change to NewTLSClient

This includes the changes from
https://github.com/grafana/alerting/pull/374
2025-09-03 10:50:27 -04:00
Moustafa Baiou
0d2ee90ff1 Alerting: Fix copying of recording rule fields
Recording rule fields were not being copied correctly when duplicating an alert rule. This manifests as missing `TargetDataSourceUID` fields from the `Record` part of the rule when rules in a group are re-ordered.

Added some additional tests to ensure we cover the generation of recording rules in tests and fixed the copying logic to ensure all fields are copied correctly.

(cherry picked from commit c73b3ccf6e)
2025-09-03 10:00:22 -04:00
grafana-delivery-bot[bot]
39ca690c5a [release-12.0.5] Forbid more redirect patterns (#110504) 2025-09-03 11:41:06 +02:00
grafana-delivery-bot[bot]
1bac5badac [release-12.0.5] Docs: Modify Go installation command for foundation SDK (#110419)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
publish-technical-documentation-release / sync (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Co-authored-by: Irene Rodríguez <irene.rodriguez@grafana.com>
2025-09-01 15:56:31 +00:00
grafana-delivery-bot[bot]
b0356011e3 [release-12.0.5] Fix link to site which is no longer relevant (#110286)
Fix link to site which is no longer relevant (#110214)

Fix link to abandoned web page

Wikipedia isn't likely to get hijacked in the same way.

(cherry picked from commit 17f6b31e8c)

Co-authored-by: Jack Baldry <jack.baldry@grafana.com>
2025-08-29 08:57:40 +01:00
Matheus Macabu
bcc78ed4eb [release-12.0.5] Dependencies: Bump github.com/go-viper/mapstructure/v2 to 2.4.0 (#110204)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Trivy Scan / trivy-scan (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Dependencies: Bump github.com/go-viper/mapstructure/v2 to 2.4.0
2025-08-27 11:28:19 +02:00
Matheus Macabu
cc150e5bb3 [release-12.0.5] Dependencies(js): Update sha.js to 2.4.12 (#110168)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Dependencies(js): Update sha.js to 2.4.12

(cherry picked from commit 474e81efd8)
2025-08-26 17:43:00 +02:00
Yuri Tseretyan
b53d333cbf [release-12.0.5] Alerting: Update alerting module (#110000)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Trivy Scan / trivy-scan (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
update alerting module to ce29dcf7efd3
2025-08-25 10:30:03 -04:00
Matheus Macabu
5ac3d0c76a [release-12.0.5] Auditing: Document new options for recording datasource query request/response body (#109980)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
publish-technical-documentation-release / sync (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Auditing: Document new options for recording datasource query request/response body (#109951)

(cherry picked from commit 91748fe115)
2025-08-21 17:33:15 +02:00
Victor Marin
cd1e08734c [release-12.0.5] Dashboard: Resume tracking changes after save from JSON model page (#109795)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Dashboard: Resume tracking changes after save from JSON model page (#109607)

(cherry picked from commit e285cc2ddc)

Co-authored-by: Bogdan Matei <bogdan.matei@grafana.com>
2025-08-19 10:28:00 +03:00
grafana-delivery-bot[bot]
5195a6d72e [release-12.0.3] Azure: Fix time management field (#108481)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
* Azure: Fix time management field (#107993)

* Fix mode setting

* Update selector

* Add tests

* Fix condition

(cherry picked from commit a421f55cd5)

* Trigger build

---------

Co-authored-by: Andreas Christou <andreas.christou@grafana.com>
2025-08-18 18:25:08 +01:00
Andreas Christou
ab1e43b8b9 [release-12.0.5] Azure: Fix logs editor rendering (#109667)
* Azure: Fix logs editor rendering (#109491)

* Fix logs editor rendering

- Add test

* Type fixes

* Fix schema conditions and add test

* Fix lint

* Update public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/TimeManagement.tsx

Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com>

* Lint

---------

Co-authored-by: Adam Yeats <16296989+adamyeats@users.noreply.github.com>
(cherry picked from commit b34642b188)

# Conflicts:
#	public/app/plugins/datasource/azuremonitor/components/LogsQueryEditor/LogsQueryEditor.test.tsx

* Mock error and warn
2025-08-18 17:37:05 +01:00
Matheus Macabu
560f875837 [release-12.0.5] Build: Add -buildvcs=false flag to go build (#109673)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Code Checks / Validate Backend Configs (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
CodeQL checks / Analyze (actions) (push) Has been cancelled
CodeQL checks / Analyze (go) (push) Has been cancelled
CodeQL checks / Analyze (javascript) (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
golangci-lint / lint-go (push) Has been cancelled
Build: Add -buildvcs=false flag to go build

(cherry picked from commit ecbe0bdaf6)
2025-08-18 17:39:01 +02:00
Isabel Matwawana
791f3f3c1d [release-12.0.5] Docs: Remove enterprise product label and add more notes (#109759)
Some checks failed
Actionlint / Lint GitHub Actions files (push) Has been cancelled
Backend Unit Tests / Detect whether code changed (push) Has been cancelled
Lint Frontend / Detect whether code changed (push) Has been cancelled
Verify i18n / verify-i18n (push) Has been cancelled
End-to-end tests / Detect whether code changed (push) Has been cancelled
Frontend tests / Detect whether code changed (push) Has been cancelled
Integration Tests / Sqlite (1/8) (push) Has been cancelled
Integration Tests / Sqlite (2/8) (push) Has been cancelled
Integration Tests / Sqlite (3/8) (push) Has been cancelled
Integration Tests / Sqlite (4/8) (push) Has been cancelled
Integration Tests / Sqlite (5/8) (push) Has been cancelled
Integration Tests / Sqlite (6/8) (push) Has been cancelled
Integration Tests / Sqlite (7/8) (push) Has been cancelled
Integration Tests / Sqlite (8/8) (push) Has been cancelled
Integration Tests / MySQL (1/8) (push) Has been cancelled
Integration Tests / MySQL (2/8) (push) Has been cancelled
Integration Tests / MySQL (3/8) (push) Has been cancelled
Integration Tests / MySQL (4/8) (push) Has been cancelled
Integration Tests / MySQL (5/8) (push) Has been cancelled
Integration Tests / MySQL (6/8) (push) Has been cancelled
Integration Tests / MySQL (7/8) (push) Has been cancelled
Integration Tests / MySQL (8/8) (push) Has been cancelled
Integration Tests / Postgres (1/8) (push) Has been cancelled
Integration Tests / Postgres (2/8) (push) Has been cancelled
Integration Tests / Postgres (3/8) (push) Has been cancelled
Integration Tests / Postgres (4/8) (push) Has been cancelled
Integration Tests / Postgres (5/8) (push) Has been cancelled
Integration Tests / Postgres (6/8) (push) Has been cancelled
Integration Tests / Postgres (7/8) (push) Has been cancelled
Integration Tests / Postgres (8/8) (push) Has been cancelled
publish-technical-documentation-release / sync (push) Has been cancelled
Reject GitHub secrets / reject-gh-secrets (push) Has been cancelled
Build Release Packages / setup (push) Has been cancelled
Run dashboard schema v2 e2e / dashboard-schema-v2-e2e (push) Has been cancelled
Shellcheck / Shellcheck scripts (push) Has been cancelled
Swagger generated code / Verify committed API specs match (push) Has been cancelled
Dispatch sync to mirror / dispatch-job (push) Has been cancelled
Backend Unit Tests / Grafana (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana (8/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (1/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (2/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (3/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (4/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (5/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (6/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (7/8) (push) Has been cancelled
Backend Unit Tests / Grafana Enterprise (8/8) (push) Has been cancelled
Backend Unit Tests / All backend unit tests complete (push) Has been cancelled
Lint Frontend / Lint (push) Has been cancelled
Lint Frontend / Typecheck (push) Has been cancelled
Lint Frontend / Betterer (push) Has been cancelled
End-to-end tests / Build & Package Grafana (push) Has been cancelled
End-to-end tests / Build E2E test runner (push) Has been cancelled
End-to-end tests / dashboards-suite (old arch) (push) Has been cancelled
End-to-end tests / panels-suite (old arch) (push) Has been cancelled
End-to-end tests / smoke-tests-suite (old arch) (push) Has been cancelled
End-to-end tests / various-suite (old arch) (push) Has been cancelled
End-to-end tests / dashboards-suite (push) Has been cancelled
End-to-end tests / panels-suite (push) Has been cancelled
End-to-end tests / smoke-tests-suite (push) Has been cancelled
End-to-end tests / various-suite (push) Has been cancelled
End-to-end tests / A11y test (push) Has been cancelled
End-to-end tests / All E2E tests complete (push) Has been cancelled
Frontend tests / Unit tests (1 / 8) (push) Has been cancelled
Frontend tests / Unit tests (2 / 8) (push) Has been cancelled
Frontend tests / Unit tests (3 / 8) (push) Has been cancelled
Frontend tests / Unit tests (4 / 8) (push) Has been cancelled
Frontend tests / Unit tests (5 / 8) (push) Has been cancelled
Frontend tests / Unit tests (6 / 8) (push) Has been cancelled
Frontend tests / Unit tests (7 / 8) (push) Has been cancelled
Frontend tests / Unit tests (8 / 8) (push) Has been cancelled
Frontend tests / All frontend unit tests complete (push) Has been cancelled
Integration Tests / All backend integration tests complete (push) Has been cancelled
Build Release Packages / Dispatch grafana-enterprise build (push) Has been cancelled
Build Release Packages / / darwin-amd64 (push) Has been cancelled
Build Release Packages / / darwin-arm64 (push) Has been cancelled
Build Release Packages / / linux-amd64 (push) Has been cancelled
Build Release Packages / / linux-armv6 (push) Has been cancelled
Build Release Packages / / linux-armv7 (push) Has been cancelled
Build Release Packages / / linux-arm64 (push) Has been cancelled
Build Release Packages / / linux-s390x (push) Has been cancelled
Build Release Packages / / windows-amd64 (push) Has been cancelled
Build Release Packages / / windows-arm64 (push) Has been cancelled
Build Release Packages / Upload artifacts (push) Has been cancelled
2025-08-15 17:29:48 -04:00
170 changed files with 4169 additions and 3944 deletions

View File

@@ -65,7 +65,7 @@ require (
github.com/go-toolsmith/astp v1.1.0 // indirect
github.com/go-toolsmith/strparse v1.1.0 // indirect
github.com/go-toolsmith/typep v1.1.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.12.1 // indirect

View File

@@ -142,8 +142,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=

View File

@@ -18,7 +18,7 @@ require (
github.com/evilmartians/lefthook v1.4.8 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect

View File

@@ -29,8 +29,8 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=

View File

@@ -24,7 +24,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/handlers v1.5.2 // indirect

View File

@@ -41,8 +41,8 @@ github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3Bum
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37 h1:KFcZmKdZmapAog2+eL1buervAYrYolBZk7fMecPPDmo=
github.com/go-swagger/go-swagger v0.30.6-0.20240310114303-db51e79a0e37/go.mod h1:i1/E+d8iPNReSE7y04FaVu5OPKB3il5cn+T1Egogg3I=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=

View File

@@ -139,6 +139,7 @@ runs:
with:
verb: run
dagger-flags: --verbose=0
version: 0.18.8
args: go run -C ${GRAFANA_PATH} ./pkg/build/cmd artifacts --artifacts ${ARTIFACTS} --grafana-dir=${GRAFANA_PATH} --alpine-base=${ALPINE_BASE} --ubuntu-base=${UBUNTU_BASE} --enterprise-dir=${ENTERPRISE_PATH} --version=${VERSION} --patches-repo=${PATCHES_REPO} --patches-ref=${PATCHES_REF} --patches-path=${PATCHES_PATH} --build-id=${BUILD_ID} --tag-format="${TAG_FORMAT}" --ubuntu-tag-format="${UBUNTU_TAG_FORMAT}" --org=${DOCKER_ORG} --registry=${DOCKER_REGISTRY} --checksum=${CHECKSUM} --verify=${VERIFY} > $OUTFILE
- id: output
shell: bash

View File

@@ -13,17 +13,29 @@ on:
required: false
permissions:
contents: write
pull-requests: write
id-token: write
contents: read
jobs:
bump-version:
runs-on: ubuntu-latest
steps:
- name: Checkout Grafana
uses: actions/checkout@v4
- uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
persist-credentials: false
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"contents": "write", "pull_requests": "write", "workflows": "write"}'
- name: Checkout Grafana
uses: actions/checkout@v5
with:
token: ${{ steps.generate_token.outputs.token }}
- name: Update package.json versions
uses: ./pkg/build/actions/bump-version
with:
@@ -35,10 +47,10 @@ jobs:
DRY_RUN: ${{ inputs.dry_run }}
REF_NAME: ${{ github.ref_name }}
RUN_ID: ${{ github.run_id }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ steps.generate_token.outputs.token }}
run: |
git config --local user.name "github-actions[bot]"
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "grafana-delivery-bot[bot]"
git config --local user.email "grafana-delivery-bot[bot]@users.noreply.github.com"
git config --local --add --bool push.autoSetupRemote true
git checkout -b "bump-version/${RUN_ID}/${VERSION}"
git add .

View File

@@ -48,6 +48,7 @@ jobs:
persist-credentials: false
- uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go -C grafana run ./pkg/build/cmd artifacts -a targz:grafana:linux/amd64 --grafana-dir="${PWD}/grafana" > out.txt
- run: mv "$(cat out.txt)" grafana.tar.gz
@@ -140,6 +141,7 @@ jobs:
- name: Run E2E tests
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/e2e --package=grafana.tar.gz
--suite=${{ matrix.path }}
@@ -178,12 +180,14 @@ jobs:
if: github.event_name == 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz
- name: Run non-PR a11y test
if: github.event_name != 'pull_request'
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/a11y --package=grafana.tar.gz --no-threshold-fail

View File

@@ -10,7 +10,7 @@ on:
schedule:
# Every weeknight at midnight
# "Scheduled workflows will only run on the default branch." (docs.github.com)
- cron: "0 0 * * 1-5"
- cron: '0 0 * * 1-5'
push:
branches:
- release-*.*.*
@@ -49,14 +49,14 @@ jobs:
setup:
name: setup
runs-on: github-hosted-ubuntu-x64-small
if: github.repository == 'grafana/grafana'
if: (github.repository == 'grafana/grafana') || (github.repository == 'grafana/grafana-security-mirror' && contains(github.ref_name, '+security'))
outputs:
version: ${{ steps.output.outputs.version }}
grafana-commit: ${{ steps.output.outputs.grafana_commit }}
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up version (Release Branches)
@@ -103,11 +103,13 @@ jobs:
BUILD_ID: ${{ github.run_id }}
BUCKET: grafana-prerelease
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
SOURCE_EVENT: ${{ inputs.source-event || github.event_name }}
REPO: ${{ github.repository }}
with:
github-token: ${{ steps.generate_token.outputs.token }}
script: |
const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT, GITHUB_EVENT_NAME} = process.env;
const {REF, VERSION, BUILD_ID, BUCKET, GRAFANA_COMMIT, SOURCE_EVENT, REPO} = process.env;
await github.rest.actions.createWorkflowDispatch({
owner: 'grafana',
repo: 'grafana-enterprise',
@@ -118,7 +120,8 @@ jobs:
"build-id": String(BUILD_ID),
"bucket": BUCKET,
"grafana-commit": GRAFANA_COMMIT,
"source-event": GITHUB_EVENT_NAME,
"source-event": SOURCE_EVENT,
"upstream": REPO,
}
})
@@ -137,7 +140,7 @@ jobs:
# The downside to this is that the frontend will be built for each one when it could be reused for all of them.
# This could be a future improvement.
include:
- name: linux-amd64
- name: linux-amd64 # publish-npm relies on this step building npm packages
artifacts: targz:grafana:linux/amd64,deb:grafana:linux/amd64,rpm:grafana:linux/amd64,docker:grafana:linux/amd64,docker:grafana:linux/amd64:ubuntu,npm:grafana,storybook
verify: true
- name: linux-arm64
@@ -165,8 +168,8 @@ jobs:
artifacts: targz:grafana:darwin/arm64
verify: true
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@main
- uses: actions/checkout@v4
- uses: grafana/shared-workflows/actions/dockerhub-login@dockerhub-login/v1.0.2
- uses: actions/checkout@v5
with:
persist-credentials: false
- name: Set up QEMU
@@ -194,6 +197,7 @@ jobs:
name: artifacts-${{ matrix.name }}
path: ${{ steps.build.outputs.dist-dir }}
retention-days: 1
publish-artifacts:
name: Upload artifacts
uses: grafana/grafana/.github/workflows/publish-artifact.yml@main
@@ -208,3 +212,135 @@ jobs:
run-id: ${{ github.run_id }}
bucket-path: ${{ needs.setup.outputs.version }}_${{ github.run_id }}
environment: prod
publish-dockerhub:
if: github.ref_name == 'main'
permissions:
contents: read
id-token: write
runs-on: ubuntu-x64-small
needs:
- setup
- build
steps:
- uses: grafana/shared-workflows/actions/dockerhub-login@dockerhub-login/v1.0.2
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-list-linux-amd64
path: .
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-list-linux-arm64
path: .
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-list-linux-armv7
path: .
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-linux-amd64
path: dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-linux-arm64
path: dist
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093
with:
name: artifacts-linux-armv7
path: dist
- name: Push to Docker Hub
env:
VERSION: ${{ needs.setup.outputs.version }}
run: |
# grep can use a wildcard but then it includes the filename as part of the result and that gets complicated.
# It's easier to use cat to combine the artifact lists
cat artifacts-*.txt > artifacts.txt
grep 'grafana_.*docker.tar.gz$' artifacts.txt | xargs -I % docker load -i % | sed 's/Loaded image: //g' | tee docker_images
while read -r line; do
# This tag will be `grafana/grafana-image-tags:...`
docker push "$line"
done < docker_images
docker manifest create grafana/grafana:main "grafana/grafana-image-tags:${VERSION}-amd64" "grafana/grafana-image-tags:${VERSION}-arm64" "grafana/grafana-image-tags:${VERSION}-armv7"
docker manifest create grafana/grafana:main-ubuntu "grafana/grafana-image-tags:${VERSION}-ubuntu-amd64" "grafana/grafana-image-tags:${VERSION}-ubuntu-arm64" "grafana/grafana-image-tags:${VERSION}-ubuntu-armv7"
docker manifest create "grafana/grafana-dev:${VERSION}" "grafana/grafana-image-tags:${VERSION}-amd64" "grafana/grafana-image-tags:${VERSION}-arm64" "grafana/grafana-image-tags:${VERSION}-armv7"
docker manifest create "grafana/grafana-dev:${VERSION}-ubuntu" "grafana/grafana-image-tags:${VERSION}-ubuntu-amd64" "grafana/grafana-image-tags:${VERSION}-ubuntu-arm64" "grafana/grafana-image-tags:${VERSION}-ubuntu-armv7"
docker manifest push grafana/grafana:main
docker manifest push grafana/grafana:main-ubuntu
docker manifest push "grafana/grafana-dev:${VERSION}"
docker manifest push "grafana/grafana-dev:${VERSION}-ubuntu"
dispatch-npm-canaries:
if: github.ref_name == 'main'
name: Dispatch publish NPM canaries
permissions:
actions: write
contents: read
runs-on: ubuntu-x64-small
needs:
- setup
steps:
- name: Dispatch action
env:
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
VERSION: ${{ needs.setup.outputs.version }}
BUILD_ID: ${{ github.run_id }}
GH_TOKEN: ${{ github.token }}
run: |
gh workflow run release-npm.yml \
--repo grafana/grafana \
--ref main \
--field grafana_commit="$GRAFANA_COMMIT" \
--field version="$VERSION" \
--field build_id="$BUILD_ID"\
--field version_type="canary"
# notify-pr creates (or updates) a comment in a pull request to link to this workflow where the release artifacts are
# being built.
notify-pr:
runs-on: ubuntu-x64-small
permissions:
contents: read
id-token: write
needs:
- setup
steps:
- id: vault-secrets
uses: grafana/shared-workflows/actions/get-vault-secrets@main
with:
repo_secrets: |
GRAFANA_DELIVERY_BOT_APP_PEM=delivery-bot-app:PRIVATE_KEY
- name: Generate token
id: generate_token
uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a
with:
app_id: ${{ vars.DELIVERY_BOT_APP_ID }}
private_key: ${{ env.GRAFANA_DELIVERY_BOT_APP_PEM }}
repositories: '["grafana"]'
permissions: '{"issues": "write", "pull_requests": "write", "contents": "read"}'
- name: Find PR
env:
GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }}
GRAFANA_COMMIT: ${{ needs.setup.outputs.grafana-commit }}
run: echo "ISSUE_NUMBER=$(gh api "/repos/grafana/grafana/commits/${GRAFANA_COMMIT}/pulls" | jq -r '.[0].number')" >> "$GITHUB_ENV"
- name: Find Comment
uses: peter-evans/find-comment@3eae4d37986fb5a8592848f6a574fdf654e61f9e # v3
id: fc
with:
issue-number: ${{ env.ISSUE_NUMBER }}
comment-author: 'grafana-delivery-bot[bot]'
body-includes: GitHub Actions Build
token: ${{ steps.generate_token.outputs.token }}
- name: Create or update comment
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
with:
token: ${{ steps.generate_token.outputs.token }}
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ env.ISSUE_NUMBER }}
body: |
:rocket: Your submission is now being built and packaged.
- [GitHub Actions Build](https://github.com/grafana/grafana/actions/runs/${{ github.run_id }})
- Version: ${{ needs.setup.outputs.version }}
edit-mode: replace

147
.github/workflows/release-npm.yml vendored Normal file
View File

@@ -0,0 +1,147 @@
name: Release NPM packages
run-name: Publish NPM ${{ inputs.version_type }} ${{ inputs.version }}
on:
workflow_call:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
type: string
version:
description: 'Version to publish as'
required: true
type: string
build_id:
description: 'Run ID from the original release-build workflow'
required: true
type: string
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
type: string
workflow_dispatch:
inputs:
grafana_commit:
description: 'Grafana commit SHA to build against'
required: true
version:
description: 'Version to publish as'
required: true
build_id:
description: 'Run ID from the original release-build workflow'
required: true
version_type:
description: 'Version type (canary, nightly, stable)'
required: true
permissions: {}
jobs:
# If called with version_type 'canary' or 'stable', build + publish to NPM
# If called with version_type 'nightly', just tag the given version with nightly tag. It was already published by the canary build.
publish:
name: Publish NPM packages
runs-on: github-hosted-ubuntu-x64-small
if: inputs.version_type == 'canary' || inputs.version_type == 'stable'
permissions:
contents: read
id-token: write
steps:
- name: Info
env:
GITHUB_REF: ${{ github.ref }}
GRAFANA_COMMIT: ${{ inputs.grafana_commit }}
run: |
echo "GRAFANA_COMMIT: $GRAFANA_COMMIT"
echo "github.ref: $GITHUB_REF"
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
fetch-depth: 100
fetch-tags: false
# this will fail with "{commit} is not a valid commit" if the commit is valid but
# not in the last 100 commits.
- name: Verify commit is in workflow HEAD
env:
GIT_COMMIT: ${{ inputs.grafana_commit }}
run: ./.github/workflows/scripts/validate-commit-in-head.sh
shell: bash
- name: Map version type to NPM tag
id: npm-tag
env:
VERSION: ${{ inputs.version }}
VERSION_TYPE: ${{ inputs.version_type }}
REFERENCE_PKG: "@grafana/runtime"
run: |
TAG=$(./.github/workflows/scripts/determine-npm-tag.sh)
echo "NPM_TAG=$TAG" >> "$GITHUB_OUTPUT"
shell: bash
- name: Checkout build commit
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ inputs.grafana_commit }}
- name: Setup Node
uses: ./.github/actions/setup-node
# Trusted Publishing is only available in npm v11.5.1 and later
- name: Update npm
run: npm install -g npm@^11.5.1
- name: Install dependencies
run: yarn install --immutable
- name: Typecheck packages
run: yarn run packages:typecheck
- name: Version, build, and pack packages
env:
VERSION: ${{ inputs.version }}
run: |
yarn run packages:build
yarn lerna version "$VERSION" \
--exact \
--no-git-tag-version \
--no-push \
--force-publish \
--yes
yarn run packages:pack
- name: Debug packed files
run: tree -a ./npm-artifacts
- name: Validate packages
run: ./scripts/validate-npm-packages.sh
- name: Debug OIDC Claims
uses: github/actions-oidc-debugger@2e9ba5d3f4bebaad1f91a2cede055115738b7ae8
with:
audience: '${{ github.server_url }}/${{ github.repository_owner }}'
- name: Publish packages
env:
NPM_TAG: ${{ steps.npm-tag.outputs.NPM_TAG }}
run: ./scripts/publish-npm-packages.sh --dist-tag "$NPM_TAG" --registry 'https://registry.npmjs.org/'
# TODO: finish this step
tag-nightly:
name: Tag nightly release
runs-on: github-hosted-ubuntu-x64-small
if: inputs.version_type == 'nightly'
steps:
- name: Checkout workflow ref
uses: actions/checkout@v4
with:
persist-credentials: false
# TODO: tag the given release with nightly

View File

@@ -198,6 +198,7 @@ jobs:
if: ${{ inputs.bump == true || inputs.bump == 'true' }}
uses: dagger/dagger-for-github@e47aba410ef9bb9ed81a4d2a97df31061e5e842e
with:
version: 0.18.8
verb: run
args: go run -C .grafana-main ./pkg/build/actions/bump-version -version="patch"

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env bash
set -euo pipefail
fail() { echo "Error: $*" >&2; exit 1; }
# Ensure required variables are set
if [[ -z "${REFERENCE_PKG}" || -z "${VERSION_TYPE}" || -z "${VERSION}" ]]; then
fail "Missing required environment variables: REFERENCE_PKG, VERSION_TYPE, VERSION"
fi
semver_cmp () {
IFS='.' read -r -a arr_a <<< "$1"
IFS='.' read -r -a arr_b <<< "$2"
for i in 0 1 2; do
local aa=${arr_a[i]:-0}
local bb=${arr_b[i]:-0}
# shellcheck disable=SC2004
if (( 10#$aa > 10#$bb )); then echo gt; return 0; fi
if (( 10#$aa < 10#$bb )); then echo lt; return 0; fi
done
echo "eq"
}
STABLE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)$' # x.y.z
PRE_REGEX='^([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)$' # x.y.z-123456
# Validate that the VERSION matches VERSION_TYPE
# - stable must be x.y.z
# - nightly/canary must be x.y.z-123456
case "$VERSION_TYPE" in
stable)
[[ $VERSION =~ $STABLE_REGEX ]] || fail "For 'stable', version must match x.y.z" ;;
nightly|canary)
[[ $VERSION =~ $PRE_REGEX ]] || fail "For '$VERSION_TYPE', version must match x.y.z-123456" ;;
*)
fail "Unknown version_type '$VERSION_TYPE'" ;;
esac
# Extract major, minor from VERSION
IFS=.- read -r major minor patch _ <<< "$VERSION"
# Determine NPM tag
case "$VERSION_TYPE" in
canary) TAG="canary" ;;
nightly) TAG="nightly" ;;
stable)
# Use npm dist-tag "latest" as the reference
LATEST="$(npm view --silent "$REFERENCE_PKG" dist-tags.latest 2>/dev/null || true)"
echo "Latest for $REFERENCE_PKG is ${LATEST:-<none>}" >&2
if [[ -z ${LATEST:-} ]]; then
TAG="latest" # first ever publish
else
case "$(semver_cmp "$VERSION" "$LATEST")" in
gt) TAG="latest" ;; # newer than reference -> latest
lt|eq) TAG="v${major}.${minor}-latest" ;; # older or equal -> vX.Y-latest
esac
fi
;;
esac
echo "Resolved NPM_TAG=$TAG (VERSION=$VERSION, current latest=${LATEST:-none})" 1>&2 # stderr
printf '%s' "$TAG"

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
if [[ -z "${GIT_COMMIT:-}" ]]; then
echo "Error: Environment variable GIT_COMMIT is required"
exit 1
fi
if git merge-base --is-ancestor "$GIT_COMMIT" HEAD; then
echo "Commit $GIT_COMMIT is contained in HEAD"
else
echo "Error: Commit $GIT_COMMIT is not contained in HEAD"
exit 1
fi

View File

@@ -24,6 +24,7 @@ require (
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect

View File

@@ -35,6 +35,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=

View File

@@ -208,6 +208,6 @@ The above configuration produces the following result in the Time series panel:
{{< figure src="/media/docs/grafana/screenshot-grafana-10-0-timeseries-time-regions.png" max-width="600px" alt="Time series visualization with time regions business hours" >}}
Toggle the **Advanced** switch and use [Cron syntax](https://crontab.run/) to set more granular time region controls. The following example sets a time region of 9:00 AM, Monday to Friday:
Toggle the **Advanced** switch and use [Cron syntax](https://en.wikipedia.org/wiki/Cron) to set more granular time region controls. The following example sets a time region of 9:00 AM, Monday to Friday:
{{< figure src="/media/docs/grafana/dashboards/screenshot-annotations-cron-option-v11.6.png" max-width="600px" alt="Time region query with cron syntax" >}}

View File

@@ -2,7 +2,6 @@
labels:
products:
- cloud
- enterprise
- oss
stage:
- experimental
@@ -161,6 +160,14 @@ To create a dashboard, follow these steps:
1. When you've saved all the changes you want to make to the dashboard, click **Back to dashboard**.
1. Toggle off the edit mode switch.
{{< admonition type="caution" >}}
Dynamic dashboards is an [experimental](https://grafana.com/docs/release-life-cycle/) feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
**Do not enable this feature in production environments as it may result in the irreversible loss of data.**
{{< /admonition >}}
## Group panels
To help create meaningful sections in your dashboard, you can group panels into rows or tabs.
@@ -294,6 +301,14 @@ To configure show/hide rules, follow these steps:
1. Click **Save**.
1. Toggle off the edit mode switch.
{{< admonition type="caution" >}}
Dynamic dashboards is an [experimental](https://grafana.com/docs/release-life-cycle/) feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
**Do not enable this feature in production environments as it may result in the irreversible loss of data.**
{{< /admonition >}}
## Edit dashboards
When the dashboard is in edit mode, the edit pane that opens displays options associated with the part of the dashboard that it's in focus.
@@ -397,3 +412,11 @@ To make a copy of a dashboard, follow these steps:
By default, the copied dashboard has the same name as the original dashboard with the word "Copy" appended and is in the same folder.
1. Click **Save**.
{{< admonition type="caution" >}}
Dynamic dashboards is an [experimental](https://grafana.com/docs/release-life-cycle/) feature. Engineering and on-call support is not available. Documentation is either limited or not provided outside of code comments. No SLA is provided. To get early access to this feature, request it through [this form](https://docs.google.com/forms/d/e/1FAIpQLSd73nQzuhzcHJOrLFK4ef_uMxHAQiPQh1-rsQUT2MRqbeMLpg/viewform?usp=dialog).
**Do not enable this feature in production environments as it may result in the irreversible loss of data.**
{{< /admonition >}}

View File

@@ -51,7 +51,7 @@ yarn add @grafana/grafana-foundation-sdk
For Go, install the SDK package via `go get`:
```go
go get github.com/grafana/grafana-foundation-sdk/go
go get github.com/grafana/grafana-foundation-sdk/go@next+cog-v0.0.x
```
### Python

View File

@@ -198,6 +198,14 @@ List of enabled loggers.
Keep dashboard content in the logs (request or response fields). This can significantly increase the size of your logs.
### log_datasource_query_request_body
Whether to record data source queries' request body. This can significantly increase the size of your logs. Enabled by default.
### log_datasource_query_response_body
Whether to record data source queries' response body. This can significantly increase the size of your logs. Enabled by default.
### verbose
Log all requests and keep requests and responses body. This can significantly increase the size of your logs.

View File

@@ -373,6 +373,10 @@ enabled = false
loggers = file
# Keep dashboard content in the logs (request or response fields); this can significantly increase the size of your logs.
log_dashboard_content = false
# Whether to record data source queries' request body. This can significantly increase the size of your logs. Enabled by default.
log_datasource_query_request_body = true
# Whether to record data source queries' response body. This can significantly increase the size of your logs. Enabled by default.
log_datasource_query_response_body = true
# Keep requests and responses body; this can significantly increase the size of your logs.
verbose = false
# Write an audit log for every status code.

42
go.mod
View File

@@ -77,7 +77,7 @@ require (
github.com/googleapis/go-sql-spanner v1.11.1 // @grafana/grafana-search-and-storage
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.3 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430 // @grafana/alerting-backend
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8 // @grafana/alerting-backend
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d // @grafana/identity-access-team
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics
@@ -89,7 +89,7 @@ require (
github.com/grafana/grafana-api-golang-client v0.27.0 // @grafana/alerting-backend
github.com/grafana/grafana-app-sdk v0.35.1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-app-sdk/logging v0.35.1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana-aws-sdk v0.31.5 // @grafana/aws-datasources
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b // @grafana/aws-datasources
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // @grafana/partner-datasources
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 // @grafana/grafana-operator-experience-squad
github.com/grafana/grafana-google-sdk-go v0.2.1 // @grafana/partner-datasources
@@ -276,25 +276,25 @@ require (
github.com/armon/go-metrics v0.4.1 // indirect
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 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2 v1.39.0 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // 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.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // 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.3.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
github.com/aws/smithy-go v1.23.0 // @grafana/aws-datasources
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -370,7 +370,7 @@ require (
github.com/go-openapi/spec v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
github.com/gogo/googleapis v1.4.1 // indirect
@@ -581,6 +581,14 @@ require (
require github.com/urfave/cli/v3 v3.3.8 // @grafana/grafana-backend-group
require (
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.50.1 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.58.0 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/ec2 v1.252.0 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/oam v1.22.3 // @grafana/aws-datasources
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.30.4 // @grafana/aws-datasources
)
require github.com/cenkalti/backoff/v5 v5.0.2 // indirect
// Use fork of crewjam/saml with fixes for some issues until changes get merged into upstream

78
go.sum
View File

@@ -843,44 +843,54 @@ github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z
github.com/aws/aws-sdk-go v1.50.29/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
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.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU=
github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
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.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.50.1 h1:OSye2F+X+KfxEdbrOT3x+p7L3kr5zPtm3BMkNWGVXQ8=
github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.50.1/go.mod h1:bNNaZaAX81KIuYDaj5ODgZwA1ybBJzpDeKYoNxEGGqw=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.58.0 h1:XH0kj0KcoKd+BAadpiS83/Wf+25q4FmH3gDei4u+PzA=
github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs v1.58.0/go.mod h1:ptJgRWK9opQK1foOTBKUg3PokkKA0/xcTXWIxwliaIY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.252.0 h1:4C/MhyZWR0Bk+7QLVvNxa4xhwEnqm1KclA5MCMepjF0=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.252.0/go.mod h1:MXJiLJZtMqb2dVXgEIn35d5+7MqLd4r8noLen881kpk=
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.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
github.com/aws/aws-sdk-go-v2/service/oam v1.22.3 h1:DCKlfaaVB7mE7dtfGD60gWX1Y5nc+yh8qcBHvpdkRqs=
github.com/aws/aws-sdk-go-v2/service/oam v1.22.3/go.mod h1:uSLwrlkn0YO7P4xzMy4yJDgyyi6BYzZA73D0iv5gPpo=
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.30.4 h1:LmoqYCi723i8jvkALGA7E+1GeaOc2OHZNLdkwp7cjZA=
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.30.4/go.mod h1:KV1rGdzLiPDfq5EId56EPFzKL5f3FQ8vB4kN/RkkVC4=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/axiomhq/hyperloglog v0.0.0-20191112132149-a4c4c47bc57f/go.mod h1:2stgcRjl6QmW+gU2h5E7BQXg4HU0gzxKWDuT5HviN9s=
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27 h1:60m4tnanN1ctzIu4V3bfCNJ39BiOPSm1gHFlFjTkRE0=
github.com/axiomhq/hyperloglog v0.0.0-20240507144631-af9851f82b27/go.mod h1:k08r+Yj1PRAmuayFiRK6MYuR5Ve4IuZtTfxErMIh0+c=
@@ -1343,8 +1353,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/go-zookeeper/zk v1.0.4 h1:DPzxraQx7OrPyXq2phlGlNSIyWEsAox0RJmjTseMV6I=
@@ -1571,8 +1581,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430 h1:qT0D7AIV0GRu8JUrSJYuyzj86kqLgksKQjwD++DqyOM=
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8 h1:tXDlMjugyEbsrSJC2Np/9ZvBzEoa1xB+KGHUBnvPIFw=
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d h1:TDVZemfYeJHPyXeYCnqL7BQqsa+mpaZYth/Qm3TKaT8=
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
@@ -1595,8 +1605,8 @@ github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK
github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI=
github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM=
github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE=
github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM=
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b h1:25XvbNSo8Sgi3MBFOtToRvlbXwfQ44nTb/NYCpZxUJE=
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b/go.mod h1:2JJbVLc3jjWu0aAQx4bhPa2ChOUjLs9x7ime6mB4O+o=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo=
github.com/grafana/grafana-cloud-migration-snapshot v1.6.0 h1:S4kHwr//AqhtL9xHBtz1gqVgZQeCRGTxjgsRBAkpjKY=

File diff suppressed because it is too large Load Diff

View File

@@ -10,11 +10,13 @@ export default [
input: entryPoint,
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-data')],
treeshake: false,
},
{
input: 'src/unstable.ts',
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-data')],
treeshake: false,
},
tsDeclarationOutput(pkg),
tsDeclarationOutput(pkg, {

View File

@@ -10,6 +10,7 @@ export default [
input: entryPoint,
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-e2e-selectors')],
treeshake: false,
},
tsDeclarationOutput(pkg),
];

View File

@@ -10,6 +10,7 @@ export default [
input: entryPoint,
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-flamegraph')],
treeshake: false,
},
tsDeclarationOutput(pkg),
];

View File

@@ -11,6 +11,7 @@ export default [
input: entryPoint,
plugins: [...plugins, image()],
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-prometheus')],
treeshake: false,
},
tsDeclarationOutput(pkg),
];

View File

@@ -10,11 +10,13 @@ export default [
input: entryPoint,
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-runtime')],
treeshake: false,
},
{
input: 'src/unstable.ts',
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-runtime')],
treeshake: false,
},
tsDeclarationOutput(pkg),
tsDeclarationOutput(pkg, {

View File

@@ -15,6 +15,7 @@ export default [
input: entryPoint,
plugins,
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-schema')],
treeshake: false,
},
tsDeclarationOutput(pkg, { input: './dist/esm/index.d.ts' }),
{
@@ -31,5 +32,6 @@ export default [
format: 'esm',
dir: path.dirname(pkg.publishConfig.module),
},
treeshake: false,
},
];

View File

@@ -24,6 +24,7 @@ export default [
}),
],
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-ui')],
treeshake: false,
},
{
input: 'src/unstable.ts',
@@ -36,6 +37,7 @@ export default [
}),
],
output: [cjsOutput(pkg), esmOutput(pkg, 'grafana-ui')],
treeshake: false,
},
tsDeclarationOutput(pkg),
tsDeclarationOutput(pkg, {

View File

@@ -41,7 +41,7 @@ var getViewIndex = func() string {
return viewIndex
}
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore.
var redirectRe = regexp.MustCompile(`^/[a-zA-Z0-9-_].*`)
var (
@@ -73,12 +73,17 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
return errForbiddenRedirectTo
}
if to.Path != "/" && !redirectRe.MatchString(to.Path) {
return errForbiddenRedirectTo
}
cleanPath := path.Clean(to.Path)
// "." is what path.Clean returns for empty paths
if cleanPath == "." {
return errForbiddenRedirectTo
}
if to.Path != "/" && !redirectRe.MatchString(cleanPath) {
if cleanPath != "/" && !redirectRe.MatchString(cleanPath) {
return errForbiddenRedirectTo
}

View File

@@ -49,7 +49,7 @@ func TestCallResource(t *testing.T) {
cfg.StaticRootPath = staticRootPath
cfg.Azure = &azsettings.AzureSettings{}
coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), nil, &cloudwatch.CloudWatchService{}, nil, nil, nil, nil,
coreRegistry := coreplugin.ProvideCoreRegistry(tracing.InitializeTracerForTest(), nil, &cloudwatch.Service{}, nil, nil, nil, nil,
nil, nil, nil, nil, testdatasource.ProvideService(), nil, nil, nil, nil, nil, nil, nil, nil)
testCtx := pluginsintegration.CreateIntegrationTestCtx(t, cfg, coreRegistry)

View File

@@ -166,6 +166,7 @@ func TestHTTPServer_RotateUserAuthTokenRedirect(t *testing.T) {
// Invalid redirects should be converted to root
{"backslash domain", `/\grafana.com`, "/"},
{"backslash domain at the start of the path", `/\grafana.com/../a`, "/"},
{"traversal backslash domain", `/a/../\grafana.com`, "/"},
{"double slash", "//grafana", "/"},
{"missing initial slash", "missingInitialSlash", "/"},
@@ -232,7 +233,7 @@ func TestHTTPServer_RotateUserAuthTokenRedirect(t *testing.T) {
res, err := server.Send(req)
require.NoError(t, err)
assert.Equal(t, 302, redirectStatusCode)
assert.Equal(t, redirectCase.expectedUrl, redirectLocation)
assert.Equal(t, redirectCase.expectedUrl, redirectLocation, "redirectTo=%s", redirectCase.redirectUrl)
require.NoError(t, res.Body.Close())
})

View File

@@ -16,6 +16,7 @@ runs:
GO_MOD_DIR: ${{ inputs.go-mod-dir }}
VERSION: ${{ inputs.version }}
with:
version: 0.18.8
verb: run
args: go run ./pkg/build/actions/bump-version -version=${VERSION}
- name: make gen-cue

View File

@@ -203,6 +203,10 @@ func doBuild(binaryName, pkg string, opts BuildOpts) error {
args = append(args, "-race")
}
// We should not publish Grafana as a Go module, disabling vcs changes the version to (devel)
// and works better with SBOM and Vulnerability Scanners.
args = append(args, "-buildvcs=false")
args = append(args, "-o", binary)
args = append(args, pkg)

View File

@@ -34,6 +34,9 @@ func GoLDFlags(flags []LDFlag) string {
// GoBuildCommand returns the arguments for go build to be used in 'WithExec'.
func GoBuildCommand(output string, ldflags []LDFlag, tags []string, main string) []string {
args := []string{"go", "build",
// We should not publish Grafana as a Go module, disabling vcs changes the version to (devel)
// and works better with SBOM and Vulnerability Scanners.
"-buildvcs=false",
fmt.Sprintf("-ldflags=\"%s\"", GoLDFlags(ldflags)),
fmt.Sprintf("-o=%s", output),
"-trimpath",

View File

@@ -0,0 +1,102 @@
package commands
import (
"flag"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
)
func TestRemoveCommand_StaticFS_FailsWithImmutableError(t *testing.T) {
t.Run("removeCommand fails with immutable error for plugins using StaticFS", func(t *testing.T) {
tmpDir := t.TempDir()
pluginID := "test-plugin"
pluginDir := filepath.Join(tmpDir, pluginID)
err := os.MkdirAll(pluginDir, 0750)
require.NoError(t, err)
pluginJSON := `{
"id": "test-plugin",
"name": "Test Plugin",
"type": "datasource",
"info": {
"version": "1.0.0"
}
}`
err = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte(pluginJSON), 0644)
require.NoError(t, err)
cmdLine := createCliContextWithArgs(t, []string{pluginID}, "pluginsDir", tmpDir)
require.NotNil(t, cmdLine)
// Verify plugin directory exists before attempting removal
_, err = os.Stat(pluginDir)
require.NoError(t, err, "Plugin directory should exist before removal attempt")
err = removeCommand(cmdLine)
require.NoError(t, err)
// Verify plugin directory has been removed
_, err = os.Stat(pluginDir)
require.ErrorIs(t, err, os.ErrNotExist)
})
}
func TestRemoveCommand_PluginNotFound(t *testing.T) {
t.Run("removeCommand should handle missing plugin gracefully", func(t *testing.T) {
tmpDir := t.TempDir()
cmdLine := createCliContextWithArgs(t, []string{"non-existent-plugin"}, "pluginsDir", tmpDir)
require.NotNil(t, cmdLine)
err := removeCommand(cmdLine)
require.NoError(t, err)
})
}
func TestRemoveCommand_MissingPluginParameter(t *testing.T) {
t.Run("removeCommand should error when no plugin ID is provided", func(t *testing.T) {
cmdLine := createCliContextWithArgs(t, []string{})
require.NotNil(t, cmdLine)
err := removeCommand(cmdLine)
require.Error(t, err)
require.Contains(t, err.Error(), "missing plugin parameter")
})
}
// createCliContextWithArgs creates a CLI context with the specified arguments and optional flag key-value pairs.
// Usage: createCliContextWithArgs(t, []string{"plugin-id"}, "pluginsDir", "/path/to/plugins", "flag2", "value2")
func createCliContextWithArgs(t *testing.T, args []string, flagPairs ...string) *utils.ContextCommandLine {
if len(flagPairs)%2 != 0 {
t.Fatalf("flagPairs must be provided in key-value pairs, got %d arguments", len(flagPairs))
}
app := &cli.App{
Name: "grafana",
}
flagSet := flag.NewFlagSet("test", 0)
// Add flags from the key-value pairs
for i := 0; i < len(flagPairs); i += 2 {
key := flagPairs[i]
value := flagPairs[i+1]
flagSet.String(key, "", "")
err := flagSet.Set(key, value)
require.NoError(t, err, "Failed to set flag %s=%s", key, value)
}
err := flagSet.Parse(args)
require.NoError(t, err)
ctx := cli.NewContext(app, flagSet, nil)
return &utils.ContextCommandLine{
Context: ctx,
}
}

View File

@@ -0,0 +1,150 @@
package commands
import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/urfave/cli/v2"
"github.com/grafana/grafana/pkg/cmd/grafana-cli/models"
)
func TestUpgradeCommand(t *testing.T) {
t.Run("Plugin is removed even if upgrade fails", func(t *testing.T) {
tmpDir := t.TempDir()
pluginID := "test-upgrade-plugin"
pluginDir := filepath.Join(tmpDir, pluginID)
err := os.MkdirAll(pluginDir, 0750)
require.NoError(t, err)
pluginJSON := `{
"id": "test-upgrade-plugin",
"name": "Test Upgrade Plugin",
"type": "datasource",
"info": {
"version": "1.0.0"
}
}`
err = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte(pluginJSON), 0644)
require.NoError(t, err)
// Create a mock HTTP server that returns plugin info with a newer version
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Handle plugin info request
if r.URL.Path == "/repo/"+pluginID {
plugin := models.Plugin{
ID: pluginID,
Versions: []models.Version{
{
Version: "2.0.0", // Newer than the local version (1.0.0)
},
},
}
w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(plugin)
require.NoError(t, err)
return
}
// For any other request (like installation), return 500 to cause the upgrade to fail
// after the removal attempt, which is what we want to test
w.WriteHeader(http.StatusInternalServerError)
_, err = w.Write([]byte("Server error"))
require.NoError(t, err)
}))
defer mockServer.Close()
// Use our test implementation that properly implements GcomToken()
cmdLine := newTestCommandLine([]string{pluginID}, tmpDir, mockServer.URL)
// Verify plugin directory exists before attempting upgrade
_, err = os.Stat(pluginDir)
require.NoError(t, err)
err = upgradeCommand(cmdLine)
require.Error(t, err)
require.Contains(t, err.Error(), "API returned invalid status: 500 Internal Server Error")
// Verify plugin directory was removed during the removal step
_, err = os.Stat(pluginDir)
require.True(t, os.IsNotExist(err))
})
}
func TestUpgradeCommand_PluginNotFound(t *testing.T) {
t.Run("upgradeCommand should handle missing plugin gracefully", func(t *testing.T) {
tmpDir := t.TempDir()
cmdLine := createCliContextWithArgs(t, []string{"non-existent-plugin"}, "pluginsDir", tmpDir)
require.NotNil(t, cmdLine)
err := upgradeCommand(cmdLine)
require.Error(t, err)
// Should fail trying to find the local plugin
require.Contains(t, err.Error(), "could not find plugin non-existent-plugin")
})
}
func TestUpgradeCommand_MissingPluginParameter(t *testing.T) {
t.Run("upgradeCommand should error when no plugin ID is provided", func(t *testing.T) {
cmdLine := createCliContextWithArgs(t, []string{})
require.NotNil(t, cmdLine)
err := upgradeCommand(cmdLine)
require.Error(t, err)
require.Contains(t, err.Error(), "please specify plugin to update")
})
}
// Simple args implementation
type simpleArgs []string
func (a simpleArgs) First() string {
if len(a) > 0 {
return a[0]
}
return ""
}
func (a simpleArgs) Get(int) string { return "" }
func (a simpleArgs) Tail() []string { return nil }
func (a simpleArgs) Len() int { return len(a) }
func (a simpleArgs) Present() bool { return len(a) > 0 }
func (a simpleArgs) Slice() []string { return []string(a) }
// Base struct with default implementations for unused CommandLine methods
type baseCommandLine struct{}
func (b baseCommandLine) ShowHelp() error { return nil }
func (b baseCommandLine) ShowVersion() {}
func (b baseCommandLine) Application() *cli.App { return nil }
func (b baseCommandLine) Int(_ string) int { return 0 }
func (b baseCommandLine) String(_ string) string { return "" }
func (b baseCommandLine) StringSlice(_ string) []string { return nil }
func (b baseCommandLine) FlagNames() []string { return nil }
func (b baseCommandLine) Generic(_ string) any { return nil }
func (b baseCommandLine) Bool(_ string) bool { return false }
func (b baseCommandLine) PluginURL() string { return "" }
func (b baseCommandLine) GcomToken() string { return "" }
// Test implementation - only implements what we actually need
type testCommandLine struct {
baseCommandLine // Embedded struct provides default implementations
args simpleArgs
pluginDir string
repoURL string
}
func newTestCommandLine(args []string, pluginDir, repoURL string) *testCommandLine {
return &testCommandLine{args: simpleArgs(args), pluginDir: pluginDir, repoURL: repoURL}
}
// Only implement the methods actually used by upgradeCommand
func (t *testCommandLine) Args() cli.Args { return t.args }
func (t *testCommandLine) PluginDirectory() string { return t.pluginDir }
func (t *testCommandLine) PluginRepoURL() string { return t.repoURL }

View File

@@ -4,9 +4,7 @@ import (
"net/http"
"time"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
awssdk "github.com/grafana/grafana-aws-sdk/pkg/sigv4"
"github.com/grafana/grafana-plugin-sdk-go/backend/gtime"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/mwitkow/go-conntrack"
@@ -46,21 +44,7 @@ func New(cfg *setting.Cfg, validator validations.DataSourceRequestURLValidator,
// SigV4 signing should be performed after all headers are added
if cfg.SigV4AuthEnabled {
authSettings := awsds.AuthSettings{
AllowedAuthProviders: cfg.AWSAllowedAuthProviders,
AssumeRoleEnabled: cfg.AWSAssumeRoleEnabled,
ExternalID: cfg.AWSExternalId,
ListMetricsPageLimit: cfg.AWSListMetricsPageLimit,
SecureSocksDSProxyEnabled: cfg.SecureSocksDSProxy.Enabled,
}
if cfg.AWSSessionDuration != "" {
sessionDuration, err := gtime.ParseDuration(cfg.AWSSessionDuration)
if err == nil {
authSettings.SessionDuration = &sessionDuration
}
}
middlewares = append(middlewares, awssdk.SigV4MiddlewareWithAuthSettings(cfg.SigV4VerboseLogging, authSettings))
middlewares = append(middlewares, awsauth.NewSigV4Middleware())
}
setDefaultTimeoutOptions(cfg)

View File

@@ -5,7 +5,7 @@ import (
"github.com/grafana/grafana/pkg/services/validations"
awssdk "github.com/grafana/grafana-aws-sdk/pkg/sigv4"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
sdkhttpclient "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/setting"
@@ -63,7 +63,7 @@ func TestHTTPClientProvider(t *testing.T) {
require.Equal(t, sdkhttpclient.ResponseLimitMiddlewareName, o.Middlewares[6].(sdkhttpclient.MiddlewareName).MiddlewareName())
require.Equal(t, HostRedirectValidationMiddlewareName, o.Middlewares[7].(sdkhttpclient.MiddlewareName).MiddlewareName())
require.Equal(t, sdkhttpclient.ErrorSourceMiddlewareName, o.Middlewares[8].(sdkhttpclient.MiddlewareName).MiddlewareName())
require.Equal(t, awssdk.SigV4MiddlewareName, o.Middlewares[9].(sdkhttpclient.MiddlewareName).MiddlewareName())
require.Equal(t, awsauth.NewSigV4Middleware().(sdkhttpclient.MiddlewareName).MiddlewareName(), o.Middlewares[9].(sdkhttpclient.MiddlewareName).MiddlewareName())
})
t.Run("When creating new provider and http logging is enabled for one plugin, it should apply expected middleware", func(t *testing.T) {

View File

@@ -13,7 +13,7 @@ import (
"github.com/grafana/grafana/pkg/web"
)
// Only allow redirects that start with an alphanumerical character, a dash or an underscore.
// Only allow redirects that start with a slash followed by an alphanumerical character, a dash or an underscore.
var redirectRe = regexp.MustCompile(`^/?[a-zA-Z0-9-_].*`)
// OrgRedirect changes org and redirects users if the
@@ -66,6 +66,9 @@ func OrgRedirect(cfg *setting.Cfg, userSvc user.Service) web.Handler {
}
func validRedirectPath(p string) bool {
if p != "" && p != "/" && !redirectRe.MatchString(p) {
return false
}
cleanPath := path.Clean(p)
return cleanPath == "." || cleanPath == "/" || redirectRe.MatchString(cleanPath)
}

View File

@@ -72,13 +72,18 @@ func TestOrgRedirectMiddleware(t *testing.T) {
})
middlewareScenario(t, "when redirecting to an invalid path", func(t *testing.T, sc *scenarioContext) {
sc.withIdentity(&authn.Identity{})
testPaths := []string{
url.QueryEscape(`/\example.com`),
`/%2fexample.com`,
}
for _, path := range testPaths {
sc.withIdentity(&authn.Identity{})
path := url.QueryEscape(`/\example.com`)
sc.m.Get(url.QueryEscape(path), sc.defaultHandler)
sc.fakeReq("GET", fmt.Sprintf("%s?orgId=3", path)).exec()
sc.m.Get(url.QueryEscape(path), sc.defaultHandler)
sc.fakeReq("GET", fmt.Sprintf("%s?orgId=3", path)).exec()
require.Equal(t, 404, sc.resp.Code)
require.Equal(t, 404, sc.resp.Code, "path: %s", path)
}
})
middlewareScenario(t, "works correctly when grafana is served under a subpath", func(t *testing.T, sc *scenarioContext) {

View File

@@ -94,7 +94,7 @@ func NewRegistry(store map[string]backendplugin.PluginFactoryFunc) *Registry {
}
}
func ProvideCoreRegistry(tracer tracing.Tracer, am *azuremonitor.Service, cw *cloudwatch.CloudWatchService, cm *cloudmonitoring.Service,
func ProvideCoreRegistry(tracer tracing.Tracer, am *azuremonitor.Service, cw *cloudwatch.Service, cm *cloudmonitoring.Service,
es *elasticsearch.Service, grap *graphite.Service, idb *influxdb.Service, lk *loki.Service, otsdb *opentsdb.Service,
pr *prometheus.Service, t *tempo.Service, td *testdatasource.Service, pg *postgres.Service, my *mysql.Service,
ms *mssql.Service, graf *grafanads.Service, pyroscope *pyroscope.Service, parca *parca.Service, zipkin *zipkin.Service, jaeger *jaeger.Service) *Registry {
@@ -102,7 +102,7 @@ func ProvideCoreRegistry(tracer tracing.Tracer, am *azuremonitor.Service, cw *cl
sdktracing.InitDefaultTracer(tracer)
return NewRegistry(map[string]backendplugin.PluginFactoryFunc{
CloudWatch: asBackendPlugin(cw.Executor),
CloudWatch: asBackendPlugin(cw),
CloudMonitoring: asBackendPlugin(cm),
AzureMonitor: asBackendPlugin(am),
Elasticsearch: asBackendPlugin(es),
@@ -217,7 +217,7 @@ func NewPlugin(pluginID string, cfg *setting.Cfg, httpClientProvider *httpclient
jsonData.AliasIDs = append(jsonData.AliasIDs, TestDataAlias)
svc = testdatasource.ProvideService()
case CloudWatch:
svc = cloudwatch.ProvideService(httpClientProvider).Executor
svc = cloudwatch.ProvideService()
case CloudMonitoring:
svc = cloudmonitoring.ProvideService(httpClientProvider)
case AzureMonitor:

View File

@@ -236,6 +236,15 @@ func (f StaticFS) Files() ([]string, error) {
return files, nil
}
func (f StaticFS) Remove() error {
if remover, ok := f.FS.(FSRemover); ok {
if err := remover.Remove(); err != nil {
return err
}
}
return nil
}
// LocalFile implements a fs.File for accessing the local filesystem.
type LocalFile struct {
f *os.File

View File

@@ -270,12 +270,27 @@ func TestStaticFS(t *testing.T) {
require.Equal(t, []string{allowedFn, deniedFn}, files)
})
t.Run("staticfs filters underelying fs's files", func(t *testing.T) {
t.Run("staticfs filters underlying fs's files", func(t *testing.T) {
files, err := staticFS.Files()
require.NoError(t, err)
require.Equal(t, []string{allowedFn}, files)
})
})
t.Run("FSRemover interface implementation verification", func(t *testing.T) {
tmpDir := t.TempDir()
lfs := NewLocalFS(tmpDir)
var localFSInterface FS = lfs
_, isRemover := localFSInterface.(FSRemover)
require.True(t, isRemover)
sfs, err := NewStaticFS(localFS)
require.NoError(t, err)
var staticFSInterface FS = sfs
_, isRemover = staticFSInterface.(FSRemover)
require.True(t, isRemover)
})
}
// TestFSTwoDotsInFileName ensures that LocalFS and StaticFS allow two dots in file names.

View File

@@ -5,6 +5,8 @@ import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"runtime"
"testing"
@@ -416,3 +418,100 @@ func createPlugin(t *testing.T, pluginID string, class plugins.Class, managed, b
func testCompatOpts() plugins.AddOpts {
return plugins.NewAddOpts("10.0.0", runtime.GOOS, runtime.GOARCH, "")
}
func TestPluginInstaller_Removal(t *testing.T) {
tmpDir := t.TempDir()
t.Run("LocalFS plugin removal succeeds via installer.Remove", func(t *testing.T) {
pluginDir := filepath.Join(tmpDir, "localfs-plugin")
err := os.MkdirAll(pluginDir, 0750)
require.NoError(t, err)
pluginJSON := `{
"id": "localfs-plugin",
"name": "LocalFS Plugin",
"type": "datasource",
"info": {
"version": "1.0.0"
}
}`
err = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte(pluginJSON), 0644)
require.NoError(t, err)
localFS := plugins.NewLocalFS(pluginDir)
pluginV1 := createPlugin(t, "localfs-plugin", plugins.ClassExternal, true, true, func(plugin *plugins.Plugin) {
plugin.Info.Version = "1.0.0"
plugin.FS = localFS
})
registry := &fakes.FakePluginRegistry{
Store: map[string]*plugins.Plugin{
"localfs-plugin": pluginV1,
},
}
loader := &fakes.FakeLoader{
UnloadFunc: func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
return p, nil
},
}
_, err = os.Stat(pluginDir)
require.NoError(t, err)
inst := New(registry, loader, &fakes.FakePluginRepo{}, &fakes.FakePluginStorage{}, storage.SimpleDirNameGeneratorFunc, &fakes.FakeAuthService{})
err = inst.Remove(context.Background(), "localfs-plugin", "1.0.0")
require.NoError(t, err)
_, err = os.Stat(pluginDir)
require.True(t, os.IsNotExist(err))
})
t.Run("StaticFS plugin removal is skipped via installer.Remove", func(t *testing.T) {
pluginDir := filepath.Join(tmpDir, "staticfs-plugin")
err := os.MkdirAll(pluginDir, 0750)
require.NoError(t, err)
pluginJSON := `{
"id": "staticfs-plugin",
"name": "StaticFS Plugin",
"type": "datasource",
"info": {
"version": "1.0.0"
}
}`
err = os.WriteFile(filepath.Join(pluginDir, "plugin.json"), []byte(pluginJSON), 0644)
require.NoError(t, err)
localFS := plugins.NewLocalFS(pluginDir)
staticFS, err := plugins.NewStaticFS(localFS)
require.NoError(t, err)
pluginV1 := createPlugin(t, "staticfs-plugin", plugins.ClassExternal, true, true, func(plugin *plugins.Plugin) {
plugin.Info.Version = "1.0.0"
plugin.FS = staticFS
})
registry := &fakes.FakePluginRegistry{
Store: map[string]*plugins.Plugin{
"staticfs-plugin": pluginV1,
},
}
loader := &fakes.FakeLoader{
UnloadFunc: func(_ context.Context, p *plugins.Plugin) (*plugins.Plugin, error) {
return p, nil
},
}
_, err = os.Stat(pluginDir)
require.NoError(t, err)
inst := New(registry, loader, &fakes.FakePluginRepo{}, &fakes.FakePluginStorage{}, storage.SimpleDirNameGeneratorFunc, &fakes.FakeAuthService{})
err = inst.Remove(context.Background(), "staticfs-plugin", "1.0.0")
require.NoError(t, err)
_, err = os.Stat(pluginDir)
require.ErrorIs(t, err, os.ErrNotExist)
})
}

View File

@@ -3,8 +3,8 @@ package datasource
import (
"context"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-aws-sdk/pkg/sigv4"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
)
@@ -16,8 +16,7 @@ func contextualMiddlewares(ctx context.Context) context.Context {
sigv4Settings := awsds.ReadSigV4Settings(ctx)
if sigv4Settings.Enabled {
authSettings, _ := awsds.ReadAuthSettingsFromContext(ctx)
ctx = httpclient.WithContextualMiddleware(ctx, sigv4.SigV4MiddlewareWithAuthSettings(sigv4Settings.VerboseLogging, *authSettings))
ctx = httpclient.WithContextualMiddleware(ctx, awsauth.NewSigV4Middleware())
}
return ctx

View File

@@ -351,7 +351,7 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
ossDataSourceRequestURLValidator := validations.ProvideURLValidator()
httpclientProvider := httpclientprovider.New(cfg, ossDataSourceRequestURLValidator, tracingService)
azuremonitorService := azuremonitor.ProvideService(httpclientProvider)
cloudWatchService := cloudwatch.ProvideService(httpclientProvider)
cloudwatchService := cloudwatch.ProvideService()
cloudmonitoringService := cloudmonitoring.ProvideService(httpclientProvider)
elasticsearchService := elasticsearch.ProvideService(httpclientProvider)
graphiteService := graphite.ProvideService(httpclientProvider, tracingService)
@@ -446,7 +446,7 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser
parcaService := parca.ProvideService(httpclientProvider)
zipkinService := zipkin.ProvideService(httpclientProvider)
jaegerService := jaeger.ProvideService(httpclientProvider)
corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudWatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService)
corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudwatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService)
providerService := provider2.ProvideService(corepluginRegistry)
processService := process.ProvideService()
retrieverService := retriever.ProvideService(sqlStore, apikeyService, kvStore, userService, orgService)
@@ -861,7 +861,7 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface {
ossDataSourceRequestURLValidator := validations.ProvideURLValidator()
httpclientProvider := httpclientprovider.New(cfg, ossDataSourceRequestURLValidator, tracingService)
azuremonitorService := azuremonitor.ProvideService(httpclientProvider)
cloudWatchService := cloudwatch.ProvideService(httpclientProvider)
cloudwatchService := cloudwatch.ProvideService()
cloudmonitoringService := cloudmonitoring.ProvideService(httpclientProvider)
elasticsearchService := elasticsearch.ProvideService(httpclientProvider)
graphiteService := graphite.ProvideService(httpclientProvider, tracingService)
@@ -956,7 +956,7 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface {
parcaService := parca.ProvideService(httpclientProvider)
zipkinService := zipkin.ProvideService(httpclientProvider)
jaegerService := jaeger.ProvideService(httpclientProvider)
corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudWatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService)
corepluginRegistry := coreplugin.ProvideCoreRegistry(tracingService, azuremonitorService, cloudwatchService, cloudmonitoringService, elasticsearchService, graphiteService, influxdbService, lokiService, opentsdbService, prometheusService, tempoService, testdatasourceService, postgresService, mysqlService, mssqlService, grafanadsService, pyroscopeService, parcaService, zipkinService, jaegerService)
providerService := provider2.ProvideService(corepluginRegistry)
processService := process.ProvideService()
retrieverService := retriever.ProvideService(sqlStore, apikeyService, kvStore, userService, orgService)

View File

@@ -12,6 +12,7 @@ import (
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/sqlstore/permissions"
"github.com/grafana/grafana/pkg/services/sqlstore/searchstore"
"github.com/grafana/grafana/pkg/setting"
)
var (
@@ -28,16 +29,21 @@ var (
)
type AuthService struct {
db db.DB
features featuremgmt.FeatureToggles
dashSvc dashboards.DashboardService
db db.DB
features featuremgmt.FeatureToggles
dashSvc dashboards.DashboardService
searchDashboardsPageLimit int64
}
func NewAuthService(db db.DB, features featuremgmt.FeatureToggles, dashSvc dashboards.DashboardService) *AuthService {
func NewAuthService(db db.DB, features featuremgmt.FeatureToggles, dashSvc dashboards.DashboardService, cfg *setting.Cfg) *AuthService {
section := cfg.Raw.Section("annotations")
searchDashboardsPageLimit := section.Key("search_dashboards_page_limit").MustInt64(1000)
return &AuthService{
db: db,
features: features,
dashSvc: dashSvc,
db: db,
features: features,
dashSvc: dashSvc,
searchDashboardsPageLimit: searchDashboardsPageLimit,
}
}
@@ -142,7 +148,7 @@ func (authz *AuthService) dashboardsWithVisibleAnnotations(ctx context.Context,
SignedInUser: query.SignedInUser,
Page: query.Page,
Type: filterType,
Limit: 1000,
Limit: authz.searchDashboardsPageLimit,
})
if err != nil {
return nil, err

View File

@@ -207,7 +207,7 @@ func TestIntegrationAuthorize(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
u.Permissions = map[int64]map[string][]string{1: tc.permissions}
testutil.SetupRBACPermission(t, sql, role, u)
authz := NewAuthService(sql, featuremgmt.WithFeatures(tc.featureToggle), dashSvc)
authz := NewAuthService(sql, featuremgmt.WithFeatures(tc.featureToggle), dashSvc, cfg)
query := annotations.ItemQuery{SignedInUser: u, OrgID: 1}
resources, err := authz.Authorize(context.Background(), query)

View File

@@ -53,7 +53,7 @@ func ProvideService(
return &RepositoryImpl{
db: db,
features: features,
authZ: accesscontrol.NewAuthService(db, features, dashSvc),
authZ: accesscontrol.NewAuthService(db, features, dashSvc, cfg),
reader: read,
writer: write,
}

View File

@@ -794,8 +794,9 @@ func (alertRule *AlertRule) Copy() *AlertRule {
if alertRule.Record != nil {
result.Record = &Record{
From: alertRule.Record.From,
Metric: alertRule.Record.Metric,
From: alertRule.Record.From,
Metric: alertRule.Record.Metric,
TargetDatasourceUID: alertRule.Record.TargetDatasourceUID,
}
}

View File

@@ -1012,6 +1012,13 @@ func TestAlertRuleCopy(t *testing.T) {
copied := rule.Copy()
require.NotSame(t, rule.Metadata.PrometheusStyleRule, copied.Metadata.PrometheusStyleRule)
})
t.Run("should return an exact copy of recording rule", func(t *testing.T) {
for i := 0; i < 100; i++ {
rule := RuleGen.With(RuleGen.WithAllRecordingRules()).GenerateRef()
copied := rule.Copy()
require.Empty(t, rule.Diff(copied))
}
})
}
// This test makes sure the default generator
@@ -1051,6 +1058,48 @@ func TestGeneratorFillsAllFields(t *testing.T) {
require.FailNow(t, "AlertRule generator does not populate fields", "skipped fields: %v", maps.Keys(fields))
}
func TestGeneratorFillsAllRecordingRuleFields(t *testing.T) {
ignoredFields := map[string]struct{}{
"ID": {},
"IsPaused": {},
"NoDataState": {},
"ExecErrState": {},
"Condition": {},
"KeepFiringFor": {},
"MissingSeriesEvalsToResolve": {},
"For": {},
"NotificationSettings": {},
}
tpe := reflect.TypeOf(AlertRule{})
fields := make(map[string]struct{}, tpe.NumField())
for i := 0; i < tpe.NumField(); i++ {
if _, ok := ignoredFields[tpe.Field(i).Name]; ok {
continue
}
fields[tpe.Field(i).Name] = struct{}{}
}
for i := 0; i < 1000; i++ {
rule := RuleGen.With(RuleGen.WithAllRecordingRules()).Generate()
v := reflect.ValueOf(rule)
for j := 0; j < tpe.NumField(); j++ {
field := tpe.Field(j)
value := v.Field(j)
if !value.IsValid() || value.Kind() == reflect.Ptr && value.IsNil() || value.IsZero() {
continue
}
delete(fields, field.Name)
if len(fields) == 0 {
return
}
}
}
require.FailNow(t, "AlertRule generator does not populate fields", "skipped fields: %v", maps.Keys(fields))
}
func TestValidateAlertRule(t *testing.T) {
t.Run("keepFiringFor", func(t *testing.T) {
testCases := []struct {

View File

@@ -561,6 +561,14 @@ func (a *AlertRuleMutators) WithAllRecordingRules() AlertRuleMutator {
}
}
func (a *AlertRuleMutators) WithoutTargetDataSource() AlertRuleMutator {
return func(rule *AlertRule) {
if rule.Record != nil {
rule.Record.TargetDatasourceUID = ""
}
}
}
func (a *AlertRuleMutators) WithMetric(metric string) AlertRuleMutator {
return func(rule *AlertRule) {
if rule.Record == nil {
@@ -1363,10 +1371,14 @@ func ConvertToRecordingRule(rule *AlertRule) {
if rule.Record.Metric == "" {
rule.Record.Metric = fmt.Sprintf("some_metric_%s", util.GenerateShortUID())
}
if rule.Record.TargetDatasourceUID == "" {
rule.Record.TargetDatasourceUID = util.GenerateShortUID()
}
rule.Condition = ""
rule.NoDataState = ""
rule.ExecErrState = ""
rule.For = 0
rule.KeepFiringFor = 0
rule.NotificationSettings = nil
rule.MissingSeriesEvalsToResolve = nil
}

View File

@@ -229,7 +229,11 @@ func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQu
}
}
ruleList = append(ruleList, r)
if q.ReceiverName != "" && (len(r.NotificationSettings) < 1 || r.NotificationSettings[0].Receiver != q.ReceiverName) {
continue
}
copyR := models.CopyRule(r)
ruleList = append(ruleList, copyR)
}
return ruleList, nil

View File

@@ -75,7 +75,7 @@ func TestIntegrationPluginManager(t *testing.T) {
hcp := httpclient.NewProvider()
am := azuremonitor.ProvideService(hcp)
cw := cloudwatch.ProvideService(hcp)
cw := cloudwatch.ProvideService()
cm := cloudmonitoring.ProvideService(hcp)
es := elasticsearch.ProvideService(hcp)
grap := graphite.ProvideService(hcp, tracer)

View File

@@ -210,4 +210,12 @@ func AddMigration(mg *migrator.Migrator) {
Type: migrator.UniqueIndex,
Cols: []string{"org_id", "user_id", "role_id"},
}))
mg.AddMigration("add permission role_id action index", migrator.NewAddIndexMigration(permissionV1, &migrator.Index{
Cols: []string{"role_id", "action"},
}))
mg.AddMigration("Remove permission role_id index", migrator.NewDropIndexMigration(permissionV1, &migrator.Index{
Cols: []string{"role_id"},
}))
}

View File

@@ -149,7 +149,12 @@ func (b *Builder) applyFilters() (ordering string) {
}
}
b.sql.WriteString("SELECT dashboard.id FROM dashboard")
forceIndex := ""
if b.Dialect.DriverName() == migrator.MySQL {
forceIndex = " FORCE INDEX (IDX_dashboard_title) "
}
b.sql.WriteString(fmt.Sprintf("SELECT dashboard.id FROM dashboard %s", forceIndex))
b.sql.WriteString(strings.Join(joins, ""))
if len(wheres) > 0 {

View File

@@ -76,25 +76,25 @@ 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.30.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2 v1.39.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // 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.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // 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.3.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
github.com/aws/smithy-go v1.23.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -178,7 +178,7 @@ require (
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.9.2 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/gofrs/uuid v4.4.0+incompatible // indirect
@@ -207,13 +207,13 @@ require (
github.com/googleapis/go-sql-spanner v1.11.1 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430 // indirect
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8 // indirect
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d // indirect
github.com/grafana/dataplane/sdata v0.0.9 // indirect
github.com/grafana/dskit v0.0.0-20241105154643-a6b453a88040 // indirect
github.com/grafana/grafana-app-sdk v0.35.1 // indirect
github.com/grafana/grafana-app-sdk/logging v0.35.1 // indirect
github.com/grafana/grafana-aws-sdk v0.31.5 // indirect
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b // indirect
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 // indirect
github.com/grafana/grafana-plugin-sdk-go v0.277.0 // indirect
github.com/grafana/grafana/apps/folder v0.0.0-20250414115220-48647355c37b // indirect

View File

@@ -740,44 +740,44 @@ 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.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU=
github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
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.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
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.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps=
@@ -1080,8 +1080,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
@@ -1265,8 +1265,8 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430 h1:qT0D7AIV0GRu8JUrSJYuyzj86kqLgksKQjwD++DqyOM=
github.com/grafana/alerting v0.0.0-20250411135245-cad0d384d430/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8 h1:tXDlMjugyEbsrSJC2Np/9ZvBzEoa1xB+KGHUBnvPIFw=
github.com/grafana/alerting v0.0.0-20250903141736-c9c007e7f0a8/go.mod h1:3ER/8BhIEhvrddcztLQSc5ez1f1jNHIPdquc1F+DzOw=
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d h1:TDVZemfYeJHPyXeYCnqL7BQqsa+mpaZYth/Qm3TKaT8=
github.com/grafana/authlib v0.0.0-20250325095148-d6da9c164a7d/go.mod h1:PBtQaXwkFu4BAt2aXsR7w8p8NVpdjV5aJYhqRDei9Us=
github.com/grafana/authlib/types v0.0.0-20250325095148-d6da9c164a7d h1:34E6btDAhdDOiSEyrMaYaHwnJpM8w9QKzVQZIBzLNmM=
@@ -1281,8 +1281,8 @@ github.com/grafana/grafana-app-sdk v0.35.1 h1:zEXubzsQrxGBOzXJJMBwhEClC/tvPi0sfK
github.com/grafana/grafana-app-sdk v0.35.1/go.mod h1:Zx5MkVppYK+ElSDUAR6+fjzOVo6I/cIgk+ty+LmNOxI=
github.com/grafana/grafana-app-sdk/logging v0.35.1 h1:taVpl+RoixTYl0JBJGhH+fPVmwA9wvdwdzJTZsv9buM=
github.com/grafana/grafana-app-sdk/logging v0.35.1/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana-aws-sdk v0.31.5 h1:4HpMQx7n4Qqoi7Bgu8KHQ2QKT9fYYdHilX/Gh3FZKBE=
github.com/grafana/grafana-aws-sdk v0.31.5/go.mod h1:5p4Cjyr5ZiR6/RT2nFWkJ8XpIKgX4lAUmUMu70m2yCM=
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b h1:25XvbNSo8Sgi3MBFOtToRvlbXwfQ44nTb/NYCpZxUJE=
github.com/grafana/grafana-aws-sdk v0.31.6-0.20250918141554-0b96cca5e46b/go.mod h1:2JJbVLc3jjWu0aAQx4bhPa2ChOUjLs9x7ime6mB4O+o=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6 h1:OfCkitCuomzZKW1WYHrG8MxKwtMhALb7jqoj+487eTg=
github.com/grafana/grafana-azure-sdk-go/v2 v2.1.6/go.mod h1:V7y2BmsWxS3A9Ohebwn4OiSfJJqi//4JQydQ8fHTduo=
github.com/grafana/grafana-plugin-sdk-go v0.277.0 h1:VDU2F4Y5NeRS//ejctdZtsAshrGaEdbtW33FsK0EQss=

View File

@@ -58,25 +58,25 @@ require (
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/apache/arrow-go/v18 v18.2.0 // indirect
github.com/aws/aws-sdk-go v1.55.7 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.27 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
github.com/aws/aws-sdk-go-v2 v1.39.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 // indirect
github.com/aws/aws-sdk-go-v2/config v1.31.8 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 // 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.15 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // 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.3.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
github.com/aws/smithy-go v1.20.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
github.com/aws/smithy-go v1.23.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bufbuild/protocompile v0.4.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect

View File

@@ -70,44 +70,44 @@ github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE=
github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw=
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.30.3 h1:jUeBtG0Ih+ZIFH0F4UkmL9w3cSpaMv9tYYDbzILP8dY=
github.com/aws/aws-sdk-go-v2 v1.30.3/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3 h1:tW1/Rkad38LA15X4UQtjXZXNKsCgkshC3EbmcUmghTg=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.3/go.mod h1:UbnqO+zjqk3uIt9yCACHJ9IVNhyhOCnYk8yA19SAWrM=
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
github.com/aws/aws-sdk-go-v2 v1.39.0 h1:xm5WV/2L4emMRmMjHFykqiA4M/ra0DJVSWUkDyBjbg4=
github.com/aws/aws-sdk-go-v2 v1.39.0/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1 h1:i8p8P4diljCr60PpJp6qZXNlgX4m2yQFpYk+9ZT+J4E=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.1/go.mod h1:ddqbooRZYNoJ2dsTwOty16rM+/Aqmk/GOXrK8cg7V00=
github.com/aws/aws-sdk-go-v2/config v1.31.8 h1:kQjtOLlTU4m4A64TsRcqwNChhGCwaPBt+zCQt/oWsHU=
github.com/aws/aws-sdk-go-v2/config v1.31.8/go.mod h1:QPpc7IgljrKwH0+E6/KolCgr4WPLerURiU592AYzfSY=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12 h1:zmc9e1q90wMn8wQbjryy8IwA6Q4XlaL9Bx2zIqdNNbk=
github.com/aws/aws-sdk-go-v2/credentials v1.18.12/go.mod h1:3VzdRDR5u3sSJRI4kYcOSIBbeYsgtVk7dG5R/U6qLWY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7 h1:Is2tPmieqGS2edBnmOJIbdvOA6Op+rRpaYR60iBAwXM=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.7/go.mod h1:F1i5V5421EGci570yABvpIXgRIBPb5JM+lSkHF6Dq5w=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10 h1:zeN9UtUlA6FTx0vFSayxSX32HDw73Yb6Hh2izDSFxXY=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15 h1:SoNJ4RlFEQEbtDcCEt+QG56MY4fm4W8rYirAmq+/DdU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.15/go.mod h1:U9ke74k1n2bf+RIgoX1SXFed1HLs51OgUSs+Ph0KJP8=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15 h1:C6WHdGnTDIYETAm5iErQUiVNsclNx9qbJVPIt03B6bI=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.15/go.mod h1:ZQLZqhcu+JhSrA9/NXRm8SkDvsycE+JkV3WGY41e+IM=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7 h1:UCxq0X9O3xrlENdKf1r9eRJoKz/b0AfGkpp3a7FPlhg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.7/go.mod h1:rHRoJUNUASj5Z/0eqI4w32vKvC7atoWR0jC+IkmVH8k=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7 h1:Y6DTZUn7ZUC4th9FMBbo8LVE+1fyq3ofw+tRwkUd3PY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.7/go.mod h1:x3XE6vMnU9QvHN/Wrx2s44kwzV2o2g5x/siw4ZUJ9g8=
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.15 h1:Z5r7SycxmSllHYmaAZPpmN8GviDrSGhMS6bldqtXZPw=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.15/go.mod h1:CetW7bDE00QoGEmPUoZuRog07SGVAUVW6LFpNP0YfIg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
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.3.17 h1:YPYe6ZmvUfDDDELqEKtAd6bo8zxhkm+XEFEzQisqUIE=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.17/go.mod h1:oBtcnYua/CgzCWYN7NZ5j7PotFDaFSUjCYVTtfyn7vw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7 h1:mLgc5QIgOy26qyh5bvW+nDoAppxgn3J2WV3m9ewq7+8=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.7/go.mod h1:wXb/eQnqt8mDQIQTTmcw58B5mYGxzLGZGK8PWNFZ0BA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15 h1:246A4lSTXWJw/rmlQI+TT2OcqeDMKBdyjEQrafMaQdA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.15/go.mod h1:haVfg3761/WF7YPuJOER2MP0k4UAXyHaLclKXB6usDg=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3 h1:hT8ZAZRIfqBqHbzKTII+CIiY8G2oC9OpLedkZ51DWl8=
github.com/aws/aws-sdk-go-v2/service/s3 v1.58.3/go.mod h1:Lcxzg5rojyVPU/0eFwLtcyTaek/6Mtic5B1gJo7e/zE=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE=
github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3 h1:7PKX3VYsZ8LUWceVRuv0+PU+E7OtQb1lgmi5vmUE9CM=
github.com/aws/aws-sdk-go-v2/service/sso v1.29.3/go.mod h1:Ql6jE9kyyWI5JHn+61UT/Y5Z0oyVJGmgmJbZD5g4unY=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 h1:e0XBRn3AptQotkyBFrHAxFB8mDhAIOfsG+7KyJ0dg98=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4/go.mod h1:XclEty74bsGBCr1s0VSaA11hQ4ZidK4viWK7rRfO88I=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 h1:PR00NXRYgY4FWHqOGx3fC3lhVKjsp1GdloDv2ynMSd8=
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4/go.mod h1:Z+Gd23v97pX9zK97+tX4ppAgqCt3Z2dIXB02CtBncK8=
github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=

View File

@@ -1,24 +1,45 @@
package routes
package cloudwatch
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
"github.com/patrickmn/go-cache"
"github.com/stretchr/testify/assert"
)
func newTestDatasource(opts ...func(*DataSource)) *DataSource {
ds := &DataSource{
AWSConfigProvider: awsauth.NewFakeConfigProvider(false),
logger: log.NewNullLogger(),
tagValueCache: cache.New(0, 0),
Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"},
},
}
ds.resourceHandler = httpadapter.New(ds.newResourceMux())
for _, opt := range opts {
opt(ds)
}
return ds
}
func Test_accounts_route(t *testing.T) {
origNewAccountsService := newAccountsService
ds := newTestDatasource()
origNewAccountsService := services.NewAccountsService
t.Cleanup(func() {
newAccountsService = origNewAccountsService
services.NewAccountsService = origNewAccountsService
})
t.Run("successfully returns array of accounts json", func(t *testing.T) {
@@ -31,13 +52,13 @@ func Test_accounts_route(t *testing.T) {
IsMonitoringAccount: true,
},
}}, nil)
newAccountsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.AccountsProvider, error) {
return &mockAccountsService, nil
services.NewAccountsService = func(_ models.OAMAPIProvider) models.AccountsProvider {
return &mockAccountsService
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/accounts?region=us-east-1", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(AccountsHandler, logger, nil))
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.AccountsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
@@ -47,7 +68,7 @@ func Test_accounts_route(t *testing.T) {
t.Run("rejects POST method", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/accounts?region=us-east-1", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(AccountsHandler, logger, nil))
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.AccountsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
})
@@ -55,7 +76,7 @@ func Test_accounts_route(t *testing.T) {
t.Run("requires region query value", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/accounts", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(AccountsHandler, logger, nil))
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.AccountsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
})
@@ -64,13 +85,13 @@ func Test_accounts_route(t *testing.T) {
mockAccountsService := mocks.AccountsServiceMock{}
mockAccountsService.On("GetAccountsForCurrentUserOrRole").Return([]resources.ResourceResponse[resources.Account](nil),
fmt.Errorf("%w: %s", services.ErrAccessDeniedException, "some AWS message"))
newAccountsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.AccountsProvider, error) {
return &mockAccountsService, nil
services.NewAccountsService = func(_ models.OAMAPIProvider) models.AccountsProvider {
return &mockAccountsService
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/accounts?region=us-east-1", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(AccountsHandler, logger, nil))
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.AccountsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusForbidden, rr.Code)
@@ -82,13 +103,13 @@ func Test_accounts_route(t *testing.T) {
t.Run("returns 500 when accounts service returns unknown error", func(t *testing.T) {
mockAccountsService := mocks.AccountsServiceMock{}
mockAccountsService.On("GetAccountsForCurrentUserOrRole").Return([]resources.ResourceResponse[resources.Account](nil), fmt.Errorf("some error"))
newAccountsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.AccountsProvider, error) {
return &mockAccountsService, nil
services.NewAccountsService = func(_ models.OAMAPIProvider) models.AccountsProvider {
return &mockAccountsService
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/accounts?region=us-east-1", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(AccountsHandler, logger, nil))
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.AccountsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)

View File

@@ -7,8 +7,10 @@ import (
"strconv"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
@@ -21,7 +23,7 @@ type annotationEvent struct {
Text string
}
func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginCtx backend.PluginContext, model DataQueryJson, query backend.DataQuery) (*backend.QueryDataResponse, error) {
func (ds *DataSource) executeAnnotationQuery(ctx context.Context, model DataQueryJson, query backend.DataQuery) (*backend.QueryDataResponse, error) {
result := backend.NewQueryDataResponse()
statistic := ""
@@ -29,14 +31,14 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
statistic = *model.Statistic
}
var period int64
var period int32
if model.Period != nil && *model.Period != "" {
p, err := strconv.ParseInt(*model.Period, 10, 64)
p, err := strconv.ParseInt(*model.Period, 10, 32)
if err != nil {
return nil, backend.DownstreamError(fmt.Errorf("query period must be an int"))
}
period = p
period = int32(p)
}
prefixMatching := false
@@ -50,7 +52,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
actionPrefix := model.ActionPrefix
alarmNamePrefix := model.AlarmNamePrefix
cli, err := e.getCWClient(ctx, pluginCtx, model.Region)
cli, err := ds.getCWClient(ctx, model.Region)
if err != nil {
result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(fmt.Errorf("%v: %w", "failed to get client", err))
return result, nil
@@ -69,11 +71,11 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
if prefixMatching {
params := &cloudwatch.DescribeAlarmsInput{
MaxRecords: aws.Int64(100),
MaxRecords: aws.Int32(100),
ActionPrefix: actionPrefix,
AlarmNamePrefix: alarmNamePrefix,
}
resp, err := cli.DescribeAlarms(params)
resp, err := cli.DescribeAlarms(ctx, params)
if err != nil {
result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarms", err)))
return result, nil
@@ -84,10 +86,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
return result, backend.DownstreamError(errors.New("invalid annotations query"))
}
var qd []*cloudwatch.Dimension
var qd []cloudwatchtypes.Dimension
for k, v := range dimensions {
for _, vvv := range v.ArrayOfString {
qd = append(qd, &cloudwatch.Dimension{
qd = append(qd, cloudwatchtypes.Dimension{
Name: aws.String(k),
Value: aws.String(vvv),
})
@@ -97,10 +99,10 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
Namespace: aws.String(model.Namespace),
MetricName: aws.String(metricName),
Dimensions: qd,
Statistic: aws.String(statistic),
Period: aws.Int64(period),
Statistic: cloudwatchtypes.Statistic(statistic),
Period: aws.Int32(period),
}
resp, err := cli.DescribeAlarmsForMetric(params)
resp, err := cli.DescribeAlarmsForMetric(ctx, params)
if err != nil {
result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmsForMetric", err)))
return result, nil
@@ -116,9 +118,9 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
AlarmName: alarmName,
StartDate: aws.Time(query.TimeRange.From),
EndDate: aws.Time(query.TimeRange.To),
MaxRecords: aws.Int64(100),
MaxRecords: aws.Int32(100),
}
resp, err := cli.DescribeAlarmHistory(params)
resp, err := cli.DescribeAlarmHistory(ctx, params)
if err != nil {
result.Responses[query.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("%v: %w", "failed to call cloudwatch:DescribeAlarmHistory", err)))
return result, nil
@@ -127,7 +129,7 @@ func (e *cloudWatchExecutor) executeAnnotationQuery(ctx context.Context, pluginC
annotations = append(annotations, &annotationEvent{
Time: *history.Timestamp,
Title: *history.AlarmName,
Tags: *history.HistoryItemType,
Tags: string(history.HistoryItemType),
Text: *history.HistorySummary,
})
}
@@ -162,7 +164,7 @@ func transformAnnotationToTable(annotations []*annotationEvent, query backend.Da
}
func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, metricName string,
dimensions dataquery.Dimensions, statistic string, period int64) []*string {
dimensions dataquery.Dimensions, statistic string, period int32) []*string {
alarmNames := make([]*string, 0)
for _, alarm := range alarms.MetricAlarms {
@@ -189,7 +191,7 @@ func filterAlarms(alarms *cloudwatch.DescribeAlarmsOutput, namespace string, met
continue
}
if *alarm.Statistic != statistic {
if string(alarm.Statistic) != statistic {
continue
}

View File

@@ -5,33 +5,31 @@ import (
"encoding/json"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestQuery_AnnotationQuery(t *testing.T) {
ds := newTestDatasource()
origNewCWClient := NewCWClient
t.Cleanup(func() {
NewCWClient = origNewCWClient
})
var client fakeCWAnnotationsClient
NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
NewCWClient = func(aws.Config) models.CWClient {
return &client
}
t.Run("DescribeAlarmsForMetric is called with minimum parameters", func(t *testing.T) {
client = fakeCWAnnotationsClient{describeAlarmsForMetricOutput: &cloudwatch.DescribeAlarmsForMetricOutput{}}
im := defaultTestInstanceManager()
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -53,17 +51,15 @@ func TestQuery_AnnotationQuery(t *testing.T) {
assert.Equal(t, &cloudwatch.DescribeAlarmsForMetricInput{
Namespace: aws.String("custom"),
MetricName: aws.String("CPUUtilization"),
Statistic: aws.String("Average"),
Period: aws.Int64(300),
Statistic: "Average",
Period: aws.Int32(300),
}, client.calls.describeAlarmsForMetric[0])
})
t.Run("DescribeAlarms is called when prefixMatching is true", func(t *testing.T) {
client = fakeCWAnnotationsClient{describeAlarmsOutput: &cloudwatch.DescribeAlarmsOutput{}}
im := defaultTestInstanceManager()
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -86,7 +82,7 @@ func TestQuery_AnnotationQuery(t *testing.T) {
require.Len(t, client.calls.describeAlarms, 1)
assert.Equal(t, &cloudwatch.DescribeAlarmsInput{
MaxRecords: aws.Int64(100),
MaxRecords: aws.Int32(100),
ActionPrefix: aws.String("some_action_prefix"),
AlarmNamePrefix: aws.String("some_alarm_name_prefix"),
}, client.calls.describeAlarms[0])

View File

@@ -1,64 +1,53 @@
package cloudwatch
import (
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/oam"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/aws/aws-sdk-go-v2/service/oam"
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
// NewMetricsAPI is a CloudWatch metrics api factory.
// NewCWClient is a CloudWatch metrics api factory.
//
// Stubbable by tests.
var NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider {
return cloudwatch.New(sess)
var NewCWClient = func(cfg aws.Config) models.CWClient {
return cloudwatch.NewFromConfig(cfg)
}
// NewLogsAPI is a CloudWatch logs api factory.
//
// Stubbable by tests.
var NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider {
return cloudwatchlogs.New(sess)
var NewLogsAPI = func(cfg aws.Config) models.CloudWatchLogsAPIProvider {
return cloudwatchlogs.NewFromConfig(cfg)
}
// NewOAMAPI is a CloudWatch OAM api factory.
// NewOAMAPI is a CloudWatch OAM API factory
//
// Stubbable by tests.
var NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider {
return oam.New(sess)
var NewOAMAPI = func(cfg aws.Config) models.OAMAPIProvider {
return oam.NewFromConfig(cfg)
}
// NewCWClient is a CloudWatch client factory.
// NewEC2API is a CloudWatch EC2 API factory
//
// Stubbable by tests.
var NewCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
return cloudwatch.New(sess)
// Stubbable by tests
var NewEC2API = func(cfg aws.Config) models.EC2APIProvider {
return ec2.NewFromConfig(cfg)
}
// NewCWLogsClient is a CloudWatch logs client factory.
//
// Stubbable by tests.
var NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
return cloudwatchlogs.New(sess)
var NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient {
return cloudwatchlogs.NewFromConfig(cfg)
}
// NewEC2Client is a client factory.
// NewRGTAClient is a ResourceGroupsTaggingAPI Client factory.
//
// Stubbable by tests.
var NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider {
return ec2.New(provider)
}
// RGTA client factory.
//
// Stubbable by tests.
var newRGTAClient = func(provider client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI {
return resourcegroupstaggingapi.New(provider)
var NewRGTAClient = func(cfg aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient {
return resourcegroupstaggingapi.NewFromConfig(cfg)
}

View File

@@ -3,41 +3,42 @@ package clients
import (
"context"
"github.com/aws/aws-sdk-go/aws/awsutil"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
)
// this client wraps the CloudWatch API and handles pagination and the composition of the MetricResponse DTO
type metricsClient struct {
models.CloudWatchMetricsAPIProvider
type MetricsClient struct {
cloudwatch.ListMetricsAPIClient
listMetricsPageLimit int
}
func NewMetricsClient(api models.CloudWatchMetricsAPIProvider, pageLimit int) *metricsClient {
return &metricsClient{CloudWatchMetricsAPIProvider: api, listMetricsPageLimit: pageLimit}
func NewMetricsClient(client cloudwatch.ListMetricsAPIClient, listMetricsPageLimit int) *MetricsClient {
return &MetricsClient{
ListMetricsAPIClient: client,
listMetricsPageLimit: listMetricsPageLimit,
}
}
func (l *metricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) {
var cloudWatchMetrics []resources.MetricResponse
pageNum := 0
err := l.ListMetricsPagesWithContext(ctx, params, func(page *cloudwatch.ListMetricsOutput, lastPage bool) bool {
pageNum++
utils.QueriesTotalCounter.WithLabelValues(utils.ListMetricsLabel).Inc()
metrics, err := awsutil.ValuesAtPath(page, "Metrics")
if err == nil {
for idx, metric := range metrics {
metric := resources.MetricResponse{Metric: metric.(*cloudwatch.Metric)}
if len(page.OwningAccounts) >= idx && params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts {
metric.AccountId = page.OwningAccounts[idx]
}
cloudWatchMetrics = append(cloudWatchMetrics, metric)
}
func (mc *MetricsClient) ListMetricsWithPageLimit(ctx context.Context, params *cloudwatch.ListMetricsInput) ([]resources.MetricResponse, error) {
var responses []resources.MetricResponse
paginator := cloudwatch.NewListMetricsPaginator(mc.ListMetricsAPIClient, params)
includeAccount := params.IncludeLinkedAccounts != nil && *params.IncludeLinkedAccounts
pages := 0
for paginator.HasMorePages() && pages < mc.listMetricsPageLimit {
pages += 1
page, err := paginator.NextPage(ctx)
if err != nil {
return responses, err
}
return !lastPage && pageNum < l.listMetricsPageLimit
})
return cloudWatchMetrics, err
for i, metric := range page.Metrics {
resp := resources.MetricResponse{Metric: metric}
if includeAccount && len(page.OwningAccounts) >= i {
resp.AccountId = &page.OwningAccounts[i]
}
responses = append(responses, resp)
}
}
return responses, nil
}

View File

@@ -4,16 +4,19 @@ import (
"context"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMetricsClient(t *testing.T) {
metrics := []*cloudwatch.Metric{
metrics := []cloudwatchtypes.Metric{
{MetricName: aws.String("Test_MetricName1")},
{MetricName: aws.String("Test_MetricName2")},
{MetricName: aws.String("Test_MetricName3")},
@@ -50,25 +53,25 @@ func TestMetricsClient(t *testing.T) {
})
t.Run("Should return account id in case IncludeLinkedAccounts is set to true", func(t *testing.T) {
fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{
fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: aws.String("Test_MetricName1")},
{MetricName: aws.String("Test_MetricName2")},
{MetricName: aws.String("Test_MetricName3")},
}, OwningAccounts: []*string{aws.String("1234567890"), aws.String("1234567890"), aws.String("1234567895")}}
}, OwningAccounts: []string{"1234567890", "1234567890", "1234567895"}}
client := NewMetricsClient(fakeApi, 100)
response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(true)})
require.NoError(t, err)
expected := []resources.MetricResponse{
{Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")},
{Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")},
{Metric: &cloudwatch.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")},
{Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName1")}, AccountId: stringPtr("1234567890")},
{Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName2")}, AccountId: stringPtr("1234567890")},
{Metric: cloudwatchtypes.Metric{MetricName: aws.String("Test_MetricName3")}, AccountId: stringPtr("1234567895")},
}
assert.Equal(t, expected, response)
})
t.Run("Should not return account id in case IncludeLinkedAccounts is set to false", func(t *testing.T) {
fakeApi := &mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []*string{aws.String("1234567890")}}
fakeApi := &mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{{MetricName: aws.String("Test_MetricName1")}}, OwningAccounts: []string{"1234567890"}}
client := NewMetricsClient(fakeApi, 100)
response, err := client.ListMetricsWithPageLimit(ctx, &cloudwatch.ListMetricsInput{IncludeLinkedAccounts: aws.Bool(false)})

View File

@@ -3,22 +3,19 @@ package cloudwatch
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"slices"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
@@ -37,22 +34,7 @@ const (
// headerFromAlert is used by datasources to identify alert queries
headerFromAlert = "FromAlert"
)
type DataQueryJson struct {
dataquery.CloudWatchAnnotationQuery
Type string `json:"type,omitempty"`
}
type DataSource struct {
Settings models.CloudWatchSettings
HTTPClient *http.Client
sessions SessionCache
tagValueCache *cache.Cache
ProxyOpts *proxy.Options
}
const (
defaultRegion = "default"
logsQueryMode = "Logs"
// QueryTypes
@@ -61,74 +43,72 @@ const (
timeSeriesQuery = "timeSeriesQuery"
)
func ProvideService(httpClientProvider *httpclient.Provider) *CloudWatchService {
logger := backend.NewLoggerWith("logger", "tsdb.cloudwatch")
logger.Debug("Initializing")
executor := newExecutor(
datasource.NewInstanceManager(NewInstanceSettings(httpClientProvider)),
logger,
)
return &CloudWatchService{
Executor: executor,
}
type DataQueryJson struct {
dataquery.CloudWatchAnnotationQuery
Type string `json:"type,omitempty"`
}
type CloudWatchService struct {
Executor *cloudWatchExecutor
}
type SessionCache interface {
GetSessionWithAuthSettings(c awsds.GetSessionConfig, as awsds.AuthSettings) (*session.Session, error)
}
func newExecutor(im instancemgmt.InstanceManager, logger log.Logger) *cloudWatchExecutor {
e := &cloudWatchExecutor{
im: im,
logger: logger,
}
e.resourceHandler = httpadapter.New(e.newResourceMux())
return e
}
func NewInstanceSettings(httpClientProvider *httpclient.Provider) datasource.InstanceFactoryFunc {
return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
instanceSettings, err := models.LoadCloudWatchSettings(ctx, settings)
if err != nil {
return nil, fmt.Errorf("error reading settings: %w", err)
}
opts, err := settings.HTTPClientOptions(ctx)
if err != nil {
return nil, err
}
httpClient, err := httpClientProvider.New(opts)
if err != nil {
return nil, fmt.Errorf("error creating http client: %w", err)
}
return DataSource{
Settings: instanceSettings,
HTTPClient: httpClient,
tagValueCache: cache.New(tagValueCacheExpiration, tagValueCacheExpiration*5),
sessions: awsds.NewSessionCache(),
// this is used to build a custom dialer when secure socks proxy is enabled
ProxyOpts: opts.ProxyOptions,
}, nil
}
}
// cloudWatchExecutor executes CloudWatch requests
type cloudWatchExecutor struct {
im instancemgmt.InstanceManager
logger log.Logger
type DataSource struct {
Settings models.CloudWatchSettings
ProxyOpts *proxy.Options
AWSConfigProvider awsauth.ConfigProvider
logger log.Logger
tagValueCache *cache.Cache
resourceHandler backend.CallResourceHandler
}
func (ds *DataSource) newAWSConfig(ctx context.Context, region string) (aws.Config, error) {
if region == defaultRegion || region == "" {
if len(ds.Settings.Region) == 0 {
return aws.Config{}, models.ErrMissingRegion
}
region = ds.Settings.Region
}
authSettings := awsauth.Settings{
CredentialsProfile: ds.Settings.Profile,
LegacyAuthType: ds.Settings.AuthType,
AssumeRoleARN: ds.Settings.AssumeRoleARN,
ExternalID: ds.Settings.ExternalID,
Endpoint: ds.Settings.Endpoint,
Region: region,
AccessKey: ds.Settings.AccessKey,
SecretKey: ds.Settings.SecretKey,
HTTPClient: &http.Client{},
}
if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled {
authSettings.ProxyOptions = ds.ProxyOpts
}
cfg, err := ds.AWSConfigProvider.GetConfig(ctx, authSettings)
if err != nil {
return aws.Config{}, err
}
return cfg, nil
}
func NewDatasource(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
instanceSettings, err := models.LoadCloudWatchSettings(ctx, settings)
if err != nil {
return nil, fmt.Errorf("error reading settings: %w", err)
}
opts, err := settings.HTTPClientOptions(ctx)
if err != nil {
return nil, err
}
ds := &DataSource{
Settings: instanceSettings,
// this is used to build a custom dialer when secure socks proxy is enabled
ProxyOpts: opts.ProxyOptions,
AWSConfigProvider: awsauth.NewConfigProvider(),
logger: backend.NewLoggerWith("logger", "grafana-cloudwatch-datasource"),
tagValueCache: cache.New(tagValueCacheExpiration, tagValueCacheExpiration*5),
}
ds.resourceHandler = httpadapter.New(ds.newResourceMux())
return ds, nil
}
// instrumentContext adds plugin key-values to the context; later, logger.FromContext(ctx) will provide a logger
// that adds these values to its output.
// TODO: move this into the sdk (see https://github.com/grafana/grafana/issues/82033)
@@ -144,59 +124,12 @@ func instrumentContext(ctx context.Context, endpoint string, pCtx backend.Plugin
return log.WithContextualAttributes(ctx, p)
}
func (e *cloudWatchExecutor) getRequestContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.RequestContext, error) {
r := region
instance, err := e.getInstance(ctx, pluginCtx)
if region == defaultRegion {
if err != nil {
return models.RequestContext{}, err
}
r = instance.Settings.Region
}
ec2Client, err := e.getEC2Client(ctx, pluginCtx, defaultRegion)
if err != nil {
return models.RequestContext{}, err
}
sess, err := instance.newSession(r)
if err != nil {
return models.RequestContext{}, err
}
return models.RequestContext{
OAMAPIProvider: NewOAMAPI(sess),
MetricsClientProvider: clients.NewMetricsClient(NewMetricsAPI(sess), instance.Settings.GrafanaSettings.ListMetricsPageLimit),
LogsAPIProvider: NewLogsAPI(sess),
EC2APIProvider: ec2Client,
Settings: instance.Settings,
Logger: e.logger.FromContext(ctx),
}, nil
}
// getRequestContextOnlySettings is useful for resource endpoints that are called before auth has been configured such as external-id that need access to settings but nothing else
func (e *cloudWatchExecutor) getRequestContextOnlySettings(ctx context.Context, pluginCtx backend.PluginContext, _ string) (models.RequestContext, error) {
instance, err := e.getInstance(ctx, pluginCtx)
if err != nil {
return models.RequestContext{}, err
}
return models.RequestContext{
OAMAPIProvider: nil,
MetricsClientProvider: nil,
LogsAPIProvider: nil,
EC2APIProvider: nil,
Settings: instance.Settings,
Logger: e.logger.FromContext(ctx),
}, nil
}
func (e *cloudWatchExecutor) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
func (ds *DataSource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error {
ctx = instrumentContext(ctx, string(backend.EndpointCallResource), req.PluginContext)
return e.resourceHandler.CallResource(ctx, req, sender)
return ds.resourceHandler.CallResource(ctx, req, sender)
}
func (e *cloudWatchExecutor) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
func (ds *DataSource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
ctx = instrumentContext(ctx, string(backend.EndpointQueryData), req.PluginContext)
q := req.Queries[0]
var model DataQueryJson
@@ -217,37 +150,37 @@ func (e *cloudWatchExecutor) QueryData(ctx context.Context, req *backend.QueryDa
fromPublicDashboard := model.Type == "" && queryMode == logsQueryMode
isSyncLogQuery := ((fromAlert || fromExpression) && queryMode == logsQueryMode) || fromPublicDashboard
if isSyncLogQuery {
return executeSyncLogQuery(ctx, e, req)
return executeSyncLogQuery(ctx, ds, req)
}
var result *backend.QueryDataResponse
switch model.Type {
case annotationQuery:
result, err = e.executeAnnotationQuery(ctx, req.PluginContext, model, q)
result, err = ds.executeAnnotationQuery(ctx, model, q)
case logAction:
result, err = e.executeLogActions(ctx, req)
result, err = ds.executeLogActions(ctx, req)
case timeSeriesQuery:
fallthrough
default:
result, err = e.executeTimeSeriesQuery(ctx, req)
result, err = ds.executeTimeSeriesQuery(ctx, req)
}
return result, err
}
func (e *cloudWatchExecutor) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
func (ds *DataSource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) {
ctx = instrumentContext(ctx, string(backend.EndpointCheckHealth), req.PluginContext)
status := backend.HealthStatusOk
metricsTest := "Successfully queried the CloudWatch metrics API."
logsTest := "Successfully queried the CloudWatch logs API."
err := e.checkHealthMetrics(ctx, req.PluginContext)
err := ds.checkHealthMetrics(ctx, req.PluginContext)
if err != nil {
status = backend.HealthStatusError
metricsTest = fmt.Sprintf("CloudWatch metrics query failed: %s", err.Error())
}
err = e.checkHealthLogs(ctx, req.PluginContext)
err = ds.checkHealthLogs(ctx)
if err != nil {
status = backend.HealthStatusError
logsTest = fmt.Sprintf("CloudWatch logs query failed: %s", err.Error())
@@ -259,7 +192,7 @@ func (e *cloudWatchExecutor) CheckHealth(ctx context.Context, req *backend.Check
}, nil
}
func (e *cloudWatchExecutor) checkHealthMetrics(ctx context.Context, pluginCtx backend.PluginContext) error {
func (ds *DataSource) checkHealthMetrics(ctx context.Context, _ backend.PluginContext) error {
namespace := "AWS/Billing"
metric := "EstimatedCharges"
params := &cloudwatch.ListMetricsInput{
@@ -267,141 +200,75 @@ func (e *cloudWatchExecutor) checkHealthMetrics(ctx context.Context, pluginCtx b
MetricName: &metric,
}
instance, err := e.getInstance(ctx, pluginCtx)
cfg, err := ds.newAWSConfig(ctx, defaultRegion)
if err != nil {
return err
}
session, err := instance.newSession(defaultRegion)
if err != nil {
return err
}
metricClient := clients.NewMetricsClient(NewMetricsAPI(session), instance.Settings.GrafanaSettings.ListMetricsPageLimit)
metricClient := clients.NewMetricsClient(NewCWClient(cfg), ds.Settings.GrafanaSettings.ListMetricsPageLimit)
_, err = metricClient.ListMetricsWithPageLimit(ctx, params)
return err
}
func (e *cloudWatchExecutor) checkHealthLogs(ctx context.Context, pluginCtx backend.PluginContext) error {
session, err := e.newSessionFromContext(ctx, pluginCtx, defaultRegion)
func (ds *DataSource) checkHealthLogs(ctx context.Context) error {
cfg, err := ds.getAWSConfig(ctx, defaultRegion)
if err != nil {
return err
}
logsClient := NewLogsAPI(session)
_, err = logsClient.DescribeLogGroupsWithContext(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(1)})
logsClient := NewLogsAPI(cfg)
_, err = logsClient.DescribeLogGroups(ctx, &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(1)})
return err
}
func (ds *DataSource) newSession(region string) (*session.Session, error) {
if region == defaultRegion {
if len(ds.Settings.Region) == 0 {
return nil, models.ErrMissingRegion
}
region = ds.Settings.Region
}
sess, err := ds.sessions.GetSessionWithAuthSettings(awsds.GetSessionConfig{
// https://github.com/grafana/grafana/issues/46365
// HTTPClient: instance.HTTPClient,
Settings: awsds.AWSDatasourceSettings{
Profile: ds.Settings.Profile,
Region: region,
AuthType: ds.Settings.AuthType,
AssumeRoleARN: ds.Settings.AssumeRoleARN,
ExternalID: ds.Settings.ExternalID,
Endpoint: ds.Settings.Endpoint,
DefaultRegion: ds.Settings.Region,
AccessKey: ds.Settings.AccessKey,
SecretKey: ds.Settings.SecretKey,
},
UserAgentName: aws.String("Cloudwatch")},
ds.Settings.GrafanaSettings)
if err != nil {
return nil, err
}
// work around until https://github.com/grafana/grafana/issues/39089 is implemented
if ds.Settings.GrafanaSettings.SecureSocksDSProxyEnabled && ds.Settings.SecureSocksProxyEnabled {
// only update the transport to try to avoid the issue mentioned here https://github.com/grafana/grafana/issues/46365
// also, 'sess' is cached and reused, so the first time it might have the transport not set, the following uses it will
if sess.Config.HTTPClient.Transport == nil {
// following go standard library logic (https://pkg.go.dev/net/http#Client), if no Transport is provided,
// then we use http.DefaultTransport
defTransport, ok := http.DefaultTransport.(*http.Transport)
if !ok {
// this should not happen but validating just in case
return nil, errors.New("default http client transport is not of type http.Transport")
}
sess.Config.HTTPClient.Transport = defTransport.Clone()
}
err = proxy.New(ds.ProxyOpts).ConfigureSecureSocksHTTPProxy(sess.Config.HTTPClient.Transport.(*http.Transport))
if err != nil {
return nil, fmt.Errorf("error configuring Secure Socks proxy for Transport: %w", err)
}
} else if sess.Config.HTTPClient != nil {
// Workaround for https://github.com/grafana/grafana/issues/91356 - PDC transport set above
// stays on the cached session after PDC is disabled
sess.Config.HTTPClient.Transport = nil
}
return sess, nil
func (ds *DataSource) getAWSConfig(ctx context.Context, region string) (aws.Config, error) {
return ds.newAWSConfig(ctx, region)
}
func (e *cloudWatchExecutor) newSessionFromContext(ctx context.Context, pluginCtx backend.PluginContext, region string) (*session.Session, error) {
instance, err := e.getInstance(ctx, pluginCtx)
func (ds *DataSource) getCWClient(ctx context.Context, region string) (models.CWClient, error) {
cfg, err := ds.getAWSConfig(ctx, region)
if err != nil {
return nil, err
}
return instance.newSession(region)
return NewCWClient(cfg), nil
}
func (e *cloudWatchExecutor) getInstance(ctx context.Context, pluginCtx backend.PluginContext) (*DataSource, error) {
i, err := e.im.Get(ctx, pluginCtx)
func (ds *DataSource) getCWLogsClient(ctx context.Context, region string) (models.CWLogsClient, error) {
cfg, err := ds.getAWSConfig(ctx, region)
if err != nil {
return nil, err
}
instance := i.(DataSource)
return &instance, nil
}
func (e *cloudWatchExecutor) getCWClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchiface.CloudWatchAPI, error) {
sess, err := e.newSessionFromContext(ctx, pluginCtx, region)
if err != nil {
return nil, err
}
return NewCWClient(sess), nil
}
func (e *cloudWatchExecutor) getCWLogsClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (cloudwatchlogsiface.CloudWatchLogsAPI, error) {
sess, err := e.newSessionFromContext(ctx, pluginCtx, region)
if err != nil {
return nil, err
}
logsClient := NewCWLogsClient(sess)
logsClient := NewCWLogsClient(cfg)
return logsClient, nil
}
func (e *cloudWatchExecutor) getEC2Client(ctx context.Context, pluginCtx backend.PluginContext, region string) (models.EC2APIProvider, error) {
sess, err := e.newSessionFromContext(ctx, pluginCtx, region)
func (ds *DataSource) getEC2Client(ctx context.Context, region string) (models.EC2APIProvider, error) {
cfg, err := ds.getAWSConfig(ctx, region)
if err != nil {
return nil, err
}
return NewEC2Client(sess), nil
return NewEC2API(cfg), nil
}
func (e *cloudWatchExecutor) getRGTAClient(ctx context.Context, pluginCtx backend.PluginContext, region string) (resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI,
func (ds *DataSource) getRGTAClient(ctx context.Context, region string) (resourcegroupstaggingapi.GetResourcesAPIClient,
error) {
sess, err := e.newSessionFromContext(ctx, pluginCtx, region)
cfg, err := ds.getAWSConfig(ctx, region)
if err != nil {
return nil, err
}
return newRGTAClient(sess), nil
return NewRGTAClient(cfg), nil
}
func isTerminated(queryStatus string) bool {
return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout"
var terminatedStates = []cloudwatchlogstypes.QueryStatus{
cloudwatchlogstypes.QueryStatusComplete,
cloudwatchlogstypes.QueryStatusCancelled,
cloudwatchlogstypes.QueryStatusFailed,
cloudwatchlogstypes.QueryStatusTimeout,
}
func isTerminated(queryStatus cloudwatchlogstypes.QueryStatus) bool {
return slices.Contains(terminatedStates, queryStatus)
}

View File

@@ -6,17 +6,13 @@ import (
"net/http"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/aws/aws-sdk-go-v2/aws"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
@@ -27,48 +23,49 @@ import (
func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
sender := &mockedCallResourceResponseSenderForOauth{}
origNewMetricsAPI := NewMetricsAPI
origNewCWClient := NewCWClient
origNewOAMAPI := NewOAMAPI
origNewLogsAPI := NewLogsAPI
origNewEC2Client := NewEC2Client
NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil }
origNewEC2API := NewEC2API
NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil }
var logApi mocks.LogsAPI
NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider {
NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider {
return &logApi
}
ec2Mock := &mocks.EC2Mock{}
ec2Mock.On("DescribeRegionsWithContext", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil)
NewEC2Client = func(provider client.ConfigProvider) models.EC2APIProvider {
ec2Mock.On("DescribeRegions", mock.Anything, mock.Anything).Return(&ec2.DescribeRegionsOutput{}, nil)
NewEC2API = func(aws.Config) models.EC2APIProvider {
return ec2Mock
}
t.Cleanup(func() {
NewOAMAPI = origNewOAMAPI
NewMetricsAPI = origNewMetricsAPI
NewCWClient = origNewCWClient
NewLogsAPI = origNewLogsAPI
NewEC2Client = origNewEC2Client
NewEC2API = origNewEC2API
})
var api mocks.FakeMetricsAPI
NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider {
NewCWClient = func(aws.Config) models.CWClient {
return &api
}
t.Run("Should handle dimension value request and return values from the api", func(t *testing.T) {
im := testInstanceManager(100)
api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}},
{MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}},
{MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}},
{MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}},
{MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}},
{MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}},
{MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}},
{MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}},
{MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}},
api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value1")}, {Name: aws.String("Test_DimensionName2"), Value: aws.String("Value2")}}},
{MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value3")}}},
{MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value1")}}},
{MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value2")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2"), Value: aws.String("Value3")}}},
{MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value4")}}},
{MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value6")}}},
{MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value7")}}},
{MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4"), Value: aws.String("Value1")}}},
{MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1"), Value: aws.String("Value2")}}},
}, MetricsPerPage: 100}
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.GrafanaSettings.ListMetricsPageLimit = 100
})
req := &backend.CallResourceRequest{
Method: "GET",
@@ -78,7 +75,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -91,20 +88,21 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle dimension key filter query and return keys from the api", func(t *testing.T) {
im := testInstanceManager(3)
api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: aws.String("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName10"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName5"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName6"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName7"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName8"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName9"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: aws.String("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName10"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName5"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName6"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName7"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName8"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName9"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
}, MetricsPerPage: 2}
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.GrafanaSettings.ListMetricsPageLimit = 3
})
req := &backend.CallResourceRequest{
Method: "GET",
@@ -114,7 +112,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -127,9 +125,10 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle standard dimension key query and return hard coded keys", func(t *testing.T) {
im := defaultTestInstanceManager()
api = mocks.FakeMetricsAPI{}
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
})
req := &backend.CallResourceRequest{
Method: "GET",
@@ -139,7 +138,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -152,9 +151,8 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle custom namespace dimension key query and return hard coded keys", func(t *testing.T) {
im := defaultTestInstanceManager()
api = mocks.FakeMetricsAPI{}
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
req := &backend.CallResourceRequest{
Method: "GET",
@@ -164,7 +162,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -177,20 +175,21 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle custom namespace metrics query and return metrics from api", func(t *testing.T) {
im := testInstanceManager(3)
api = mocks.FakeMetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []*cloudwatch.Dimension{{Name: aws.String("Test_DimensionName1")}}},
api = mocks.FakeMetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: aws.String("Test_MetricName1"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}, {Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName2"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName3"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName10"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}, {Name: aws.String("Test_DimensionName5")}}},
{MetricName: aws.String("Test_MetricName4"), Namespace: aws.String("AWS/ECS"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName2")}}},
{MetricName: aws.String("Test_MetricName5"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName6"), Namespace: aws.String("AWS/Redshift"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
{MetricName: aws.String("Test_MetricName7"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName8"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName4")}}},
{MetricName: aws.String("Test_MetricName9"), Namespace: aws.String("AWS/EC2"), Dimensions: []cloudwatchtypes.Dimension{{Name: aws.String("Test_DimensionName1")}}},
}, MetricsPerPage: 2}
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.GrafanaSettings.ListMetricsPageLimit = 3
})
req := &backend.CallResourceRequest{
Method: "GET",
@@ -200,7 +199,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -213,21 +212,20 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle log group fields request", func(t *testing.T) {
im := defaultTestInstanceManager()
logApi = mocks.LogsAPI{}
logApi.On("GetLogGroupFieldsWithContext", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{
LogGroupFields: []*cloudwatchlogs.LogGroupField{
logApi.On("GetLogGroupFields", mock.Anything).Return(&cloudwatchlogs.GetLogGroupFieldsOutput{
LogGroupFields: []cloudwatchlogstypes.LogGroupField{
{
Name: aws.String("field1"),
Percent: aws.Int64(50),
Percent: 50,
},
{
Name: aws.String("field2"),
Percent: aws.Int64(50),
Percent: 50,
},
},
}, nil)
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
req := &backend.CallResourceRequest{
Method: "GET",
@@ -237,7 +235,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
@@ -248,8 +246,9 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should handle region requests and return regions from the api", func(t *testing.T) {
im := defaultTestInstanceManager()
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-2"
})
req := &backend.CallResourceRequest{
Method: "GET",
Path: `/regions`,
@@ -258,7 +257,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
require.NotNil(t, sent)
@@ -268,14 +267,11 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
})
t.Run("Should error for any request when a default region is not selected", func(t *testing.T) {
imWithoutDefaultRegion := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{},
GrafanaSettings: awsds.AuthSettings{ListMetricsPageLimit: 1000},
}}, nil
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.GrafanaSettings.ListMetricsPageLimit = 1000
ds.Settings.Region = ""
})
executor := newExecutor(imWithoutDefaultRegion, log.NewNullLogger())
req := &backend.CallResourceRequest{
Method: "GET",
Path: `/regions`,
@@ -284,7 +280,7 @@ func Test_CloudWatch_CallResource_Integration_Test(t *testing.T) {
PluginID: "cloudwatch",
},
}
err := executor.CallResource(context.Background(), req, sender)
err := ds.CallResource(context.Background(), req, sender)
require.NoError(t, err)
sent := sender.Response
require.NotNil(t, sent)

View File

@@ -6,18 +6,15 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
awsclient "github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/google/go-cmp/cmp"
"github.com/grafana/grafana-aws-sdk/pkg/awsauth"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/httpclient"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/backend/proxy"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
@@ -34,7 +31,7 @@ func TestNewInstanceSettings(t *testing.T) {
name string
settings backend.DataSourceInstanceSettings
settingCtx context.Context
expectedDS DataSource
expectedDS *DataSource
Err require.ErrorAssertionFunc
}{
{
@@ -62,7 +59,7 @@ func TestNewInstanceSettings(t *testing.T) {
awsds.ListMetricsPageLimitKeyName: "50",
proxy.PluginSecureSocksProxyEnabled: "true",
})),
expectedDS: DataSource{
expectedDS: &DataSource{
Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{
Profile: "foo",
@@ -91,11 +88,11 @@ func TestNewInstanceSettings(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := NewInstanceSettings(httpclient.NewProvider())
model, err := f(tt.settingCtx, tt.settings)
instance, err := NewDatasource(tt.settingCtx, tt.settings)
ds := instance.(*DataSource)
tt.Err(t, err)
assert.Equal(t, tt.expectedDS.Settings.GrafanaSettings, model.(DataSource).Settings.GrafanaSettings)
datasourceComparer := cmp.Comparer(func(d1 DataSource, d2 DataSource) bool {
assert.Equal(t, tt.expectedDS.Settings.GrafanaSettings, ds.Settings.GrafanaSettings)
datasourceComparer := cmp.Comparer(func(d1 *DataSource, d2 *DataSource) bool {
return d1.Settings.Profile == d2.Settings.Profile &&
d1.Settings.Region == d2.Settings.Region &&
d1.Settings.AuthType == d2.Settings.AuthType &&
@@ -106,40 +103,39 @@ func TestNewInstanceSettings(t *testing.T) {
d1.Settings.AccessKey == d2.Settings.AccessKey &&
d1.Settings.SecretKey == d2.Settings.SecretKey
})
if !cmp.Equal(model.(DataSource), tt.expectedDS, datasourceComparer) {
t.Errorf("Unexpected result. Expecting\n%v \nGot:\n%v", model, tt.expectedDS)
if !cmp.Equal(instance.(*DataSource), tt.expectedDS, datasourceComparer) {
t.Errorf("Unexpected result. Expecting\n%v \nGot:\n%v", instance, tt.expectedDS)
}
})
}
}
func Test_CheckHealth(t *testing.T) {
origNewMetricsAPI := NewMetricsAPI
origNewCWClient := NewCWClient
origNewCWLogsClient := NewCWLogsClient
origNewLogsAPI := NewLogsAPI
t.Cleanup(func() {
NewMetricsAPI = origNewMetricsAPI
NewCWClient = origNewCWClient
NewCWLogsClient = origNewCWLogsClient
NewLogsAPI = origNewLogsAPI
})
var client fakeCheckHealthClient
NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider {
NewCWClient = func(aws.Config) models.CWClient {
return client
}
NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider {
NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider {
return client
}
im := defaultTestInstanceManager()
t.Run("successfully query metrics and logs", func(t *testing.T) {
client = fakeCheckHealthClient{}
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-1"
})
resp, err := ds.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
@@ -149,14 +145,15 @@ func Test_CheckHealth(t *testing.T) {
})
t.Run("successfully queries metrics, fails during logs query", func(t *testing.T) {
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-1"
})
client = fakeCheckHealthClient{
describeLogGroups: func(input *cloudwatchlogs.DescribeLogGroupsInput) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
describeLogGroupsFunction: func(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
return nil, fmt.Errorf("some logs query error")
}}
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
},
}
resp, err := ds.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
@@ -168,14 +165,15 @@ func Test_CheckHealth(t *testing.T) {
})
t.Run("successfully queries logs, fails during metrics query", func(t *testing.T) {
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-1"
ds.Settings.GrafanaSettings.ListMetricsPageLimit = 1
})
client = fakeCheckHealthClient{
listMetricsPages: func(input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool) error {
return fmt.Errorf("some list metrics error")
listMetricsFunction: func(context.Context, *cloudwatch.ListMetricsInput, ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) {
return nil, fmt.Errorf("some list metrics error")
}}
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
resp, err := ds.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
@@ -188,30 +186,25 @@ func Test_CheckHealth(t *testing.T) {
t.Run("fail to get clients", func(t *testing.T) {
client = fakeCheckHealthClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{
Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "us-east-1"}},
sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) {
return nil, fmt.Errorf("some sessions error")
}},
}, nil
ds := newTestDatasource(func(ds *DataSource) {
ds.AWSConfigProvider = awsauth.NewFakeConfigProvider(true)
ds.Settings.Region = "us-east-1"
})
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.CheckHealth(context.Background(), &backend.CheckHealthRequest{
resp, err := ds.CheckHealth(context.Background(), &backend.CheckHealthRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
})
assert.NoError(t, err)
assert.Equal(t, &backend.CheckHealthResult{
Status: backend.HealthStatusError,
Message: "1. CloudWatch metrics query failed: some sessions error\n2. CloudWatch logs query failed: some sessions error",
Message: "1. CloudWatch metrics query failed: LoadDefaultConfig failed\n2. CloudWatch logs query failed: LoadDefaultConfig failed",
}, resp)
})
}
func TestNewSession_passes_authSettings(t *testing.T) {
func TestGetAWSConfig_passes_authSettings(t *testing.T) {
// TODO: update this for the new auth structure, or remove it
t.Skip()
ctxDuration := 15 * time.Minute
expectedSettings := awsds.AuthSettings{
AllowedAuthProviders: []string{"foo", "bar", "baz"},
@@ -221,56 +214,40 @@ func TestNewSession_passes_authSettings(t *testing.T) {
ListMetricsPageLimit: 50,
SecureSocksDSProxyEnabled: true,
}
im := datasource.NewInstanceManager((func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{
Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{
Region: "us-east-1",
},
GrafanaSettings: expectedSettings,
},
sessions: &fakeSessionCache{getSessionWithAuthSettings: func(c awsds.GetSessionConfig, a awsds.AuthSettings) (*session.Session, error) {
assert.Equal(t, expectedSettings, a)
return &session.Session{
Config: &aws.Config{},
}, nil
}},
}, nil
}))
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-1"
ds.Settings.GrafanaSettings = expectedSettings
})
_, err := executor.newSessionFromContext(context.Background(),
backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}}, "us-east-1")
_, err := ds.getAWSConfig(context.Background(), "us-east-1")
require.NoError(t, err)
}
func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *testing.T) {
sender := &mockedCallResourceResponseSenderForOauth{}
origNewMetricsAPI := NewMetricsAPI
origNewMetricsAPI := NewCWClient
origNewOAMAPI := NewOAMAPI
origNewLogsAPI := NewLogsAPI
origNewEC2Client := NewEC2Client
NewMetricsAPI = func(sess *session.Session) models.CloudWatchMetricsAPIProvider { return nil }
NewOAMAPI = func(sess *session.Session) models.OAMAPIProvider { return nil }
NewEC2Client = func(provider awsclient.ConfigProvider) models.EC2APIProvider { return nil }
origNewEC2API := NewEC2API
NewCWClient = func(aws.Config) models.CWClient { return nil }
NewOAMAPI = func(aws.Config) models.OAMAPIProvider { return nil }
NewEC2API = func(aws.Config) models.EC2APIProvider { return nil }
t.Cleanup(func() {
NewOAMAPI = origNewOAMAPI
NewMetricsAPI = origNewMetricsAPI
NewCWClient = origNewMetricsAPI
NewLogsAPI = origNewLogsAPI
NewEC2Client = origNewEC2Client
NewEC2API = origNewEC2API
})
var logsApi mocks.LogsAPI
NewLogsAPI = func(sess *session.Session) models.CloudWatchLogsAPIProvider {
NewLogsAPI = func(aws.Config) models.CloudWatchLogsAPIProvider {
return &logsApi
}
im := defaultTestInstanceManager()
t.Run("maps log group api response to resource response of log-groups", func(t *testing.T) {
logsApi = mocks.LogsAPI{}
logsApi.On("DescribeLogGroupsWithContext", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{
LogGroups: []*cloudwatchlogs.LogGroup{
logsApi.On("DescribeLogGroups", mock.Anything).Return(&cloudwatchlogs.DescribeLogGroupsOutput{
LogGroups: []cloudwatchlogstypes.LogGroup{
{Arn: aws.String("arn:aws:logs:us-east-1:111:log-group:group_a"), LogGroupName: aws.String("group_a")},
},
}, nil)
@@ -283,8 +260,9 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te
},
}
executor := newExecutor(im, log.NewNullLogger())
err := executor.CallResource(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), req, sender)
ds := newTestDatasource()
err := ds.CallResource(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), req, sender)
assert.NoError(t, err)
assert.JSONEq(t, `[
@@ -297,11 +275,11 @@ func TestQuery_ResourceRequest_DescribeLogGroups_with_CrossAccountQuerying(t *te
}
]`, string(sender.Response.Body))
logsApi.AssertCalled(t, "DescribeLogGroupsWithContext",
logsApi.AssertCalled(t, "DescribeLogGroups",
&cloudwatchlogs.DescribeLogGroupsInput{
AccountIdentifiers: []*string{utils.Pointer("some-account-id")},
AccountIdentifiers: []string{"some-account-id"},
IncludeLinkedAccounts: utils.Pointer(true),
Limit: utils.Pointer(int64(50)),
Limit: aws.Int32(50),
LogGroupNamePrefix: utils.Pointer("some-pattern"),
})
})

View File

@@ -1,21 +1,12 @@
package constants
import "github.com/grafana/grafana-aws-sdk/pkg/cloudWatchConsts"
// NamespaceMetricsMap is a map of Cloudwatch namespaces to their metrics
// Deprecated: use cloudWatchConsts.NamespaceMetricsMap from grafana-aws-sdk instead
var NamespaceMetricsMap = cloudWatchConsts.NamespaceMetricsMap
// NamespaceDimensionKeysMap is a map of CloudWatch namespaces to their dimension keys
// Deprecated: use cloudWatchConsts.NamespaceDimensionKeysMap from grafana-aws-sdk instead
var NamespaceDimensionKeysMap = cloudWatchConsts.NamespaceDimensionKeysMap
type RegionsSet map[string]struct{}
func Regions() RegionsSet {
return RegionsSet{
"af-south-1": {},
"ap-east-1": {},
"ap-east-2": {},
"ap-northeast-1": {},
"ap-northeast-2": {},
"ap-northeast-3": {},
@@ -25,7 +16,10 @@ func Regions() RegionsSet {
"ap-southeast-2": {},
"ap-southeast-3": {},
"ap-southeast-4": {},
"ap-southeast-5": {},
"ap-southeast-7": {},
"ca-central-1": {},
"ca-west-1": {},
"cn-north-1": {},
"cn-northwest-1": {},
"eu-central-1": {},
@@ -39,6 +33,7 @@ func Regions() RegionsSet {
"il-central-1": {},
"me-central-1": {},
"me-south-1": {},
"mx-central-1": {},
"sa-east-1": {},
"us-east-1": {},
"us-east-2": {},

View File

@@ -1,15 +1,12 @@
package routes
package cloudwatch
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -20,11 +17,19 @@ import (
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
)
var logger = log.NewNullLogger()
func Test_DimensionKeys_Route(t *testing.T) {
origNewListMetricsService := services.NewListMetricsService
t.Cleanup(func() {
services.NewListMetricsService = origNewListMetricsService
})
var mockListMetricsService mocks.ListMetricsServiceMock
services.NewListMetricsService = func(models.MetricsClientProvider) models.ListMetricsProvider {
return &mockListMetricsService
}
t.Run("calls FilterDimensionKeysRequest when a StandardDimensionKeysRequest is passed", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetDimensionKeysByDimensionFilter", mock.MatchedBy(func(r resources.DimensionKeysRequest) bool {
return r.ResourceRequest != nil && *r.ResourceRequest == resources.ResourceRequest{Region: "us-east-2"} &&
r.Namespace == "AWS/EC2" &&
@@ -33,12 +38,10 @@ func Test_DimensionKeys_Route(t *testing.T) {
assert.Contains(t, r.DimensionFilter, &resources.Dimension{Name: "NodeID", Value: "Shared"}) &&
assert.Contains(t, r.DimensionFilter, &resources.Dimension{Name: "stage", Value: "QueryCommit"})
})).Return([]resources.ResourceResponse[string]{}, nil).Once()
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", `/dimension-keys?region=us-east-2&namespace=AWS/EC2&metricName=CPUUtilization&dimensionFilters={"NodeID":["Shared"],"stage":["QueryCommit"]}`, nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(DimensionKeysHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.DimensionKeysHandler))
handler.ServeHTTP(rr, req)
})
@@ -56,7 +59,8 @@ func Test_DimensionKeys_Route(t *testing.T) {
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/dimension-keys?region=us-east-2&namespace=AWS/EC2&metricName=CPUUtilization", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(DimensionKeysHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.DimensionKeysHandler))
handler.ServeHTTP(rr, req)
res := []resources.Metric{}
err := json.Unmarshal(rr.Body.Bytes(), &res)
@@ -66,14 +70,12 @@ func Test_DimensionKeys_Route(t *testing.T) {
})
t.Run("return 500 if GetDimensionKeysByDimensionFilter returns an error", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetDimensionKeysByDimensionFilter", mock.Anything).Return([]resources.ResourceResponse[string]{}, fmt.Errorf("some error"))
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", `/dimension-keys?region=us-east-2&namespace=AWS/EC2&metricName=CPUUtilization&dimensionFilters={"NodeID":["Shared"],"stage":["QueryCommit"]}`, nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(DimensionKeysHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.DimensionKeysHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
assert.Equal(t, `{"Message":"error in DimensionKeyHandler: some error","Error":"some error","StatusCode":500}`, rr.Body.String())

View File

@@ -1,14 +1,13 @@
package routes
package cloudwatch
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
@@ -17,8 +16,18 @@ import (
)
func Test_DimensionValues_Route(t *testing.T) {
origNewListMetricsService := services.NewListMetricsService
t.Cleanup(func() {
services.NewListMetricsService = origNewListMetricsService
})
var mockListMetricsService mocks.ListMetricsServiceMock
services.NewListMetricsService = func(models.MetricsClientProvider) models.ListMetricsProvider {
return &mockListMetricsService
}
t.Run("Calls GetDimensionValuesByDimensionFilter when a valid request is passed", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetDimensionValuesByDimensionFilter", mock.MatchedBy(func(r resources.DimensionValuesRequest) bool {
return r.ResourceRequest != nil && *r.ResourceRequest == resources.ResourceRequest{Region: "us-east-2"} &&
r.Namespace == "AWS/EC2" &&
@@ -28,24 +37,20 @@ func Test_DimensionValues_Route(t *testing.T) {
assert.Contains(t, r.DimensionFilter, &resources.Dimension{Name: "NodeID", Value: "Shared"}) &&
assert.Contains(t, r.DimensionFilter, &resources.Dimension{Name: "stage", Value: "QueryCommit"})
})).Return([]resources.ResourceResponse[string]{}, nil).Once()
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", `/dimension-values?region=us-east-2&dimensionKey=instanceId&namespace=AWS/EC2&metricName=CPUUtilization&dimensionFilters={"NodeID":["Shared"],"stage":["QueryCommit"]}`, nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(DimensionValuesHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.DimensionValuesHandler))
handler.ServeHTTP(rr, req)
})
t.Run("returns 500 if GetDimensionValuesByDimensionFilter returns an error", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetDimensionValuesByDimensionFilter", mock.Anything).Return([]resources.ResourceResponse[string]{}, fmt.Errorf("some error"))
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", `/dimension-values?region=us-east-2&dimensionKey=instanceId&namespace=AWS/EC2&metricName=CPUUtilization&dimensionFilters={"NodeID":["Shared"],"stage":["QueryCommit"]}`, nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(DimensionValuesHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.DimensionValuesHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
assert.Equal(t, `{"Message":"error in DimensionValuesHandler: some error","Error":"some error","StatusCode":500}`, rr.Body.String())

View File

@@ -0,0 +1,40 @@
package cloudwatch
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_external_id_route(t *testing.T) {
t.Run("successfully returns an external id from the instance", func(t *testing.T) {
t.Setenv("AWS_AUTH_EXTERNAL_ID", "mock-external-id")
rr := httptest.NewRecorder()
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.GrafanaSettings.ExternalID = "mock-external-id"
})
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.ExternalIdHandler))
req := httptest.NewRequest("GET", "/external-id", nil)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `{"externalId":"mock-external-id"}`, rr.Body.String())
})
t.Run("returns an empty string if there is no external id", func(t *testing.T) {
rr := httptest.NewRecorder()
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.ExternalIdHandler))
req := httptest.NewRequest("GET", "/external-id", nil)
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `{"externalId":""}`, rr.Body.String())
})
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
"github.com/patrickmn/go-cache"
)
@@ -26,10 +27,10 @@ func shouldSkipFetchingWildcards(ctx context.Context, q *models.CloudWatchQuery)
}
// getDimensionValues gets the actual dimension values for dimensions with a wildcard
func (e *cloudWatchExecutor) getDimensionValuesForWildcards(
func (ds *DataSource) getDimensionValuesForWildcards(
ctx context.Context,
region string,
client models.CloudWatchMetricsAPIProvider,
client models.CWClient,
origQueries []*models.CloudWatchQuery,
tagValueCache *cache.Cache,
listMetricsPageLimit int,
@@ -57,12 +58,12 @@ func (e *cloudWatchExecutor) getDimensionValuesForWildcards(
cacheKey := fmt.Sprintf("%s-%s-%s-%s-%s", region, accountID, query.Namespace, query.MetricName, dimensionKey)
cachedDimensions, found := tagValueCache.Get(cacheKey)
if found {
e.logger.FromContext(ctx).Debug("Fetching dimension values from cache")
ds.logger.FromContext(ctx).Debug("Fetching dimension values from cache")
query.Dimensions[dimensionKey] = cachedDimensions.([]string)
continue
}
e.logger.FromContext(ctx).Debug("Cache miss, fetching dimension values from AWS")
ds.logger.FromContext(ctx).Debug("Cache miss, fetching dimension values from AWS")
request := resources.DimensionValuesRequest{
ResourceRequest: &resources.ResourceRequest{
Region: region,

View File

@@ -4,8 +4,8 @@ import (
"context"
"testing"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
@@ -14,10 +14,10 @@ import (
"github.com/stretchr/testify/assert"
)
func noSkip(ctx context.Context, q *models.CloudWatchQuery) bool { return false }
func noSkip(context.Context, *models.CloudWatchQuery) bool { return false }
func TestGetDimensionValuesForWildcards(t *testing.T) {
executor := &cloudWatchExecutor{im: defaultTestInstanceManager(), logger: log.NewNullLogger()}
ds := newTestDatasource()
ctx := context.Background()
t.Run("Tag value cache", func(t *testing.T) {
@@ -29,17 +29,17 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}},
api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName"), Value: utils.Pointer("Value")}}},
}}
api.On("ListMetricsPagesWithContext").Return(nil)
_, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
api.On("ListMetrics").Return(nil)
_, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
assert.Nil(t, err)
// make sure the original query wasn't altered
assert.Equal(t, map[string][]string{"Test_DimensionName": {"*"}}, query.Dimensions)
//setting the api to nil confirms that it's using the cached value
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, map[string][]string{"Test_DimensionName": {"Value"}}, queries[0].Dimensions)
@@ -52,20 +52,20 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName2": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{}}
api.On("ListMetricsPagesWithContext").Return(nil)
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{}}
api.On("ListMetrics").Return(nil)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
// assert that the values was set to an empty array
assert.Equal(t, map[string][]string{"Test_DimensionName2": {}}, queries[0].Dimensions)
// Confirm that it calls the api again if the last call did not return any values
api.Metrics = []*cloudwatch.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}},
api.Metrics = []cloudwatchtypes.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value")}}},
}
api.On("ListMetricsPagesWithContext").Return(nil)
queries, err = executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
api.On("ListMetrics").Return(nil)
queries, err = ds.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, tagValueCache, 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, map[string][]string{"Test_DimensionName2": {"Value"}}, queries[0].Dimensions)
@@ -81,7 +81,7 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, []string{"*"}, queries[0].Dimensions["Test_DimensionName1"])
@@ -93,7 +93,7 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, []string{"*"}, queries[0].Dimensions["Test_DimensionName1"])
@@ -107,7 +107,7 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName1": {"Value1"}}
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.NotNil(t, queries[0].Dimensions["Test_DimensionName1"], 1)
@@ -119,7 +119,7 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.MetricName = "Test_MetricName1"
query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.NotNil(t, queries[0].Dimensions["Test_DimensionName1"])
@@ -132,14 +132,14 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{"Test_DimensionName1": {"*"}}
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}},
{MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}},
{MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}},
{MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}},
api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: utils.Pointer("Test_MetricName1"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Value2")}}},
{MetricName: utils.Pointer("Test_MetricName2"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value3")}}},
{MetricName: utils.Pointer("Test_MetricName3"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value4")}}},
{MetricName: utils.Pointer("Test_MetricName4"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Value2")}}},
}}
api.On("ListMetricsPagesWithContext").Return(nil)
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
api.On("ListMetrics").Return(nil)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, shouldSkipFetchingWildcards)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, map[string][]string{"Test_DimensionName1": {"Value1", "Value2", "Value3", "Value4"}}, queries[0].Dimensions)
@@ -167,14 +167,14 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
}
query.MetricQueryType = models.MetricQueryTypeQuery
api := &mocks.MetricsAPI{Metrics: []*cloudwatch.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []*cloudwatch.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}},
api := &mocks.MetricsAPI{Metrics: []cloudwatchtypes.Metric{
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value1")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value1")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value2")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value2")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value3")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value3")}}},
{MetricName: utils.Pointer("Test_MetricName"), Dimensions: []cloudwatchtypes.Dimension{{Name: utils.Pointer("Test_DimensionName1"), Value: utils.Pointer("Dimension1Value4")}, {Name: utils.Pointer("Test_DimensionName2"), Value: utils.Pointer("Dimension2Value4")}}},
}}
api.On("ListMetricsPagesWithContext").Return(nil)
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
api.On("ListMetrics").Return(nil)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", api, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, map[string][]string{
@@ -190,7 +190,7 @@ func TestGetDimensionValuesForWildcards(t *testing.T) {
query.Dimensions = map[string][]string{}
query.MetricQueryType = models.MetricQueryTypeQuery
queries, err := executor.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
queries, err := ds.getDimensionValuesForWildcards(ctx, "us-east-1", nil, []*models.CloudWatchQuery{query}, cache.New(0, 0), 50, noSkip)
assert.Nil(t, err)
assert.Len(t, queries, 1)
assert.Equal(t, map[string][]string{}, queries[0].Dimensions)

View File

@@ -4,15 +4,16 @@ import (
"context"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
)
func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwatchiface.CloudWatchAPI,
func (ds *DataSource) executeRequest(ctx context.Context, client models.CWClient,
metricDataInput *cloudwatch.GetMetricDataInput) ([]*cloudwatch.GetMetricDataOutput, error) {
mdo := make([]*cloudwatch.GetMetricDataOutput, 0)
@@ -26,7 +27,7 @@ func (e *cloudWatchExecutor) executeRequest(ctx context.Context, client cloudwat
*metricDataInput.EndTime = metricDataInput.EndTime.Truncate(time.Minute).Add(time.Minute)
}
resp, err := client.GetMetricDataWithContext(ctx, metricDataInput)
resp, err := client.GetMetricData(ctx, metricDataInput)
if err != nil {
return mdo, backend.DownstreamError(err)
}

View File

@@ -5,8 +5,10 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/stretchr/testify/assert"
@@ -16,39 +18,39 @@ import (
func TestGetMetricDataExecutorTestRequest(t *testing.T) {
t.Run("Should round up end time if cloudWatchRoundUpEndTime is enabled", func(t *testing.T) {
executor := &cloudWatchExecutor{}
executor := &DataSource{}
queryEndTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:45:04Z")
inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}}
inputs := &cloudwatch.GetMetricDataInput{EndTime: &queryEndTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}}
mockMetricClient := &mocks.MetricsAPI{}
mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(
mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(
&cloudwatch.GetMetricDataOutput{
MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{}}},
MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{}}},
}, nil).Once()
_, err := executor.executeRequest(contextWithFeaturesEnabled(features.FlagCloudWatchRoundUpEndTime), mockMetricClient, inputs)
require.NoError(t, err)
expectedTime, _ := time.Parse("2006-01-02T15:04:05Z07:00", "2024-05-01T01:46:00Z")
expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []*cloudwatch.MetricDataQuery{}}
mockMetricClient.AssertCalled(t, "GetMetricDataWithContext", mock.Anything, expectedInput, mock.Anything)
expectedInput := &cloudwatch.GetMetricDataInput{EndTime: &expectedTime, MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}}
mockMetricClient.AssertCalled(t, "GetMetricData", mock.Anything, expectedInput, mock.Anything)
})
}
func TestGetMetricDataExecutorTestResponse(t *testing.T) {
executor := &cloudWatchExecutor{}
inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []*cloudwatch.MetricDataQuery{}}
executor := &DataSource{}
inputs := &cloudwatch.GetMetricDataInput{EndTime: aws.Time(time.Now()), MetricDataQueries: []cloudwatchtypes.MetricDataQuery{}}
mockMetricClient := &mocks.MetricsAPI{}
mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(
mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(
&cloudwatch.GetMetricDataOutput{
MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(12.3), aws.Float64(23.5)}}},
MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{12.3, 23.5}}},
NextToken: aws.String("next"),
}, nil).Once()
mockMetricClient.On("GetMetricDataWithContext", mock.Anything, mock.Anything, mock.Anything).Return(
mockMetricClient.On("GetMetricData", mock.Anything, mock.Anything, mock.Anything).Return(
&cloudwatch.GetMetricDataOutput{
MetricDataResults: []*cloudwatch.MetricDataResult{{Values: []*float64{aws.Float64(100)}}},
MetricDataResults: []cloudwatchtypes.MetricDataResult{{Values: []float64{100}}},
}, nil).Once()
res, err := executor.executeRequest(context.Background(), mockMetricClient, inputs)
require.NoError(t, err)
require.Len(t, res, 2)
require.Len(t, res[0].MetricDataResults[0].Values, 2)
assert.Equal(t, 23.5, *res[0].MetricDataResults[0].Values[1])
assert.Equal(t, 100.0, *res[1].MetricDataResults[0].Values[0])
assert.Equal(t, 23.5, res[0].MetricDataResults[0].Values[1])
assert.Equal(t, 100.0, res[1].MetricDataResults[0].Values[0])
}

View File

@@ -10,11 +10,12 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/aws/smithy-go"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"golang.org/x/sync/errgroup"
@@ -25,10 +26,8 @@ import (
)
const (
limitExceededException = "LimitExceededException"
throttlingException = "ThrottlingException"
defaultEventLimit = int64(10)
defaultLogGroupLimit = int64(50)
defaultEventLimit = int32(10)
defaultLogGroupLimit = int32(50)
logIdentifierInternal = "__log__grafana_internal__"
logStreamIdentifierInternal = "__logstream__grafana_internal__"
)
@@ -43,46 +42,7 @@ func (e *AWSError) Error() string {
return fmt.Sprintf("CloudWatch error: %s: %s", e.Code, e.Message)
}
// StartQueryInputWithLanguage copies the StartQueryInput struct from aws-sdk-go@v1.55.5
// (https://github.com/aws/aws-sdk-go/blob/7112c0a0c2d01713a9db2d57f0e5722225baf5b5/service/cloudwatchlogs/api.go#L19541)
// to add support for the new QueryLanguage parameter, which is unlikely to be backported
// since v1 of the aws-sdk-go is in maintenance mode. We've removed the comments for
// clarity.
type StartQueryInputWithLanguage struct {
_ struct{} `type:"structure"`
EndTime *int64 `locationName:"endTime" type:"long" required:"true"`
Limit *int64 `locationName:"limit" min:"1" type:"integer"`
LogGroupIdentifiers []*string `locationName:"logGroupIdentifiers" type:"list"`
LogGroupName *string `locationName:"logGroupName" min:"1" type:"string"`
LogGroupNames []*string `locationName:"logGroupNames" type:"list"`
QueryString *string `locationName:"queryString" type:"string" required:"true"`
// QueryLanguage is the only change here from the original code.
QueryLanguage *string `locationName:"queryLanguage" type:"string"`
StartTime *int64 `locationName:"startTime" type:"long" required:"true"`
}
type WithQueryLanguageFunc func(language *dataquery.LogsQueryLanguage) func(*request.Request)
// WithQueryLanguage assigns the function to a variable in order to mock it in log_actions_test.go
var WithQueryLanguage WithQueryLanguageFunc = withQueryLanguage
func withQueryLanguage(language *dataquery.LogsQueryLanguage) func(request *request.Request) {
return func(request *request.Request) {
sqi := request.Params.(*cloudwatchlogs.StartQueryInput)
request.Params = &StartQueryInputWithLanguage{
EndTime: sqi.EndTime,
Limit: sqi.Limit,
LogGroupIdentifiers: sqi.LogGroupIdentifiers,
LogGroupName: sqi.LogGroupName,
LogGroupNames: sqi.LogGroupNames,
QueryString: sqi.QueryString,
QueryLanguage: (*string)(language),
StartTime: sqi.StartTime,
}
}
}
func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
func (ds *DataSource) executeLogActions(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.NewQueryDataResponse()
resultChan := make(chan backend.Responses, len(req.Queries))
@@ -97,7 +57,7 @@ func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend
query := query
eg.Go(func() error {
dataframe, err := e.executeLogAction(ectx, logsQuery, query, req.PluginContext)
dataframe, err := ds.executeLogAction(ectx, logsQuery, query)
if err != nil {
resultChan <- backend.Responses{
query.RefID: backend.ErrorResponseWithErrorSource(err),
@@ -134,71 +94,64 @@ func (e *cloudWatchExecutor) executeLogActions(ctx context.Context, req *backend
return resp, nil
}
func (e *cloudWatchExecutor) executeLogAction(ctx context.Context, logsQuery models.LogsQuery, query backend.DataQuery, pluginCtx backend.PluginContext) (*data.Frame, error) {
instance, err := e.getInstance(ctx, pluginCtx)
if err != nil {
return nil, err
}
region := instance.Settings.Region
func (ds *DataSource) executeLogAction(ctx context.Context, logsQuery models.LogsQuery, query backend.DataQuery) (*data.Frame, error) {
region := ds.Settings.Region
if logsQuery.Region != "" {
region = logsQuery.Region
}
logsClient, err := e.getCWLogsClient(ctx, pluginCtx, region)
logsClient, err := ds.getCWLogsClient(ctx, region)
if err != nil {
return nil, err
}
var data *data.Frame = nil
var frame *data.Frame
switch logsQuery.Subtype {
case "StartQuery":
data, err = e.handleStartQuery(ctx, logsClient, logsQuery, query.TimeRange, query.RefID)
frame, err = ds.handleStartQuery(ctx, logsClient, logsQuery, query.TimeRange, query.RefID)
case "StopQuery":
data, err = e.handleStopQuery(ctx, logsClient, logsQuery)
frame, err = ds.handleStopQuery(ctx, logsClient, logsQuery)
case "GetQueryResults":
data, err = e.handleGetQueryResults(ctx, logsClient, logsQuery, query.RefID)
frame, err = ds.handleGetQueryResults(ctx, logsClient, logsQuery, query.RefID)
case "GetLogEvents":
data, err = e.handleGetLogEvents(ctx, logsClient, logsQuery)
frame, err = ds.handleGetLogEvents(ctx, logsClient, logsQuery)
}
if err != nil {
return nil, fmt.Errorf("failed to execute log action with subtype: %s: %w", logsQuery.Subtype, err)
}
return data, nil
return frame, nil
}
func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) handleGetLogEvents(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery) (*data.Frame, error) {
limit := defaultEventLimit
if logsQuery.Limit != nil && *logsQuery.Limit > 0 {
limit = *logsQuery.Limit
}
if logsQuery.LogGroupName == "" {
return nil, backend.DownstreamError(fmt.Errorf("parameter 'logGroupName' is required"))
}
if logsQuery.LogStreamName == "" {
return nil, backend.DownstreamError(fmt.Errorf("parameter 'logStreamName' is required"))
}
queryRequest := &cloudwatchlogs.GetLogEventsInput{
Limit: aws.Int64(limit),
Limit: aws.Int32(limit),
StartFromHead: aws.Bool(logsQuery.StartFromHead),
LogGroupName: &logsQuery.LogGroupName,
LogStreamName: &logsQuery.LogStreamName,
}
if logsQuery.LogGroupName == "" {
return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logGroupName' is required"))
}
queryRequest.SetLogGroupName(logsQuery.LogGroupName)
if logsQuery.LogStreamName == "" {
return nil, backend.DownstreamError(fmt.Errorf("Error: Parameter 'logStreamName' is required"))
}
queryRequest.SetLogStreamName(logsQuery.LogStreamName)
if logsQuery.StartTime != nil && *logsQuery.StartTime != 0 {
queryRequest.SetStartTime(*logsQuery.StartTime)
queryRequest.StartTime = logsQuery.StartTime
}
if logsQuery.EndTime != nil && *logsQuery.EndTime != 0 {
queryRequest.SetEndTime(*logsQuery.EndTime)
queryRequest.EndTime = logsQuery.EndTime
}
logEvents, err := logsClient.GetLogEventsWithContext(ctx, queryRequest)
logEvents, err := logsClient.GetLogEvents(ctx, queryRequest)
if err != nil {
return nil, backend.DownstreamError(err)
}
@@ -223,7 +176,7 @@ func (e *cloudWatchExecutor) handleGetLogEvents(ctx context.Context, logsClient
return data.NewFrame("logEvents", timestampField, messageField), nil
}
func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) executeStartQuery(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery, timeRange backend.TimeRange) (*cloudwatchlogs.StartQueryOutput, error) {
startTime := timeRange.From
endTime := timeRange.To
@@ -267,36 +220,36 @@ func (e *cloudWatchExecutor) executeStartQuery(ctx context.Context, logsClient c
// due to a bug in the startQuery api, we remove * from the arn, otherwise it throws an error
logGroupIdentifiers = append(logGroupIdentifiers, strings.TrimSuffix(arn, "*"))
}
startQueryInput.LogGroupIdentifiers = aws.StringSlice(logGroupIdentifiers)
startQueryInput.LogGroupIdentifiers = logGroupIdentifiers
} else {
// even though log group names are being phased out, we still need to support them for backwards compatibility and alert queries
startQueryInput.LogGroupNames = aws.StringSlice(logsQuery.LogGroupNames)
startQueryInput.LogGroupNames = logsQuery.LogGroupNames
}
}
if logsQuery.Limit != nil {
startQueryInput.Limit = aws.Int64(*logsQuery.Limit)
startQueryInput.Limit = aws.Int32(*logsQuery.Limit)
}
if logsQuery.QueryLanguage != nil {
startQueryInput.QueryLanguage = cloudwatchlogstypes.QueryLanguage(*logsQuery.QueryLanguage)
}
e.logger.FromContext(ctx).Debug("Calling startquery with context with input", "input", startQueryInput)
resp, err := logsClient.StartQueryWithContext(ctx, startQueryInput, WithQueryLanguage(logsQuery.QueryLanguage))
ds.logger.FromContext(ctx).Debug("Calling startquery with context with input", "input", startQueryInput)
resp, err := logsClient.StartQuery(ctx, startQueryInput)
if err != nil {
var awsErr awserr.Error
if errors.As(err, &awsErr) && awsErr.Code() == "LimitExceededException" {
e.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", awsErr)
err = &AWSError{Code: limitExceededException, Message: err.Error()}
} else if errors.As(err, &awsErr) && awsErr.Code() == "ThrottlingException" {
e.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", awsErr)
err = &AWSError{Code: throttlingException, Message: err.Error()}
if errors.Is(err, &cloudwatchlogstypes.LimitExceededException{}) {
ds.logger.FromContext(ctx).Debug("ExecuteStartQuery limit exceeded", "err", err)
} else if errors.Is(err, &cloudwatchlogstypes.ThrottlingException{}) {
ds.logger.FromContext(ctx).Debug("ExecuteStartQuery rate exceeded", "err", err)
}
err = backend.DownstreamError(err)
}
return resp, err
}
func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) handleStartQuery(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery, timeRange backend.TimeRange, refID string) (*data.Frame, error) {
startQueryResponse, err := e.executeStartQuery(ctx, logsClient, logsQuery, timeRange)
startQueryResponse, err := ds.executeStartQuery(ctx, logsClient, logsQuery, timeRange)
if err != nil {
return nil, err
}
@@ -318,20 +271,19 @@ func (e *cloudWatchExecutor) handleStartQuery(ctx context.Context, logsClient cl
return dataFrame, nil
}
func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) executeStopQuery(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery) (*cloudwatchlogs.StopQueryOutput, error) {
queryInput := &cloudwatchlogs.StopQueryInput{
QueryId: aws.String(logsQuery.QueryId),
}
response, err := logsClient.StopQueryWithContext(ctx, queryInput)
response, err := logsClient.StopQuery(ctx, queryInput)
if err != nil {
// If the query has already stopped by the time CloudWatch receives the stop query request,
// an "InvalidParameterException" error is returned. For our purposes though the query has been
// stopped, so we ignore the error.
var awsErr awserr.Error
if errors.As(err, &awsErr) && awsErr.Code() == "InvalidParameterException" {
response = &cloudwatchlogs.StopQueryOutput{Success: aws.Bool(false)}
if errors.Is(err, &cloudwatchlogstypes.InvalidParameterException{}) {
response = &cloudwatchlogs.StopQueryOutput{Success: false}
err = nil
} else {
err = backend.DownstreamError(err)
@@ -341,37 +293,37 @@ func (e *cloudWatchExecutor) executeStopQuery(ctx context.Context, logsClient cl
return response, err
}
func (e *cloudWatchExecutor) handleStopQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) handleStopQuery(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery) (*data.Frame, error) {
response, err := e.executeStopQuery(ctx, logsClient, logsQuery)
response, err := ds.executeStopQuery(ctx, logsClient, logsQuery)
if err != nil {
return nil, err
}
dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{*response.Success}))
dataFrame := data.NewFrame("StopQueryResponse", data.NewField("success", nil, []bool{response.Success}))
return dataFrame, nil
}
func (e *cloudWatchExecutor) executeGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) executeGetQueryResults(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery) (*cloudwatchlogs.GetQueryResultsOutput, error) {
queryInput := &cloudwatchlogs.GetQueryResultsInput{
QueryId: aws.String(logsQuery.QueryId),
}
getQueryResultsResponse, err := logsClient.GetQueryResultsWithContext(ctx, queryInput)
getQueryResultsResponse, err := logsClient.GetQueryResults(ctx, queryInput)
if err != nil {
var awsErr awserr.Error
var awsErr smithy.APIError
if errors.As(err, &awsErr) {
err = &AWSError{Code: awsErr.Code(), Message: err.Error()}
err = &AWSError{Code: awsErr.ErrorCode(), Message: awsErr.ErrorMessage()}
}
err = backend.DownstreamError(err)
}
return getQueryResultsResponse, err
}
func (e *cloudWatchExecutor) handleGetQueryResults(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) handleGetQueryResults(ctx context.Context, logsClient models.CWLogsClient,
logsQuery models.LogsQuery, refID string) (*data.Frame, error) {
getQueryResultsOutput, err := e.executeGetQueryResults(ctx, logsClient, logsQuery)
getQueryResultsOutput, err := ds.executeGetQueryResults(ctx, logsClient, logsQuery)
if err != nil {
return nil, err
}

View File

@@ -6,19 +6,13 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
@@ -36,7 +30,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient {
return &cli
}
const refID = "A"
@@ -57,7 +51,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents
expectedInput: []*cloudwatchlogs.GetLogEventsInput{
{
EndTime: aws.Int64(1),
Limit: aws.Int64(10),
Limit: aws.Int32(10),
LogGroupName: aws.String("foo"),
LogStreamName: aws.String("bar"),
StartFromHead: aws.Bool(false),
@@ -76,7 +70,7 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents
expectedInput: []*cloudwatchlogs.GetLogEventsInput{
{
StartTime: aws.Int64(1),
Limit: aws.Int64(10),
Limit: aws.Int32(10),
LogGroupName: aws.String("foo"),
LogStreamName: aws.String("bar"),
StartFromHead: aws.Bool(true),
@@ -88,13 +82,8 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents
for name, test := range testCases {
t.Run(name, func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -108,8 +97,8 @@ func TestQuery_handleGetLogEvents_passes_nil_start_and_end_times_to_GetLogEvents
})
require.NoError(t, err)
require.Len(t, cli.calls.getEventsWithContext, 1)
assert.Equal(t, test.expectedInput, cli.calls.getEventsWithContext)
require.Len(t, cli.calls.getEvents, 1)
assert.Equal(t, test.expectedInput, cli.calls.getEvents)
})
}
}
@@ -120,22 +109,19 @@ func TestQuery_GetLogEvents_returns_response_from_GetLogEvents_to_data_frame_fie
NewCWLogsClient = origNewCWLogsClient
})
var cli *mocks.MockLogEvents
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient {
return cli
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
cli = &mocks.MockLogEvents{}
cli.On("GetLogEventsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{
Events: []*cloudwatchlogs.OutputLogEvent{{
cli.On("GetLogEvents", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetLogEventsOutput{
Events: []cloudwatchlogstypes.OutputLogEvent{{
Message: utils.Pointer("some message"),
Timestamp: utils.Pointer(int64(15)),
}}}, nil)
resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -174,7 +160,7 @@ func TestQuery_StartQuery(t *testing.T) {
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient {
return &cli
}
@@ -183,18 +169,18 @@ func TestQuery_StartQuery(t *testing.T) {
cli = fakeCWLogsClient{
logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{
LogGroupFields: []*cloudwatchlogs.LogGroupField{
LogGroupFields: []cloudwatchlogstypes.LogGroupField{
{
Name: aws.String("field_a"),
Percent: aws.Int64(100),
Percent: 100,
},
{
Name: aws.String("field_b"),
Percent: aws.Int64(30),
Percent: 30,
},
{
Name: aws.String("field_c"),
Percent: aws.Int64(55),
Percent: 55,
},
},
},
@@ -205,16 +191,10 @@ func TestQuery_StartQuery(t *testing.T) {
To: time.Unix(1584700643, 0),
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{
Region: "us-east-2",
},
}, sessions: &fakeSessionCache{}}, nil
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-2"
})
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -241,18 +221,18 @@ func TestQuery_StartQuery(t *testing.T) {
const refID = "A"
cli = fakeCWLogsClient{
logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{
LogGroupFields: []*cloudwatchlogs.LogGroupField{
LogGroupFields: []cloudwatchlogstypes.LogGroupField{
{
Name: aws.String("field_a"),
Percent: aws.Int64(100),
Percent: 100,
},
{
Name: aws.String("field_b"),
Percent: aws.Int64(30),
Percent: 30,
},
{
Name: aws.String("field_c"),
Percent: aws.Int64(55),
Percent: 55,
},
},
},
@@ -263,16 +243,10 @@ func TestQuery_StartQuery(t *testing.T) {
To: time.Unix(1584873443000, 0),
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{
AWSDatasourceSettings: awsds.AWSDatasourceSettings{
Region: "us-east-2",
},
}, sessions: &fakeSessionCache{}}, nil
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "us-east-2"
})
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -311,26 +285,6 @@ func TestQuery_StartQuery(t *testing.T) {
})
}
type withQueryLanguageMock struct {
capturedLanguage *dataquery.LogsQueryLanguage
mockWithQueryLanguage func(language *dataquery.LogsQueryLanguage) func(request *request.Request)
}
func newWithQueryLanguageMock() *withQueryLanguageMock {
mock := &withQueryLanguageMock{
capturedLanguage: new(dataquery.LogsQueryLanguage),
}
mock.mockWithQueryLanguage = func(language *dataquery.LogsQueryLanguage) func(request *request.Request) {
*mock.capturedLanguage = *language
return func(req *request.Request) {
}
}
return mock
}
func Test_executeStartQuery(t *testing.T) {
origNewCWLogsClient := NewCWLogsClient
t.Cleanup(func() {
@@ -339,15 +293,15 @@ func Test_executeStartQuery(t *testing.T) {
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(cfg aws.Config) models.CWLogsClient {
return &cli
}
t.Run("successfully parses information from JSON to StartQueryWithContext for language", func(t *testing.T) {
t.Run("successfully parses information from JSON to StartQuery for language", func(t *testing.T) {
testCases := map[string]struct {
queries []backend.DataQuery
expectedOutput []*cloudwatchlogs.StartQueryInput
queryLanguage dataquery.LogsQueryLanguage
queryLanguage cloudwatchlogstypes.QueryLanguage
}{
"not defined": {
queries: []backend.DataQuery{
@@ -366,11 +320,12 @@ func Test_executeStartQuery(t *testing.T) {
expectedOutput: []*cloudwatchlogs.StartQueryInput{{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupNames: []*string{aws.String("some name"), aws.String("another name")},
LogGroupNames: []string{"some name", "another name"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
}},
queryLanguage: dataquery.LogsQueryLanguageCWLI,
queryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
"CWLI": {
queries: []backend.DataQuery{{
@@ -389,12 +344,13 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupNames: []*string{aws.String("some name"), aws.String("another name")},
LogGroupNames: []string{"some name", "another name"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
},
queryLanguage: dataquery.LogsQueryLanguageCWLI,
queryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
"PPL": {
queries: []backend.DataQuery{{
@@ -413,12 +369,13 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("source logs | fields @message"),
LogGroupNames: []*string{aws.String("some name"), aws.String("another name")},
LogGroupNames: []string{"some name", "another name"},
QueryLanguage: cloudwatchlogstypes.QueryLanguagePpl,
},
},
queryLanguage: dataquery.LogsQueryLanguagePPL,
queryLanguage: cloudwatchlogstypes.QueryLanguagePpl,
},
"SQL": {
queries: []backend.DataQuery{
@@ -439,49 +396,35 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("SELECT * FROM logs"),
LogGroupNames: nil,
QueryLanguage: cloudwatchlogstypes.QueryLanguageSql,
},
},
queryLanguage: dataquery.LogsQueryLanguageSQL,
queryLanguage: cloudwatchlogstypes.QueryLanguageSql,
},
}
for name, test := range testCases {
t.Run(name, func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
languageMock := newWithQueryLanguageMock()
originalWithQueryLanguage := WithQueryLanguage
WithQueryLanguage = languageMock.mockWithQueryLanguage
defer func() {
WithQueryLanguage = originalWithQueryLanguage
}()
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: test.queries,
})
assert.NoError(t, err)
assert.Equal(t, test.expectedOutput, cli.calls.startQueryWithContext)
assert.Equal(t, &test.queryLanguage, languageMock.capturedLanguage)
assert.Equal(t, test.expectedOutput, cli.calls.startQuery)
})
}
})
t.Run("does not populate StartQueryInput.limit when no limit provided", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -496,18 +439,15 @@ func Test_executeStartQuery(t *testing.T) {
})
assert.NoError(t, err)
require.Len(t, cli.calls.startQueryWithContext, 1)
assert.Nil(t, cli.calls.startQueryWithContext[0].Limit)
require.Len(t, cli.calls.startQuery, 1)
assert.Nil(t, cli.calls.startQuery[0].Limit)
})
t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
_, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
_, err := ds.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -530,21 +470,19 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupIdentifiers: []*string{aws.String("fakeARN")},
LogGroupIdentifiers: []string{"fakeARN"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
}, cli.calls.startQueryWithContext)
}, cli.calls.startQuery)
})
t.Run("attaches logGroupIdentifiers if the crossAccount feature is enabled and strips out trailing *", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
_, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
_, err := ds.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -566,20 +504,18 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupIdentifiers: []*string{aws.String("*fake**ARN")},
LogGroupIdentifiers: []string{"*fake**ARN"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
}, cli.calls.startQueryWithContext)
}, cli.calls.startQuery)
})
t.Run("uses LogGroupNames if the cross account feature flag is not enabled, and log group names is present", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -601,20 +537,18 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupNames: []*string{aws.String("/log-group-name")},
LogGroupNames: []string{"/log-group-name"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
}, cli.calls.startQueryWithContext)
}, cli.calls.startQuery)
})
t.Run("ignores logGroups if feature flag is disabled even if logGroupNames is not present", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -635,20 +569,18 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupNames: []*string{},
LogGroupNames: nil,
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
}, cli.calls.startQueryWithContext)
}, cli.calls.startQuery)
})
t.Run("it always uses logGroups when feature flag is enabled and ignores log group names", func(t *testing.T) {
cli = fakeCWLogsClient{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
ds := newTestDatasource()
_, err := ds.QueryData(contextWithFeaturesEnabled(features.FlagCloudWatchCrossAccountQuerying), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -670,11 +602,12 @@ func Test_executeStartQuery(t *testing.T) {
{
StartTime: aws.Int64(0),
EndTime: aws.Int64(1),
Limit: aws.Int64(12),
Limit: aws.Int32(12),
QueryString: aws.String("fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|fields @message"),
LogGroupIdentifiers: []*string{aws.String("*fake**ARN")},
LogGroupIdentifiers: []string{"*fake**ARN"},
QueryLanguage: cloudwatchlogstypes.QueryLanguageCwli,
},
}, cli.calls.startQueryWithContext)
}, cli.calls.startQuery)
})
}
@@ -686,40 +619,36 @@ func TestQuery_StopQuery(t *testing.T) {
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(aws.Config) models.CWLogsClient {
return &cli
}
cli = fakeCWLogsClient{
logGroupFields: cloudwatchlogs.GetLogGroupFieldsOutput{
LogGroupFields: []*cloudwatchlogs.LogGroupField{
LogGroupFields: []cloudwatchlogstypes.LogGroupField{
{
Name: aws.String("field_a"),
Percent: aws.Int64(100),
Percent: 100,
},
{
Name: aws.String("field_b"),
Percent: aws.Int64(30),
Percent: 30,
},
{
Name: aws.String("field_c"),
Percent: aws.Int64(55),
Percent: 55,
},
},
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
timeRange := backend.TimeRange{
From: time.Unix(1584873443, 0),
To: time.Unix(1584700643, 0),
}
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},
@@ -758,14 +687,14 @@ func TestQuery_GetQueryResults(t *testing.T) {
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(aws.Config) models.CWLogsClient {
return &cli
}
const refID = "A"
cli = fakeCWLogsClient{
queryResults: cloudwatchlogs.GetQueryResultsOutput{
Results: [][]*cloudwatchlogs.ResultField{
Results: [][]cloudwatchlogstypes.ResultField{
{
{
Field: aws.String("@timestamp"),
@@ -795,21 +724,17 @@ func TestQuery_GetQueryResults(t *testing.T) {
},
},
},
Statistics: &cloudwatchlogs.QueryStatistics{
BytesScanned: aws.Float64(512),
RecordsMatched: aws.Float64(256),
RecordsScanned: aws.Float64(1024),
Statistics: &cloudwatchlogstypes.QueryStatistics{
BytesScanned: 512,
RecordsMatched: 256,
RecordsScanned: 1024,
},
Status: aws.String("Complete"),
Status: "Complete",
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
ds := newTestDatasource()
resp, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
},

View File

@@ -1,29 +1,30 @@
package routes
package cloudwatch
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
)
func TestLogGroupFieldsRoute(t *testing.T) {
reqCtxFunc := func(_ context.Context, pluginCtx backend.PluginContext, region string) (reqCtx models.RequestContext, err error) {
return models.RequestContext{}, err
}
origLogGroupsService := services.NewLogGroupsService
t.Cleanup(func() {
services.NewLogGroupsService = origLogGroupsService
})
t.Run("returns 400 if an invalid LogGroupFieldsRequest is used", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", `/log-group-fields?region=us-east-2`, nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupFieldsHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupFieldsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Equal(t, `{"Message":"error in LogGroupFieldsHandler: you need to specify either logGroupName or logGroupArn","Error":"you need to specify either logGroupName or logGroupArn","StatusCode":400}`, rr.Body.String())
@@ -31,14 +32,15 @@ func TestLogGroupFieldsRoute(t *testing.T) {
t.Run("returns 500 if GetLogGroupFields method fails", func(t *testing.T) {
mockLogsService := mocks.LogsService{}
mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api"))
newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) {
return &mockLogsService, nil
mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{}, fmt.Errorf("error from api"))
services.NewLogGroupsService = func(_ models.CloudWatchLogsAPIProvider, _ bool) models.LogGroupsProvider {
return &mockLogsService
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-group-fields?region=us-east-2&logGroupName=test", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupFieldsHandler, logger, reqCtxFunc))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupFieldsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
@@ -47,7 +49,7 @@ func TestLogGroupFieldsRoute(t *testing.T) {
t.Run("returns valid json response if everything is ok", func(t *testing.T) {
mockLogsService := mocks.LogsService{}
mockLogsService.On("GetLogGroupFieldsWithContext", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{
mockLogsService.On("GetLogGroupFields", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroupField]{
{
AccountId: new(string),
Value: resources.LogGroupField{
@@ -63,13 +65,14 @@ func TestLogGroupFieldsRoute(t *testing.T) {
},
},
}, nil)
newLogGroupsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.LogGroupsProvider, error) {
return &mockLogsService, nil
services.NewLogGroupsService = func(_ models.CloudWatchLogsAPIProvider, _ bool) models.LogGroupsProvider {
return &mockLogsService
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-group-fields?region=us-east-2&logGroupName=test", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(LogGroupFieldsHandler, logger, reqCtxFunc))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupFieldsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)

View File

@@ -0,0 +1,219 @@
package cloudwatch
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/services"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
)
func TestLogGroupsRoute(t *testing.T) {
origLogGroupsService := services.NewLogGroupsService
t.Cleanup(func() {
services.NewLogGroupsService = origLogGroupsService
})
var mockLogsService = mocks.LogsService{}
services.NewLogGroupsService = func(models.CloudWatchLogsAPIProvider, bool) models.LogGroupsProvider {
return &mockLogsService
}
t.Run("successfully returns 1 log group with account id", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{{
Value: resources.LogGroup{
Arn: "some arn",
Name: "some name",
},
AccountId: utils.Pointer("111"),
}}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `[{"value":{"name":"some name", "arn":"some arn"},"accountId":"111"}]`, rr.Body.String())
})
t.Run("successfully returns multiple log groups with account id", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return(
[]resources.ResourceResponse[resources.LogGroup]{
{
Value: resources.LogGroup{
Arn: "arn 1",
Name: "name 1",
},
AccountId: utils.Pointer("111"),
}, {
Value: resources.LogGroup{
Arn: "arn 2",
Name: "name 2",
},
AccountId: utils.Pointer("222"),
},
}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusOK, rr.Code)
assert.JSONEq(t, `[
{
"value":{
"name":"name 1",
"arn":"arn 1"
},
"accountId":"111"
},
{
"value":{
"name":"name 2",
"arn":"arn 2"
},
"accountId":"222"
}
]`, rr.Body.String())
})
t.Run("returns error when both logGroupPrefix and logGroup Pattern are provided", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups?logGroupNamePrefix=some-prefix&logGroupPattern=some-pattern", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.JSONEq(t, `{"Error":"cannot set both log group name prefix and pattern", "Message":"cannot set both log group name prefix and pattern: cannot set both log group name prefix and pattern", "StatusCode":400}`, rr.Body.String())
})
t.Run("passes default log group limit and nil for logGroupNamePrefix, accountId, and logGroupPattern", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 50,
ResourceRequest: resources.ResourceRequest{},
LogGroupNamePrefix: nil,
LogGroupNamePattern: nil,
})
})
t.Run("passes default log group limit and nil for logGroupNamePrefix when both are absent", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 50,
LogGroupNamePrefix: nil,
})
})
t.Run("passes log group limit from query parameter", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups?limit=2", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 2,
})
})
t.Run("passes logGroupPrefix from query parameter", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups?logGroupNamePrefix=some-prefix", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 50,
LogGroupNamePrefix: utils.Pointer("some-prefix"),
})
})
t.Run("passes logGroupPattern from query parameter", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups?logGroupPattern=some-pattern", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 50,
LogGroupNamePattern: utils.Pointer("some-pattern"),
})
})
t.Run("passes logGroupPattern from query parameter", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).Return([]resources.ResourceResponse[resources.LogGroup]{}, nil)
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups?accountId=some-account-id", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
mockLogsService.AssertCalled(t, "GetLogGroups", resources.LogGroupsRequest{
Limit: 50,
ResourceRequest: resources.ResourceRequest{AccountId: utils.Pointer("some-account-id")},
})
})
t.Run("returns error if service returns error", func(t *testing.T) {
mockLogsService = mocks.LogsService{}
mockLogsService.On("GetLogGroups", mock.Anything).
Return([]resources.ResourceResponse[resources.LogGroup]{}, fmt.Errorf("some error"))
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/log-groups", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.LogGroupsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
assert.JSONEq(t, `{"Error":"some error","Message":"GetLogGroups error: some error","StatusCode":500}`, rr.Body.String())
})
}

View File

@@ -7,7 +7,9 @@ import (
"strconv"
"time"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/grafana/grafana-plugin-sdk-go/data"
)
@@ -18,7 +20,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro
return nil, fmt.Errorf("response is nil, cannot convert log results to data frames")
}
nonEmptyRows := make([][]*cloudwatchlogs.ResultField, 0)
nonEmptyRows := make([][]cloudwatchlogstypes.ResultField, 0)
for _, row := range response.Results {
// Sometimes CloudWatch can send empty rows
if len(row) == 0 {
@@ -116,26 +118,20 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro
queryStats := make([]data.QueryStat, 0)
if response.Statistics != nil {
if response.Statistics.BytesScanned != nil {
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"},
Value: *response.Statistics.BytesScanned,
})
}
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Bytes scanned"},
Value: response.Statistics.BytesScanned,
})
if response.Statistics.RecordsScanned != nil {
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Records scanned"},
Value: *response.Statistics.RecordsScanned,
})
}
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Records scanned"},
Value: response.Statistics.RecordsScanned,
})
if response.Statistics.RecordsMatched != nil {
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Records matched"},
Value: *response.Statistics.RecordsMatched,
})
}
queryStats = append(queryStats, data.QueryStat{
FieldConfig: data.FieldConfig{DisplayName: "Records matched"},
Value: response.Statistics.RecordsMatched,
})
}
frame := data.NewFrame("CloudWatchLogsResponse", newFields...)
@@ -148,10 +144,8 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro
frame.Meta.Stats = queryStats
}
if response.Status != nil {
frame.Meta.Custom = map[string]any{
"Status": *response.Status,
}
frame.Meta.Custom = map[string]any{
"Status": string(response.Status),
}
// Results aren't guaranteed to come ordered by time (ascending), so we need to sort
@@ -159,7 +153,7 @@ func logsResultsToDataframes(response *cloudwatchlogs.GetQueryResultsOutput, gro
return frame, nil
}
func changeToStringField(lengthOfValues int, rows [][]*cloudwatchlogs.ResultField, logEventField string) []*string {
func changeToStringField(lengthOfValues int, rows [][]cloudwatchlogstypes.ResultField, logEventField string) []*string {
fieldValuesAsStrings := make([]*string, lengthOfValues)
for i, resultFields := range rows {
for _, field := range resultFields {

View File

@@ -5,8 +5,10 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/assert"
@@ -19,63 +21,63 @@ import (
func TestLogsResultsToDataframes(t *testing.T) {
fakeCloudwatchResponse := &cloudwatchlogs.GetQueryResultsOutput{
Results: [][]*cloudwatchlogs.ResultField{
Results: [][]cloudwatchlogstypes.ResultField{
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@ptr"),
Value: aws.String("fake ptr"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String("2020-03-02 15:04:05.000"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("line"),
Value: aws.String("test message 1"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@logStream"),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@log"),
Value: aws.String("fakelog"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logStreamIdentifierInternal),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logIdentifierInternal),
Value: aws.String("fakelog"),
},
},
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@ptr"),
Value: aws.String("fake ptr"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String("2020-03-02 16:04:05.000"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("line"),
Value: aws.String("test message 2"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@logStream"),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@log"),
Value: aws.String("fakelog"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logStreamIdentifierInternal),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logIdentifierInternal),
Value: aws.String("fakelog"),
},
@@ -84,47 +86,47 @@ func TestLogsResultsToDataframes(t *testing.T) {
{},
// or rows with only timestamp
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String("2020-03-02 17:04:05.000"),
},
},
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@ptr"),
Value: aws.String("fake ptr"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String("2020-03-02 17:04:05.000"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("line"),
Value: aws.String("test message 3"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@logStream"),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@log"),
Value: aws.String("fakelog"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logStreamIdentifierInternal),
Value: aws.String("fakelogstream"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String(logIdentifierInternal),
Value: aws.String("fakelog"),
},
},
},
Status: aws.String("ok"),
Statistics: &cloudwatchlogs.QueryStatistics{
BytesScanned: aws.Float64(2000),
RecordsMatched: aws.Float64(3),
RecordsScanned: aws.Float64(5000),
Status: "ok",
Statistics: &cloudwatchlogstypes.QueryStatistics{
BytesScanned: 2000,
RecordsMatched: 3,
RecordsScanned: 5000,
},
}
@@ -224,33 +226,33 @@ func TestLogsResultsToDataframes(t *testing.T) {
func TestLogsResultsToDataframes_MixedTypes_NumericValuesMixedWithStringFallBackToStringValues(t *testing.T) {
dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{
Results: [][]*cloudwatchlogs.ResultField{
Results: [][]cloudwatchlogstypes.ResultField{
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("numberOrString"),
Value: aws.String("-1.234"),
},
},
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("numberOrString"),
Value: aws.String("1"),
},
},
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("numberOrString"),
Value: aws.String("not a number"),
},
},
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("numberOrString"),
Value: aws.String("2.000"),
},
},
},
Status: aws.String("ok"),
Status: "ok",
}, []string{})
require.NoError(t, err)
@@ -284,27 +286,27 @@ func TestLogsResultsToDataframes_With_Millisecond_Timestamps(t *testing.T) {
ingestionTimeField := int64(1732790372916)
dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{
Results: [][]*cloudwatchlogs.ResultField{
Results: [][]cloudwatchlogstypes.ResultField{
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String(fmt.Sprintf("%d", timestampField)),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@ingestionTime"),
Value: aws.String(fmt.Sprintf("%d", ingestionTimeField)),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("stringTimeField"),
Value: aws.String(stringTimeField),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("message"),
Value: aws.String("log message"),
},
},
},
Status: aws.String("ok"),
Status: "ok",
}, []string{})
require.NoError(t, err)
@@ -348,23 +350,23 @@ func TestLogsResultsToDataframes_With_Int_Grouping_Field(t *testing.T) {
timestampField := int64(1732749534876)
dataframes, err := logsResultsToDataframes(&cloudwatchlogs.GetQueryResultsOutput{
Results: [][]*cloudwatchlogs.ResultField{
Results: [][]cloudwatchlogstypes.ResultField{
{
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("@timestamp"),
Value: aws.String(fmt.Sprintf("%d", timestampField)),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("numberField"),
Value: aws.String("8"),
},
&cloudwatchlogs.ResultField{
cloudwatchlogstypes.ResultField{
Field: aws.String("groupingNumber"),
Value: aws.String("100"),
},
},
},
Status: aws.String("ok"),
Status: "ok",
}, []string{"groupingNumber"})
require.NoError(t, err)

View File

@@ -7,8 +7,8 @@ import (
"fmt"
"time"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/kinds/dataquery"
@@ -17,15 +17,9 @@ import (
const initialAlertPollPeriod = time.Second
var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
var executeSyncLogQuery = func(ctx context.Context, ds *DataSource, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
resp := backend.NewQueryDataResponse()
instance, err := e.getInstance(ctx, req.PluginContext)
if err != nil {
resp.Responses[req.Queries[0].RefID] = backend.ErrorResponseWithErrorSource(err)
return resp, nil
}
for _, q := range req.Queries {
var logsQuery models.LogsQuery
err := json.Unmarshal(q.JSON, &logsQuery)
@@ -40,10 +34,10 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *
region := logsQuery.Region
if region == "" || region == defaultRegion {
logsQuery.Region = instance.Settings.Region
logsQuery.Region = ds.Settings.Region
}
logsClient, err := e.getCWLogsClient(ctx, req.PluginContext, region)
logsClient, err := ds.getCWLogsClient(ctx, region)
if err != nil {
return nil, err
}
@@ -53,7 +47,7 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *
refId = q.RefID
}
getQueryResultsOutput, err := e.syncQuery(ctx, logsClient, q, logsQuery, instance.Settings.LogsTimeout.Duration)
getQueryResultsOutput, err := ds.syncQuery(ctx, logsClient, q, logsQuery, ds.Settings.LogsTimeout.Duration)
var sourceError backend.ErrorWithSource
if errors.As(err, &sourceError) {
resp.Responses[refId] = backend.ErrorResponseWithErrorSource(sourceError)
@@ -86,9 +80,9 @@ var executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *
return resp, nil
}
func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatchlogsiface.CloudWatchLogsAPI,
func (ds *DataSource) syncQuery(ctx context.Context, logsClient models.CWLogsClient,
queryContext backend.DataQuery, logsQuery models.LogsQuery, logsTimeout time.Duration) (*cloudwatchlogs.GetQueryResultsOutput, error) {
startQueryOutput, err := e.executeStartQuery(ctx, logsClient, logsQuery, queryContext.TimeRange)
startQueryOutput, err := ds.executeStartQuery(ctx, logsClient, logsQuery, queryContext.TimeRange)
if err != nil {
return nil, err
}
@@ -113,11 +107,11 @@ func (e *cloudWatchExecutor) syncQuery(ctx context.Context, logsClient cloudwatc
attemptCount := 1
for range ticker.C {
res, err := e.executeGetQueryResults(ctx, logsClient, requestParams)
res, err := ds.executeGetQueryResults(ctx, logsClient, requestParams)
if err != nil {
return nil, err
}
if isTerminated(*res.Status) {
if isTerminated(res.Status) {
return res, err
}
if time.Duration(attemptCount)*time.Second >= logsTimeout {

View File

@@ -7,15 +7,11 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/grafana/grafana-aws-sdk/pkg/awsds"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
cloudwatchlogstypes "github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs/types"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/utils"
@@ -31,19 +27,15 @@ func Test_executeSyncLogQuery(t *testing.T) {
})
var cli fakeCWLogsClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(aws.Config) models.CWLogsClient {
return &cli
}
t.Run("getCWLogsClient is called with region from input JSON", func(t *testing.T) {
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
sess := fakeSessionCache{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &sess}, nil
})
executor := newExecutor(im, log.NewNullLogger())
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}}
ds := newTestDatasource()
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -58,18 +50,15 @@ func Test_executeSyncLogQuery(t *testing.T) {
})
assert.NoError(t, err)
assert.Equal(t, []string{"some region"}, sess.calledRegions)
//assert.Equal(t, []string{"some region"}, sess.calledRegions)
})
t.Run("getCWLogsClient is called with region from instance manager when region is default", func(t *testing.T) {
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
sess := fakeSessionCache{}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &sess}, nil
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}}
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "instance manager's region"
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -84,7 +73,7 @@ func Test_executeSyncLogQuery(t *testing.T) {
})
assert.NoError(t, err)
assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions)
//assert.Equal(t, []string{"instance manager's region"}, sess.calledRegions)
})
t.Run("with header", func(t *testing.T) {
@@ -111,7 +100,7 @@ func Test_executeSyncLogQuery(t *testing.T) {
}
origExecuteSyncLogQuery := executeSyncLogQuery
var syncCalled bool
executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
executeSyncLogQuery = func(ctx context.Context, e *DataSource, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
syncCalled = true
return nil, nil
}
@@ -119,13 +108,11 @@ func Test_executeSyncLogQuery(t *testing.T) {
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
syncCalled = false
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}}
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "instance manager's region"
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: tc.headers,
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -153,7 +140,7 @@ func Test_executeSyncLogQuery(t *testing.T) {
t.Run("when query mode is 'Logs' and does not include type or subtype", func(t *testing.T) {
origExecuteSyncLogQuery := executeSyncLogQuery
syncCalled := false
executeSyncLogQuery = func(ctx context.Context, e *cloudWatchExecutor, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
executeSyncLogQuery = func(ctx context.Context, e *DataSource, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
syncCalled = true
return nil, nil
}
@@ -161,13 +148,12 @@ func Test_executeSyncLogQuery(t *testing.T) {
executeSyncLogQuery = origExecuteSyncLogQuery
})
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{AWSDatasourceSettings: awsds.AWSDatasourceSettings{Region: "instance manager's region"}}, sessions: &fakeSessionCache{}}, nil
cli = fakeCWLogsClient{queryResults: cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}}
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.Region = "instance manager's region"
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
{
@@ -192,22 +178,19 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) {
})
var cli *mockLogsSyncClient
NewCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
NewCWLogsClient = func(aws.Config) models.CWLogsClient {
return cli
}
t.Run("when a query refId is not provided, 'A' is assigned by default", func(t *testing.T) {
cli = &mockLogsSyncClient{}
cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("abcd-efgh-ijkl-mnop"),
}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil)
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil)
ds := newTestDatasource()
res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
res, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -227,16 +210,13 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) {
t.Run("when a query refId is provided, it is returned in the response", func(t *testing.T) {
cli = &mockLogsSyncClient{}
cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("abcd-efgh-ijkl-mnop"),
}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")}, nil)
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"}, nil)
ds := newTestDatasource()
res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
res, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -269,43 +249,40 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) {
// when each query has a different response from AWS API calls, the RefIds are correctly reassigned to the associated response.
cli = &mockLogsSyncClient{}
// mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for A"
cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool {
cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool {
return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for A"
}), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("queryId for A"),
}, nil)
// mock.MatchedBy makes sure that the QueryId below will only be returned when the input expression = "query string for B"
cli.On("StartQueryWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool {
cli.On("StartQuery", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.StartQueryInput) bool {
return *input.QueryString == "fields @timestamp,ltrim(@log) as __log__grafana_internal__,ltrim(@logStream) as __logstream__grafana_internal__|query string for B"
}), mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("queryId for B"),
}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool {
cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool {
return *input.QueryId == "queryId for A"
}), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{
// this result will only be returned when the argument is QueryId = "queryId for A"
Results: [][]*cloudwatchlogs.ResultField{{{
Results: [][]cloudwatchlogstypes.ResultField{{{
Field: utils.Pointer("@log"),
Value: utils.Pointer("A result"),
}}},
Status: aws.String("Complete")}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool {
Status: "Complete"}, nil)
cli.On("GetQueryResults", mock.Anything, mock.MatchedBy(func(input *cloudwatchlogs.GetQueryResultsInput) bool {
return *input.QueryId == "queryId for B"
}), mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{
// this result will only be returned when the argument is QueryId = "queryId for B"
Results: [][]*cloudwatchlogs.ResultField{{{
Results: [][]cloudwatchlogstypes.ResultField{{{
Field: utils.Pointer("@log"),
Value: utils.Pointer("B result"),
}}},
Status: aws.String("Complete")}, nil)
Status: "Complete"}, nil)
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
res, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -328,30 +305,29 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) {
},
})
expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId A
expectedLogFieldFromFirstCall := data.NewField("@log", nil, []*string{utils.Pointer("A result")}) // verifies the response from GetQueryResults matches the input RefId A
assert.NoError(t, err)
respA, ok := res.Responses["A"]
require.True(t, ok)
assert.Equal(t, []*data.Field{expectedLogFieldFromFirstCall}, respA.Frames[0].Fields)
expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResultsWithContext matches the input RefId B
expectedLogFieldFromSecondCall := data.NewField("@log", nil, []*string{utils.Pointer("B result")}) // verifies the response from GetQueryResults matches the input RefId B
respB, ok := res.Responses["B"]
require.True(t, ok)
assert.Equal(t, []*data.Field{expectedLogFieldFromSecondCall}, respB.Frames[0].Fields)
})
t.Run("when logsTimeout setting is defined, the polling period will be set to that variable", func(t *testing.T) {
cli = &mockLogsSyncClient{}
cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("abcd-efgh-ijkl-mnop"),
}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Running")}, nil)
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{LogsTimeout: models.Duration{Duration: time.Millisecond}}, sessions: &fakeSessionCache{}}, nil
cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.GetQueryResultsOutput{Status: "Running"}, nil)
ds := newTestDatasource(func(ds *DataSource) {
ds.Settings.LogsTimeout = models.Duration{Duration: time.Millisecond}
})
executor := newExecutor(im, log.NewNullLogger())
_, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
_, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{
@@ -366,24 +342,21 @@ func Test_executeSyncLogQuery_handles_RefId_from_input_queries(t *testing.T) {
},
})
assert.Error(t, err)
cli.AssertNumberOfCalls(t, "GetQueryResultsWithContext", 1)
cli.AssertNumberOfCalls(t, "GetQueryResults", 1)
})
t.Run("when getQueryResults returns aws error is returned, it keeps the context", func(t *testing.T) {
cli = &mockLogsSyncClient{}
cli.On("StartQueryWithContext", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
cli.On("StartQuery", mock.Anything, mock.Anything, mock.Anything).Return(&cloudwatchlogs.StartQueryOutput{
QueryId: aws.String("abcd-efgh-ijkl-mnop"),
}, nil)
cli.On("GetQueryResultsWithContext", mock.Anything, mock.Anything, mock.Anything).Return(
&cloudwatchlogs.GetQueryResultsOutput{Status: aws.String("Complete")},
&fakeAWSError{code: "foo", message: "bar"},
cli.On("GetQueryResults", mock.Anything, mock.Anything, mock.Anything).Return(
&cloudwatchlogs.GetQueryResultsOutput{Status: "Complete"},
&fakeSmithyError{code: "foo", message: "bar"},
)
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
res, err := executor.QueryData(context.Background(), &backend.QueryDataRequest{
res, err := ds.QueryData(context.Background(), &backend.QueryDataRequest{
Headers: map[string]string{headerFromAlert: "some value"},
PluginContext: backend.PluginContext{DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{}},
Queries: []backend.DataQuery{

View File

@@ -4,30 +4,31 @@ import (
"context"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
func (e *cloudWatchExecutor) buildMetricDataInput(ctx context.Context, startTime time.Time, endTime time.Time,
func (ds *DataSource) buildMetricDataInput(ctx context.Context, startTime time.Time, endTime time.Time,
queries []*models.CloudWatchQuery) (*cloudwatch.GetMetricDataInput, error) {
metricDataInput := &cloudwatch.GetMetricDataInput{
StartTime: aws.Time(startTime),
EndTime: aws.Time(endTime),
ScanBy: aws.String("TimestampAscending"),
ScanBy: cloudwatchtypes.ScanByTimestampAscending,
}
shouldSetLabelOptions := len(queries) > 0 && len(queries[0].TimezoneUTCOffset) > 0
if shouldSetLabelOptions {
metricDataInput.LabelOptions = &cloudwatch.LabelOptions{
metricDataInput.LabelOptions = &cloudwatchtypes.LabelOptions{
Timezone: aws.String(queries[0].TimezoneUTCOffset),
}
}
for _, query := range queries {
metricDataQuery, err := e.buildMetricDataQuery(ctx, query)
metricDataQuery, err := ds.buildMetricDataQuery(ctx, query)
if err != nil {
return nil, &models.QueryError{Err: err, RefID: query.RefId}
}

View File

@@ -5,12 +5,12 @@ import (
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
@@ -20,21 +20,21 @@ func TestMetricDataInputBuilder(t *testing.T) {
tests := []struct {
name string
timezoneUTCOffset string
expectedLabelOptions *cloudwatch.LabelOptions
expectedLabelOptions *cloudwatchtypes.LabelOptions
}{
{name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatch.LabelOptions{Timezone: aws.String("+1234")}},
{name: "when timezoneUTCOffset is provided", timezoneUTCOffset: "+1234", expectedLabelOptions: &cloudwatchtypes.LabelOptions{Timezone: aws.String("+1234")}},
{name: "when timezoneUTCOffset is not provided", timezoneUTCOffset: "", expectedLabelOptions: nil},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
ds := newTestDatasource()
query := getBaseQuery()
query.TimezoneUTCOffset = tc.timezoneUTCOffset
from := now.Add(time.Hour * -2)
to := now.Add(time.Hour * -1)
mdi, err := executor.buildMetricDataInput(context.Background(), from, to, []*models.CloudWatchQuery{query})
mdi, err := ds.buildMetricDataInput(context.Background(), from, to, []*models.CloudWatchQuery{query})
assert.NoError(t, err)
require.NotNil(t, mdi)

View File

@@ -4,11 +4,10 @@ import (
"context"
"fmt"
"sort"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go-v2/aws"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
@@ -16,8 +15,8 @@ import (
const keySeparator = "|&|"
func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) {
mdq := &cloudwatch.MetricDataQuery{
func (ds *DataSource) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (cloudwatchtypes.MetricDataQuery, error) {
mdq := cloudwatchtypes.MetricDataQuery{
Id: aws.String(query.Id),
ReturnData: aws.Bool(query.ReturnData),
}
@@ -28,10 +27,10 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo
switch query.GetGetMetricDataAPIMode() {
case models.GMDApiModeMathExpression:
mdq.Period = aws.Int64(int64(query.Period))
mdq.Period = &query.Period
mdq.Expression = aws.String(query.Expression)
case models.GMDApiModeSQLExpression:
mdq.Period = aws.Int64(int64(query.Period))
mdq.Period = &query.Period
mdq.Expression = aws.String(query.SqlExpression)
case models.GMDApiModeInferredSearchExpression:
mdq.Expression = aws.String(buildSearchExpression(query, query.Statistic))
@@ -39,17 +38,17 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *mo
mdq.Label = aws.String(buildSearchExpressionLabel(query))
}
case models.GMDApiModeMetricStat:
mdq.MetricStat = &cloudwatch.MetricStat{
Metric: &cloudwatch.Metric{
mdq.MetricStat = &cloudwatchtypes.MetricStat{
Metric: &cloudwatchtypes.Metric{
Namespace: aws.String(query.Namespace),
MetricName: aws.String(query.MetricName),
Dimensions: make([]*cloudwatch.Dimension, 0),
Dimensions: make([]cloudwatchtypes.Dimension, 0),
},
Period: aws.Int64(int64(query.Period)),
Period: &query.Period,
}
for key, values := range query.Dimensions {
mdq.MetricStat.Metric.Dimensions = append(mdq.MetricStat.Metric.Dimensions,
&cloudwatch.Dimension{
cloudwatchtypes.Dimension{
Name: aws.String(key),
Value: aws.String(values[0]),
})
@@ -121,14 +120,14 @@ func buildSearchExpression(query *models.CloudWatchQuery, stat string) string {
}
schema = fmt.Sprintf("{%s}", schema)
schemaSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{schema, searchTerm, account}, " "))
return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %s))", schemaSearchTermAndAccount, stat, strconv.Itoa(query.Period))
return fmt.Sprintf("REMOVE_EMPTY(SEARCH('%s', '%s', %d))", schemaSearchTermAndAccount, stat, query.Period)
}
sort.Strings(dimensionNamesWithoutKnownValues)
searchTerm = appendSearch(searchTerm, join(dimensionNamesWithoutKnownValues, " ", `"`, `"`))
namespace := fmt.Sprintf("Namespace=%q", query.Namespace)
namespaceSearchTermAndAccount := strings.TrimSpace(strings.Join([]string{namespace, searchTerm, account}, " "))
return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %s))`, namespaceSearchTermAndAccount, stat, strconv.Itoa(query.Period))
return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %d))`, namespaceSearchTermAndAccount, stat, query.Period)
}
func buildSearchExpressionLabel(query *models.CloudWatchQuery) string {

View File

@@ -4,8 +4,8 @@ import (
"context"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/features"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/stretchr/testify/assert"
@@ -13,13 +13,13 @@ import (
)
func TestMetricDataQueryBuilder(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
ds := newTestDatasource()
t.Run("buildMetricDataQuery", func(t *testing.T) {
t.Run("should use metric stat", func(t *testing.T) {
query := getBaseQuery()
query.MetricEditorMode = models.MetricEditorModeBuilder
query.MetricQueryType = models.MetricQueryTypeSearch
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
require.Empty(t, mdq.Expression)
assert.Equal(t, query.MetricName, *mdq.MetricStat.Metric.MetricName)
@@ -31,7 +31,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
query.MetricEditorMode = models.MetricEditorModeBuilder
query.MetricQueryType = models.MetricQueryTypeSearch
query.AccountId = aws.String("some account id")
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
assert.Equal(t, "some account id", *mdq.AccountId)
})
@@ -40,7 +40,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
query := getBaseQuery()
query.MetricEditorMode = models.MetricEditorModeBuilder
query.MetricQueryType = models.MetricQueryTypeSearch
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
assert.Nil(t, mdq.AccountId)
})
@@ -50,7 +50,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
query.MetricEditorMode = models.MetricEditorModeBuilder
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
require.Nil(t, mdq.MetricStat)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"="lb1"', '', 300))`, *mdq.Expression)
@@ -61,7 +61,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
query.MetricEditorMode = models.MetricEditorModeRaw
query.MetricQueryType = models.MetricQueryTypeQuery
query.SqlExpression = `SELECT SUM(CPUUTilization) FROM "AWS/EC2"`
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
require.Nil(t, mdq.MetricStat)
assert.Equal(t, query.SqlExpression, *mdq.Expression)
@@ -72,32 +72,30 @@ func TestMetricDataQueryBuilder(t *testing.T) {
query.MetricEditorMode = models.MetricEditorModeRaw
query.MetricQueryType = models.MetricQueryTypeSearch
query.Expression = `SUM(x+y)`
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
require.Nil(t, mdq.MetricStat)
assert.Equal(t, query.Expression, *mdq.Expression)
})
t.Run("should set period in user defined expression", func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
query := getBaseQuery()
query.MetricEditorMode = models.MetricEditorModeRaw
query.MetricQueryType = models.MetricQueryTypeSearch
query.MatchExact = false
query.Expression = `SUM([a,b])`
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
require.NoError(t, err)
require.Nil(t, mdq.MetricStat)
assert.Equal(t, int64(300), *mdq.Period)
assert.Equal(t, int32(300), *mdq.Period)
assert.Equal(t, `SUM([a,b])`, *mdq.Expression)
})
t.Run("should set label", func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
query := getBaseQuery()
query.Label = "some label"
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
assert.NoError(t, err)
require.NotNil(t, mdq.Label)
@@ -105,18 +103,16 @@ func TestMetricDataQueryBuilder(t *testing.T) {
})
t.Run("should not set label for empty string query label", func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
query := getBaseQuery()
query.Label = ""
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
assert.NoError(t, err)
assert.Nil(t, mdq.Label)
})
t.Run(`should not specify accountId when it is "all"`, func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
query := &models.CloudWatchQuery{
Namespace: "AWS/EC2",
MetricName: "CPUUtilization",
@@ -126,7 +122,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
AccountId: aws.String("all"),
}
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
assert.NoError(t, err)
require.Nil(t, mdq.MetricStat)
@@ -134,7 +130,6 @@ func TestMetricDataQueryBuilder(t *testing.T) {
})
t.Run("should set accountId when it is specified", func(t *testing.T) {
executor := newExecutor(nil, log.NewNullLogger())
query := &models.CloudWatchQuery{
Namespace: "AWS/EC2",
MetricName: "CPUUtilization",
@@ -144,7 +139,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
AccountId: aws.String("12345"),
}
mdq, err := executor.buildMetricDataQuery(context.Background(), query)
mdq, err := ds.buildMetricDataQuery(context.Background(), query)
assert.NoError(t, err)
require.Nil(t, mdq.MetricStat)
@@ -170,7 +165,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -192,7 +187,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "InstanceId"=("i-123" OR "i-456" OR "i-789") "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -213,7 +208,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -235,7 +230,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -258,7 +253,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3") :aws.AccountId="some account id"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -279,7 +274,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/Kafka","Cluster Name"} MetricName="CpuUser" "Cluster Name"=("dev-cluster" OR "prod-cluster")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.Cluster Name')}", *mdq.Label)
@@ -301,7 +296,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"Test-API Cache by Minute","InstanceId","LoadBalancer"} MetricName="CpuUser" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -324,7 +319,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"CPUUtilization","InstanceId","LoadBalancer"} MetricName="CpuUser" "LoadBalancer"="lb1"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "LB: ${PROP('Dim.LoadBalancer')|&|${PROP('Dim.InstanceId')}", *mdq.Label)
@@ -349,7 +344,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -371,7 +366,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "InstanceId"=("i-123" OR "i-456" OR "i-789") "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -392,7 +387,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -414,7 +409,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3") "InstanceId"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -437,7 +432,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3") "InstanceId" :aws.AccountId="some account id"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "${LABEL}|&|${PROP('Dim.InstanceId')}|&|${PROP('Dim.LoadBalancer')}", *mdq.Label)
@@ -460,7 +455,7 @@ func TestMetricDataQueryBuilder(t *testing.T) {
MetricEditorMode: models.MetricEditorModeBuilder,
}
mdq, err := executor.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
mdq, err := ds.buildMetricDataQuery(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), query)
require.NoError(t, err)
assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"="lb1" "InstanceId"', 'Average', 300))`, *mdq.Expression)
assert.Equal(t, "LB: ${PROP('Dim.LoadBalancer')|&|${PROP('Dim.InstanceId')}", *mdq.Label)

View File

@@ -12,11 +12,13 @@ import (
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
)
type suggestData struct {
@@ -39,12 +41,12 @@ func parseMultiSelectValue(input string) []string {
return []string{trimmedInput}
}
func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) {
func (ds *DataSource) handleGetEbsVolumeIds(ctx context.Context, parameters url.Values) ([]suggestData, error) {
region := parameters.Get("region")
instanceId := parameters.Get("instanceId")
instanceIds := aws.StringSlice(parseMultiSelectValue(instanceId))
instances, err := e.ec2DescribeInstances(ctx, pluginCtx, region, nil, instanceIds)
instanceIds := parseMultiSelectValue(instanceId)
instances, err := ds.ec2DescribeInstances(ctx, region, nil, instanceIds)
if err != nil {
return nil, err
}
@@ -61,7 +63,7 @@ func (e *cloudWatchExecutor) handleGetEbsVolumeIds(ctx context.Context, pluginCt
return result, nil
}
func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) {
func (ds *DataSource) handleGetEc2InstanceAttribute(ctx context.Context, parameters url.Values) ([]suggestData, error) {
region := parameters.Get("region")
attributeName := parameters.Get("attributeName")
filterJson := parameters.Get("filters")
@@ -72,23 +74,23 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
return nil, fmt.Errorf("error unmarshaling filter: %v", err)
}
var filters []*ec2.Filter
var filters []ec2types.Filter
for k, v := range filterMap {
if vv, ok := v.([]any); ok {
var values []*string
var values []string
for _, vvv := range vv {
if vvvv, ok := vvv.(string); ok {
values = append(values, &vvvv)
values = append(values, vvvv)
}
}
filters = append(filters, &ec2.Filter{
filters = append(filters, ec2types.Filter{
Name: aws.String(k),
Values: values,
})
}
}
instances, err := e.ec2DescribeInstances(ctx, pluginCtx, region, filters, nil)
instances, err := ds.ec2DescribeInstances(ctx, region, filters, nil)
if err != nil {
return nil, err
}
@@ -120,7 +122,7 @@ func (e *cloudWatchExecutor) handleGetEc2InstanceAttribute(ctx context.Context,
return result, nil
}
func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (value string, found bool, err error) {
func getInstanceAttributeValue(attributeName string, instance ec2types.Instance) (value string, found bool, err error) {
tags := make(map[string]string)
for _, tag := range instance.Tags {
tags[*tag.Key] = *tag.Value
@@ -152,7 +154,12 @@ func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (va
if v.Kind() == reflect.Ptr && v.IsNil() {
return "", false, nil
}
if attr, ok := v.Interface().(*string); ok {
if v.Kind() == reflect.String {
if v.String() == "" {
return "", false, nil
}
data = v.String()
} else if attr, ok := v.Interface().(*string); ok {
data = *attr
} else if attr, ok := v.Interface().(*time.Time); ok {
data = attr.String()
@@ -168,7 +175,7 @@ func getInstanceAttributeValue(attributeName string, instance *ec2.Instance) (va
return data, true, nil
}
func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) {
func (ds *DataSource) handleGetResourceArns(ctx context.Context, parameters url.Values) ([]suggestData, error) {
region := parameters.Get("region")
resourceType := parameters.Get("resourceType")
tagsJson := parameters.Get("tags")
@@ -179,26 +186,25 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt
return nil, fmt.Errorf("error unmarshaling filter: %v", err)
}
var filters []*resourcegroupstaggingapi.TagFilter
var filters []resourcegroupstaggingapitypes.TagFilter
for k, v := range tagsMap {
if vv, ok := v.([]any); ok {
var values []*string
var values []string
for _, vvv := range vv {
if vvvv, ok := vvv.(string); ok {
values = append(values, &vvvv)
values = append(values, vvvv)
}
}
filters = append(filters, &resourcegroupstaggingapi.TagFilter{
filters = append(filters, resourcegroupstaggingapitypes.TagFilter{
Key: aws.String(k),
Values: values,
})
}
}
var resourceTypes []*string
resourceTypes = append(resourceTypes, &resourceType)
resourceTypes := []string{resourceType}
resources, err := e.resourceGroupsGetResources(ctx, pluginCtx, region, filters, resourceTypes)
resources, err := ds.resourceGroupsGetResources(ctx, region, filters, resourceTypes)
if err != nil {
return nil, err
}
@@ -212,75 +218,77 @@ func (e *cloudWatchExecutor) handleGetResourceArns(ctx context.Context, pluginCt
return result, nil
}
func (e *cloudWatchExecutor) ec2DescribeInstances(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*ec2.Filter, instanceIds []*string) (*ec2.DescribeInstancesOutput, error) {
func (ds *DataSource) ec2DescribeInstances(ctx context.Context, region string, filters []ec2types.Filter, instanceIds []string) (*ec2.DescribeInstancesOutput, error) {
params := &ec2.DescribeInstancesInput{
Filters: filters,
InstanceIds: instanceIds,
}
client, err := e.getEC2Client(ctx, pluginCtx, region)
client, err := ds.getEC2Client(ctx, region)
if err != nil {
return nil, err
}
var resp ec2.DescribeInstancesOutput
if err := client.DescribeInstancesPagesWithContext(ctx, params, func(page *ec2.DescribeInstancesOutput, lastPage bool) bool {
resp := &ec2.DescribeInstancesOutput{}
pager := ec2.NewDescribeInstancesPaginator(client, params)
for pager.HasMorePages() {
page, err := pager.NextPage(ctx)
if err != nil {
return resp, fmt.Errorf("describe instances pager failed: %w", err)
}
resp.Reservations = append(resp.Reservations, page.Reservations...)
return !lastPage
}); err != nil {
return nil, fmt.Errorf("failed to call ec2:DescribeInstances, %w", err)
}
return &resp, nil
return resp, nil
}
func (e *cloudWatchExecutor) resourceGroupsGetResources(ctx context.Context, pluginCtx backend.PluginContext, region string, filters []*resourcegroupstaggingapi.TagFilter,
resourceTypes []*string) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
func (ds *DataSource) resourceGroupsGetResources(ctx context.Context, region string, filters []resourcegroupstaggingapitypes.TagFilter,
resourceTypes []string) (*resourcegroupstaggingapi.GetResourcesOutput, error) {
params := &resourcegroupstaggingapi.GetResourcesInput{
ResourceTypeFilters: resourceTypes,
TagFilters: filters,
}
client, err := e.getRGTAClient(ctx, pluginCtx, region)
client, err := ds.getRGTAClient(ctx, region)
if err != nil {
return nil, err
}
var resp resourcegroupstaggingapi.GetResourcesOutput
if err := client.GetResourcesPagesWithContext(ctx, params,
func(page *resourcegroupstaggingapi.GetResourcesOutput, lastPage bool) bool {
resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...)
return !lastPage
}); err != nil {
return nil, fmt.Errorf("failed to call tag:GetResources, %w", err)
paginator := resourcegroupstaggingapi.NewGetResourcesPaginator(client, params)
for paginator.HasMorePages() {
page, err := paginator.NextPage(ctx)
if err != nil {
return nil, fmt.Errorf("get resource groups paginator failed: %w", err)
}
resp.ResourceTagMappingList = append(resp.ResourceTagMappingList, page.ResourceTagMappingList...)
}
return &resp, nil
}
// legacy route, will be removed once GovCloud supports Cross Account Observability
func (e *cloudWatchExecutor) handleGetLogGroups(ctx context.Context, pluginCtx backend.PluginContext, parameters url.Values) ([]suggestData, error) {
func (ds *DataSource) handleGetLogGroups(ctx context.Context, parameters url.Values) ([]suggestData, error) {
region := parameters.Get("region")
limit := parameters.Get("limit")
logGroupNamePrefix := parameters.Get("logGroupNamePrefix")
logsClient, err := e.getCWLogsClient(ctx, pluginCtx, region)
logsClient, err := ds.getCWLogsClient(ctx, region)
if err != nil {
return nil, err
}
logGroupLimit := defaultLogGroupLimit
intLimit, err := strconv.ParseInt(limit, 10, 64)
intLimit, err := strconv.ParseInt(limit, 10, 32)
if err == nil && intLimit > 0 {
logGroupLimit = intLimit
logGroupLimit = int32(intLimit)
}
input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int64(logGroupLimit)}
input := &cloudwatchlogs.DescribeLogGroupsInput{Limit: aws.Int32(logGroupLimit)}
if len(logGroupNamePrefix) > 0 {
input.LogGroupNamePrefix = aws.String(logGroupNamePrefix)
}
var response *cloudwatchlogs.DescribeLogGroupsOutput
response, err = logsClient.DescribeLogGroupsWithContext(ctx, input)
response, err = logsClient.DescribeLogGroups(ctx, input)
if err != nil || response == nil {
return nil, err
}

View File

@@ -6,41 +6,37 @@ import (
"net/url"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/client"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi"
"github.com/aws/aws-sdk-go/service/resourcegroupstaggingapi/resourcegroupstaggingapiiface"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend/datasource"
"github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt"
"github.com/grafana/grafana-plugin-sdk-go/backend/log"
"github.com/aws/aws-sdk-go-v2/aws"
ec2types "github.com/aws/aws-sdk-go-v2/service/ec2/types"
"github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi"
resourcegroupstaggingapitypes "github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestQuery_InstanceAttributes(t *testing.T) {
origNewEC2Client := NewEC2Client
origNewEC2API := NewEC2API
t.Cleanup(func() {
NewEC2Client = origNewEC2Client
NewEC2API = origNewEC2API
})
var cli oldEC2Client
NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider {
NewEC2API = func(aws.Config) models.EC2APIProvider {
return cli
}
t.Run("Get instance ID", func(t *testing.T) {
const instanceID = "i-12345678"
cli = oldEC2Client{
reservations: []*ec2.Reservation{
reservations: []ec2types.Reservation{
{
Instances: []*ec2.Instance{
Instances: []ec2types.Instance{
{
InstanceId: aws.String(instanceID),
Tags: []*ec2.Tag{
Tags: []ec2types.Tag{
{
Key: aws.String("Environment"),
Value: aws.String("production"),
@@ -52,22 +48,16 @@ func TestQuery_InstanceAttributes(t *testing.T) {
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
filterMap := map[string][]string{
"tag:Environment": {"production"},
}
filterJson, err := json.Marshal(filterMap)
require.NoError(t, err)
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.handleGetEc2InstanceAttribute(
ds := newTestDatasource()
resp, err := ds.handleGetEc2InstanceAttribute(
context.Background(),
backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
}, url.Values{
url.Values{
"region": []string{"us-east-1"},
"attributeName": []string{"InstanceId"},
"filters": []string{string(filterJson)},
@@ -82,17 +72,17 @@ func TestQuery_InstanceAttributes(t *testing.T) {
})
t.Run("Get different types", func(t *testing.T) {
var expectedInt int64 = 3
var expectedInt int32 = 3
var expectedBool = true
var expectedArn = "arn"
cli = oldEC2Client{
reservations: []*ec2.Reservation{
reservations: []ec2types.Reservation{
{
Instances: []*ec2.Instance{
Instances: []ec2types.Instance{
{
AmiLaunchIndex: &expectedInt,
EbsOptimized: &expectedBool,
IamInstanceProfile: &ec2.IamInstanceProfile{
IamInstanceProfile: &ec2types.IamInstanceProfile{
Arn: &expectedArn,
},
},
@@ -101,11 +91,7 @@ func TestQuery_InstanceAttributes(t *testing.T) {
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
ds := newTestDatasource()
testcases := []struct {
name string
@@ -145,11 +131,9 @@ func TestQuery_InstanceAttributes(t *testing.T) {
filterJson, err := json.Marshal(filterMap)
require.NoError(t, err)
resp, err := executor.handleGetEc2InstanceAttribute(
resp, err := ds.handleGetEc2InstanceAttribute(
context.Background(),
backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
}, url.Values{
url.Values{
"region": []string{"us-east-1"},
"attributeName": []string{tc.attributeName},
"filters": []string{string(filterJson)},
@@ -163,52 +147,52 @@ func TestQuery_InstanceAttributes(t *testing.T) {
}
func TestQuery_EBSVolumeIDs(t *testing.T) {
origNewEC2Client := NewEC2Client
origNewEC2API := NewEC2API
t.Cleanup(func() {
NewEC2Client = origNewEC2Client
NewEC2API = origNewEC2API
})
var cli oldEC2Client
NewEC2Client = func(client.ConfigProvider) models.EC2APIProvider {
NewEC2API = func(aws.Config) models.EC2APIProvider {
return cli
}
t.Run("", func(t *testing.T) {
cli = oldEC2Client{
reservations: []*ec2.Reservation{
reservations: []ec2types.Reservation{
{
Instances: []*ec2.Instance{
Instances: []ec2types.Instance{
{
InstanceId: aws.String("i-1"),
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}},
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}},
BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-1")}},
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-1-2")}},
},
},
{
InstanceId: aws.String("i-2"),
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}},
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}},
BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-1")}},
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-2-2")}},
},
},
},
},
{
Instances: []*ec2.Instance{
Instances: []ec2types.Instance{
{
InstanceId: aws.String("i-3"),
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}},
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}},
BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-1")}},
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-3-2")}},
},
},
{
InstanceId: aws.String("i-4"),
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}},
{Ebs: &ec2.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}},
BlockDeviceMappings: []ec2types.InstanceBlockDeviceMapping{
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-1")}},
{Ebs: &ec2types.EbsInstanceBlockDevice{VolumeId: aws.String("vol-4-2")}},
},
},
},
@@ -216,16 +200,10 @@ func TestQuery_EBSVolumeIDs(t *testing.T) {
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.handleGetEbsVolumeIds(
ds := newTestDatasource()
resp, err := ds.handleGetEbsVolumeIds(
context.Background(),
backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
}, url.Values{
url.Values{
"region": []string{"us-east-1"},
"instanceId": []string{"{i-1, i-2, i-3}"},
},
@@ -242,23 +220,23 @@ func TestQuery_EBSVolumeIDs(t *testing.T) {
}
func TestQuery_ResourceARNs(t *testing.T) {
origNewRGTAClient := newRGTAClient
origNewRGTAClient := NewRGTAClient
t.Cleanup(func() {
newRGTAClient = origNewRGTAClient
NewRGTAClient = origNewRGTAClient
})
var cli fakeRGTAClient
newRGTAClient = func(client.ConfigProvider) resourcegroupstaggingapiiface.ResourceGroupsTaggingAPIAPI {
NewRGTAClient = func(aws.Config) resourcegroupstaggingapi.GetResourcesAPIClient {
return cli
}
t.Run("", func(t *testing.T) {
cli = fakeRGTAClient{
tagMapping: []*resourcegroupstaggingapi.ResourceTagMapping{
tagMapping: []resourcegroupstaggingapitypes.ResourceTagMapping{
{
ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-12345678901234567"),
Tags: []*resourcegroupstaggingapi.Tag{
Tags: []resourcegroupstaggingapitypes.Tag{
{
Key: aws.String("Environment"),
Value: aws.String("production"),
@@ -267,7 +245,7 @@ func TestQuery_ResourceARNs(t *testing.T) {
},
{
ResourceARN: aws.String("arn:aws:ec2:us-east-1:123456789012:instance/i-76543210987654321"),
Tags: []*resourcegroupstaggingapi.Tag{
Tags: []resourcegroupstaggingapitypes.Tag{
{
Key: aws.String("Environment"),
Value: aws.String("production"),
@@ -277,22 +255,16 @@ func TestQuery_ResourceARNs(t *testing.T) {
},
}
im := datasource.NewInstanceManager(func(ctx context.Context, s backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) {
return DataSource{Settings: models.CloudWatchSettings{}, sessions: &fakeSessionCache{}}, nil
})
tagMap := map[string][]string{
"Environment": {"production"},
}
tagJson, err := json.Marshal(tagMap)
require.NoError(t, err)
executor := newExecutor(im, log.NewNullLogger())
resp, err := executor.handleGetResourceArns(
ds := newTestDatasource()
resp, err := ds.handleGetResourceArns(
context.Background(),
backend.PluginContext{
DataSourceInstanceSettings: &backend.DataSourceInstanceSettings{},
}, url.Values{
url.Values{
"region": []string{"us-east-1"},
"resourceType": []string{"ec2:instance"},
"tags": []string{string(tagJson)},

View File

@@ -1,13 +1,11 @@
package routes
package cloudwatch
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/mocks"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
@@ -17,15 +15,21 @@ import (
)
func Test_Metrics_Route(t *testing.T) {
origNewListMetricsServices := services.NewListMetricsService
t.Cleanup(func() {
services.NewListMetricsService = origNewListMetricsServices
})
var mockListMetricsService mocks.ListMetricsServiceMock
services.NewListMetricsService = func(provider models.MetricsClientProvider) models.ListMetricsProvider {
return &mockListMetricsService
}
t.Run("calls GetMetricsByNamespace when a CustomNamespaceRequestType is passed", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetMetricsByNamespace", mock.Anything).Return([]resources.ResourceResponse[resources.Metric]{}, nil)
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/metrics?region=us-east-2&namespace=customNamespace", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(MetricsHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.MetricsHandler))
handler.ServeHTTP(rr, req)
mockListMetricsService.AssertNumberOfCalls(t, "GetMetricsByNamespace", 1)
})
@@ -42,7 +46,8 @@ func Test_Metrics_Route(t *testing.T) {
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/metrics?region=us-east-2", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(MetricsHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.MetricsHandler))
handler.ServeHTTP(rr, req)
assert.True(t, haveBeenCalled)
})
@@ -61,21 +66,20 @@ func Test_Metrics_Route(t *testing.T) {
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/metrics?region=us-east-2&namespace=AWS/DMS", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(MetricsHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.MetricsHandler))
handler.ServeHTTP(rr, req)
assert.True(t, haveBeenCalled)
assert.Equal(t, "AWS/DMS", usedNamespace)
})
t.Run("returns 500 if GetMetricsByNamespace returns an error", func(t *testing.T) {
mockListMetricsService := mocks.ListMetricsServiceMock{}
mockListMetricsService = mocks.ListMetricsServiceMock{}
mockListMetricsService.On("GetMetricsByNamespace", mock.Anything).Return([]resources.ResourceResponse[resources.Metric]{}, fmt.Errorf("some error"))
newListMetricsService = func(_ context.Context, pluginCtx backend.PluginContext, reqCtxFactory models.RequestContextFactoryFunc, region string) (models.ListMetricsProvider, error) {
return &mockListMetricsService, nil
}
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/metrics?region=us-east-2&namespace=customNamespace", nil)
handler := http.HandlerFunc(ResourceRequestMiddleware(MetricsHandler, logger, nil))
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(ds.MetricsHandler))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusInternalServerError, rr.Code)
assert.Equal(t, `{"Message":"error in MetricsHandler: some error","Error":"some error","StatusCode":500}`, rr.Body.String())

View File

@@ -0,0 +1,39 @@
package cloudwatch
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
)
func Test_Middleware(t *testing.T) {
t.Run("rejects POST method", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/dimension-keys?region=us-east-1", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(func(_ context.Context, parameters url.Values) ([]byte, *models.HttpError) {
return []byte{}, nil
}))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusMethodNotAllowed, rr.Code)
})
t.Run("should propagate handler error to response", func(t *testing.T) {
rr := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/some-path", nil)
ds := newTestDatasource()
handler := http.HandlerFunc(ds.resourceRequestMiddleware(func(_ context.Context, parameters url.Values) ([]byte, *models.HttpError) {
return []byte{}, models.NewHttpError("error", http.StatusBadRequest, fmt.Errorf("error from handler"))
}))
handler.ServeHTTP(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Equal(t, `{"Message":"error: error from handler","Error":"error from handler","StatusCode":400}`, rr.Body.String())
})
}

View File

@@ -11,7 +11,7 @@ type AccountsServiceMock struct {
mock.Mock
}
func (a *AccountsServiceMock) GetAccountsForCurrentUserOrRole(ctx context.Context) ([]resources.ResourceResponse[resources.Account], error) {
func (a *AccountsServiceMock) GetAccountsForCurrentUserOrRole(_ context.Context) ([]resources.ResourceResponse[resources.Account], error) {
args := a.Called()
return args.Get(0).([]resources.ResourceResponse[resources.Account]), args.Error(1)

View File

@@ -1,68 +1,65 @@
package mocks
import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/cloudwatch"
"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/cloudwatch"
cloudwatchtypes "github.com/aws/aws-sdk-go-v2/service/cloudwatch/types"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models"
"github.com/stretchr/testify/mock"
)
type FakeMetricsAPI struct {
Metrics []*cloudwatch.Metric
OwningAccounts []*string
models.CWClient
Metrics []cloudwatchtypes.Metric
OwningAccounts []string
MetricsPerPage int
cursor int
}
func (c *FakeMetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error {
func (c *FakeMetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) {
if c.MetricsPerPage == 0 {
c.MetricsPerPage = 1000
}
chunks := chunkSlice(c.Metrics, c.MetricsPerPage)
for i, metrics := range chunks {
response := fn(&cloudwatch.ListMetricsOutput{
Metrics: metrics,
OwningAccounts: c.OwningAccounts,
}, i+1 == len(chunks))
if !response {
break
var metrics []cloudwatchtypes.Metric
nextToken := aws.String("yes")
if c.cursor < len(c.Metrics) {
end := c.cursor + c.MetricsPerPage
if end > len(c.Metrics) {
end = len(c.Metrics)
nextToken = nil
}
metrics = c.Metrics[c.cursor:end]
}
return nil
}
c.cursor += c.MetricsPerPage
func chunkSlice(slice []*cloudwatch.Metric, chunkSize int) [][]*cloudwatch.Metric {
var chunks [][]*cloudwatch.Metric
for len(slice) != 0 {
if len(slice) < chunkSize {
chunkSize = len(slice)
}
chunks = append(chunks, slice[0:chunkSize])
slice = slice[chunkSize:]
}
return chunks
return &cloudwatch.ListMetricsOutput{
Metrics: metrics,
OwningAccounts: c.OwningAccounts,
NextToken: nextToken,
}, nil
}
type MetricsAPI struct {
cloudwatchiface.CloudWatchAPI
mock.Mock
models.CWClient
Metrics []*cloudwatch.Metric
Metrics []cloudwatchtypes.Metric
}
func (m *MetricsAPI) GetMetricDataWithContext(ctx aws.Context, input *cloudwatch.GetMetricDataInput, opts ...request.Option) (*cloudwatch.GetMetricDataOutput, error) {
args := m.Called(ctx, input, opts)
func (m *MetricsAPI) GetMetricData(ctx context.Context, input *cloudwatch.GetMetricDataInput, optFns ...func(*cloudwatch.Options)) (*cloudwatch.GetMetricDataOutput, error) {
args := m.Called(ctx, input, optFns)
return args.Get(0).(*cloudwatch.GetMetricDataOutput), args.Error(1)
}
func (m *MetricsAPI) ListMetricsPagesWithContext(ctx aws.Context, input *cloudwatch.ListMetricsInput, fn func(*cloudwatch.ListMetricsOutput, bool) bool, opts ...request.Option) error {
fn(&cloudwatch.ListMetricsOutput{
func (m *MetricsAPI) ListMetrics(_ context.Context, _ *cloudwatch.ListMetricsInput, _ ...func(*cloudwatch.Options)) (*cloudwatch.ListMetricsOutput, error) {
return &cloudwatch.ListMetricsOutput{
Metrics: m.Metrics,
}, true)
return m.Called().Error(0)
}, m.Called().Error(0)
}

View File

@@ -11,19 +11,19 @@ type ListMetricsServiceMock struct {
mock.Mock
}
func (a *ListMetricsServiceMock) GetDimensionKeysByDimensionFilter(ctx context.Context, r resources.DimensionKeysRequest) ([]resources.ResourceResponse[string], error) {
func (a *ListMetricsServiceMock) GetDimensionKeysByDimensionFilter(_ context.Context, r resources.DimensionKeysRequest) ([]resources.ResourceResponse[string], error) {
args := a.Called(r)
return args.Get(0).([]resources.ResourceResponse[string]), args.Error(1)
}
func (a *ListMetricsServiceMock) GetDimensionValuesByDimensionFilter(ctx context.Context, r resources.DimensionValuesRequest) ([]resources.ResourceResponse[string], error) {
func (a *ListMetricsServiceMock) GetDimensionValuesByDimensionFilter(_ context.Context, r resources.DimensionValuesRequest) ([]resources.ResourceResponse[string], error) {
args := a.Called(r)
return args.Get(0).([]resources.ResourceResponse[string]), args.Error(1)
}
func (a *ListMetricsServiceMock) GetMetricsByNamespace(ctx context.Context, r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error) {
func (a *ListMetricsServiceMock) GetMetricsByNamespace(_ context.Context, r resources.MetricsRequest) ([]resources.ResourceResponse[resources.Metric], error) {
args := a.Called(r)
return args.Get(0).([]resources.ResourceResponse[resources.Metric]), args.Error(1)

View File

@@ -3,10 +3,7 @@ package mocks
import (
"context"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs/cloudwatchlogsiface"
"github.com/aws/aws-sdk-go-v2/service/cloudwatchlogs"
"github.com/stretchr/testify/mock"
"github.com/grafana/grafana/pkg/tsdb/cloudwatch/models/resources"
@@ -16,13 +13,13 @@ type LogsAPI struct {
mock.Mock
}
func (l *LogsAPI) DescribeLogGroupsWithContext(ctx context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, option ...request.Option) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
func (l *LogsAPI) DescribeLogGroups(_ context.Context, input *cloudwatchlogs.DescribeLogGroupsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
args := l.Called(input)
return args.Get(0).(*cloudwatchlogs.DescribeLogGroupsOutput), args.Error(1)
}
func (l *LogsAPI) GetLogGroupFieldsWithContext(ctx context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, option ...request.Option) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) {
func (l *LogsAPI) GetLogGroupFields(_ context.Context, input *cloudwatchlogs.GetLogGroupFieldsInput, _ ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogGroupFieldsOutput, error) {
args := l.Called(input)
return args.Get(0).(*cloudwatchlogs.GetLogGroupFieldsOutput), args.Error(1)
@@ -32,26 +29,40 @@ type LogsService struct {
mock.Mock
}
func (l *LogsService) GetLogGroupsWithContext(ctx context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) {
func (l *LogsService) GetLogGroups(_ context.Context, request resources.LogGroupsRequest) ([]resources.ResourceResponse[resources.LogGroup], error) {
args := l.Called(request)
return args.Get(0).([]resources.ResourceResponse[resources.LogGroup]), args.Error(1)
}
func (l *LogsService) GetLogGroupFieldsWithContext(ctx context.Context, request resources.LogGroupFieldsRequest, option ...request.Option) ([]resources.ResourceResponse[resources.LogGroupField], error) {
func (l *LogsService) GetLogGroupFields(_ context.Context, request resources.LogGroupFieldsRequest) ([]resources.ResourceResponse[resources.LogGroupField], error) {
args := l.Called(request)
return args.Get(0).([]resources.ResourceResponse[resources.LogGroupField]), args.Error(1)
}
type MockLogEvents struct {
cloudwatchlogsiface.CloudWatchLogsAPI
mock.Mock
}
func (m *MockLogEvents) GetLogEventsWithContext(ctx aws.Context, input *cloudwatchlogs.GetLogEventsInput, option ...request.Option) (*cloudwatchlogs.GetLogEventsOutput, error) {
args := m.Called(ctx, input, option)
func (m *MockLogEvents) StartQuery(context.Context, *cloudwatchlogs.StartQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StartQueryOutput, error) {
return nil, nil
}
func (m *MockLogEvents) StopQuery(context.Context, *cloudwatchlogs.StopQueryInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.StopQueryOutput, error) {
return nil, nil
}
func (m *MockLogEvents) GetQueryResults(context.Context, *cloudwatchlogs.GetQueryResultsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetQueryResultsOutput, error) {
return nil, nil
}
func (m *MockLogEvents) DescribeLogGroups(context.Context, *cloudwatchlogs.DescribeLogGroupsInput, ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.DescribeLogGroupsOutput, error) {
return nil, nil
}
func (m *MockLogEvents) GetLogEvents(ctx context.Context, input *cloudwatchlogs.GetLogEventsInput, optFns ...func(*cloudwatchlogs.Options)) (*cloudwatchlogs.GetLogEventsOutput, error) {
args := m.Called(ctx, input, optFns)
return args.Get(0).(*cloudwatchlogs.GetLogEventsOutput), args.Error(1)
}

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