diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go index 9b1b670d423..c2af4f9518e 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource.go @@ -107,6 +107,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe } azureURL := BuildSubscriptionMetricsURL(queryJSONModel.Subscription) + filterInBody := true if azJSONModel.Region != "" { params.Add("region", azJSONModel.Region) } else { @@ -121,6 +122,8 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe ResourceName: azJSONModel.ResourceName, } azureURL = ub.BuildMetricsURL() + // POST requests are only supported at the subscription level + filterInBody = false } // old model @@ -174,15 +177,22 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe logger.Debug("Azuremonitor request", "params", params) } - azureMonitorQueries = append(azureMonitorQueries, &types.AzureMonitorQuery{ + query := &types.AzureMonitorQuery{ URL: azureURL, Target: target, Params: params, RefID: query.RefID, Alias: alias, TimeRange: query.TimeRange, - Filter: filterString, - }) + } + if filterString != "" { + if filterInBody { + query.BodyFilter = filterString + } else { + query.Params.Add("$filter", filterString) + } + } + azureMonitorQueries = append(azureMonitorQueries, query) } return azureMonitorQueries, nil @@ -200,9 +210,9 @@ func (e *AzureMonitorDatasource) executeQuery(ctx context.Context, logger log.Lo req.URL.Path = path.Join(req.URL.Path, query.URL) req.URL.RawQuery = query.Params.Encode() - if query.Filter != "" { + if query.BodyFilter != "" { req.Method = http.MethodPost - req.Body = io.NopCloser(strings.NewReader(fmt.Sprintf(`{"filter": "%s"}`, query.Filter))) + req.Body = io.NopCloser(strings.NewReader(fmt.Sprintf(`{"filter": "%s"}`, query.BodyFilter))) } ctx, span := tracer.Start(ctx, "azuremonitor query") diff --git a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go index 722421cf3d7..0c1707fc15e 100644 --- a/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go +++ b/pkg/tsdb/azuremonitor/metrics/azuremonitor-datasource_test.go @@ -15,6 +15,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/grafana/grafana-plugin-sdk-go/backend" "github.com/grafana/grafana-plugin-sdk-go/data" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ptr "github.com/xorcare/pointer" @@ -208,6 +209,19 @@ func TestAzureMonitorBuildQueries(t *testing.T) { azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines®ion=westus×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z", expectedURL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/microsoft.insights/metrics", }, + { + name: "Includes a region and a filter", + azureMonitorVariedProperties: map[string]interface{}{ + "timeGrain": "PT1M", + "top": "10", + "region": "westus", + "resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}}, + }, + expectedInterval: "PT1M", + azureMonitorQueryTarget: "aggregation=Average&api-version=2021-05-01&interval=PT1M&metricnames=Percentage+CPU&metricnamespace=Microsoft.Compute%2FvirtualMachines®ion=westus×pan=2018-03-15T13%3A00%3A00Z%2F2018-03-15T13%3A34%3A00Z", + expectedURL: "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/providers/microsoft.insights/metrics", + expectedFilter: "Microsoft.ResourceId eq '/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm'", + }, { name: "includes a resource as a filter", azureMonitorVariedProperties: map[string]interface{}{ @@ -266,6 +280,9 @@ func TestAzureMonitorBuildQueries(t *testing.T) { }, } + queries, err := datasource.buildQueries(log.New("test"), tsdbQuery, dsInfo) + require.NoError(t, err) + azureMonitorQuery := &types.AzureMonitorQuery{ URL: tt.expectedURL, Target: tt.azureMonitorQueryTarget, @@ -275,14 +292,22 @@ func TestAzureMonitorBuildQueries(t *testing.T) { From: fromStart, To: fromStart.Add(34 * time.Minute), }, - Filter: tt.expectedFilter, + } + if tt.azureMonitorVariedProperties["region"] != nil { + // If the region is included, the filter will be added in the Body of the request + azureMonitorQuery.BodyFilter = tt.expectedFilter + } else { + // In other case, the filter will be added in the URL + if tt.expectedFilter != "" { + assert.Equal(t, tt.expectedFilter, queries[0].Params.Get("$filter")) + } else { + assert.Equal(t, false, queries[0].Params.Has("$filter")) + } } if azureMonitorQuery.URL == "" { azureMonitorQuery.URL = "/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana/providers/microsoft.insights/metrics" } - queries, err := datasource.buildQueries(log.New("test"), tsdbQuery, dsInfo) - require.NoError(t, err) if diff := cmp.Diff(azureMonitorQuery, queries[0], cmpopts.IgnoreUnexported(simplejson.Json{}), cmpopts.IgnoreFields(types.AzureMonitorQuery{}, "Params")); diff != "" { t.Errorf("Result mismatch (-want +got):\n%s", diff) } diff --git a/pkg/tsdb/azuremonitor/types/types.go b/pkg/tsdb/azuremonitor/types/types.go index f7b8a1b04d5..915e8fcef76 100644 --- a/pkg/tsdb/azuremonitor/types/types.go +++ b/pkg/tsdb/azuremonitor/types/types.go @@ -59,13 +59,13 @@ type DatasourceInfo struct { // AzureMonitorQuery is the query for all the services as they have similar queries // with a url, a querystring and an alias field type AzureMonitorQuery struct { - URL string - Target string - Params url.Values - RefID string - Alias string - TimeRange backend.TimeRange - Filter string + URL string + Target string + Params url.Values + RefID string + Alias string + TimeRange backend.TimeRange + BodyFilter string } // AzureMonitorResponse is the json response from the Azure Monitor API