From 58f32150c262605d188874b88f44a7dece4b9388 Mon Sep 17 00:00:00 2001 From: Isabella Siu Date: Fri, 5 Apr 2024 11:57:56 -0400 Subject: [PATCH] CloudWatch: Improve metric label parsing (#84835) --- .../feature-toggles/index.md | 1 + .../src/types/featureToggles.gen.ts | 1 + pkg/services/featuremgmt/registry.go | 9 + pkg/services/featuremgmt/toggles_gen.csv | 1 + pkg/services/featuremgmt/toggles_gen.go | 4 + pkg/services/featuremgmt/toggles_gen.json | 3813 ++++++++--------- pkg/tsdb/cloudwatch/features/features.go | 1 + .../cloudwatch/metric_data_input_builder.go | 5 +- .../metric_data_input_builder_test.go | 3 +- .../cloudwatch/metric_data_query_builder.go | 32 +- .../metric_data_query_builder_test.go | 279 +- pkg/tsdb/cloudwatch/response_parser.go | 45 +- pkg/tsdb/cloudwatch/response_parser_test.go | 79 +- pkg/tsdb/cloudwatch/time_series_query.go | 12 +- 14 files changed, 2229 insertions(+), 2056 deletions(-) diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index aa2c5ac93dd..8581062e27e 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -60,6 +60,7 @@ Some features are enabled by default. You can disable these feature by setting t | `enablePluginsTracingByDefault` | Enable plugin tracing for all external plugins | Yes | | `alertingQueryOptimization` | Optimizes eligible queries in order to reduce load on datasources | | | `betterPageScrolling` | Removes CustomScrollbar from the UI, relying on native browser scrollbars | Yes | +| `cloudWatchNewLabelParsing` | Updates CloudWatch label parsing to be more accurate | Yes | ## Preview feature toggles diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 946657f4b15..0855802955f 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -179,4 +179,5 @@ export interface FeatureToggles { usePrometheusFrontendPackage?: boolean; oauthRequireSubClaim?: boolean; newDashboardWithFiltersAndGroupBy?: boolean; + cloudWatchNewLabelParsing?: boolean; } diff --git a/pkg/services/featuremgmt/registry.go b/pkg/services/featuremgmt/registry.go index e0f642bd789..d2513c8141b 100644 --- a/pkg/services/featuremgmt/registry.go +++ b/pkg/services/featuremgmt/registry.go @@ -1205,6 +1205,15 @@ var ( HideFromDocs: true, HideFromAdminPage: true, }, + { + Name: "cloudWatchNewLabelParsing", + Description: "Updates CloudWatch label parsing to be more accurate", + Stage: FeatureStageGeneralAvailability, + Expression: "true", // enabled by default + Owner: awsDatasourcesSquad, + FrontendOnly: false, + AllowSelfServe: false, + }, } ) diff --git a/pkg/services/featuremgmt/toggles_gen.csv b/pkg/services/featuremgmt/toggles_gen.csv index 907e45a5c34..093a0ebe1fa 100644 --- a/pkg/services/featuremgmt/toggles_gen.csv +++ b/pkg/services/featuremgmt/toggles_gen.csv @@ -160,3 +160,4 @@ ssoSettingsSAML,experimental,@grafana/identity-access-team,false,false,false usePrometheusFrontendPackage,experimental,@grafana/observability-metrics,false,false,true oauthRequireSubClaim,experimental,@grafana/identity-access-team,false,false,false newDashboardWithFiltersAndGroupBy,experimental,@grafana/dashboards-squad,false,false,false +cloudWatchNewLabelParsing,GA,@grafana/aws-datasources,false,false,false diff --git a/pkg/services/featuremgmt/toggles_gen.go b/pkg/services/featuremgmt/toggles_gen.go index 89688915fe1..8d5a835b3d3 100644 --- a/pkg/services/featuremgmt/toggles_gen.go +++ b/pkg/services/featuremgmt/toggles_gen.go @@ -650,4 +650,8 @@ const ( // FlagNewDashboardWithFiltersAndGroupBy // Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering. FlagNewDashboardWithFiltersAndGroupBy = "newDashboardWithFiltersAndGroupBy" + + // FlagCloudWatchNewLabelParsing + // Updates CloudWatch label parsing to be more accurate + FlagCloudWatchNewLabelParsing = "cloudWatchNewLabelParsing" ) diff --git a/pkg/services/featuremgmt/toggles_gen.json b/pkg/services/featuremgmt/toggles_gen.json index 139dff08e7e..5855736f83a 100644 --- a/pkg/services/featuremgmt/toggles_gen.json +++ b/pkg/services/featuremgmt/toggles_gen.json @@ -3,129 +3,11 @@ "apiVersion": "featuretoggle.grafana.app/v0alpha1", "metadata": {}, "items": [ - { - "metadata": { - "name": "publicDashboardsEmailSharing", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables public dashboard sharing to be restricted to only allowed emails", - "stage": "preview", - "codeowner": "@grafana/sharing-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "autoMigrateTablePanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old table panel to supported table panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "scenes", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Experimental framework to build interactive dashboards", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "nestedFolders", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable folder nesting", - "stage": "GA", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "cloudRBACRoles", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enabled grafana cloud specific RBAC roles", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team", - "requiresRestart": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "disableSSEDataplane", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Disables dataplane specific processing in server side expressions.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "faroDatasourceSelector", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable the data source selector within the Frontend Apps section of the Frontend Observability", - "stage": "preview", - "codeowner": "@grafana/app-o11y", - "frontend": true - } - }, - { - "metadata": { - "name": "aiGeneratedDashboardChanges", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable AI powered features for dashboards to auto-summary changes when saving", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingInsights", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Show the new alerting insights landing page", - "stage": "GA", - "codeowner": "@grafana/alerting-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, { "metadata": { "name": "dashboardScene", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Enables dashboard rendering using scenes for all roles", @@ -136,1252 +18,35 @@ }, { "metadata": { - "name": "teamHttpHeaders", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "angularDeprecationUI", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables Team LBAC for datasources to apply team headers to the client requests", - "stage": "preview", - "codeowner": "@grafana/identity-access-team" - } - }, - { - "metadata": { - "name": "logRowsPopoverMenu", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable filtering menu displayed when text of a log line is selected", + "description": "Display Angular warnings in dashboards and panels", "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "queryOverLive", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Use Grafana Live WebSocket to execute backend queries", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "panelTitleSearch", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Search for dashboards using panel title", - "stage": "preview", - "codeowner": "@grafana/grafana-app-platform-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "returnToPrevious", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the return to previous context functionality", - "stage": "preview", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "cloudWatchCrossAccountQuerying", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables cross-account querying in CloudWatch datasources", - "stage": "GA", - "codeowner": "@grafana/aws-datasources", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "individualCookiePreferences", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Support overriding cookie preferences per user", - "stage": "experimental", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "panelMonitoring", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables panel monitoring through logs and measurements", - "stage": "GA", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "pluginsSkipHostEnvVars", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Disables passing host environment variable to plugin processes", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "groupToNestedTableTransformation", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the group to nested table transformation", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "migrationLocking", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z", - "deletionTimestamp": "2024-03-25T05:44:42Z" - }, - "spec": { - "description": "Lock database during migrations", - "stage": "preview", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "correlations", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Correlations page", - "stage": "GA", - "codeowner": "@grafana/explore-squad", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "nodeGraphDotLayout", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Changed the layout algorithm for the node graph", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "groupByVariable", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable groupBy variable support in scenes dashboards", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "publicDashboards", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.", - "stage": "GA", - "codeowner": "@grafana/sharing-squad", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "autoMigratePiechartPanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "autoMigrateXYChartPanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old XYChart panel to new XYChart2 model", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "editPanelCSVDragAndDrop", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables drag and drop for CSV and Excel files", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "prometheusMetricEncyclopedia", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "influxdbBackendMigration", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Query InfluxDB InfluxQL without the proxy", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "disableSecretsCompatibility", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Disable duplicated secret storage in legacy tables", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "annotationPermissionUpdate", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Change the way annotation permissions work by scoping them to folders and dashboards.", - "stage": "GA", - "codeowner": "@grafana/identity-access-team" - } - }, - { - "metadata": { - "name": "scopeFilters", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the use of scope filters in Grafana", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "lokiExperimentalStreaming", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Support new streaming approach for loki (prototype, needs special loki build)", - "stage": "experimental", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "logRequestsInstrumentedAsUnknown", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Logs the path for requests that are instrumented as unknown", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" - } - }, - { - "metadata": { - "name": "frontendSandboxMonitorOnly", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables monitor only in the plugin frontend sandbox (if enabled)", - "stage": "experimental", "codeowner": "@grafana/plugins-platform-backend", "frontend": true } }, { "metadata": { - "name": "lokiFormatQuery", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "kubernetesPlaylists", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables the ability to format Loki queries", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "externalServiceAccounts", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Automatic service account and token setup for plugins", - "stage": "preview", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "alertStateHistoryLokiOnly", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Disable Grafana alerts from emitting annotations when a remote Loki instance is available.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "transformationsVariableSupport", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allows using variables in transformations", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiStructuredMetadata", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the loki data source to request structured metadata from the Loki server", - "stage": "GA", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "alertingSaveStatePeriodic", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Writes the state periodically to the database, asynchronous to rule evaluation", - "stage": "privatePreview", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "sqlExpressions", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables using SQL and DuckDB functions as Expressions.", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad" - } - }, - { - "metadata": { - "name": "transformationsRedesign", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the transformations redesign", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "dashgpt", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable AI powered features in dashboards", - "stage": "GA", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "kubernetesSnapshots", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Routes snapshot requests from /api to the /apis endpoint", + "description": "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s", "stage": "experimental", "codeowner": "@grafana/grafana-app-platform-squad", "requiresRestart": true } }, - { - "metadata": { - "name": "dashboardSceneForViewers", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables dashboard rendering using Scenes for viewer roles", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "datatrails", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the new core app datatrails", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "jitterAlertRulesWithinGroups", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Distributes alert rule evaluations more evenly over time, including spreading out rules within the same group", - "stage": "preview", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "accessControlOnCall", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Access control primitives for OnCall", - "stage": "preview", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "lokiMetricDataplane", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Changes metric responses from Loki to be compliant with the dataplane specification.", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "enableNativeHTTPHistogram", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables native HTTP Histograms", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" - } - }, - { - "metadata": { - "name": "showDashboardValidationWarnings", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Show warnings when dashboards do not validate against the schema", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad" - } - }, - { - "metadata": { - "name": "unifiedRequestLog", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Writes error logs to the request logger", - "stage": "experimental", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "prometheusPromQAIL", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Prometheus and AI/ML to assist users in creating a query", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingSimplifiedRouting", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule", - "stage": "GA", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "newVizTooltips", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z", - "deletionTimestamp": "2024-04-02T22:11:56Z" - }, - "spec": { - "description": "New visualizations tooltips UX", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "influxdbRunQueriesInParallel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables running InfluxDB Influxql queries in parallel", - "stage": "privatePreview", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "disableAngular", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime.", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "influxqlStreamingParser", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable streaming JSON parser for InfluxDB datasource InfluxQL query language", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "alertStateHistoryLokiSecondary", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "metricsSummary", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables metrics summary queries in the Tempo data source", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingQueryOptimization", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Optimizes eligible queries in order to reduce load on datasources", - "stage": "GA", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "promQLScope", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "In-development feature that will allow injection of labels into prometheus queries.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "lokiLogsDataplane", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Changes logs responses from Loki to be compliant with the dataplane specification.", - "stage": "experimental", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "pluginsFrontendSandbox", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the plugins frontend sandbox", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "prometheusIncrementalQueryInstrumentation", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Adds RudderStack events to incremental queries", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "panelTitleSearchInV1", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable searching for dashboards using panel title in search v1", - "stage": "experimental", - "codeowner": "@grafana/backend-platform", - "requiresDevMode": true - } - }, - { - "metadata": { - "name": "pluginsDynamicAngularDetectionPatterns", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables fetching Angular detection patterns for plugins from GCOM and fallback to hardcoded ones", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "pluginsAPIMetrics", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Sends metrics of public grafana packages usage by plugins", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "awsAsyncQueryCaching", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled", - "stage": "GA", - "codeowner": "@grafana/aws-datasources" - } - }, - { - "metadata": { - "name": "autoMigrateStatPanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "dataplaneFrontendFallback", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Support dataplane contract field name change for transformations and field name matchers where the name is different", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "enableDatagridEditing", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the edit functionality in the datagrid panel", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "vizAndWidgetSplit", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Split panels between visualizations and widgets", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "logsExploreTableVisualisation", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "A table visualisation for logs in Explore", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "traceQLStreaming", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables response streaming of TraceQL queries of the Tempo data source", - "stage": "GA", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "ssoSettingsSAML", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Use the new SSO Settings API to configure the SAML connector", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team", - "hideFromAdminPage": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "nestedFolderPicker", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the new folder picker to work with nested folders. Requires the nestedFolders feature toggle", - "stage": "GA", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "alertingNoNormalState", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Stop maintaining state of alerts that are not firing", - "stage": "preview", - "codeowner": "@grafana/alerting-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "alertStateHistoryLokiPrimary", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable a remote Loki instance as the primary source for state history reads.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "awsDatasourcesTempCredentials", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Support temporary security credentials in AWS plugins for Grafana Cloud customers", - "stage": "experimental", - "codeowner": "@grafana/aws-datasources" - } - }, - { - "metadata": { - "name": "permissionsFilterRemoveSubquery", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder", - "stage": "experimental", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "ssoSettingsApi", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the SSO settings API and the OAuth configuration UIs in Grafana", - "stage": "preview", - "codeowner": "@grafana/identity-access-team", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "renderAuthJWT", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Uses JWT-based auth for rendering instead of relying on remote cache", - "stage": "preview", - "codeowner": "@grafana/grafana-as-code", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "enableElasticsearchBackendQuerying", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable the processing of queries and responses in the Elasticsearch data source through backend", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "sqlDatasourceDatabaseSelection", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables previous SQL data source dataset dropdown behavior", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "lokiRunQueriesInParallel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables running Loki queries in parallel", - "stage": "privatePreview", - "codeowner": "@grafana/observability-logs" - } - }, - { - "metadata": { - "name": "tableSharedCrosshair", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables shared crosshair in table panel", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "expressionParser", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable new expression parser", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "storage", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Configurable storage for dashboards, datasources, and resources", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad" - } - }, - { - "metadata": { - "name": "logsContextDatasourceUi", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow datasource to provide custom UI for context view", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "prometheusConfigOverhaulAuth", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Update the Prometheus configuration page with the new auth component", - "stage": "GA", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "betterPageScrolling", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Removes CustomScrollbar from the UI, relying on native browser scrollbars", - "stage": "GA", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "alertmanagerRemoteSecondary", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable Grafana to sync configuration and state with a remote Alertmanager.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "panelFilterVariable", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables use of the `systemPanelFilterVar` variable to filter panels in a dashboard", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true, - "hideFromDocs": true - } - }, - { - "metadata": { - "name": "datasourceQueryMultiStatus", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Introduce HTTP 207 Multi Status for api/ds/query", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "autoMigrateWorldmapPanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "canvasPanelNesting", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow elements nesting", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true, - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "featureToggleAdminPage", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable admin page for managing feature toggles from the Grafana front-end", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "externalCorePlugins", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow core plugins to be loaded as external", - "stage": "experimental", - "codeowner": "@grafana/plugins-platform-backend" - } - }, - { - "metadata": { - "name": "idForwarding", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Generate signed id token for identity that can be forwarded to plugins and external services", - "stage": "experimental", - "codeowner": "@grafana/identity-access-team" - } - }, - { - "metadata": { - "name": "logsInfiniteScrolling", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables infinite scrolling for the Logs panel in Explore and Dashboards", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "mlExpressions", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable support for Machine Learning in server-side expressions", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "libraryPanelRBAC", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables RBAC support for library panels", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "kubernetesQueryServiceRewrite", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Rewrite requests targeting /ds/query to the query service", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresDevMode": true, - "requiresRestart": true - } - }, - { - "metadata": { - "name": "managedPluginsInstall", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Install managed plugins directly from plugins catalog", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend" - } - }, { "metadata": { "name": "kubernetesFeatureToggles", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Use the kubernetes API for feature toggle management in the frontend", @@ -1391,207 +56,11 @@ "hideFromAdminPage": true } }, - { - "metadata": { - "name": "featureHighlights", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Highlight Grafana Enterprise features", - "stage": "GA", - "codeowner": "@grafana/grafana-as-code", - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "exploreContentOutline", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Content outline sidebar", - "stage": "GA", - "codeowner": "@grafana/explore-squad", - "frontend": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "influxdbSqlSupport", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable InfluxDB SQL query language support with new querying UI", - "stage": "GA", - "codeowner": "@grafana/observability-metrics", - "requiresRestart": true, - "allowSelfServe": true - } - }, - { - "metadata": { - "name": "alertingNoDataErrorExecution", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Changes how Alerting state manager handles execution of NoData/Error", - "stage": "GA", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "autoMigrateOldPanels", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old angular panels to supported versions (graph, table-old, worldmap, etc)", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "grpcServer", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Run the GRPC server", - "stage": "preview", - "codeowner": "@grafana/grafana-app-platform-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "sseGroupByDatasource", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "extractFieldsNameDeduplication", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Make sure extracted field names are unique in the dataframe", - "stage": "experimental", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "newFolderPicker", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables the nested folder picker without having nested folders enabled", - "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform", - "frontend": true - } - }, - { - "metadata": { - "name": "refactorVariablesTimeRange", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Refactor time range variables flow to reduce number of API calls made when query variables are chained", - "stage": "preview", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "lokiPredefinedOperations", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Adds predefined query operations to Loki query editor", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "grafanaAPIServerWithExperimentalAPIs", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Register experimental APIs with the k8s API server", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresDevMode": true, - "requiresRestart": true - } - }, - { - "metadata": { - "name": "onPremToCloudMigrations", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "In-development feature that will allow users to easily migrate their on-prem Grafana instances to Grafana Cloud.", - "stage": "experimental", - "codeowner": "@grafana/grafana-operator-experience-squad" - } - }, - { - "metadata": { - "name": "dashboardSceneSolo", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables rendering dashboards using scenes for solo panels", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "canvasPanelPanZoom", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow pan and zoom in canvas panel", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, { "metadata": { "name": "lokiQuerySplitting", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Split large interval queries into subqueries with smaller time intervals", @@ -1601,11 +70,460 @@ "allowSelfServe": true } }, + { + "metadata": { + "name": "alertingSimplifiedRouting", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables users to easily configure alert notifications by specifying a contact point directly when editing or creating an alert rule", + "stage": "GA", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "kubernetesQueryServiceRewrite", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Rewrite requests targeting /ds/query to the query service", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresDevMode": true, + "requiresRestart": true + } + }, + { + "metadata": { + "name": "lokiQueryHints", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables query hints for Loki", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "nodeGraphDotLayout", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Changed the layout algorithm for the node graph", + "stage": "experimental", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "enableElasticsearchBackendQuerying", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable the processing of queries and responses in the Elasticsearch data source through backend", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "alertingNoDataErrorExecution", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Changes how Alerting state manager handles execution of NoData/Error", + "stage": "GA", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "alertmanagerRemoteOnly", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disable the internal Alertmanager and only use the external one defined.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "prometheusMetricEncyclopedia", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Adds the metrics explorer component to the Prometheus query builder as an option in metric select", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "disableSSEDataplane", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disables dataplane specific processing in server side expressions.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "featureToggleAdminPage", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable admin page for managing feature toggles from the Grafana front-end", + "stage": "experimental", + "codeowner": "@grafana/grafana-operator-experience-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "sseGroupByDatasource", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Send query to the same datasource in a single request when using server side expressions. The `cloudWatchBatchQueries` feature toggle should be enabled if this used with CloudWatch.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "pluginsAPIMetrics", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Sends metrics of public grafana packages usage by plugins", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true + } + }, + { + "metadata": { + "name": "teamHttpHeaders", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables Team LBAC for datasources to apply team headers to the client requests", + "stage": "preview", + "codeowner": "@grafana/identity-access-team" + } + }, + { + "metadata": { + "name": "extractFieldsNameDeduplication", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Make sure extracted field names are unique in the dataframe", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "newFolderPicker", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the nested folder picker without having nested folders enabled", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "nestedFolderPicker", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the new folder picker to work with nested folders. Requires the nestedFolders feature toggle", + "stage": "GA", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "alertStateHistoryLokiPrimary", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable a remote Loki instance as the primary source for state history reads.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "newPDFRendering", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "New implementation for the dashboard-to-PDF rendering", + "stage": "preview", + "codeowner": "@grafana/sharing-squad" + } + }, + { + "metadata": { + "name": "prometheusIncrementalQueryInstrumentation", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Adds RudderStack events to incremental queries", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "enablePluginsTracingByDefault", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable plugin tracing for all external plugins", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "cloudRBACRoles", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enabled grafana cloud specific RBAC roles", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team", + "requiresRestart": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "jitterAlertRulesWithinGroups", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Distributes alert rule evaluations more evenly over time, including spreading out rules within the same group", + "stage": "preview", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "disableEnvelopeEncryption", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disable envelope encryption (emergency only)", + "stage": "GA", + "codeowner": "@grafana/grafana-as-code", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "unifiedRequestLog", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Writes error logs to the request logger", + "stage": "experimental", + "codeowner": "@grafana/backend-platform" + } + }, + { + "metadata": { + "name": "formatString", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable format string transformer", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "onPremToCloudMigrations", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "In-development feature that will allow users to easily migrate their on-prem Grafana instances to Grafana Cloud.", + "stage": "experimental", + "codeowner": "@grafana/grafana-operator-experience-squad" + } + }, + { + "metadata": { + "name": "influxdbRunQueriesInParallel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables running InfluxDB Influxql queries in parallel", + "stage": "privatePreview", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "logsExploreTableVisualisation", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "A table visualisation for logs in Explore", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "extraThemes", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables extra themes", + "stage": "experimental", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiPredefinedOperations", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Adds predefined query operations to Loki query editor", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "frontendSandboxMonitorOnly", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables monitor only in the plugin frontend sandbox (if enabled)", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true + } + }, + { + "metadata": { + "name": "regressionTransformation", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables regression analysis transformation", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "unifiedStorage", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "SQL-based k8s storage", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresDevMode": true, + "requiresRestart": true + } + }, + { + "metadata": { + "name": "alertStateHistoryLokiOnly", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disable Grafana alerts from emitting annotations when a remote Loki instance is available.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "mlExpressions", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable support for Machine Learning in server-side expressions", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, { "metadata": { "name": "grafanaAPIServerEnsureKubectlAccess", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Start an additional https handler and write kubectl options", @@ -1618,8 +536,8 @@ { "metadata": { "name": "configurableSchedulerTick", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Enable changing the scheduler base interval via configuration option unified_alerting.scheduler_tick_interval", @@ -1631,61 +549,25 @@ }, { "metadata": { - "name": "wargamesTesting", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "panelMonitoring", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Placeholder feature flag for internal testing", - "stage": "experimental", - "codeowner": "@grafana/hosted-grafana-team" - } - }, - { - "metadata": { - "name": "recoveryThreshold", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables feature recovery threshold (aka hysteresis) for threshold server-side expression", + "description": "Enables panel monitoring through logs and measurements", "stage": "GA", - "codeowner": "@grafana/alerting-squad", - "requiresRestart": true + "codeowner": "@grafana/dataviz-squad", + "frontend": true } }, { "metadata": { - "name": "alertmanagerRemoteOnly", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "autoMigrateWorldmapPanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Disable the internal Alertmanager and only use the external one defined.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "recordedQueriesMulti", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables writing multiple items from a single query within Recorded Queries", - "stage": "GA", - "codeowner": "@grafana/observability-metrics" - } - }, - { - "metadata": { - "name": "formatString", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable format string transformer", + "description": "Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking", "stage": "preview", "codeowner": "@grafana/dataviz-squad", "frontend": true @@ -1693,85 +575,74 @@ }, { "metadata": { - "name": "pdfTables", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "logsContextDatasourceUi", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables generating table data as PDF in reporting", - "stage": "preview", - "codeowner": "@grafana/sharing-squad" - } - }, - { - "metadata": { - "name": "kubernetesAggregator", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable grafana aggregator", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "prometheusDataplane", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", + "description": "Allow datasource to provide custom UI for context view", "stage": "GA", - "codeowner": "@grafana/observability-metrics", + "codeowner": "@grafana/observability-logs", + "frontend": true, "allowSelfServe": true } }, { "metadata": { - "name": "extraThemes", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "prometheusPromQAIL", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables extra themes", + "description": "Prometheus and AI/ML to assist users in creating a query", "stage": "experimental", - "codeowner": "@grafana/grafana-frontend-platform", + "codeowner": "@grafana/observability-metrics", "frontend": true } }, { "metadata": { - "name": "reportingRetries", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "accessControlOnCall", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables rendering retries for the reporting feature", + "description": "Access control primitives for OnCall", "stage": "preview", - "codeowner": "@grafana/sharing-squad", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "lokiRunQueriesInParallel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables running Loki queries in parallel", + "stage": "privatePreview", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "libraryPanelRBAC", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables RBAC support for library panels", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", "requiresRestart": true } }, - { - "metadata": { - "name": "cloudWatchBatchQueries", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Runs CloudWatch metrics queries as separate batches", - "stage": "preview", - "codeowner": "@grafana/aws-datasources" - } - }, { "metadata": { "name": "cachingOptimizeSerializationMemoryUsage", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "If enabled, the caching backend gradually serializes query responses for the cache, comparing against the configured `[caching]max_value_mb` value as it goes. This can can help prevent Grafana from running out of memory while attempting to cache very large query responses.", @@ -1781,12 +652,12 @@ }, { "metadata": { - "name": "addFieldFromCalculationStatFunctions", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "groupToNestedTableTransformation", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Add cumulative and window functions to the add field from calculation transformation", + "description": "Enables the group to nested table transformation", "stage": "preview", "codeowner": "@grafana/dataviz-squad", "frontend": true @@ -1794,268 +665,22 @@ }, { "metadata": { - "name": "usePrometheusFrontendPackage", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" + "name": "expressionParser", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Use the @grafana/prometheus frontend package in core Prometheus.", - "stage": "experimental", - "codeowner": "@grafana/observability-metrics", - "frontend": true - } - }, - { - "metadata": { - "name": "live-service-web-worker", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "This will use a webworker thread to processes events rather than the main thread", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "kubernetesPlaylists", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Use the kubernetes API in the frontend for playlists, and route /api/playlist requests to k8s", + "description": "Enable new expression parser", "stage": "experimental", "codeowner": "@grafana/grafana-app-platform-squad", "requiresRestart": true } }, - { - "metadata": { - "name": "flameGraphItemCollapsing", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow collapsing of flame graph items", - "stage": "experimental", - "codeowner": "@grafana/observability-traces-and-profiling", - "frontend": true - } - }, - { - "metadata": { - "name": "regressionTransformation", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables regression analysis transformation", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "enablePluginsTracingByDefault", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable plugin tracing for all external plugins", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend", - "requiresRestart": true - } - }, - { - "metadata": { - "name": "newPDFRendering", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "New implementation for the dashboard-to-PDF rendering", - "stage": "preview", - "codeowner": "@grafana/sharing-squad" - } - }, - { - "metadata": { - "name": "topnav", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables topnav support in external plugins. The new Grafana navigation cannot be disabled.", - "stage": "deprecated", - "codeowner": "@grafana/grafana-frontend-platform" - } - }, - { - "metadata": { - "name": "unifiedStorage", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "SQL-based k8s storage", - "stage": "experimental", - "codeowner": "@grafana/grafana-app-platform-squad", - "requiresDevMode": true, - "requiresRestart": true - } - }, - { - "metadata": { - "name": "mysqlAnsiQuotes", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Use double quotes to escape keyword in a MySQL query", - "stage": "experimental", - "codeowner": "@grafana/backend-platform" - } - }, - { - "metadata": { - "name": "angularDeprecationUI", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Display Angular warnings in dashboards and panels", - "stage": "GA", - "codeowner": "@grafana/plugins-platform-backend", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiQueryHints", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables query hints for Loki", - "stage": "GA", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "autoMigrateGraphPanel", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Migrate old graph panel to supported time series panel - broken out from autoMigrateOldPanels to enable granular tracking", - "stage": "preview", - "codeowner": "@grafana/dataviz-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "lokiQuerySplittingConfig", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Give users the option to configure split durations for Loki queries", - "stage": "experimental", - "codeowner": "@grafana/observability-logs", - "frontend": true - } - }, - { - "metadata": { - "name": "awsDatasourcesNewFormStyling", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Applies new form styling for configuration and query editors in AWS plugins", - "stage": "preview", - "codeowner": "@grafana/aws-datasources", - "frontend": true - } - }, - { - "metadata": { - "name": "disableEnvelopeEncryption", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Disable envelope encryption (emergency only)", - "stage": "GA", - "codeowner": "@grafana/grafana-as-code", - "hideFromAdminPage": true - } - }, - { - "metadata": { - "name": "publicDashboardsScene", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enables public dashboard rendering using scenes", - "stage": "experimental", - "codeowner": "@grafana/sharing-squad", - "frontend": true - } - }, - { - "metadata": { - "name": "alertingBacktesting", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Rule backtesting API for alerting", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, - { - "metadata": { - "name": "dashboardEmbed", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Allow embedding dashboard for external use in Code editors", - "stage": "experimental", - "codeowner": "@grafana/grafana-as-code", - "frontend": true - } - }, - { - "metadata": { - "name": "alertmanagerRemotePrimary", - "resourceVersion": "1711130211436", - "creationTimestamp": "2024-03-22T17:56:51Z" - }, - "spec": { - "description": "Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager.", - "stage": "experimental", - "codeowner": "@grafana/alerting-squad" - } - }, { "metadata": { "name": "oauthRequireSubClaim", - "resourceVersion": "1711371458317", - "creationTimestamp": "2024-03-25T10:50:29Z", - "annotations": { - "grafana.app/updatedTimestamp": "2024-03-25 12:57:38.317427693 +0000 UTC" - } + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Require that sub claims is present in oauth tokens.", @@ -2065,11 +690,1281 @@ "hideFromDocs": true } }, + { + "metadata": { + "name": "lokiQuerySplittingConfig", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Give users the option to configure split durations for Loki queries", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "refactorVariablesTimeRange", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Refactor time range variables flow to reduce number of API calls made when query variables are chained", + "stage": "preview", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "editPanelCSVDragAndDrop", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables drag and drop for CSV and Excel files", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "prometheusDataplane", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Changes responses to from Prometheus to be compliant with the dataplane specification. In particular, when this feature toggle is active, the numeric `Field.Name` is set from 'Value' to the value of the `__name__` label.", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "dashboardEmbed", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allow embedding dashboard for external use in Code editors", + "stage": "experimental", + "codeowner": "@grafana/grafana-as-code", + "frontend": true + } + }, + { + "metadata": { + "name": "sqlDatasourceDatabaseSelection", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables previous SQL data source dataset dropdown behavior", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "transformationsVariableSupport", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allows using variables in transformations", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "pdfTables", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables generating table data as PDF in reporting", + "stage": "preview", + "codeowner": "@grafana/sharing-squad" + } + }, + { + "metadata": { + "name": "publicDashboardsScene", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables public dashboard rendering using scenes", + "stage": "experimental", + "codeowner": "@grafana/sharing-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "storage", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Configurable storage for dashboards, datasources, and resources", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, + { + "metadata": { + "name": "transformationsRedesign", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the transformations redesign", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "influxdbSqlSupport", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable InfluxDB SQL query language support with new querying UI", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "requiresRestart": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "addFieldFromCalculationStatFunctions", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Add cumulative and window functions to the add field from calculation transformation", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "logRowsPopoverMenu", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable filtering menu displayed when text of a log line is selected", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "alertingQueryOptimization", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Optimizes eligible queries in order to reduce load on datasources", + "stage": "GA", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "autoMigrateStatPanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "lokiFormatQuery", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the ability to format Loki queries", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "influxqlStreamingParser", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable streaming JSON parser for InfluxDB datasource InfluxQL query language", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "traceQLStreaming", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables response streaming of TraceQL queries of the Tempo data source", + "stage": "GA", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "usePrometheusFrontendPackage", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Use the @grafana/prometheus frontend package in core Prometheus.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "queryOverLive", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Use Grafana Live WebSocket to execute backend queries", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "featureHighlights", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Highlight Grafana Enterprise features", + "stage": "GA", + "codeowner": "@grafana/grafana-as-code", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "returnToPrevious", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the return to previous context functionality", + "stage": "preview", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "recordedQueriesMulti", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables writing multiple items from a single query within Recorded Queries", + "stage": "GA", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "externalServiceAccounts", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Automatic service account and token setup for plugins", + "stage": "preview", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "awsDatasourcesNewFormStyling", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Applies new form styling for configuration and query editors in AWS plugins", + "stage": "preview", + "codeowner": "@grafana/aws-datasources", + "frontend": true + } + }, + { + "metadata": { + "name": "panelFilterVariable", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables use of the `systemPanelFilterVar` variable to filter panels in a dashboard", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "canvasPanelPanZoom", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allow pan and zoom in canvas panel", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "autoMigrateOldPanels", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Migrate old angular panels to supported versions (graph, table-old, worldmap, etc)", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "canvasPanelNesting", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allow elements nesting", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "lokiLogsDataplane", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Changes logs responses from Loki to be compliant with the dataplane specification.", + "stage": "experimental", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "annotationPermissionUpdate", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Change the way annotation permissions work by scoping them to folders and dashboards.", + "stage": "GA", + "codeowner": "@grafana/identity-access-team" + } + }, + { + "metadata": { + "name": "sqlExpressions", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables using SQL and DuckDB functions as Expressions.", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad" + } + }, + { + "metadata": { + "name": "autoMigrateGraphPanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Migrate old graph panel to supported time series panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "influxdbBackendMigration", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Query InfluxDB InfluxQL without the proxy", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true + } + }, + { + "metadata": { + "name": "grpcServer", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Run the GRPC server", + "stage": "preview", + "codeowner": "@grafana/grafana-app-platform-squad", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "alertingBacktesting", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Rule backtesting API for alerting", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "individualCookiePreferences", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Support overriding cookie preferences per user", + "stage": "experimental", + "codeowner": "@grafana/backend-platform" + } + }, + { + "metadata": { + "name": "alertingInsights", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Show the new alerting insights landing page", + "stage": "GA", + "codeowner": "@grafana/alerting-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "externalCorePlugins", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allow core plugins to be loaded as external", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "panelTitleSearchInV1", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable searching for dashboards using panel title in search v1", + "stage": "experimental", + "codeowner": "@grafana/backend-platform", + "requiresDevMode": true + } + }, + { + "metadata": { + "name": "publicDashboards", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "[Deprecated] Public dashboards are now enabled by default; to disable them, use the configuration setting. This feature toggle will be removed in the next major version.", + "stage": "GA", + "codeowner": "@grafana/sharing-squad", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "disableAngular", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime.", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true, + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "promQLScope", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "In-development feature that will allow injection of labels into prometheus queries.", + "stage": "experimental", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "logRequestsInstrumentedAsUnknown", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Logs the path for requests that are instrumented as unknown", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" + } + }, + { + "metadata": { + "name": "faroDatasourceSelector", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable the data source selector within the Frontend Apps section of the Frontend Observability", + "stage": "preview", + "codeowner": "@grafana/app-o11y", + "frontend": true + } + }, + { + "metadata": { + "name": "pluginsFrontendSandbox", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the plugins frontend sandbox", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend", + "frontend": true + } + }, + { + "metadata": { + "name": "dashboardSceneSolo", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables rendering dashboards using scenes for solo panels", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "tableSharedCrosshair", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables shared crosshair in table panel", + "stage": "experimental", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "alertingSaveStatePeriodic", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Writes the state periodically to the database, asynchronous to rule evaluation", + "stage": "privatePreview", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "exploreContentOutline", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Content outline sidebar", + "stage": "GA", + "codeowner": "@grafana/explore-squad", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "datasourceQueryMultiStatus", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Introduce HTTP 207 Multi Status for api/ds/query", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "cloudWatchNewLabelParsing", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Updates CloudWatch label parsing to be more accurate", + "stage": "GA", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "awsAsyncQueryCaching", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable caching for async queries for Redshift and Athena. Requires that the datasource has caching and async query support enabled", + "stage": "GA", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "aiGeneratedDashboardChanges", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable AI powered features for dashboards to auto-summary changes when saving", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "betterPageScrolling", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Removes CustomScrollbar from the UI, relying on native browser scrollbars", + "stage": "GA", + "codeowner": "@grafana/grafana-frontend-platform", + "frontend": true + } + }, + { + "metadata": { + "name": "publicDashboardsEmailSharing", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables public dashboard sharing to be restricted to only allowed emails", + "stage": "preview", + "codeowner": "@grafana/sharing-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "nestedFolders", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable folder nesting", + "stage": "GA", + "codeowner": "@grafana/backend-platform" + } + }, + { + "metadata": { + "name": "ssoSettingsApi", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the SSO settings API and the OAuth configuration UIs in Grafana", + "stage": "preview", + "codeowner": "@grafana/identity-access-team", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "ssoSettingsSAML", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Use the new SSO Settings API to configure the SAML connector", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "cloudWatchBatchQueries", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Runs CloudWatch metrics queries as separate batches", + "stage": "preview", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "managedPluginsInstall", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Install managed plugins directly from plugins catalog", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "logsInfiniteScrolling", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables infinite scrolling for the Logs panel in Explore and Dashboards", + "stage": "experimental", + "codeowner": "@grafana/observability-logs", + "frontend": true + } + }, + { + "metadata": { + "name": "scenes", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Experimental framework to build interactive dashboards", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "reportingRetries", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables rendering retries for the reporting feature", + "stage": "preview", + "codeowner": "@grafana/sharing-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "scopeFilters", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the use of scope filters in Grafana", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "autoMigrateTablePanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Migrate old table panel to supported table panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "alertingNoNormalState", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Stop maintaining state of alerts that are not firing", + "stage": "preview", + "codeowner": "@grafana/alerting-squad", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "flameGraphItemCollapsing", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Allow collapsing of flame graph items", + "stage": "experimental", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "groupByVariable", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable groupBy variable support in scenes dashboards", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "lokiExperimentalStreaming", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Support new streaming approach for loki (prototype, needs special loki build)", + "stage": "experimental", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "dataplaneFrontendFallback", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Support dataplane contract field name change for transformations and field name matchers where the name is different", + "stage": "GA", + "codeowner": "@grafana/observability-metrics", + "frontend": true, + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "mysqlAnsiQuotes", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Use double quotes to escape keyword in a MySQL query", + "stage": "experimental", + "codeowner": "@grafana/backend-platform" + } + }, + { + "metadata": { + "name": "renderAuthJWT", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Uses JWT-based auth for rendering instead of relying on remote cache", + "stage": "preview", + "codeowner": "@grafana/grafana-as-code", + "hideFromAdminPage": true + } + }, + { + "metadata": { + "name": "prometheusConfigOverhaulAuth", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Update the Prometheus configuration page with the new auth component", + "stage": "GA", + "codeowner": "@grafana/observability-metrics" + } + }, + { + "metadata": { + "name": "recoveryThreshold", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables feature recovery threshold (aka hysteresis) for threshold server-side expression", + "stage": "GA", + "codeowner": "@grafana/alerting-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "live-service-web-worker", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "This will use a webworker thread to processes events rather than the main thread", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "topnav", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables topnav support in external plugins. The new Grafana navigation cannot be disabled.", + "stage": "deprecated", + "codeowner": "@grafana/grafana-frontend-platform" + } + }, + { + "metadata": { + "name": "metricsSummary", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables metrics summary queries in the Tempo data source", + "stage": "experimental", + "codeowner": "@grafana/observability-traces-and-profiling", + "frontend": true + } + }, + { + "metadata": { + "name": "alertmanagerRemoteSecondary", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable Grafana to sync configuration and state with a remote Alertmanager.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "dashboardSceneForViewers", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables dashboard rendering using Scenes for viewer roles", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "correlations", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Correlations page", + "stage": "GA", + "codeowner": "@grafana/explore-squad", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "showDashboardValidationWarnings", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Show warnings when dashboards do not validate against the schema", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad" + } + }, + { + "metadata": { + "name": "grafanaAPIServerWithExperimentalAPIs", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Register experimental APIs with the k8s API server", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresDevMode": true, + "requiresRestart": true + } + }, + { + "metadata": { + "name": "idForwarding", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Generate signed id token for identity that can be forwarded to plugins and external services", + "stage": "experimental", + "codeowner": "@grafana/identity-access-team" + } + }, + { + "metadata": { + "name": "lokiStructuredMetadata", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the loki data source to request structured metadata from the Loki server", + "stage": "GA", + "codeowner": "@grafana/observability-logs" + } + }, + { + "metadata": { + "name": "kubernetesAggregator", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable grafana aggregator", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "autoMigrateXYChartPanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Migrate old XYChart panel to new XYChart2 model", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "pluginsDynamicAngularDetectionPatterns", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables fetching Angular detection patterns for plugins from GCOM and fallback to hardcoded ones", + "stage": "GA", + "codeowner": "@grafana/plugins-platform-backend" + } + }, + { + "metadata": { + "name": "newDashboardWithFiltersAndGroupBy", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering.", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "hideFromAdminPage": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "awsDatasourcesTempCredentials", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Support temporary security credentials in AWS plugins for Grafana Cloud customers", + "stage": "experimental", + "codeowner": "@grafana/aws-datasources" + } + }, + { + "metadata": { + "name": "datatrails", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the new core app datatrails", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true, + "hideFromDocs": true + } + }, + { + "metadata": { + "name": "wargamesTesting", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Placeholder feature flag for internal testing", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" + } + }, + { + "metadata": { + "name": "kubernetesSnapshots", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Routes snapshot requests from /api to the /apis endpoint", + "stage": "experimental", + "codeowner": "@grafana/grafana-app-platform-squad", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "lokiMetricDataplane", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Changes metric responses from Loki to be compliant with the dataplane specification.", + "stage": "GA", + "codeowner": "@grafana/observability-logs", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "enableDatagridEditing", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables the edit functionality in the datagrid panel", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "alertStateHistoryLokiSecondary", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable Grafana to write alert state history to an external Loki instance in addition to Grafana annotations.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" + } + }, + { + "metadata": { + "name": "vizAndWidgetSplit", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Split panels between visualizations and widgets", + "stage": "experimental", + "codeowner": "@grafana/dashboards-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "pluginsSkipHostEnvVars", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disables passing host environment variable to plugin processes", + "stage": "experimental", + "codeowner": "@grafana/plugins-platform-backend" + } + }, { "metadata": { "name": "authAPIAccessTokenAuth", - "resourceVersion": "1711701535283", - "creationTimestamp": "2024-03-29T08:38:55Z" + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { "description": "Enables the use of Auth API access tokens for authentication", @@ -2081,31 +1976,103 @@ }, { "metadata": { - "name": "filtersOnByDefault", - "resourceVersion": "1712149621080", - "creationTimestamp": "2024-04-03T13:07:01Z", - "deletionTimestamp": "2024-04-04T10:35:56Z" + "name": "panelTitleSearch", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering.", - "stage": "experimental", - "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true + "description": "Search for dashboards using panel title", + "stage": "preview", + "codeowner": "@grafana/grafana-app-platform-squad", + "hideFromAdminPage": true } }, { "metadata": { - "name": "newDashboardWithFiltersAndGroupBy", - "resourceVersion": "1712226956055", - "creationTimestamp": "2024-04-04T10:35:56Z" + "name": "autoMigratePiechartPanel", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" }, "spec": { - "description": "Enables filters and group by variables on all new dashboards. Variables are added only if default data source supports filtering.", - "stage": "experimental", + "description": "Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking", + "stage": "preview", + "codeowner": "@grafana/dataviz-squad", + "frontend": true + } + }, + { + "metadata": { + "name": "dashgpt", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable AI powered features in dashboards", + "stage": "GA", "codeowner": "@grafana/dashboards-squad", - "hideFromAdminPage": true, - "hideFromDocs": true + "frontend": true + } + }, + { + "metadata": { + "name": "enableNativeHTTPHistogram", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables native HTTP Histograms", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team" + } + }, + { + "metadata": { + "name": "cloudWatchCrossAccountQuerying", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enables cross-account querying in CloudWatch datasources", + "stage": "GA", + "codeowner": "@grafana/aws-datasources", + "allowSelfServe": true + } + }, + { + "metadata": { + "name": "permissionsFilterRemoveSubquery", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Alternative permission filter implementation that does not use subqueries for fetching the dashboard folder", + "stage": "experimental", + "codeowner": "@grafana/backend-platform" + } + }, + { + "metadata": { + "name": "disableSecretsCompatibility", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Disable duplicated secret storage in legacy tables", + "stage": "experimental", + "codeowner": "@grafana/hosted-grafana-team", + "requiresRestart": true + } + }, + { + "metadata": { + "name": "alertmanagerRemotePrimary", + "resourceVersion": "1712260369192", + "creationTimestamp": "2024-04-04T19:52:49Z" + }, + "spec": { + "description": "Enable Grafana to have a remote Alertmanager instance as the primary Alertmanager.", + "stage": "experimental", + "codeowner": "@grafana/alerting-squad" } }, { diff --git a/pkg/tsdb/cloudwatch/features/features.go b/pkg/tsdb/cloudwatch/features/features.go index 2c01a2673bf..1ec7975d6e7 100644 --- a/pkg/tsdb/cloudwatch/features/features.go +++ b/pkg/tsdb/cloudwatch/features/features.go @@ -9,6 +9,7 @@ import ( const ( FlagCloudWatchCrossAccountQuerying = "cloudWatchCrossAccountQuerying" FlagCloudWatchBatchQueries = "cloudWatchBatchQueries" + FlagCloudWatchNewLabelParsing = "cloudWatchNewLabelParsing" ) func IsEnabled(ctx context.Context, feature string) bool { diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder.go b/pkg/tsdb/cloudwatch/metric_data_input_builder.go index 1cbb848258b..2bbf4da5df4 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder.go @@ -1,6 +1,7 @@ package cloudwatch import ( + "context" "time" "github.com/aws/aws-sdk-go/aws" @@ -9,7 +10,7 @@ import ( "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) -func (e *cloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime time.Time, +func (e *cloudWatchExecutor) buildMetricDataInput(ctx context.Context, startTime time.Time, endTime time.Time, queries []*models.CloudWatchQuery) (*cloudwatch.GetMetricDataInput, error) { metricDataInput := &cloudwatch.GetMetricDataInput{ StartTime: aws.Time(startTime), @@ -26,7 +27,7 @@ func (e *cloudWatchExecutor) buildMetricDataInput(startTime time.Time, endTime t } for _, query := range queries { - metricDataQuery, err := e.buildMetricDataQuery(query) + metricDataQuery, err := e.buildMetricDataQuery(ctx, query) if err != nil { return nil, &models.QueryError{Err: err, RefID: query.RefId} } diff --git a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go index e891f996d2b..5b8ba2beb53 100644 --- a/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_input_builder_test.go @@ -1,6 +1,7 @@ package cloudwatch import ( + "context" "testing" "time" @@ -33,7 +34,7 @@ func TestMetricDataInputBuilder(t *testing.T) { from := now.Add(time.Hour * -2) to := now.Add(time.Hour * -1) - mdi, err := executor.buildMetricDataInput(from, to, []*models.CloudWatchQuery{query}) + mdi, err := executor.buildMetricDataInput(context.Background(), from, to, []*models.CloudWatchQuery{query}) assert.NoError(t, err) require.NotNil(t, mdi) diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder.go b/pkg/tsdb/cloudwatch/metric_data_query_builder.go index aa30e28f5cb..596fe7bf0a5 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder.go @@ -1,6 +1,7 @@ package cloudwatch import ( + "context" "fmt" "sort" "strconv" @@ -9,10 +10,13 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) -func (e *cloudWatchExecutor) buildMetricDataQuery(query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) { +const keySeparator = "|&|" + +func (e *cloudWatchExecutor) buildMetricDataQuery(ctx context.Context, query *models.CloudWatchQuery) (*cloudwatch.MetricDataQuery, error) { mdq := &cloudwatch.MetricDataQuery{ Id: aws.String(query.Id), ReturnData: aws.Bool(query.ReturnData), @@ -31,6 +35,9 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(query *models.CloudWatchQuery) mdq.Expression = aws.String(query.SqlExpression) case models.GMDApiModeInferredSearchExpression: mdq.Expression = aws.String(buildSearchExpression(query, query.Statistic)) + if features.IsEnabled(ctx, features.FlagCloudWatchNewLabelParsing) { + mdq.Label = aws.String(buildSearchExpressionLabel(query)) + } case models.GMDApiModeMetricStat: mdq.MetricStat = &cloudwatch.MetricStat{ Metric: &cloudwatch.Metric{ @@ -60,6 +67,10 @@ func (e *cloudWatchExecutor) buildMetricDataQuery(query *models.CloudWatchQuery) return mdq, nil } +func isSingleValue(values []string) bool { + return len(values) == 1 && values[0] != "*" +} + func buildSearchExpression(query *models.CloudWatchQuery, stat string) string { knownDimensions := make(map[string][]string) dimensionNames := []string{} @@ -120,6 +131,25 @@ func buildSearchExpression(query *models.CloudWatchQuery, stat string) string { return fmt.Sprintf(`REMOVE_EMPTY(SEARCH('%s', '%s', %s))`, namespaceSearchTermAndAccount, stat, strconv.Itoa(query.Period)) } +func buildSearchExpressionLabel(query *models.CloudWatchQuery) string { + label := "${LABEL}" + if len(query.Label) > 0 { + label = query.Label + } + + multiDims := []string{} + for key, values := range query.Dimensions { + if !isSingleValue(values) { + multiDims = append(multiDims, key) + } + } + sort.Strings(multiDims) + for _, key := range multiDims { + label += fmt.Sprintf("%s${PROP('Dim.%s')}", keySeparator, key) + } + return label +} + func escapeDoubleQuotes(arr []string) []string { result := []string{} for _, value := range arr { diff --git a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go index b956a88a45f..7a8f483afea 100644 --- a/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go +++ b/pkg/tsdb/cloudwatch/metric_data_query_builder_test.go @@ -1,23 +1,25 @@ package cloudwatch import ( + "context" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/grafana/grafana-plugin-sdk-go/backend/log" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMetricDataQueryBuilder(t *testing.T) { + executor := newExecutor(nil, log.NewNullLogger()) t.Run("buildMetricDataQuery", func(t *testing.T) { t.Run("should use metric stat", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeBuilder query.MetricQueryType = models.MetricQueryTypeSearch - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Empty(t, mdq.Expression) assert.Equal(t, query.MetricName, *mdq.MetricStat.Metric.MetricName) @@ -25,57 +27,52 @@ func TestMetricDataQueryBuilder(t *testing.T) { }) t.Run("should pass AccountId in metric stat query", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeBuilder query.MetricQueryType = models.MetricQueryTypeSearch query.AccountId = aws.String("some account id") - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) assert.Equal(t, "some account id", *mdq.AccountId) }) t.Run("should leave AccountId in metric stat query", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeBuilder query.MetricQueryType = models.MetricQueryTypeSearch - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) assert.Nil(t, mdq.AccountId) }) t.Run("should use custom built expression", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeBuilder query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.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) }) t.Run("should use sql expression", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeRaw query.MetricQueryType = models.MetricQueryTypeQuery query.SqlExpression = `SELECT SUM(CPUUTilization) FROM "AWS/EC2"` - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) assert.Equal(t, query.SqlExpression, *mdq.Expression) }) t.Run("should use user defined math expression", func(t *testing.T) { - executor := newExecutor(nil, log.NewNullLogger()) query := getBaseQuery() query.MetricEditorMode = models.MetricEditorModeRaw query.MetricQueryType = models.MetricQueryTypeSearch query.Expression = `SUM(x+y)` - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) assert.Equal(t, query.Expression, *mdq.Expression) @@ -88,7 +85,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { query.MetricQueryType = models.MetricQueryTypeSearch query.MatchExact = false query.Expression = `SUM([a,b])` - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) require.NoError(t, err) require.Nil(t, mdq.MetricStat) assert.Equal(t, int64(300), *mdq.Period) @@ -100,7 +97,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { query := getBaseQuery() query.Label = "some label" - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) assert.NoError(t, err) require.NotNil(t, mdq.Label) @@ -112,7 +109,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { query := getBaseQuery() query.Label = "" - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) assert.NoError(t, err) assert.Nil(t, mdq.Label) @@ -129,7 +126,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { AccountId: aws.String("all"), } - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) assert.NoError(t, err) require.Nil(t, mdq.MetricStat) @@ -147,7 +144,7 @@ func TestMetricDataQueryBuilder(t *testing.T) { AccountId: aws.String("12345"), } - mdq, err := executor.buildMetricDataQuery(query) + mdq, err := executor.buildMetricDataQuery(context.Background(), query) assert.NoError(t, err) require.Nil(t, mdq.MetricStat) @@ -165,13 +162,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"lb1", "lb2", "lb3"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("Query has three dimension values for two given dimension keys", func(t *testing.T) { @@ -182,29 +184,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "i-456", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - 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))`, res) - }) - - t.Run("No OR operator was added if a star was used for dimension value", func(t *testing.T) { - query := &models.CloudWatchQuery{ - Namespace: "AWS/EC2", - MetricName: "CPUUtilization", - Dimensions: map[string][]string{ - "LoadBalancer": {"*"}, - }, - Period: 300, - Expression: "", - MatchExact: matchExact, - } - - res := buildSearchExpression(query, "Average") - assert.NotContains(t, res, "OR") + mdq, err := executor.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) }) t.Run("Query has one dimension key with a * value", func(t *testing.T) { @@ -214,13 +205,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"*"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","LoadBalancer"} MetricName="CPUUtilization"', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("Query has three dimension values for two given dimension keys, and one value is a star", func(t *testing.T) { @@ -231,13 +227,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "*", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/EC2","InstanceId","LoadBalancer"} MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("Query has multiple dimensions and an account Id", func(t *testing.T) { @@ -248,14 +249,19 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "*", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, - AccountId: aws.String("some account id"), + Period: 300, + Expression: "", + MatchExact: matchExact, + AccountId: aws.String("some account id"), + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - 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))`, res) + mdq, err := executor.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) }) t.Run("Query has a dimension key with a space", func(t *testing.T) { @@ -263,15 +269,20 @@ func TestMetricDataQueryBuilder(t *testing.T) { Namespace: "AWS/Kafka", MetricName: "CpuUser", Dimensions: map[string][]string{ - "Cluster Name": {"dev-cluster"}, + "Cluster Name": {"dev-cluster", "prod-cluster"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"AWS/Kafka","Cluster Name"} MetricName="CpuUser" "Cluster Name"="dev-cluster"', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("Query has a custom namespace contains spaces", func(t *testing.T) { @@ -282,13 +293,41 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "*", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('{"Test-API Cache by Minute","InstanceId","LoadBalancer"} MetricName="CpuUser" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res) + mdq, err := executor.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) + }) + + t.Run("Query has a custom label", func(t *testing.T) { + query := &models.CloudWatchQuery{ + Namespace: "CPUUtilization", + MetricName: "CpuUser", + Dimensions: map[string][]string{ + "LoadBalancer": {"lb1"}, + "InstanceId": {"i-123", "*", "i-789"}, + }, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + Label: "LB: ${PROP('Dim.LoadBalancer')", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, + } + + mdq, err := executor.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) }) }) @@ -302,13 +341,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"lb1", "lb2", "lb3"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3")', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("Query has three dimension values for two given dimension keys", func(t *testing.T) { @@ -319,13 +363,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "i-456", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - 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))`, res) + mdq, err := executor.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) }) t.Run("Query has one dimension key with a * value", func(t *testing.T) { @@ -335,13 +384,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"*"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("query has three dimension values for two given dimension keys, and one value is a star", func(t *testing.T) { @@ -352,13 +406,18 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "*", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - assert.Equal(t, `REMOVE_EMPTY(SEARCH('Namespace="AWS/EC2" MetricName="CPUUtilization" "LoadBalancer"=("lb1" OR "lb2" OR "lb3") "InstanceId"', 'Average', 300))`, res) + mdq, err := executor.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) }) t.Run("query has multiple dimensions and an account Id", func(t *testing.T) { @@ -369,14 +428,42 @@ func TestMetricDataQueryBuilder(t *testing.T) { "LoadBalancer": {"lb1", "lb2", "lb3"}, "InstanceId": {"i-123", "*", "i-789"}, }, - Period: 300, - Expression: "", - MatchExact: matchExact, - AccountId: aws.String("some account id"), + Period: 300, + Expression: "", + MatchExact: matchExact, + AccountId: aws.String("some account id"), + Statistic: "Average", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, } - res := buildSearchExpression(query, "Average") - 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))`, res) + mdq, err := executor.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) + }) + + t.Run("Query has a custom label", func(t *testing.T) { + query := &models.CloudWatchQuery{ + Namespace: "AWS/EC2", + MetricName: "CPUUtilization", + Dimensions: map[string][]string{ + "LoadBalancer": {"lb1"}, + "InstanceId": {"i-123", "*", "i-789"}, + }, + Period: 300, + Expression: "", + MatchExact: matchExact, + Statistic: "Average", + Label: "LB: ${PROP('Dim.LoadBalancer')", + MetricQueryType: models.MetricQueryTypeSearch, + MetricEditorMode: models.MetricEditorModeBuilder, + } + + mdq, err := executor.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) }) }) diff --git a/pkg/tsdb/cloudwatch/response_parser.go b/pkg/tsdb/cloudwatch/response_parser.go index 05bb8dcd9e6..f8953021a1f 100644 --- a/pkg/tsdb/cloudwatch/response_parser.go +++ b/pkg/tsdb/cloudwatch/response_parser.go @@ -1,6 +1,7 @@ package cloudwatch import ( + "context" "fmt" "regexp" "sort" @@ -10,13 +11,14 @@ import ( "github.com/aws/aws-sdk-go/service/cloudwatch" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" ) // matches a dynamic label var dynamicLabel = regexp.MustCompile(`\$\{.+\}`) -func (e *cloudWatchExecutor) parseResponse(startTime time.Time, endTime time.Time, metricDataOutputs []*cloudwatch.GetMetricDataOutput, +func (e *cloudWatchExecutor) parseResponse(ctx context.Context, startTime time.Time, endTime time.Time, metricDataOutputs []*cloudwatch.GetMetricDataOutput, queries []*models.CloudWatchQuery) ([]*responseWrapper, error) { aggregatedResponse := aggregateResponse(metricDataOutputs) queriesById := map[string]*models.CloudWatchQuery{} @@ -34,7 +36,7 @@ func (e *cloudWatchExecutor) parseResponse(startTime time.Time, endTime time.Tim } var err error - dataRes.Frames, err = buildDataFrames(startTime, endTime, response, queryRow) + dataRes.Frames, err = buildDataFrames(ctx, startTime, endTime, response, queryRow) if err != nil { return nil, err } @@ -87,6 +89,31 @@ func aggregateResponse(getMetricDataOutputs []*cloudwatch.GetMetricDataOutput) m return responseByID } +func parseLabels(cloudwatchLabel string, query *models.CloudWatchQuery) (string, data.Labels) { + dims := make([]string, 0, len(query.Dimensions)) + for k := range query.Dimensions { + dims = append(dims, k) + } + sort.Strings(dims) + + splitLabels := strings.Split(cloudwatchLabel, keySeparator) + // The first part is the name of the time series, followed by the labels + labelsIndex := 1 + + labels := data.Labels{} + for _, dim := range dims { + values := query.Dimensions[dim] + if isSingleValue(values) { + labels[dim] = values[0] + continue + } + + labels[dim] = splitLabels[labelsIndex] + labelsIndex++ + } + return splitLabels[0], labels +} + func getLabels(cloudwatchLabel string, query *models.CloudWatchQuery) data.Labels { dims := make([]string, 0, len(query.Dimensions)) for k := range query.Dimensions { @@ -111,7 +138,7 @@ func getLabels(cloudwatchLabel string, query *models.CloudWatchQuery) data.Label return labels } -func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse models.QueryRowResponse, +func buildDataFrames(ctx context.Context, startTime time.Time, endTime time.Time, aggregatedResponse models.QueryRowResponse, query *models.CloudWatchQuery) (data.Frames, error) { frames := data.Frames{} hasStaticLabel := query.Label != "" && !dynamicLabel.MatchString(query.Label) @@ -127,6 +154,9 @@ func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse // In case a multi-valued dimension is used and the cloudwatch query yields no values, create one empty time // series for each dimension value. Use that dimension value to expand the alias field if len(metric.Values) == 0 && query.IsMultiValuedDimensionExpression() { + if features.IsEnabled(ctx, features.FlagCloudWatchNewLabelParsing) { + label, _, _ = strings.Cut(label, keySeparator) + } series := 0 multiValuedDimension := "" for key, values := range query.Dimensions { @@ -163,7 +193,13 @@ func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse continue } - labels := getLabels(label, query) + name := label + var labels data.Labels + if features.IsEnabled(ctx, features.FlagCloudWatchNewLabelParsing) { + name, labels = parseLabels(label, query) + } else { + labels = getLabels(label, query) + } timestamps := []*time.Time{} points := []*float64{} for j, t := range metric.Timestamps { @@ -175,7 +211,6 @@ func buildDataFrames(startTime time.Time, endTime time.Time, aggregatedResponse timeField := data.NewField(data.TimeSeriesTimeFieldName, nil, timestamps) valueField := data.NewField(data.TimeSeriesValueFieldName, labels, points) - name := label // CloudWatch appends the dimensions to the returned label if the query label is not dynamic, so static labels need to be set if hasStaticLabel { name = query.Label diff --git a/pkg/tsdb/cloudwatch/response_parser_test.go b/pkg/tsdb/cloudwatch/response_parser_test.go index 5cdedf42967..bd992d17f10 100644 --- a/pkg/tsdb/cloudwatch/response_parser_test.go +++ b/pkg/tsdb/cloudwatch/response_parser_test.go @@ -9,6 +9,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/cloudwatch" + "github.com/grafana/grafana/pkg/tsdb/cloudwatch/features" "github.com/grafana/grafana/pkg/tsdb/cloudwatch/models" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -136,16 +137,17 @@ func TestCloudWatchResponseParser(t *testing.T) { }) } -func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { +func Test_buildDataFrames_parse_label_to_name_and_labels(t *testing.T) { startTime := time.Now() endTime := startTime.Add(2 * time.Hour) - t.Run("using exact match", func(t *testing.T) { + + t.Run("using multi filter", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("id1"), - Label: aws.String("lb1"), + Label: aws.String("lb1|&|lb1"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -160,7 +162,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { }, { Id: aws.String("id2"), - Label: aws.String("lb2"), + Label: aws.String("lb2|&|lb2"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -189,26 +191,29 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { Period: 60, MetricQueryType: models.MetricQueryTypeSearch, MetricEditorMode: models.MetricEditorModeBuilder, + MatchExact: true, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) frame1 := frames[0] assert.Equal(t, "lb1", frame1.Name) assert.Equal(t, "lb1", frame1.Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "tg", frame1.Fields[1].Labels["TargetGroup"]) frame2 := frames[1] assert.Equal(t, "lb2", frame2.Name) assert.Equal(t, "lb2", frame2.Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "tg", frame2.Fields[1].Labels["TargetGroup"]) }) - t.Run("using wildcard", func(t *testing.T) { + t.Run("using multiple wildcard filters", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), - Label: aws.String("some label lb3"), + Label: aws.String("some label lb3|&|inst1|&|balancer 1"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -223,7 +228,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { }, { Id: aws.String("lb4"), - Label: aws.String("some label lb4"), + Label: aws.String("some label lb4|&|inst2|&|balancer 2"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -246,6 +251,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricName: "TargetResponseTime", Dimensions: map[string][]string{ "LoadBalancer": {"*"}, + "InstanceType": {"*"}, "TargetGroup": {"tg"}, }, Statistic: "Average", @@ -253,20 +259,28 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricQueryType: models.MetricQueryTypeSearch, MetricEditorMode: models.MetricEditorModeBuilder, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Equal(t, "some label lb3", frames[0].Name) + assert.Equal(t, "balancer 1", frames[0].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "inst1", frames[0].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "tg", frames[0].Fields[1].Labels["TargetGroup"]) + assert.Equal(t, "some label lb4", frames[1].Name) + assert.Equal(t, "balancer 2", frames[1].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "inst2", frames[1].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "tg", frames[1].Fields[1].Labels["TargetGroup"]) }) t.Run("when no values are returned and a multi-valued template variable is used", func(t *testing.T) { timestamp := time.Unix(0, 0) + // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), - Label: aws.String("some label"), + Label: aws.String("some label|&|--"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -290,21 +304,24 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricQueryType: models.MetricQueryTypeSearch, MetricEditorMode: models.MetricEditorModeBuilder, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Len(t, frames, 2) assert.Equal(t, "some label", frames[0].Name) + assert.Equal(t, "lb1", frames[0].Fields[1].Labels["LoadBalancer"]) assert.Equal(t, "some label", frames[1].Name) + assert.Equal(t, "lb2", frames[1].Fields[1].Labels["LoadBalancer"]) }) t.Run("when no values are returned and a multi-valued template variable and two single-valued dimensions are used", func(t *testing.T) { timestamp := time.Unix(0, 0) + // When there are no results, CloudWatch sets the label values to -- response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), - Label: aws.String("some label"), + Label: aws.String("some label|&|--"), Timestamps: []*time.Time{ aws.Time(timestamp), aws.Time(timestamp.Add(time.Minute)), @@ -331,15 +348,22 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricQueryType: models.MetricQueryTypeSearch, MetricEditorMode: models.MetricEditorModeBuilder, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Len(t, frames, 2) assert.Equal(t, "some label", frames[0].Name) + assert.Equal(t, "lb1", frames[0].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "micro", frames[0].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "res", frames[0].Fields[1].Labels["Resource"]) + assert.Equal(t, "some label", frames[1].Name) + assert.Equal(t, "lb2", frames[1].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "micro", frames[1].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "res", frames[1].Fields[1].Labels["Resource"]) }) - t.Run("when using SQL queries", func(t *testing.T) { + t.Run("when not using multi-value dimension filters", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ @@ -370,10 +394,13 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricQueryType: models.MetricQueryTypeQuery, MetricEditorMode: models.MetricEditorModeRaw, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Equal(t, "some label", frames[0].Name) + assert.Equal(t, "lb1", frames[0].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "micro", frames[0].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "res", frames[0].Fields[1].Labels["Resource"]) }) t.Run("when non-static label set on query", func(t *testing.T) { @@ -382,7 +409,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), - Label: aws.String("some label"), + Label: aws.String("some label|&|res"), Timestamps: []*time.Time{ aws.Time(timestamp), }, @@ -400,7 +427,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"lb1"}, "InstanceType": {"micro"}, - "Resource": {"res"}, + "Resource": {"*"}, }, Statistic: "Average", Period: 60, @@ -408,19 +435,22 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricEditorMode: models.MetricEditorModeBuilder, Label: "set ${AVG} label", } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Equal(t, "some label", frames[0].Name) + assert.Equal(t, "lb1", frames[0].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "micro", frames[0].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "res", frames[0].Fields[1].Labels["Resource"]) }) - t.Run("unless static label set on query", func(t *testing.T) { + t.Run("when static label set on query", func(t *testing.T) { timestamp := time.Unix(0, 0) response := &models.QueryRowResponse{ Metrics: []*cloudwatch.MetricDataResult{ { Id: aws.String("lb3"), - Label: aws.String("some label"), + Label: aws.String("some label|&|res"), Timestamps: []*time.Time{ aws.Time(timestamp), }, @@ -438,7 +468,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { Dimensions: map[string][]string{ "LoadBalancer": {"lb1"}, "InstanceType": {"micro"}, - "Resource": {"res"}, + "Resource": {"*"}, }, Statistic: "Average", Period: 60, @@ -446,10 +476,13 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricEditorMode: models.MetricEditorModeBuilder, Label: "actual", } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) assert.Equal(t, "actual", frames[0].Name) + assert.Equal(t, "lb1", frames[0].Fields[1].Labels["LoadBalancer"]) + assert.Equal(t, "micro", frames[0].Fields[1].Labels["InstanceType"]) + assert.Equal(t, "res", frames[0].Fields[1].Labels["Resource"]) }) t.Run("Parse cloudwatch response", func(t *testing.T) { @@ -488,7 +521,7 @@ func Test_buildDataFrames_uses_response_label_as_frame_name(t *testing.T) { MetricQueryType: models.MetricQueryTypeSearch, MetricEditorMode: models.MetricEditorModeBuilder, } - frames, err := buildDataFrames(startTime, endTime, *response, query) + frames, err := buildDataFrames(contextWithFeaturesEnabled(features.FlagCloudWatchNewLabelParsing), startTime, endTime, *response, query) require.NoError(t, err) frame := frames[0] diff --git a/pkg/tsdb/cloudwatch/time_series_query.go b/pkg/tsdb/cloudwatch/time_series_query.go index cc6b9e772af..6b5777fc79e 100644 --- a/pkg/tsdb/cloudwatch/time_series_query.go +++ b/pkg/tsdb/cloudwatch/time_series_query.go @@ -86,7 +86,7 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, req *ba return err } - metricDataInput, err := e.buildMetricDataInput(startTime, endTime, requestQueries) + metricDataInput, err := e.buildMetricDataInput(ctx, startTime, endTime, requestQueries) if err != nil { return err } @@ -96,12 +96,14 @@ func (e *cloudWatchExecutor) executeTimeSeriesQuery(ctx context.Context, req *ba return err } - requestQueries, err = e.getDimensionValuesForWildcards(ctx, region, client, requestQueries, instance.tagValueCache, instance.Settings.GrafanaSettings.ListMetricsPageLimit) - if err != nil { - return err + if !features.IsEnabled(ctx, features.FlagCloudWatchNewLabelParsing) { + requestQueries, err = e.getDimensionValuesForWildcards(ctx, region, client, requestQueries, instance.tagValueCache, instance.Settings.GrafanaSettings.ListMetricsPageLimit) + if err != nil { + return err + } } - res, err := e.parseResponse(startTime, endTime, mdo, requestQueries) + res, err := e.parseResponse(ctx, startTime, endTime, mdo, requestQueries) if err != nil { return err }