From dd77ff6bcde9f363927795ef70769989d7b22181 Mon Sep 17 00:00:00 2001 From: Andreas Christou Date: Wed, 3 Jan 2024 19:20:22 +0000 Subject: [PATCH] Plugins: Externalise Azure Monitor data source (#79545) * Set up frontend linting for Azure - Fix final frontend import - Fix other lint issues * Add Azure Monitor to backend linting * Remove featuremgmt dependency * Add intervalv2 to list of disallowed imports * Remove config dependency - Replace with function from azure-sdk * Remove util dependency * Duplicate interval functionality from core * Add required backend wrappers * Update frontend * Add testing helper * Add missing package * Bump minimum grafana dependency * Fix dependency * Regen cue * Fix lint * Update expected response file * Update import and dependency --- .eslintrc | 2 + .golangci.toml | 3 + package.json | 1 + .../x/AzureMonitorDataQuery_types.gen.ts | 2 +- .../plugins_integration_test.go | 2 +- .../api/plugins/data/expectedListResp.json | 6 +- pkg/tsdb/azuremonitor/azuremonitor.go | 26 +++--- pkg/tsdb/azuremonitor/azuremonitor_test.go | 9 +- pkg/tsdb/azuremonitor/httpclient.go | 6 +- pkg/tsdb/azuremonitor/httpclient_test.go | 12 ++- pkg/tsdb/azuremonitor/macros/macros.go | 4 +- .../metrics/azuremonitor-datasource.go | 4 +- .../metrics/azuremonitor-datasource_test.go | 19 ++-- .../azuremonitor/standalone/datasource.go | 38 ++++++++ pkg/tsdb/azuremonitor/standalone/main.go | 23 +++++ pkg/tsdb/azuremonitor/time/interval.go | 93 +++++++++++++++++++ pkg/tsdb/azuremonitor/time/time-grain.go | 4 +- .../app/features/plugins/built_in_plugins.ts | 2 +- .../datasource/azuremonitor/CHANGELOG.md | 0 .../azuremonitor/__mocks__/variables.ts | 13 ++- .../DimensionFields.test.tsx | 2 +- .../MetricsQueryEditor.test.tsx | 2 +- .../QueryEditor/QueryEditor.test.tsx | 2 +- .../TracesQueryEditor/Filters.test.tsx | 2 +- .../datasource/azuremonitor/package.json | 50 ++++++++++ .../datasource/azuremonitor/plugin.json | 6 +- .../datasource/azuremonitor/tsconfig.json | 4 + .../azuremonitor/utils/common.test.ts | 2 +- .../azuremonitor/utils/testUtils.ts | 8 ++ .../datasource/azuremonitor/webpack.config.ts | 3 + yarn.lock | 41 ++++++++ 31 files changed, 332 insertions(+), 59 deletions(-) create mode 100644 pkg/tsdb/azuremonitor/standalone/datasource.go create mode 100644 pkg/tsdb/azuremonitor/standalone/main.go create mode 100644 pkg/tsdb/azuremonitor/time/interval.go create mode 100644 public/app/plugins/datasource/azuremonitor/CHANGELOG.md create mode 100644 public/app/plugins/datasource/azuremonitor/package.json create mode 100644 public/app/plugins/datasource/azuremonitor/tsconfig.json create mode 100644 public/app/plugins/datasource/azuremonitor/utils/testUtils.ts create mode 100644 public/app/plugins/datasource/azuremonitor/webpack.config.ts diff --git a/.eslintrc b/.eslintrc index 1403b7bc7d7..73fc9db1801 100644 --- a/.eslintrc +++ b/.eslintrc @@ -98,6 +98,8 @@ "files": [ "public/app/plugins/datasource/grafana-testdata-datasource/*.{ts,tsx}", "public/app/plugins/datasource/grafana-testdata-datasource/**/*.{ts,tsx}", + "public/app/plugins/datasource/azuremonitor/*.{ts,tsx}", + "public/app/plugins/datasource/azuremonitor/**/*.{ts,tsx}", "public/app/plugins/datasource/parca/*.{ts,tsx}", "public/app/plugins/datasource/parca/**/*.{ts,tsx}" ], diff --git a/.golangci.toml b/.golangci.toml index 3fe5ae002aa..9051dd65829 100644 --- a/.golangci.toml +++ b/.golangci.toml @@ -52,10 +52,13 @@ deny = [ { pkg = "github.com/grafana/grafana/pkg/server", desc = "Core plugins are not allowed to depend on Grafana core packages" }, { pkg = "github.com/grafana/grafana/pkg/tests", desc = "Core plugins are not allowed to depend on Grafana core packages" }, { pkg = "github.com/grafana/grafana/pkg/web", desc = "Core plugins are not allowed to depend on Grafana core packages" }, + { pkg = "github.com/grafana/grafana/pkg/tsdb/intervalv2", desc = "Core plugins are not allowed to depend on Grafana core packages" }, ] files = [ "**/pkg/tsdb/grafana-testdata-datasource/*", "**/pkg/tsdb/grafana-testdata-datasource/**/*", + "**/pkg/tsdb/azuremonitor/*", + "**/pkg/tsdb/azuremonitor/**/*", "**/pkg/tsdb/parca/*", "**/pkg/tsdb/parca/**/*", ] diff --git a/package.json b/package.json index 3742772540c..a4b96dc53c3 100644 --- a/package.json +++ b/package.json @@ -243,6 +243,7 @@ "@fingerprintjs/fingerprintjs": "^3.4.2", "@floating-ui/react": "0.26.4", "@glideapps/glide-data-grid": "^5.2.1", + "@grafana-plugins/grafana-azure-monitor-datasource": "workspace:*", "@grafana-plugins/grafana-testdata-datasource": "workspace:*", "@grafana-plugins/parca": "workspace:*", "@grafana/aws-sdk": "0.3.1", diff --git a/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts b/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts index 712b35a32f8..a22f7674cbf 100644 --- a/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts +++ b/packages/grafana-schema/src/raw/composable/azuremonitor/dataquery/x/AzureMonitorDataQuery_types.gen.ts @@ -11,7 +11,7 @@ import * as common from '@grafana/schema'; -export const pluginVersion = "1.0.0"; +export const pluginVersion = "%VERSION%"; export interface AzureMonitorQuery extends common.DataQuery { /** diff --git a/pkg/services/pluginsintegration/plugins_integration_test.go b/pkg/services/pluginsintegration/plugins_integration_test.go index f7a6da7f1c2..773e1b7703e 100644 --- a/pkg/services/pluginsintegration/plugins_integration_test.go +++ b/pkg/services/pluginsintegration/plugins_integration_test.go @@ -80,7 +80,7 @@ func TestIntegrationPluginManager(t *testing.T) { tracer := tracing.InitializeTracerForTest() hcp := httpclient.NewProvider() - am := azuremonitor.ProvideService(cfg, hcp, features) + am := azuremonitor.ProvideService(hcp) cw := cloudwatch.ProvideService(cfg, hcp, features) cm := cloudmonitoring.ProvideService(hcp, tracer) es := elasticsearch.ProvideService(hcp, tracer) diff --git a/pkg/tests/api/plugins/data/expectedListResp.json b/pkg/tests/api/plugins/data/expectedListResp.json index ee92e7548e5..0bdf55c3358 100644 --- a/pkg/tests/api/plugins/data/expectedListResp.json +++ b/pkg/tests/api/plugins/data/expectedListResp.json @@ -156,12 +156,12 @@ "path": "/public/app/plugins/datasource/azuremonitor/img/azure_monitor_cpu.png" } ], - "version": "1.0.0", + "version": "", "updated": "" }, "dependencies": { - "grafanaDependency": "", - "grafanaVersion": "5.2.x", + "grafanaDependency": ">=10.3.0", + "grafanaVersion": "*", "plugins": [] }, "latestVersion": "", diff --git a/pkg/tsdb/azuremonitor/azuremonitor.go b/pkg/tsdb/azuremonitor/azuremonitor.go index 03fdc7c0945..11320d52b91 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor.go +++ b/pkg/tsdb/azuremonitor/azuremonitor.go @@ -18,8 +18,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" "github.com/grafana/grafana-plugin-sdk-go/backend/resource/httpadapter" - "github.com/grafana/grafana/pkg/services/featuremgmt" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/azmoncredentials" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/metrics" @@ -27,16 +25,16 @@ import ( "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) -func ProvideService(cfg *setting.Cfg, httpClientProvider *httpclient.Provider, features featuremgmt.FeatureToggles) *Service { +func ProvideService(httpClientProvider *httpclient.Provider) *Service { proxy := &httpServiceProxy{} executors := map[string]azDatasourceExecutor{ - azureMonitor: &metrics.AzureMonitorDatasource{Proxy: proxy, Features: features}, + azureMonitor: &metrics.AzureMonitorDatasource{Proxy: proxy}, azureLogAnalytics: &loganalytics.AzureLogAnalyticsDatasource{Proxy: proxy}, azureResourceGraph: &resourcegraph.AzureResourceGraphDatasource{Proxy: proxy}, azureTraces: &loganalytics.AzureLogAnalyticsDatasource{Proxy: proxy}, } - im := datasource.NewInstanceManager(NewInstanceSettings(cfg, httpClientProvider, executors)) + im := datasource.NewInstanceManager(NewInstanceSettings(httpClientProvider, executors)) s := &Service{ im: im, @@ -65,9 +63,9 @@ type Service struct { resourceHandler backend.CallResourceHandler } -func getDatasourceService(ctx context.Context, settings *backend.DataSourceInstanceSettings, cfg *setting.Cfg, clientProvider *httpclient.Provider, dsInfo types.DatasourceInfo, routeName string) (types.DatasourceService, error) { +func getDatasourceService(ctx context.Context, settings *backend.DataSourceInstanceSettings, azureSettings *azsettings.AzureSettings, clientProvider *httpclient.Provider, dsInfo types.DatasourceInfo, routeName string) (types.DatasourceService, error) { route := dsInfo.Routes[routeName] - client, err := newHTTPClient(ctx, route, dsInfo, settings, cfg, clientProvider) + client, err := newHTTPClient(ctx, route, dsInfo, settings, azureSettings, clientProvider) if err != nil { return types.DatasourceService{}, err } @@ -77,7 +75,7 @@ func getDatasourceService(ctx context.Context, settings *backend.DataSourceInsta }, nil } -func NewInstanceSettings(cfg *setting.Cfg, clientProvider *httpclient.Provider, executors map[string]azDatasourceExecutor) datasource.InstanceFactoryFunc { +func NewInstanceSettings(clientProvider *httpclient.Provider, executors map[string]azDatasourceExecutor) datasource.InstanceFactoryFunc { return func(ctx context.Context, settings backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { jsonData := map[string]any{} err := json.Unmarshal(settings.JSONData, &jsonData) @@ -91,14 +89,20 @@ func NewInstanceSettings(cfg *setting.Cfg, clientProvider *httpclient.Provider, return nil, fmt.Errorf("error reading settings: %w", err) } + azureSettings, err := azsettings.ReadSettings(ctx) + if err != nil { + backend.Logger.Error("failed to read Azure settings from Grafana", "error", err.Error()) + return nil, err + } + credentials, err := azmoncredentials.FromDatasourceData(jsonData, settings.DecryptedSecureJSONData) if err != nil { return nil, fmt.Errorf("error getting credentials: %w", err) } else if credentials == nil { - credentials = azmoncredentials.GetDefaultCredentials(cfg.Azure) + credentials = azmoncredentials.GetDefaultCredentials(azureSettings) } - cloud, err := azcredentials.GetAzureCloud(cfg.Azure, credentials) + cloud, err := azcredentials.GetAzureCloud(azureSettings, credentials) if err != nil { return nil, fmt.Errorf("error getting credentials: %w", err) } @@ -120,7 +124,7 @@ func NewInstanceSettings(cfg *setting.Cfg, clientProvider *httpclient.Provider, } for routeName := range executors { - service, err := getDatasourceService(ctx, &settings, cfg, clientProvider, model, routeName) + service, err := getDatasourceService(ctx, &settings, azureSettings, clientProvider, model, routeName) if err != nil { return nil, err } diff --git a/pkg/tsdb/azuremonitor/azuremonitor_test.go b/pkg/tsdb/azuremonitor/azuremonitor_test.go index 6ccc527228d..3983682dbb3 100644 --- a/pkg/tsdb/azuremonitor/azuremonitor_test.go +++ b/pkg/tsdb/azuremonitor/azuremonitor_test.go @@ -17,7 +17,6 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" "github.com/stretchr/testify/assert" @@ -86,15 +85,9 @@ func TestNewInstanceSettings(t *testing.T) { }, } - cfg := &setting.Cfg{ - Azure: &azsettings.AzureSettings{ - Cloud: azsettings.AzurePublic, - }, - } - for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - factory := NewInstanceSettings(cfg, &httpclient.Provider{}, map[string]azDatasourceExecutor{}) + factory := NewInstanceSettings(&httpclient.Provider{}, map[string]azDatasourceExecutor{}) instance, err := factory(context.Background(), tt.settings) tt.Err(t, err) if !cmp.Equal(instance, tt.expectedModel) { diff --git a/pkg/tsdb/azuremonitor/httpclient.go b/pkg/tsdb/azuremonitor/httpclient.go index 7aef1b53e05..77ae5779579 100644 --- a/pkg/tsdb/azuremonitor/httpclient.go +++ b/pkg/tsdb/azuremonitor/httpclient.go @@ -8,10 +8,10 @@ import ( "github.com/grafana/grafana-azure-sdk-go/azcredentials" "github.com/grafana/grafana-azure-sdk-go/azhttpclient" + "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" ) @@ -21,7 +21,7 @@ type Provider interface { GetTLSConfig(...httpclient.Options) (*tls.Config, error) } -func newHTTPClient(ctx context.Context, route types.AzRoute, model types.DatasourceInfo, settings *backend.DataSourceInstanceSettings, cfg *setting.Cfg, clientProvider Provider) (*http.Client, error) { +func newHTTPClient(ctx context.Context, route types.AzRoute, model types.DatasourceInfo, settings *backend.DataSourceInstanceSettings, azureSettings *azsettings.AzureSettings, clientProvider Provider) (*http.Client, error) { clientOpts, err := settings.HTTPClientOptions(ctx) if err != nil { return nil, fmt.Errorf("error getting HTTP options: %w", err) @@ -37,7 +37,7 @@ func newHTTPClient(ctx context.Context, route types.AzRoute, model types.Datasou return nil, fmt.Errorf("unable to initialize HTTP Client: clientSecret not found") } - authOpts := azhttpclient.NewAuthOptions(cfg.Azure) + authOpts := azhttpclient.NewAuthOptions(azureSettings) authOpts.Scopes(route.Scopes) azhttpclient.AddAzureAuthentication(&clientOpts, authOpts, model.Credentials) } diff --git a/pkg/tsdb/azuremonitor/httpclient_test.go b/pkg/tsdb/azuremonitor/httpclient_test.go index 9cd4a41c0cc..d6fe1d4840c 100644 --- a/pkg/tsdb/azuremonitor/httpclient_test.go +++ b/pkg/tsdb/azuremonitor/httpclient_test.go @@ -8,10 +8,10 @@ import ( "testing" "github.com/grafana/grafana-azure-sdk-go/azcredentials" + "github.com/grafana/grafana-azure-sdk-go/azsettings" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" - "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -32,7 +32,9 @@ func TestHttpClient_AzureCredentials(t *testing.T) { }, } - cfg := &setting.Cfg{} + azureSettings := &azsettings.AzureSettings{ + Cloud: azsettings.AzurePublic, + } provider := &fakeHttpClientProvider{} t.Run("should have Azure middleware when scopes provided", func(t *testing.T) { @@ -40,7 +42,7 @@ func TestHttpClient_AzureCredentials(t *testing.T) { Scopes: []string{"https://management.azure.com/.default"}, } - _, err := newHTTPClient(context.Background(), route, model, settings, cfg, provider) + _, err := newHTTPClient(context.Background(), route, model, settings, azureSettings, provider) require.NoError(t, err) require.NotNil(t, provider.opts) @@ -53,7 +55,7 @@ func TestHttpClient_AzureCredentials(t *testing.T) { Scopes: []string{}, } - _, err := newHTTPClient(context.Background(), route, model, settings, cfg, provider) + _, err := newHTTPClient(context.Background(), route, model, settings, azureSettings, provider) require.NoError(t, err) assert.NotNil(t, provider.opts) @@ -74,7 +76,7 @@ func TestHttpClient_AzureCredentials(t *testing.T) { "GrafanaHeader": "GrafanaValue", "AzureHeader": "AzureValue", } - _, err := newHTTPClient(context.Background(), route, model, settings, cfg, provider) + _, err := newHTTPClient(context.Background(), route, model, settings, azureSettings, provider) require.NoError(t, err) assert.NotNil(t, provider.opts) diff --git a/pkg/tsdb/azuremonitor/macros/macros.go b/pkg/tsdb/azuremonitor/macros/macros.go index 75f15871757..11304b7416d 100644 --- a/pkg/tsdb/azuremonitor/macros/macros.go +++ b/pkg/tsdb/azuremonitor/macros/macros.go @@ -10,8 +10,8 @@ import ( "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery" + azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/grafana/grafana/pkg/tsdb/intervalv2" ) const rsIdentifier = `__(timeFilter|timeFrom|timeTo|interval|contains|escapeMulti)` @@ -126,7 +126,7 @@ func (m *kqlMacroEngine) evaluateMacro(name string, defaultTimeField string, arg if dsInterval, ok = dsInfo.JSONData["interval"].(string); !ok { dsInterval = "" } - it, err = intervalv2.GetIntervalFrom(dsInterval, queryInterval.Interval, queryInterval.IntervalMs, defaultInterval) + it, err = azTime.GetIntervalFrom(dsInterval, queryInterval.Interval, queryInterval.IntervalMs, defaultInterval) if err != nil { it = defaultInterval } diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index a58e3d4abb9..5e1eb376804 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -19,7 +19,6 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" - "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/loganalytics" azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time" @@ -28,8 +27,7 @@ import ( // AzureMonitorDatasource calls the Azure Monitor API - one of the four API's supported type AzureMonitorDatasource struct { - Proxy types.ServiceProxy - Features featuremgmt.FeatureToggles + Proxy types.ServiceProxy } var ( diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go index f09fa76c177..1e372632be9 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go @@ -23,9 +23,10 @@ import ( "github.com/grafana/grafana/pkg/tsdb/azuremonitor/testdata" azTime "github.com/grafana/grafana/pkg/tsdb/azuremonitor/time" "github.com/grafana/grafana/pkg/tsdb/azuremonitor/types" - "github.com/grafana/grafana/pkg/util" ) +func Pointer[T any](v T) *T { return &v } + func TestAzureMonitorBuildQueries(t *testing.T) { datasource := &AzureMonitorDatasource{} dsInfo := types.DatasourceInfo{ @@ -117,7 +118,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob eq '*'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "legacy query without resourceURI and has dimensionFilter*s* property with two dimensions", @@ -130,7 +131,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob eq '*' and tier eq '*'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "legacy query without resourceURI and has a dimension filter without specifying a top", @@ -155,7 +156,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob ne 'test'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "has dimensionFilter*s* property with startsWith operator", @@ -168,7 +169,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob sw 'test'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A3%2C%22values%22%3A%5B%22test%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A3%2C%22values%22%3A%5B%22test%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "correctly sets dimension operator to eq (irrespective of operator) when filter value is '*'", @@ -181,7 +182,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob eq '*' and tier eq '*'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "correctly constructs target when multiple filter values are provided for the 'eq' operator", @@ -194,7 +195,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob eq 'test' or blob eq 'test2'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A0%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A0%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "correctly constructs target when multiple filter values are provided for ne 'eq' operator", @@ -207,7 +208,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedInterval: "PT1M", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedParamFilter: "blob ne 'test' and blob ne 'test2'", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, { name: "Includes a region", @@ -257,7 +258,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) { expectedURL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/microsoft.insights/metrics", azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z&top=30", expectedBodyFilter: "(Microsoft.ResourceId eq '/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm' or Microsoft.ResourceId eq '/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/rg2/providers/Microsoft.Compute/virtualMachines/vm2') and (blob ne 'test' and blob ne 'test2')", - expectedPortalURL: util.Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), + expectedPortalURL: Pointer("http://ds/#blade/Microsoft_Azure_MonitoringMetrics/Metrics.ReactView/Referer/MetricsExplorer/TimeContext/%7B%22absolute%22%3A%7B%22startTime%22%3A%222018-03-15T13%3A00%3A00Z%22%2C%22endTime%22%3A%222018-03-15T13%3A34%3A00Z%22%7D%7D/ChartDefinition/%7B%22v2charts%22%3A%5B%7B%22filterCollection%22%3A%7B%22filters%22%3A%5B%7B%22key%22%3A%22blob%22%2C%22operator%22%3A1%2C%22values%22%3A%5B%22test%22%2C%22test2%22%5D%7D%5D%7D%2C%22grouping%22%3A%7B%22dimension%22%3A%22blob%22%2C%22sort%22%3A2%2C%22top%22%3A10%7D%2C%22metrics%22%3A%5B%7B%22resourceMetadata%22%3A%7B%22id%22%3A%22%2Fsubscriptions%2F12345678-aaaa-bbbb-cccc-123456789abc%2FresourceGroups%2Fgrafanastaging%2Fproviders%2FMicrosoft.Compute%2FvirtualMachines%2Fgrafana%22%7D%2C%22name%22%3A%22Percentage%20CPU%22%2C%22aggregationType%22%3A4%2C%22namespace%22%3A%22Microsoft.Compute%2FvirtualMachines%22%2C%22metricVisualization%22%3A%7B%22displayName%22%3A%22Percentage%20CPU%22%2C%22resourceDisplayName%22%3A%22grafana%22%7D%7D%5D%7D%5D%7D"), }, } diff --git a/pkg/tsdb/azuremonitor/standalone/datasource.go b/pkg/tsdb/azuremonitor/standalone/datasource.go new file mode 100644 index 00000000000..082ee29ac32 --- /dev/null +++ b/pkg/tsdb/azuremonitor/standalone/datasource.go @@ -0,0 +1,38 @@ +package main + +import ( + "context" + + "github.com/grafana/grafana-plugin-sdk-go/backend" + "github.com/grafana/grafana-plugin-sdk-go/backend/httpclient" + "github.com/grafana/grafana-plugin-sdk-go/backend/instancemgmt" + azuremonitor "github.com/grafana/grafana/pkg/tsdb/azuremonitor" +) + +var ( + _ backend.QueryDataHandler = (*Datasource)(nil) + _ backend.CheckHealthHandler = (*Datasource)(nil) + _ backend.CallResourceHandler = (*Datasource)(nil) +) + +func NewDatasource(context.Context, backend.DataSourceInstanceSettings) (instancemgmt.Instance, error) { + return &Datasource{ + Service: azuremonitor.ProvideService(httpclient.NewProvider()), + }, nil +} + +type Datasource struct { + Service *azuremonitor.Service +} + +func (d *Datasource) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { + return d.Service.QueryData(ctx, req) +} + +func (d *Datasource) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { + return d.Service.CallResource(ctx, req, sender) +} + +func (d *Datasource) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { + return d.Service.CheckHealth(ctx, req) +} diff --git a/pkg/tsdb/azuremonitor/standalone/main.go b/pkg/tsdb/azuremonitor/standalone/main.go new file mode 100644 index 00000000000..e1af6def7a7 --- /dev/null +++ b/pkg/tsdb/azuremonitor/standalone/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "os" + + "github.com/grafana/grafana-plugin-sdk-go/backend/datasource" + "github.com/grafana/grafana-plugin-sdk-go/backend/log" +) + +func main() { + // Start listening to requests sent from Grafana. This call is blocking so + // it won't finish until Grafana shuts down the process or the plugin choose + // to exit by itself using os.Exit. Manage automatically manages life cycle + // of datasource instances. It accepts datasource instance factory as first + // argument. This factory will be automatically called on incoming request + // from Grafana to create different instances of SampleDatasource (per datasource + // ID). When datasource configuration changed Dispose method will be called and + // new datasource instance created using NewSampleDatasource factory. + if err := datasource.Manage("grafana-azure-monitor-datasource", NewDatasource, datasource.ManageOpts{}); err != nil { + log.DefaultLogger.Error(err.Error()) + os.Exit(1) + } +} diff --git a/pkg/tsdb/azuremonitor/time/interval.go b/pkg/tsdb/azuremonitor/time/interval.go new file mode 100644 index 00000000000..ed8a7587c8c --- /dev/null +++ b/pkg/tsdb/azuremonitor/time/interval.go @@ -0,0 +1,93 @@ +// Copied from https://github.com/grafana/grafana/blob/main/pkg/tsdb/intervalv2/intervalv2.go +// We're copying this to not block ourselves from decoupling until the conversation here is resolved +// https://raintank-corp.slack.com/archives/C05QFJUHUQ6/p1700064431005089 +package time + +import ( + "fmt" + "regexp" + "strings" + "time" + + "github.com/grafana/grafana-plugin-sdk-go/backend/gtime" +) + +var ( + year = time.Hour * 24 * 365 + day = time.Hour * 24 +) + +// GetIntervalFrom returns the minimum interval. +// dsInterval is the string representation of data source min interval, if configured. +// queryInterval is the string representation of query interval (min interval), e.g. "10ms" or "10s". +// queryIntervalMS is a pre-calculated numeric representation of the query interval in milliseconds. +func GetIntervalFrom(dsInterval, queryInterval string, queryIntervalMS int64, defaultInterval time.Duration) (time.Duration, error) { + // Apparently we are setting default value of queryInterval to 0s now + interval := queryInterval + if interval == "0s" { + interval = "" + } + if interval == "" { + if queryIntervalMS != 0 { + return time.Duration(queryIntervalMS) * time.Millisecond, nil + } + } + if interval == "" && dsInterval != "" { + interval = dsInterval + } + if interval == "" { + return defaultInterval, nil + } + + parsedInterval, err := ParseIntervalStringToTimeDuration(interval) + if err != nil { + return time.Duration(0), err + } + + return parsedInterval, nil +} + +func ParseIntervalStringToTimeDuration(interval string) (time.Duration, error) { + formattedInterval := strings.Replace(strings.Replace(interval, "<", "", 1), ">", "", 1) + isPureNum, err := regexp.MatchString(`^\d+$`, formattedInterval) + if err != nil { + return time.Duration(0), err + } + if isPureNum { + formattedInterval += "s" + } + parsedInterval, err := gtime.ParseDuration(formattedInterval) + if err != nil { + return time.Duration(0), err + } + return parsedInterval, nil +} + +// FormatDuration converts a duration into the kbn format e.g. 1m 2h or 3d +func FormatDuration(inter time.Duration) string { + if inter >= year { + return fmt.Sprintf("%dy", inter/year) + } + + if inter >= day { + return fmt.Sprintf("%dd", inter/day) + } + + if inter >= time.Hour { + return fmt.Sprintf("%dh", inter/time.Hour) + } + + if inter >= time.Minute { + return fmt.Sprintf("%dm", inter/time.Minute) + } + + if inter >= time.Second { + return fmt.Sprintf("%ds", inter/time.Second) + } + + if inter >= time.Millisecond { + return fmt.Sprintf("%dms", inter/time.Millisecond) + } + + return "1ms" +} diff --git a/pkg/tsdb/azuremonitor/time/time-grain.go b/pkg/tsdb/azuremonitor/time/time-grain.go index 6c69642482c..f7c5b1e4d1c 100644 --- a/pkg/tsdb/azuremonitor/time/time-grain.go +++ b/pkg/tsdb/azuremonitor/time/time-grain.go @@ -5,8 +5,6 @@ import ( "strconv" "strings" "time" - - "github.com/grafana/grafana/pkg/tsdb/intervalv2" ) // TimeGrain handles conversions between @@ -17,7 +15,7 @@ var ( ) func CreateISO8601DurationFromIntervalMS(it int64) (string, error) { - formatted := intervalv2.FormatDuration(time.Duration(it) * time.Millisecond) + formatted := FormatDuration(time.Duration(it) * time.Millisecond) if strings.Contains(formatted, "ms") { return "PT1M", nil diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 90b023d14da..77e88d82562 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -32,7 +32,7 @@ const testDataDSPlugin = async () => const cloudMonitoringPlugin = async () => await import(/* webpackChunkName: "cloudMonitoringPlugin" */ 'app/plugins/datasource/cloud-monitoring/module'); const azureMonitorPlugin = async () => - await import(/* webpackChunkName: "azureMonitorPlugin" */ 'app/plugins/datasource/azuremonitor/module'); + await import(/* webpackChunkName: "azureMonitorPlugin" */ '@grafana-plugins/grafana-azure-monitor-datasource/module'); const tempoPlugin = async () => await import(/* webpackChunkName: "tempoPlugin" */ 'app/plugins/datasource/tempo/module'); const alertmanagerPlugin = async () => diff --git a/public/app/plugins/datasource/azuremonitor/CHANGELOG.md b/public/app/plugins/datasource/azuremonitor/CHANGELOG.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts b/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts index 54c0c46ef84..a7a90aefdcb 100644 --- a/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts +++ b/public/app/plugins/datasource/azuremonitor/__mocks__/variables.ts @@ -1,4 +1,4 @@ -import { BaseVariableModel, CustomVariableModel, LoadingState, VariableHide } from '@grafana/data'; +import { BaseVariableModel, CustomVariableModel, LoadingState, VariableHide, VariableOption } from '@grafana/data'; const initialVariableModelState: BaseVariableModel = { id: '00000000-0000-0000-0000-000000000000', @@ -63,3 +63,14 @@ export const multiVariable: CustomVariableModel = { hide: VariableHide.dontHide, type: 'custom', }; + +export const initialCustomVariableModelState: CustomVariableModel = { + ...initialVariableModelState, + type: 'custom', + multi: false, + includeAll: false, + allValue: null, + query: '', + options: [], + current: {} as VariableOption, +}; diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/DimensionFields.test.tsx b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/DimensionFields.test.tsx index 913a37be6f6..f0ea59df1ee 100644 --- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/DimensionFields.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/DimensionFields.test.tsx @@ -2,11 +2,11 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; import { openMenu } from 'react-select-event'; -import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import createMockDatasource from '../../__mocks__/datasource'; import createMockPanelData from '../../__mocks__/panelData'; import createMockQuery from '../../__mocks__/query'; +import { selectOptionInTest } from '../../utils/testUtils'; import DimensionFields from './DimensionFields'; import { appendDimensionFilter, setDimensionFilterValue } from './setQueryValue'; diff --git a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx index 03c0d4dddf2..b5bfa0f27b1 100644 --- a/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/MetricsQueryEditor/MetricsQueryEditor.test.tsx @@ -1,7 +1,6 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import createMockDatasource from '../../__mocks__/datasource'; import { createMockInstanceSetttings } from '../../__mocks__/instanceSettings'; @@ -14,6 +13,7 @@ import { } from '../../__mocks__/resourcePickerRows'; import { selectors } from '../../e2e/selectors'; import ResourcePickerData from '../../resourcePicker/resourcePickerData'; +import { selectOptionInTest } from '../../utils/testUtils'; import MetricsQueryEditor from './MetricsQueryEditor'; diff --git a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx index 18621f8bbf4..629634f8e6e 100644 --- a/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/QueryEditor/QueryEditor.test.tsx @@ -1,6 +1,5 @@ import { render, screen, waitFor } from '@testing-library/react'; import React from 'react'; -import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import * as ui from '@grafana/ui'; @@ -9,6 +8,7 @@ import { invalidNamespaceError } from '../../__mocks__/errors'; import createMockQuery from '../../__mocks__/query'; import { selectors } from '../../e2e/selectors'; import { AzureQueryType } from '../../types'; +import { selectOptionInTest } from '../../utils/testUtils'; import QueryEditor from './QueryEditor'; diff --git a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx index 8af4599abd3..388969bcfc9 100644 --- a/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx +++ b/public/app/plugins/datasource/azuremonitor/components/TracesQueryEditor/Filters.test.tsx @@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event'; import { UserEvent } from '@testing-library/user-event/dist/types/setup/setup'; import React from 'react'; import { of } from 'rxjs'; -import { selectOptionInTest } from 'test/helpers/selectOptionInTest'; import { CoreApp } from '@grafana/data'; @@ -12,6 +11,7 @@ import createMockQuery from '../../__mocks__/query'; import { AzureQueryType } from '../../dataquery.gen'; import Datasource from '../../datasource'; import { AzureMonitorQuery } from '../../types'; +import { selectOptionInTest } from '../../utils/testUtils'; import Filters from './Filters'; import { setFilters } from './setQueryValue'; diff --git a/public/app/plugins/datasource/azuremonitor/package.json b/public/app/plugins/datasource/azuremonitor/package.json new file mode 100644 index 00000000000..76274b41914 --- /dev/null +++ b/public/app/plugins/datasource/azuremonitor/package.json @@ -0,0 +1,50 @@ +{ + "name": "@grafana-plugins/grafana-azure-monitor-datasource", + "description": "Grafana data source for Azure Monitor", + "private": true, + "version": "10.3.0-pre", + "dependencies": { + "@emotion/css": "11.11.2", + "@grafana/data": "10.3.0-pre", + "@grafana/experimental": "1.7.4", + "@grafana/runtime": "10.3.0-pre", + "@grafana/schema": "10.3.0-pre", + "@grafana/ui": "10.3.0-pre", + "@kusto/monaco-kusto": "^7.4.0", + "fast-deep-equal": "^3.1.3", + "i18next": "^22.0.0", + "immer": "10.0.2", + "lodash": "4.17.21", + "monaco-editor": "0.34.0", + "prismjs": "1.29.0", + "react": "18.2.0", + "react-use": "17.4.0", + "rxjs": "7.8.1", + "tslib": "2.6.0" + }, + "devDependencies": { + "@grafana/e2e-selectors": "10.3.0-pre", + "@grafana/plugin-configs": "10.3.0-pre", + "@testing-library/react": "14.0.0", + "@testing-library/user-event": "14.5.1", + "@types/jest": "29.5.4", + "@types/lodash": "4.14.195", + "@types/node": "20.8.10", + "@types/prismjs": "1.26.0", + "@types/react": "18.2.15", + "@types/testing-library__jest-dom": "5.14.8", + "react-select-event": "5.5.1", + "ts-node": "10.9.1", + "typescript": "5.2.2", + "webpack": "5.89.0" + }, + "peerDependencies": { + "@grafana/runtime": "*" + }, + "scripts": { + "build": "webpack -c ./webpack.config.ts --env production", + "build:commit": "webpack -c ./webpack.config.ts --env production --env commit=$(git rev-parse --short HEAD)", + "dev": "webpack -w -c ./webpack.config.ts --env development" + }, + "packageManager": "yarn@3.6.0" +} diff --git a/public/app/plugins/datasource/azuremonitor/plugin.json b/public/app/plugins/datasource/azuremonitor/plugin.json index 04c933839f0..9e1d8c49be3 100644 --- a/public/app/plugins/datasource/azuremonitor/plugin.json +++ b/public/app/plugins/datasource/azuremonitor/plugin.json @@ -77,7 +77,7 @@ }, { "type": "dashboard", "name": "Azure / Resources Overview", "path": "dashboards/arg.json" } ], - + "executable": "gpx_azuremonitor", "info": { "description": "Data source for Microsoft Azure Monitor & Application Insights", "author": { @@ -98,11 +98,11 @@ { "name": "Azure Monitor Network", "path": "img/azure_monitor_network.png" }, { "name": "Azure Monitor CPU", "path": "img/azure_monitor_cpu.png" } ], - "version": "1.0.0" + "version": "%VERSION%" }, "dependencies": { - "grafanaVersion": "5.2.x", + "grafanaDependency": ">=10.3.0", "plugins": [] }, diff --git a/public/app/plugins/datasource/azuremonitor/tsconfig.json b/public/app/plugins/datasource/azuremonitor/tsconfig.json new file mode 100644 index 00000000000..7daf2ee8aba --- /dev/null +++ b/public/app/plugins/datasource/azuremonitor/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "@grafana/plugin-configs/tsconfig.json", + "include": ["."] +} diff --git a/public/app/plugins/datasource/azuremonitor/utils/common.test.ts b/public/app/plugins/datasource/azuremonitor/utils/common.test.ts index 066d63a8123..0c55057fbfb 100644 --- a/public/app/plugins/datasource/azuremonitor/utils/common.test.ts +++ b/public/app/plugins/datasource/azuremonitor/utils/common.test.ts @@ -1,4 +1,4 @@ -import { initialCustomVariableModelState } from 'app/features/variables/custom/reducer'; +import { initialCustomVariableModelState } from '../__mocks__/variables'; import { hasOption, interpolateVariable } from './common'; diff --git a/public/app/plugins/datasource/azuremonitor/utils/testUtils.ts b/public/app/plugins/datasource/azuremonitor/utils/testUtils.ts new file mode 100644 index 00000000000..62bd8c16949 --- /dev/null +++ b/public/app/plugins/datasource/azuremonitor/utils/testUtils.ts @@ -0,0 +1,8 @@ +import { waitFor } from '@testing-library/react'; +import { select } from 'react-select-event'; + +// Used to select an option or options from a Select in unit tests +export const selectOptionInTest = async ( + input: HTMLElement, + optionOrOptions: string | RegExp | Array +) => await waitFor(() => select(input, optionOrOptions, { container: document.body })); diff --git a/public/app/plugins/datasource/azuremonitor/webpack.config.ts b/public/app/plugins/datasource/azuremonitor/webpack.config.ts new file mode 100644 index 00000000000..4da5a990cfa --- /dev/null +++ b/public/app/plugins/datasource/azuremonitor/webpack.config.ts @@ -0,0 +1,3 @@ +import config from '@grafana/plugin-configs/webpack.config'; + +export default config; diff --git a/yarn.lock b/yarn.lock index 74e7cef3d7c..5820a940f83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2872,6 +2872,46 @@ __metadata: languageName: node linkType: hard +"@grafana-plugins/grafana-azure-monitor-datasource@workspace:*, @grafana-plugins/grafana-azure-monitor-datasource@workspace:public/app/plugins/datasource/azuremonitor": + version: 0.0.0-use.local + resolution: "@grafana-plugins/grafana-azure-monitor-datasource@workspace:public/app/plugins/datasource/azuremonitor" + dependencies: + "@emotion/css": "npm:11.11.2" + "@grafana/data": "npm:10.3.0-pre" + "@grafana/e2e-selectors": "npm:10.3.0-pre" + "@grafana/experimental": "npm:1.7.4" + "@grafana/plugin-configs": "npm:10.3.0-pre" + "@grafana/runtime": "npm:10.3.0-pre" + "@grafana/schema": "npm:10.3.0-pre" + "@grafana/ui": "npm:10.3.0-pre" + "@kusto/monaco-kusto": "npm:^7.4.0" + "@testing-library/react": "npm:14.0.0" + "@testing-library/user-event": "npm:14.5.1" + "@types/jest": "npm:29.5.4" + "@types/lodash": "npm:4.14.195" + "@types/node": "npm:20.8.10" + "@types/prismjs": "npm:1.26.0" + "@types/react": "npm:18.2.15" + "@types/testing-library__jest-dom": "npm:5.14.8" + fast-deep-equal: "npm:^3.1.3" + i18next: "npm:^22.0.0" + immer: "npm:10.0.2" + lodash: "npm:4.17.21" + monaco-editor: "npm:0.34.0" + prismjs: "npm:1.29.0" + react: "npm:18.2.0" + react-select-event: "npm:5.5.1" + react-use: "npm:17.4.0" + rxjs: "npm:7.8.1" + ts-node: "npm:10.9.1" + tslib: "npm:2.6.0" + typescript: "npm:5.2.2" + webpack: "npm:5.89.0" + peerDependencies: + "@grafana/runtime": "*" + languageName: unknown + linkType: soft + "@grafana-plugins/grafana-testdata-datasource@workspace:*, @grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource": version: 0.0.0-use.local resolution: "@grafana-plugins/grafana-testdata-datasource@workspace:public/app/plugins/datasource/grafana-testdata-datasource" @@ -17363,6 +17403,7 @@ __metadata: "@fingerprintjs/fingerprintjs": "npm:^3.4.2" "@floating-ui/react": "npm:0.26.4" "@glideapps/glide-data-grid": "npm:^5.2.1" + "@grafana-plugins/grafana-azure-monitor-datasource": "workspace:*" "@grafana-plugins/grafana-testdata-datasource": "workspace:*" "@grafana-plugins/parca": "workspace:*" "@grafana/aws-sdk": "npm:0.3.1"