Azure Monitor: allow metrics call to use resource uri (#46858)

* Azure Monitor: allow metrics call to use resource uri

* test case when only resource uri is provided

* remove logs

* Rename json field name from resource to resourceUri

* Group legacy URL builder params test cases

* move comment to the correct position

* Add clarifications in comments

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>

Co-authored-by: Sarah Zinger <sarah.zinger@grafana.com>
This commit is contained in:
Kevin Yu 2022-03-31 08:20:29 -07:00 committed by GitHub
parent 65fdc51052
commit 1a4b1184bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 126 additions and 78 deletions

View File

@ -81,13 +81,15 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
urlComponents["resourceName"] = azJSONModel.ResourceName
ub := urlBuilder{
ResourceURI: azJSONModel.ResourceURI,
// Legacy, used to reconstruct resource URI if it's not present
DefaultSubscription: dsInfo.Settings.SubscriptionId,
Subscription: queryJSONModel.Subscription,
ResourceGroup: queryJSONModel.AzureMonitor.ResourceGroup,
ResourceGroup: azJSONModel.ResourceGroup,
MetricDefinition: azJSONModel.MetricDefinition,
ResourceName: azJSONModel.ResourceName,
}
azureURL := ub.Build()
azureURL := ub.BuildMetricsURL()
alias := azJSONModel.Alias

View File

@ -7,6 +7,10 @@ import (
// urlBuilder builds the URL for calling the Azure Monitor API
type urlBuilder struct {
ResourceURI string
// Following fields will be deprecated in grafana 9 and will not included in new queries.
// For backwards compat, we recreate the ResourceURI using these fields
DefaultSubscription string
Subscription string
ResourceGroup string
@ -14,26 +18,39 @@ type urlBuilder struct {
ResourceName string
}
// Build checks the metric definition property to see which form of the url
// BuildMetricsURL checks the metric definition property to see which form of the url
// should be returned
func (ub *urlBuilder) Build() string {
subscription := ub.Subscription
func (params *urlBuilder) BuildMetricsURL() string {
resourceURI := params.ResourceURI
if ub.Subscription == "" {
subscription = ub.DefaultSubscription
// Prior to Grafana 9, we had a legacy query object rather than a resourceURI, so we manually create the resource URI
if resourceURI == "" {
subscription := params.Subscription
if params.Subscription == "" {
subscription = params.DefaultSubscription
}
metricDefinitionArray := strings.Split(params.MetricDefinition, "/")
resourceNameArray := strings.Split(params.ResourceName, "/")
provider := metricDefinitionArray[0]
metricDefinitionArray = metricDefinitionArray[1:]
urlArray := []string{
subscription,
"resourceGroups",
params.ResourceGroup,
"providers",
provider,
}
for i := range metricDefinitionArray {
urlArray = append(urlArray, metricDefinitionArray[i])
urlArray = append(urlArray, resourceNameArray[i])
}
resourceURI = strings.Join(urlArray[:], "/")
}
metricDefinitionArray := strings.Split(ub.MetricDefinition, "/")
resourceNameArray := strings.Split(ub.ResourceName, "/")
provider := metricDefinitionArray[0]
metricDefinitionArray = metricDefinitionArray[1:]
urlArray := []string{subscription, "resourceGroups", ub.ResourceGroup, "providers", provider}
for i := range metricDefinitionArray {
urlArray = append(urlArray, metricDefinitionArray[i])
urlArray = append(urlArray, resourceNameArray[i])
}
return fmt.Sprintf("%s/providers/microsoft.insights/metrics", strings.Join(urlArray[:], "/"))
return fmt.Sprintf("%s/providers/microsoft.insights/metrics", resourceURI)
}

View File

@ -8,65 +8,89 @@ import (
func TestURLBuilder(t *testing.T) {
t.Run("AzureMonitor URL Builder", func(t *testing.T) {
t.Run("when metric definition is in the short form", func(t *testing.T) {
t.Run("when only resource uri is provided it returns resource/uri/providers/microsoft.insights/metrics", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Compute/virtualMachines",
ResourceName: "rn",
ResourceURI: "resource/uri",
}
url := ub.Build()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metrics")
url := ub.BuildMetricsURL()
require.Equal(t, url, "resource/uri/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is in the short form and a subscription is defined", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
Subscription: "specified-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Compute/virtualMachines",
ResourceName: "rn",
}
url := ub.Build()
require.Equal(t, url, "specified-sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/blobServices",
ResourceName: "rn1/default",
}
url := ub.Build()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.Storage/storageAccounts/fileServices", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/fileServices",
ResourceName: "rn1/default",
}
url := ub.Build()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes", func(t *testing.T) {
t.Run("when resource uri and legacy fields are provided the legacy fields are ignored", func(t *testing.T) {
ub := &urlBuilder{
ResourceURI: "resource/uri",
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.NetApp/netAppAccounts/capacityPools/volumes",
ResourceName: "rn1/rn2/rn3",
}
url := ub.Build()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/providers/microsoft.insights/metrics")
url := ub.BuildMetricsURL()
require.Equal(t, url, "resource/uri/providers/microsoft.insights/metrics")
})
t.Run("Legacy URL Builder params", func(t *testing.T) {
t.Run("when metric definition is in the short form", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Compute/virtualMachines",
ResourceName: "rn",
}
url := ub.BuildMetricsURL()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is in the short form and a subscription is defined", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
Subscription: "specified-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Compute/virtualMachines",
ResourceName: "rn",
}
url := ub.BuildMetricsURL()
require.Equal(t, url, "specified-sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/rn/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/blobServices",
ResourceName: "rn1/default",
}
url := ub.BuildMetricsURL()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.Storage/storageAccounts/fileServices", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.Storage/storageAccounts/fileServices",
ResourceName: "rn1/default",
}
url := ub.BuildMetricsURL()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/providers/microsoft.insights/metrics")
})
t.Run("when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes", func(t *testing.T) {
ub := &urlBuilder{
DefaultSubscription: "default-sub",
ResourceGroup: "rg",
MetricDefinition: "Microsoft.NetApp/netAppAccounts/capacityPools/volumes",
ResourceName: "rn1/rn2/rn3",
}
url := ub.BuildMetricsURL()
require.Equal(t, url, "default-sub/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/providers/microsoft.insights/metrics")
})
})
})
}

View File

@ -109,21 +109,26 @@ type AzureResponseTable struct {
// AzureMonitorJSONQuery is the frontend JSON query model for an Azure Monitor query.
type AzureMonitorJSONQuery struct {
AzureMonitor struct {
Aggregation string `json:"aggregation"`
Alias string `json:"alias"`
ResourceURI string `json:"resourceUri"`
MetricNamespace string `json:"metricNamespace"`
MetricName string `json:"metricName"`
Aggregation string `json:"aggregation"`
Alias string `json:"alias"`
DimensionFilters []AzureMonitorDimensionFilter `json:"dimensionFilters"` // new model
TimeGrain string `json:"timeGrain"`
Top string `json:"top"`
// Legecy "resource" fields from before the resource picker provided just a single ResourceURI
// These are used for pre-resource picker queries to reconstruct a resource URI
MetricDefinition string `json:"metricDefinition"`
ResourceGroup string `json:"resourceGroup"`
ResourceName string `json:"resourceName"`
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
Dimension string `json:"dimension"` // old model
DimensionFilter string `json:"dimensionFilter"` // old model
Format string `json:"format"`
MetricDefinition string `json:"metricDefinition"`
MetricName string `json:"metricName"`
MetricNamespace string `json:"metricNamespace"`
ResourceGroup string `json:"resourceGroup"`
ResourceName string `json:"resourceName"`
TimeGrain string `json:"timeGrain"`
Top string `json:"top"`
DimensionFilters []AzureMonitorDimensionFilter `json:"dimensionFilters"` // new model
} `json:"azureMonitor"`
Subscription string `json:"subscription"`
}