mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Azuremonitor schematize backend (#69822)
* metrics schematize * metrics now use schemas * logs schema * remove unused code * fix tests
This commit is contained in:
parent
d29124214a
commit
8d37d8f60b
@ -89,7 +89,7 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
resources := []string{}
|
||||
var resourceOrWorkspace string
|
||||
var queryString string
|
||||
var resultFormat string
|
||||
var resultFormat dataquery.ResultFormat
|
||||
traceExploreQuery := ""
|
||||
traceParentExploreQuery := ""
|
||||
traceLogsExploreQuery := ""
|
||||
@ -103,7 +103,9 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
azureLogAnalyticsTarget := queryJSONModel.AzureLogAnalytics
|
||||
logger.Debug("AzureLogAnalytics", "target", azureLogAnalyticsTarget)
|
||||
|
||||
resultFormat = azureLogAnalyticsTarget.ResultFormat
|
||||
if azureLogAnalyticsTarget.ResultFormat != nil {
|
||||
resultFormat = *azureLogAnalyticsTarget.ResultFormat
|
||||
}
|
||||
if resultFormat == "" {
|
||||
resultFormat = types.TimeSeries
|
||||
}
|
||||
@ -115,14 +117,16 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
if len(azureLogAnalyticsTarget.Resources) > 0 {
|
||||
resources = azureLogAnalyticsTarget.Resources
|
||||
resourceOrWorkspace = azureLogAnalyticsTarget.Resources[0]
|
||||
} else if azureLogAnalyticsTarget.Resource != "" {
|
||||
resources = []string{azureLogAnalyticsTarget.Resource}
|
||||
resourceOrWorkspace = azureLogAnalyticsTarget.Resource
|
||||
} else {
|
||||
resourceOrWorkspace = azureLogAnalyticsTarget.Workspace
|
||||
} else if azureLogAnalyticsTarget.Resource != nil && *azureLogAnalyticsTarget.Resource != "" {
|
||||
resources = []string{*azureLogAnalyticsTarget.Resource}
|
||||
resourceOrWorkspace = *azureLogAnalyticsTarget.Resource
|
||||
} else if azureLogAnalyticsTarget.Workspace != nil {
|
||||
resourceOrWorkspace = *azureLogAnalyticsTarget.Workspace
|
||||
}
|
||||
|
||||
queryString = azureLogAnalyticsTarget.Query
|
||||
if azureLogAnalyticsTarget.Query != nil {
|
||||
queryString = *azureLogAnalyticsTarget.Query
|
||||
}
|
||||
}
|
||||
|
||||
if query.QueryType == string(dataquery.AzureQueryTypeAzureTraces) {
|
||||
@ -205,7 +209,7 @@ func (e *AzureLogAnalyticsDatasource) buildQueries(ctx context.Context, logger l
|
||||
|
||||
azureLogAnalyticsQueries = append(azureLogAnalyticsQueries, &AzureLogAnalyticsQuery{
|
||||
RefID: query.RefID,
|
||||
ResultFormat: resultFormat,
|
||||
ResultFormat: string(resultFormat),
|
||||
URL: apiURL,
|
||||
JSON: query.JSON,
|
||||
TimeRange: query.TimeRange,
|
||||
@ -701,7 +705,7 @@ func encodeQuery(rawQuery string) (string, error) {
|
||||
return base64.StdEncoding.EncodeToString(b.Bytes()), nil
|
||||
}
|
||||
|
||||
func buildTracesQuery(operationId string, parentSpanID *string, traceTypes []string, filters []types.TracesFilters, resultFormat *string, resources []string) string {
|
||||
func buildTracesQuery(operationId string, parentSpanID *string, traceTypes []string, filters []dataquery.AzureTracesFilter, resultFormat *dataquery.ResultFormat, resources []string) string {
|
||||
types := traceTypes
|
||||
if len(types) == 0 {
|
||||
types = Tables
|
||||
@ -709,7 +713,7 @@ func buildTracesQuery(operationId string, parentSpanID *string, traceTypes []str
|
||||
|
||||
filteredTypes := make([]string, 0)
|
||||
// If the result format is set to trace then we filter out all events that are of the type traces as they don't make sense when visualised as a span
|
||||
if resultFormat != nil && dataquery.ResultFormat(*resultFormat) == dataquery.ResultFormatTrace {
|
||||
if resultFormat != nil && *resultFormat == dataquery.ResultFormatTrace {
|
||||
filteredTypes = slices.Filter(filteredTypes, types, func(s string) bool { return s != "traces" })
|
||||
} else {
|
||||
filteredTypes = types
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"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"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
||||
@ -69,7 +70,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
|
||||
for _, query := range queries {
|
||||
var target string
|
||||
queryJSONModel := types.AzureMonitorJSONQuery{}
|
||||
queryJSONModel := dataquery.AzureMonitorQuery{}
|
||||
err := json.Unmarshal(query.JSON, &queryJSONModel)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode the Azure Monitor query object from JSON: %w", err)
|
||||
@ -77,49 +78,29 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
|
||||
azJSONModel := queryJSONModel.AzureMonitor
|
||||
// Legacy: If only MetricDefinition is set, use it as namespace
|
||||
if azJSONModel.MetricDefinition != "" && azJSONModel.MetricNamespace == "" {
|
||||
if azJSONModel.MetricDefinition != nil && *azJSONModel.MetricDefinition != "" &&
|
||||
azJSONModel.MetricNamespace != nil && *azJSONModel.MetricNamespace == "" {
|
||||
azJSONModel.MetricNamespace = azJSONModel.MetricDefinition
|
||||
}
|
||||
|
||||
azJSONModel.DimensionFilters = MigrateDimensionFilters(azJSONModel.DimensionFilters)
|
||||
|
||||
alias := azJSONModel.Alias
|
||||
|
||||
timeGrain := azJSONModel.TimeGrain
|
||||
timeGrains := azJSONModel.AllowedTimeGrainsMs
|
||||
|
||||
if timeGrain == "auto" {
|
||||
timeGrain, err = azTime.SetAutoTimeGrain(query.Interval.Milliseconds(), timeGrains)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
alias := ""
|
||||
if azJSONModel.Alias != nil {
|
||||
alias = *azJSONModel.Alias
|
||||
}
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("api-version", AzureMonitorAPIVersion)
|
||||
params.Add("timespan", fmt.Sprintf("%v/%v", query.TimeRange.From.UTC().Format(time.RFC3339), query.TimeRange.To.UTC().Format(time.RFC3339)))
|
||||
params.Add("interval", timeGrain)
|
||||
params.Add("aggregation", azJSONModel.Aggregation)
|
||||
params.Add("metricnames", azJSONModel.MetricName)
|
||||
|
||||
if azJSONModel.CustomNamespace != "" {
|
||||
params.Add("metricnamespace", azJSONModel.CustomNamespace)
|
||||
} else {
|
||||
params.Add("metricnamespace", azJSONModel.MetricNamespace)
|
||||
azureURL := ""
|
||||
if queryJSONModel.Subscription != nil {
|
||||
azureURL = BuildSubscriptionMetricsURL(*queryJSONModel.Subscription)
|
||||
}
|
||||
|
||||
azureURL := BuildSubscriptionMetricsURL(queryJSONModel.Subscription)
|
||||
filterInBody := true
|
||||
if azJSONModel.Region != "" {
|
||||
params.Add("region", azJSONModel.Region)
|
||||
}
|
||||
resourceIDs := []string{}
|
||||
resourceMap := map[string]types.AzureMonitorResource{}
|
||||
resourceMap := map[string]dataquery.AzureMonitorResource{}
|
||||
if hasOne, resourceGroup, resourceName := hasOneResource(queryJSONModel); hasOne {
|
||||
ub := urlBuilder{
|
||||
ResourceURI: azJSONModel.ResourceURI,
|
||||
ResourceURI: azJSONModel.ResourceUri,
|
||||
// Alternative, used to reconstruct resource URI if it's not present
|
||||
DefaultSubscription: dsInfo.Settings.SubscriptionId,
|
||||
DefaultSubscription: &dsInfo.Settings.SubscriptionId,
|
||||
Subscription: queryJSONModel.Subscription,
|
||||
ResourceGroup: resourceGroup,
|
||||
MetricNamespace: azJSONModel.MetricNamespace,
|
||||
@ -128,25 +109,36 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
azureURL = ub.BuildMetricsURL()
|
||||
// POST requests are only supported at the subscription level
|
||||
filterInBody = false
|
||||
resourceMap[ub.buildResourceURI()] = types.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName}
|
||||
resourceUri := ub.buildResourceURI()
|
||||
if resourceUri != nil {
|
||||
resourceMap[*resourceUri] = dataquery.AzureMonitorResource{ResourceGroup: resourceGroup, ResourceName: resourceName}
|
||||
}
|
||||
} else {
|
||||
for _, r := range azJSONModel.Resources {
|
||||
ub := urlBuilder{
|
||||
DefaultSubscription: dsInfo.Settings.SubscriptionId,
|
||||
DefaultSubscription: &dsInfo.Settings.SubscriptionId,
|
||||
Subscription: queryJSONModel.Subscription,
|
||||
ResourceGroup: r.ResourceGroup,
|
||||
MetricNamespace: azJSONModel.MetricNamespace,
|
||||
ResourceName: r.ResourceName,
|
||||
}
|
||||
resourceUri := ub.buildResourceURI()
|
||||
resourceMap[resourceUri] = r
|
||||
resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", resourceUri))
|
||||
if resourceUri != nil {
|
||||
resourceMap[*resourceUri] = r
|
||||
}
|
||||
resourceIDs = append(resourceIDs, fmt.Sprintf("Microsoft.ResourceId eq '%s'", *resourceUri))
|
||||
}
|
||||
}
|
||||
|
||||
// old model
|
||||
dimension := strings.TrimSpace(azJSONModel.Dimension)
|
||||
dimensionFilter := strings.TrimSpace(azJSONModel.DimensionFilter)
|
||||
dimension := ""
|
||||
if azJSONModel.Dimension != nil {
|
||||
dimension = strings.TrimSpace(*azJSONModel.Dimension)
|
||||
}
|
||||
dimensionFilter := ""
|
||||
if azJSONModel.DimensionFilter != nil {
|
||||
dimensionFilter = strings.TrimSpace(*azJSONModel.DimensionFilter)
|
||||
}
|
||||
|
||||
dimSB := strings.Builder{}
|
||||
|
||||
@ -155,9 +147,9 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
} else {
|
||||
for i, filter := range azJSONModel.DimensionFilters {
|
||||
if len(filter.Filters) == 0 {
|
||||
dimSB.WriteString(fmt.Sprintf("%s eq '*'", filter.Dimension))
|
||||
dimSB.WriteString(fmt.Sprintf("%s eq '*'", *filter.Dimension))
|
||||
} else {
|
||||
dimSB.WriteString(filter.ConstructFiltersString())
|
||||
dimSB.WriteString(types.ConstructFiltersString(filter))
|
||||
}
|
||||
if i != len(azJSONModel.DimensionFilters)-1 {
|
||||
dimSB.WriteString(" and ")
|
||||
@ -175,16 +167,21 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
}
|
||||
}
|
||||
|
||||
if azJSONModel.Top != "" {
|
||||
params.Add("top", azJSONModel.Top)
|
||||
params, err := getParams(azJSONModel, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
target = params.Encode()
|
||||
|
||||
if setting.Env == setting.Dev {
|
||||
logger.Debug("Azuremonitor request", "params", params)
|
||||
}
|
||||
|
||||
sub := ""
|
||||
if queryJSONModel.Subscription != nil {
|
||||
sub = *queryJSONModel.Subscription
|
||||
}
|
||||
|
||||
query := &types.AzureMonitorQuery{
|
||||
URL: azureURL,
|
||||
Target: target,
|
||||
@ -194,7 +191,7 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
TimeRange: query.TimeRange,
|
||||
Dimensions: azJSONModel.DimensionFilters,
|
||||
Resources: resourceMap,
|
||||
Subscription: queryJSONModel.Subscription,
|
||||
Subscription: sub,
|
||||
}
|
||||
if filterString != "" {
|
||||
if filterInBody {
|
||||
@ -209,6 +206,45 @@ func (e *AzureMonitorDatasource) buildQueries(logger log.Logger, queries []backe
|
||||
return azureMonitorQueries, nil
|
||||
}
|
||||
|
||||
func getParams(azJSONModel *dataquery.AzureMetricQuery, query backend.DataQuery) (url.Values, error) {
|
||||
params := url.Values{}
|
||||
|
||||
timeGrain := azJSONModel.TimeGrain
|
||||
timeGrains := azJSONModel.AllowedTimeGrainsMs
|
||||
|
||||
if timeGrain != nil && *timeGrain == "auto" {
|
||||
var err error
|
||||
timeGrain, err = azTime.SetAutoTimeGrain(query.Interval.Milliseconds(), timeGrains)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
params.Add("api-version", AzureMonitorAPIVersion)
|
||||
params.Add("timespan", fmt.Sprintf("%v/%v", query.TimeRange.From.UTC().Format(time.RFC3339), query.TimeRange.To.UTC().Format(time.RFC3339)))
|
||||
if timeGrain != nil {
|
||||
params.Add("interval", *timeGrain)
|
||||
}
|
||||
if azJSONModel.Aggregation != nil {
|
||||
params.Add("aggregation", *azJSONModel.Aggregation)
|
||||
}
|
||||
if azJSONModel.MetricName != nil {
|
||||
params.Add("metricnames", *azJSONModel.MetricName)
|
||||
}
|
||||
if azJSONModel.CustomNamespace != nil && *azJSONModel.CustomNamespace != "" {
|
||||
params.Add("metricnamespace", *azJSONModel.CustomNamespace)
|
||||
} else if azJSONModel.MetricNamespace != nil {
|
||||
params.Add("metricnamespace", *azJSONModel.MetricNamespace)
|
||||
}
|
||||
if azJSONModel.Region != nil && *azJSONModel.Region != "" {
|
||||
params.Add("region", *azJSONModel.Region)
|
||||
}
|
||||
if azJSONModel.Top != nil && *azJSONModel.Top != "" {
|
||||
params.Add("top", *azJSONModel.Top)
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func retrieveSubscriptionDetails(e *AzureMonitorDatasource, cli *http.Client, ctx context.Context, logger log.Logger, tracer tracing.Tracer, subscriptionId string, baseUrl string, dsId int64, orgId int64) {
|
||||
req, err := e.createRequest(ctx, logger, fmt.Sprintf("%s/subscriptions/%s", baseUrl, subscriptionId))
|
||||
if err != nil {
|
||||
@ -489,7 +525,20 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, res
|
||||
continue
|
||||
}
|
||||
|
||||
switch dimension.Operator {
|
||||
if dimension.Dimension == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if dimension.Operator == nil {
|
||||
filter := types.AzureMonitorDimensionFilterBackend{
|
||||
Key: *dimension.Dimension,
|
||||
Operator: 0,
|
||||
Values: dimensionFilters,
|
||||
}
|
||||
filters = append(filters, filter)
|
||||
continue
|
||||
}
|
||||
switch *dimension.Operator {
|
||||
case "eq":
|
||||
dimensionInt = 0
|
||||
case "ne":
|
||||
@ -499,7 +548,7 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, res
|
||||
}
|
||||
|
||||
filter := types.AzureMonitorDimensionFilterBackend{
|
||||
Key: dimension.Dimension,
|
||||
Key: *dimension.Dimension,
|
||||
Operator: dimensionInt,
|
||||
Values: dimensionFilters,
|
||||
}
|
||||
@ -552,7 +601,7 @@ func getQueryUrl(query *types.AzureMonitorQuery, azurePortalUrl, resourceID, res
|
||||
|
||||
// formatAzureMonitorLegendKey builds the legend key or timeseries name
|
||||
// Alias patterns like {{resourcename}} are replaced with the appropriate data values.
|
||||
func formatAzureMonitorLegendKey(alias string, subscriptionId string, resource types.AzureMonitorResource, metricName string, metadataName string,
|
||||
func formatAzureMonitorLegendKey(alias string, subscriptionId string, resource dataquery.AzureMonitorResource, metricName string, metadataName string,
|
||||
metadataValue string, namespace string, seriesID string, labels data.Labels) string {
|
||||
// Could be a collision problem if there were two keys that varied only in case, but I don't think that would happen in azure.
|
||||
lowerLabels := data.Labels{}
|
||||
@ -582,16 +631,16 @@ func formatAzureMonitorLegendKey(alias string, subscriptionId string, resource t
|
||||
}
|
||||
}
|
||||
|
||||
if metaPartName == "resourcegroup" {
|
||||
return []byte(resource.ResourceGroup)
|
||||
if metaPartName == "resourcegroup" && resource.ResourceGroup != nil {
|
||||
return []byte(*resource.ResourceGroup)
|
||||
}
|
||||
|
||||
if metaPartName == "namespace" {
|
||||
return []byte(namespace)
|
||||
}
|
||||
|
||||
if metaPartName == "resourcename" {
|
||||
return []byte(resource.ResourceName)
|
||||
if metaPartName == "resourcename" && resource.ResourceName != nil {
|
||||
return []byte(*resource.ResourceName)
|
||||
}
|
||||
|
||||
if metaPartName == "metric" {
|
||||
@ -674,17 +723,17 @@ func extractResourceIDFromMetricsURL(url string) string {
|
||||
return strings.Split(url, "/providers/microsoft.insights/metrics")[0]
|
||||
}
|
||||
|
||||
func hasOneResource(query types.AzureMonitorJSONQuery) (bool, string, string) {
|
||||
func hasOneResource(query dataquery.AzureMonitorQuery) (bool, *string, *string) {
|
||||
azJSONModel := query.AzureMonitor
|
||||
if len(azJSONModel.Resources) > 1 {
|
||||
return false, "", ""
|
||||
return false, nil, nil
|
||||
}
|
||||
if len(azJSONModel.Resources) == 1 {
|
||||
return true, azJSONModel.Resources[0].ResourceGroup, azJSONModel.Resources[0].ResourceName
|
||||
}
|
||||
if azJSONModel.ResourceGroup != "" || azJSONModel.ResourceName != "" {
|
||||
if (azJSONModel.ResourceGroup != nil && *azJSONModel.ResourceGroup != "") || (azJSONModel.ResourceName != nil && *azJSONModel.ResourceName != "") {
|
||||
// Deprecated, Resources should be used instead
|
||||
return true, azJSONModel.ResourceGroup, azJSONModel.ResourceName
|
||||
}
|
||||
return false, "", ""
|
||||
return false, nil, nil
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
|
||||
"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"
|
||||
@ -48,7 +49,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
expectedBodyFilter string
|
||||
expectedParamFilter string
|
||||
expectedPortalURL *string
|
||||
resources map[string]types.AzureMonitorResource
|
||||
resources map[string]dataquery.AzureMonitorResource
|
||||
}{
|
||||
{
|
||||
name: "Parse queries from frontend and build AzureMonitor API queries",
|
||||
@ -110,7 +111,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "legacy query without resourceURI and has dimensionFilter*s* property with one dimension",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "eq", Filter: &wildcardFilter}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("eq"), Filter: &wildcardFilter}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -123,7 +124,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "legacy query without resourceURI and has dimensionFilter*s* property with two dimensions",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "eq", Filter: &wildcardFilter}, {Dimension: "tier", Operator: "eq", Filter: &wildcardFilter}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("eq"), Filter: &wildcardFilter}, {Dimension: strPtr("tier"), Operator: strPtr("eq"), Filter: &wildcardFilter}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -148,7 +149,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "has dimensionFilter*s* property with not equals operator",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "ne", Filter: &wildcardFilter, Filters: []string{"test"}}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("ne"), Filter: &wildcardFilter, Filters: []string{"test"}}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -161,7 +162,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "has dimensionFilter*s* property with startsWith operator",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "sw", Filter: &testFilter}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("sw"), Filter: &testFilter}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -174,7 +175,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "correctly sets dimension operator to eq (irrespective of operator) when filter value is '*'",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "sw", Filter: &wildcardFilter}, {Dimension: "tier", Operator: "ne", Filter: &wildcardFilter}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("sw"), Filter: &wildcardFilter}, {Dimension: strPtr("tier"), Operator: strPtr("ne"), Filter: &wildcardFilter}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -187,7 +188,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "correctly constructs target when multiple filter values are provided for the 'eq' operator",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "eq", Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("eq"), Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -200,7 +201,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "correctly constructs target when multiple filter values are provided for ne 'eq' operator",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "ne", Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("ne"), Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -226,7 +227,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
"timeGrain": "PT1M",
|
||||
"top": "10",
|
||||
"region": "westus",
|
||||
"resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}, {ResourceGroup: "rg2", ResourceName: "vm2"}},
|
||||
"resources": []dataquery.AzureMonitorResource{{ResourceGroup: strPtr("rg"), ResourceName: strPtr("vm")}, {ResourceGroup: strPtr("rg2"), ResourceName: strPtr("vm2")}},
|
||||
},
|
||||
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&top=10",
|
||||
@ -237,7 +238,7 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "includes a single resource as a parameter filter",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}},
|
||||
"resources": []dataquery.AzureMonitorResource{{ResourceGroup: strPtr("rg"), ResourceName: strPtr("vm")}},
|
||||
},
|
||||
queryInterval: duration,
|
||||
expectedInterval: "PT1M",
|
||||
@ -248,8 +249,8 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
name: "includes a resource and a dimesion as filters",
|
||||
azureMonitorVariedProperties: map[string]interface{}{
|
||||
"timeGrain": "PT1M",
|
||||
"resources": []types.AzureMonitorResource{{ResourceGroup: "rg", ResourceName: "vm"}, {ResourceGroup: "rg2", ResourceName: "vm2"}},
|
||||
"dimensionFilters": []types.AzureMonitorDimensionFilter{{Dimension: "blob", Operator: "ne", Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"resources": []dataquery.AzureMonitorResource{{ResourceGroup: strPtr("rg"), ResourceName: strPtr("vm")}, {ResourceGroup: strPtr("rg2"), ResourceName: strPtr("vm2")}},
|
||||
"dimensionFilters": []dataquery.AzureMetricDimension{{Dimension: strPtr("blob"), Operator: strPtr("ne"), Filter: &wildcardFilter, Filters: []string{"test", "test2"}}},
|
||||
"top": "30",
|
||||
},
|
||||
queryInterval: duration,
|
||||
@ -296,14 +297,14 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
|
||||
queries, err := datasource.buildQueries(log.New("test"), tsdbQuery, dsInfo)
|
||||
require.NoError(t, err)
|
||||
|
||||
resources := map[string]types.AzureMonitorResource{}
|
||||
resources := map[string]dataquery.AzureMonitorResource{}
|
||||
if tt.azureMonitorVariedProperties["resources"] != nil {
|
||||
resourceSlice := tt.azureMonitorVariedProperties["resources"].([]types.AzureMonitorResource)
|
||||
resourceSlice := tt.azureMonitorVariedProperties["resources"].([]dataquery.AzureMonitorResource)
|
||||
for _, resource := range resourceSlice {
|
||||
resources[fmt.Sprintf("/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", resource.ResourceGroup, resource.ResourceName)] = resource
|
||||
resources[fmt.Sprintf("/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/%s/providers/Microsoft.Compute/virtualMachines/%s", *resource.ResourceGroup, *resource.ResourceName)] = resource
|
||||
}
|
||||
} else {
|
||||
resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] = types.AzureMonitorResource{ResourceGroup: "grafanastaging", ResourceName: "grafana"}
|
||||
resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] = dataquery.AzureMonitorResource{ResourceGroup: strPtr("grafanastaging"), ResourceName: strPtr("grafana")}
|
||||
}
|
||||
|
||||
azureMonitorQuery := &types.AzureMonitorQuery{
|
||||
@ -366,8 +367,9 @@ func TestCustomNamespace(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAzureMonitorParseResponse(t *testing.T) {
|
||||
resources := map[string]types.AzureMonitorResource{}
|
||||
resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] = types.AzureMonitorResource{ResourceGroup: "grafanastaging", ResourceName: "grafana"}
|
||||
resources := map[string]dataquery.AzureMonitorResource{}
|
||||
resources["/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanastaging/providers/Microsoft.Compute/virtualMachines/grafana"] =
|
||||
dataquery.AzureMonitorResource{ResourceGroup: strPtr("grafanastaging"), ResourceName: strPtr("grafana")}
|
||||
subscription := "12345678-aaaa-bbbb-cccc-123456789abc"
|
||||
|
||||
tests := []struct {
|
||||
@ -484,7 +486,7 @@ func TestAzureMonitorParseResponse(t *testing.T) {
|
||||
Params: url.Values{
|
||||
"aggregation": {"Average"},
|
||||
},
|
||||
Resources: map[string]types.AzureMonitorResource{"/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanatest/providers/Microsoft.Storage/storageAccounts/testblobaccount/blobServices/default/providers/Microsoft.Insights/metrics": {ResourceGroup: "grafanatest", ResourceName: "testblobaccount"}},
|
||||
Resources: map[string]dataquery.AzureMonitorResource{"/subscriptions/12345678-aaaa-bbbb-cccc-123456789abc/resourceGroups/grafanatest/providers/Microsoft.Storage/storageAccounts/testblobaccount/blobServices/default/providers/Microsoft.Insights/metrics": {ResourceGroup: strPtr("grafanatest"), ResourceName: strPtr("testblobaccount")}},
|
||||
Subscription: subscription,
|
||||
},
|
||||
},
|
||||
@ -681,3 +683,7 @@ func TestExtractResourceNameFromMetricsURL(t *testing.T) {
|
||||
require.Equal(t, expected, extractResourceNameFromMetricsURL((url)))
|
||||
})
|
||||
}
|
||||
|
||||
func strPtr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
@ -1,11 +1,9 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
||||
)
|
||||
import "github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
|
||||
|
||||
func MigrateDimensionFilters(filters []types.AzureMonitorDimensionFilter) []types.AzureMonitorDimensionFilter {
|
||||
var newFilters []types.AzureMonitorDimensionFilter
|
||||
func MigrateDimensionFilters(filters []dataquery.AzureMetricDimension) []dataquery.AzureMetricDimension {
|
||||
var newFilters []dataquery.AzureMetricDimension
|
||||
for _, filter := range filters {
|
||||
newFilter := filter
|
||||
// Ignore the deprecation check as this is a migration
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/types"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
|
||||
)
|
||||
|
||||
func TestDimensionFiltersMigration(t *testing.T) {
|
||||
@ -14,38 +14,38 @@ func TestDimensionFiltersMigration(t *testing.T) {
|
||||
additionalTestFilter := "testFilter2"
|
||||
tests := []struct {
|
||||
name string
|
||||
dimensionFilters []types.AzureMonitorDimensionFilter
|
||||
expectedDimensionFilters []types.AzureMonitorDimensionFilter
|
||||
dimensionFilters []dataquery.AzureMetricDimension
|
||||
expectedDimensionFilters []dataquery.AzureMetricDimension
|
||||
}{
|
||||
{
|
||||
name: "will return new format unchanged",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{"testFilter"}}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{"testFilter"}}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{"testFilter"}}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{"testFilter"}}},
|
||||
},
|
||||
{
|
||||
name: "correctly updates old format with wildcard",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filter: &wildcard}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq"}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filter: &wildcard}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq")}},
|
||||
},
|
||||
{
|
||||
name: "correctly updates old format with a value",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filter: &testFilter}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{testFilter}}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filter: &testFilter}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{testFilter}}},
|
||||
},
|
||||
{
|
||||
name: "correctly ignores wildcard if filters has a value",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filter: &wildcard, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{testFilter}}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filter: &wildcard, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{testFilter}}},
|
||||
},
|
||||
{
|
||||
name: "correctly merges values if filters has a value (ignores duplicates)",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filter: &testFilter, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{testFilter}}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filter: &testFilter, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{testFilter}}},
|
||||
},
|
||||
{
|
||||
name: "correctly merges values if filters has a value",
|
||||
dimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filter: &additionalTestFilter, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []types.AzureMonitorDimensionFilter{{Dimension: "testDimension", Operator: "eq", Filters: []string{testFilter, additionalTestFilter}}},
|
||||
dimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filter: &additionalTestFilter, Filters: []string{testFilter}}},
|
||||
expectedDimensionFilters: []dataquery.AzureMetricDimension{{Dimension: strPtr("testDimension"), Operator: strPtr("eq"), Filters: []string{testFilter, additionalTestFilter}}},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -7,42 +7,54 @@ import (
|
||||
|
||||
// urlBuilder builds the URL for calling the Azure Monitor API
|
||||
type urlBuilder struct {
|
||||
ResourceURI string
|
||||
ResourceURI *string
|
||||
|
||||
// Following fields will be used to generate a ResourceURI
|
||||
DefaultSubscription string
|
||||
Subscription string
|
||||
ResourceGroup string
|
||||
MetricNamespace string
|
||||
ResourceName string
|
||||
DefaultSubscription *string
|
||||
Subscription *string
|
||||
ResourceGroup *string
|
||||
MetricNamespace *string
|
||||
ResourceName *string
|
||||
}
|
||||
|
||||
func (params *urlBuilder) buildResourceURI() string {
|
||||
if params.ResourceURI != "" {
|
||||
func (params *urlBuilder) buildResourceURI() *string {
|
||||
if params.ResourceURI != nil && *params.ResourceURI != "" {
|
||||
return params.ResourceURI
|
||||
}
|
||||
|
||||
subscription := params.Subscription
|
||||
|
||||
if params.Subscription == "" {
|
||||
if params.Subscription == nil || *params.Subscription == "" {
|
||||
subscription = params.DefaultSubscription
|
||||
}
|
||||
|
||||
metricNamespaceArray := strings.Split(params.MetricNamespace, "/")
|
||||
resourceNameArray := strings.Split(params.ResourceName, "/")
|
||||
if params.MetricNamespace == nil || *params.MetricNamespace == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
metricNamespaceArray := strings.Split(*params.MetricNamespace, "/")
|
||||
var resourceNameArray []string
|
||||
if params.ResourceName != nil && *params.ResourceName != "" {
|
||||
resourceNameArray = strings.Split(*params.ResourceName, "/")
|
||||
}
|
||||
provider := metricNamespaceArray[0]
|
||||
metricNamespaceArray = metricNamespaceArray[1:]
|
||||
|
||||
if strings.HasPrefix(strings.ToLower(params.MetricNamespace), "microsoft.storage/storageaccounts/") &&
|
||||
!strings.HasSuffix(params.ResourceName, "default") {
|
||||
if strings.HasPrefix(strings.ToLower(*params.MetricNamespace), "microsoft.storage/storageaccounts/") &&
|
||||
params.ResourceName != nil &&
|
||||
!strings.HasSuffix(*params.ResourceName, "default") {
|
||||
resourceNameArray = append(resourceNameArray, "default")
|
||||
}
|
||||
|
||||
resGroup := ""
|
||||
if params.ResourceGroup != nil {
|
||||
resGroup = *params.ResourceGroup
|
||||
}
|
||||
urlArray := []string{
|
||||
"/subscriptions",
|
||||
subscription,
|
||||
*subscription,
|
||||
"resourceGroups",
|
||||
params.ResourceGroup,
|
||||
resGroup,
|
||||
"providers",
|
||||
provider,
|
||||
}
|
||||
@ -52,7 +64,7 @@ func (params *urlBuilder) buildResourceURI() string {
|
||||
}
|
||||
|
||||
resourceURI := strings.Join(urlArray, "/")
|
||||
return resourceURI
|
||||
return &resourceURI
|
||||
}
|
||||
|
||||
// BuildMetricsURL checks the metric properties to see which form of the url
|
||||
@ -61,11 +73,11 @@ func (params *urlBuilder) BuildMetricsURL() string {
|
||||
resourceURI := params.ResourceURI
|
||||
|
||||
// Prior to Grafana 9, we had a legacy query object rather than a resourceURI, so we manually create the resource URI
|
||||
if resourceURI == "" {
|
||||
if resourceURI == nil || *resourceURI == "" {
|
||||
resourceURI = params.buildResourceURI()
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s/providers/microsoft.insights/metrics", resourceURI)
|
||||
return fmt.Sprintf("%s/providers/microsoft.insights/metrics", *resourceURI)
|
||||
}
|
||||
|
||||
// BuildSubscriptionMetricsURL returns a URL for querying metrics for all resources in a subscription
|
||||
|
@ -10,7 +10,7 @@ func TestURLBuilder(t *testing.T) {
|
||||
t.Run("AzureMonitor URL Builder", 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{
|
||||
ResourceURI: "/subscriptions/sub/resource/uri",
|
||||
ResourceURI: strPtr("/subscriptions/sub/resource/uri"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -19,11 +19,11 @@ func TestURLBuilder(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: "/subscriptions/sub/resource/uri",
|
||||
DefaultSubscription: "default-sub",
|
||||
ResourceGroup: "rg",
|
||||
MetricNamespace: "Microsoft.NetApp/netAppAccounts/capacityPools/volumes",
|
||||
ResourceName: "rn1/rn2/rn3",
|
||||
ResourceURI: strPtr("/subscriptions/sub/resource/uri"),
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.NetApp/netAppAccounts/capacityPools/volumes"),
|
||||
ResourceName: strPtr("rn1/rn2/rn3"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -33,10 +33,10 @@ func TestURLBuilder(t *testing.T) {
|
||||
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",
|
||||
MetricNamespace: "Microsoft.Compute/virtualMachines",
|
||||
ResourceName: "rn",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.Compute/virtualMachines"),
|
||||
ResourceName: strPtr("rn"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -45,11 +45,11 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
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",
|
||||
MetricNamespace: "Microsoft.Compute/virtualMachines",
|
||||
ResourceName: "rn",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
Subscription: strPtr("specified-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.Compute/virtualMachines"),
|
||||
ResourceName: strPtr("rn"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -58,10 +58,10 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
t.Run("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func(t *testing.T) {
|
||||
ub := &urlBuilder{
|
||||
DefaultSubscription: "default-sub",
|
||||
ResourceGroup: "rg",
|
||||
MetricNamespace: "Microsoft.Storage/storageAccounts/blobServices",
|
||||
ResourceName: "rn1/default",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.Storage/storageAccounts/blobServices"),
|
||||
ResourceName: strPtr("rn1/default"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -70,10 +70,10 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
t.Run("when metric definition is Microsoft.Storage/storageAccounts/fileServices", func(t *testing.T) {
|
||||
ub := &urlBuilder{
|
||||
DefaultSubscription: "default-sub",
|
||||
ResourceGroup: "rg",
|
||||
MetricNamespace: "Microsoft.Storage/storageAccounts/fileServices",
|
||||
ResourceName: "rn1/default",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.Storage/storageAccounts/fileServices"),
|
||||
ResourceName: strPtr("rn1/default"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -82,10 +82,10 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
t.Run("when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes", func(t *testing.T) {
|
||||
ub := &urlBuilder{
|
||||
DefaultSubscription: "default-sub",
|
||||
ResourceGroup: "rg",
|
||||
MetricNamespace: "Microsoft.NetApp/netAppAccounts/capacityPools/volumes",
|
||||
ResourceName: "rn1/rn2/rn3",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.NetApp/netAppAccounts/capacityPools/volumes"),
|
||||
ResourceName: strPtr("rn1/rn2/rn3"),
|
||||
}
|
||||
|
||||
url := ub.BuildMetricsURL()
|
||||
@ -94,13 +94,13 @@ func TestURLBuilder(t *testing.T) {
|
||||
|
||||
t.Run("when metric definition is Microsoft.Storage/storageAccounts/blobServices", func(t *testing.T) {
|
||||
ub := &urlBuilder{
|
||||
DefaultSubscription: "default-sub",
|
||||
ResourceGroup: "rg",
|
||||
MetricNamespace: "Microsoft.Storage/storageAccounts/blobServices",
|
||||
ResourceName: "rn1",
|
||||
DefaultSubscription: strPtr("default-sub"),
|
||||
ResourceGroup: strPtr("rg"),
|
||||
MetricNamespace: strPtr("Microsoft.Storage/storageAccounts/blobServices"),
|
||||
ResourceName: strPtr("rn1"),
|
||||
}
|
||||
|
||||
url := ub.buildResourceURI()
|
||||
url := *ub.buildResourceURI()
|
||||
assert.Equal(t, "/subscriptions/default-sub/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default", url)
|
||||
})
|
||||
})
|
||||
|
@ -8,14 +8,14 @@ var (
|
||||
// SetAutoTimeGrain tries to find the closest interval to the query's intervalMs value
|
||||
// if the metric has a limited set of possible intervals/time grains then use those
|
||||
// instead of the default list of intervals
|
||||
func SetAutoTimeGrain(intervalMs int64, timeGrains []int64) (string, error) {
|
||||
func SetAutoTimeGrain(intervalMs int64, timeGrains []int64) (*string, error) {
|
||||
autoInterval := FindClosestAllowedIntervalMS(intervalMs, timeGrains)
|
||||
autoTimeGrain, err := CreateISO8601DurationFromIntervalMS(autoInterval)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return autoTimeGrain, nil
|
||||
return &autoTimeGrain, nil
|
||||
}
|
||||
|
||||
// FindClosestAllowedIntervalMS is used for the auto time grain setting.
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana-azure-sdk-go/azcredentials"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana/pkg/tsdb/azuremonitor/kinds/dataquery"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -83,8 +84,8 @@ type AzureMonitorQuery struct {
|
||||
Alias string
|
||||
TimeRange backend.TimeRange
|
||||
BodyFilter string
|
||||
Dimensions []AzureMonitorDimensionFilter
|
||||
Resources map[string]AzureMonitorResource
|
||||
Dimensions []dataquery.AzureMetricDimension
|
||||
Resources map[string]dataquery.AzureMonitorResource
|
||||
Subscription string
|
||||
}
|
||||
|
||||
@ -138,110 +139,38 @@ type AzureMonitorResource struct {
|
||||
ResourceName string `json:"resourceName"`
|
||||
}
|
||||
|
||||
// AzureMonitorJSONQuery is the frontend JSON query model for an Azure Monitor query.
|
||||
type AzureMonitorJSONQuery struct {
|
||||
AzureMonitor struct {
|
||||
ResourceURI string `json:"resourceUri"`
|
||||
// These are used to reconstruct a resource URI
|
||||
MetricNamespace string `json:"metricNamespace"`
|
||||
CustomNamespace string `json:"customNamespace"`
|
||||
MetricName string `json:"metricName"`
|
||||
Region string `json:"region"`
|
||||
Resources []AzureMonitorResource `json:"resources"`
|
||||
|
||||
Aggregation string `json:"aggregation"`
|
||||
Alias string `json:"alias"`
|
||||
DimensionFilters []AzureMonitorDimensionFilter `json:"dimensionFilters"` // new model
|
||||
TimeGrain string `json:"timeGrain"`
|
||||
Top string `json:"top"`
|
||||
|
||||
AllowedTimeGrainsMs []int64 `json:"allowedTimeGrainsMs"`
|
||||
Dimension string `json:"dimension"` // old model
|
||||
DimensionFilter string `json:"dimensionFilter"` // old model
|
||||
Format string `json:"format"`
|
||||
|
||||
// Deprecated, MetricNamespace should be used instead
|
||||
MetricDefinition string `json:"metricDefinition"`
|
||||
// Deprecated: Use Resources with a single element instead
|
||||
AzureMonitorResource
|
||||
} `json:"azureMonitor"`
|
||||
Subscription string `json:"subscription"`
|
||||
}
|
||||
|
||||
// AzureMonitorDimensionFilter is the model for the frontend sent for azureMonitor metric
|
||||
// queries like "BlobType", "eq", "*"
|
||||
type AzureMonitorDimensionFilter struct {
|
||||
Dimension string `json:"dimension"`
|
||||
Operator string `json:"operator"`
|
||||
Filters []string `json:"filters,omitempty"`
|
||||
// Deprecated: To support multiselection, filters are passed in a slice now. Also migrated in frontend.
|
||||
Filter *string `json:"filter,omitempty"`
|
||||
}
|
||||
|
||||
type AzureMonitorDimensionFilterBackend struct {
|
||||
Key string `json:"key"`
|
||||
Operator int `json:"operator"`
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
||||
func (a AzureMonitorDimensionFilter) ConstructFiltersString() string {
|
||||
func ConstructFiltersString(a dataquery.AzureMetricDimension) string {
|
||||
var filterStrings []string
|
||||
for _, filter := range a.Filters {
|
||||
filterStrings = append(filterStrings, fmt.Sprintf("%v %v '%v'", a.Dimension, a.Operator, filter))
|
||||
dimension := ""
|
||||
operator := ""
|
||||
if a.Dimension != nil {
|
||||
dimension = *a.Dimension
|
||||
}
|
||||
if a.Operator != nil {
|
||||
operator = *a.Operator
|
||||
}
|
||||
filterStrings = append(filterStrings, fmt.Sprintf("%v %v '%v'", dimension, operator, filter))
|
||||
}
|
||||
if a.Operator == "eq" {
|
||||
if a.Operator != nil && *a.Operator == "eq" {
|
||||
return strings.Join(filterStrings, " or ")
|
||||
} else {
|
||||
return strings.Join(filterStrings, " and ")
|
||||
}
|
||||
return strings.Join(filterStrings, " and ")
|
||||
}
|
||||
|
||||
// LogJSONQuery is the frontend JSON query model for an Azure Log Analytics query.
|
||||
type LogJSONQuery struct {
|
||||
AzureLogAnalytics struct {
|
||||
Query string `json:"query"`
|
||||
ResultFormat string `json:"resultFormat"`
|
||||
Resources []string `json:"resources"`
|
||||
OperationId string `json:"operationId"`
|
||||
|
||||
// Deprecated: Queries should be migrated to use Resource instead
|
||||
Workspace string `json:"workspace"`
|
||||
// Deprecated: Use Resources instead
|
||||
Resource string `json:"resource"`
|
||||
} `json:"azureLogAnalytics"`
|
||||
AzureLogAnalytics dataquery.AzureLogsQuery `json:"azureLogAnalytics"`
|
||||
}
|
||||
|
||||
type TracesJSONQuery struct {
|
||||
AzureTraces struct {
|
||||
// Filters for property values.
|
||||
Filters []TracesFilters `json:"filters"`
|
||||
|
||||
// Operation ID. Used only for Traces queries.
|
||||
OperationId *string `json:"operationId"`
|
||||
|
||||
// KQL query to be executed.
|
||||
Query *string `json:"query"`
|
||||
|
||||
// Array of resource URIs to be queried.
|
||||
Resources []string `json:"resources"`
|
||||
|
||||
// Specifies the format results should be returned as.
|
||||
ResultFormat *string `json:"resultFormat"`
|
||||
|
||||
// Types of events to filter by.
|
||||
TraceTypes []string `json:"traceTypes"`
|
||||
} `json:"azureTraces"`
|
||||
}
|
||||
|
||||
type TracesFilters struct {
|
||||
// Values to filter by.
|
||||
Filters []string `json:"filters"`
|
||||
|
||||
// Comparison operator to use. Either equals or not equals.
|
||||
Operation string `json:"operation"`
|
||||
|
||||
// Property name, auto-populated based on available traces.
|
||||
Property string `json:"property"`
|
||||
AzureTraces dataquery.AzureTracesQuery `json:"azureTraces"`
|
||||
}
|
||||
|
||||
// MetricChartDefinition is the JSON model for a metrics chart definition
|
||||
|
Loading…
Reference in New Issue
Block a user