Skip to content

Commit

Permalink
[v10.0.x] AzureMonitor: Fix metric names for multi-resources. (#70994)
Browse files Browse the repository at this point in the history
AzureMonitor: Fix metric names for multi-resources. (#70864)

When building a query for multiple resources only a subset of metrics
are valid and that selection is only available via the API version `2017-12-01-preview`.

fixes #68603

(cherry picked from commit f32f185)

Co-authored-by: Adam Simpson <adam@adamsimpson.net>
  • Loading branch information
grafana-delivery-bot[bot] and asimpson committed Jun 30, 2023
1 parent 47ac8e3 commit df3e56a
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 32 deletions.
Expand Up @@ -317,7 +317,7 @@ describe('AzureMonitorDatasource', () => {
],
};

beforeEach(() => {
it('should return list of Metric Names', () => {
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp';
const expected =
Expand All @@ -327,9 +327,6 @@ describe('AzureMonitorDatasource', () => {
expect(path).toBe(expected);
return Promise.resolve(response);
});
});

it('should return list of Metric Names', () => {
return ctx.ds.azureMonitorDatasource
.getMetricNames({
resourceUri:
Expand All @@ -344,6 +341,33 @@ describe('AzureMonitorDatasource', () => {
expect(results[1].value).toEqual('FreeCapacity');
});
});

it('should return list of Metric Names appropriate when multiple resources are selected', () => {
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp';
const expected =
basePath +
'/providers/microsoft.insights/metricdefinitions?api-version=2017-12-01-preview&metricnamespace=microsoft.insights%2Fcomponents&region=region';
expect(path).toBe(expected);
return Promise.resolve(response);
});
return ctx.ds.azureMonitorDatasource
.getMetricNames(
{
resourceUri: '/subscriptions/mock-subscription-id/resourceGroups/nodeapp',
metricNamespace: 'microsoft.insights/components',
},
true,
'region'
)
.then((results: Array<{ text: string; value: string }>) => {
expect(results.length).toEqual(2);
expect(results[0].text).toEqual('Used capacity');
expect(results[0].value).toEqual('UsedCapacity');
expect(results[1].text).toEqual('Free capacity');
expect(results[1].value).toEqual('FreeCapacity');
});
});
});

describe('When performing getMetricMetadata', () => {
Expand Down
Expand Up @@ -265,27 +265,33 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
});
}

getMetricNames(query: GetMetricNamesQuery) {
getMetricNames(query: GetMetricNamesQuery, multipleResources?: boolean, region?: string) {
const apiVersion = multipleResources ? this.apiPreviewVersion : this.apiVersion;
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
this.resourcePath,
this.apiVersion,
apiVersion,
// Only use the first query, as the metric names should be the same for all queries
this.replaceSingleTemplateVariables(query),
this.templateSrv
this.templateSrv,
multipleResources,
region
);
return this.getResource(url).then((result: AzureMonitorMetricNamesResponse) => {
return ResponseParser.parseResponseValues(result, 'name.localizedValue', 'name.value');
});
}

getMetricMetadata(query: GetMetricMetadataQuery) {
getMetricMetadata(query: GetMetricMetadataQuery, multipleResources?: boolean, region?: string) {
const { metricName } = query;
const apiVersion = multipleResources ? this.apiPreviewVersion : this.apiVersion;
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
this.resourcePath,
this.apiVersion,
apiVersion,
// Only use the first query, as the metric metadata should be the same for all queries
this.replaceSingleTemplateVariables(query),
this.templateSrv
this.templateSrv,
multipleResources,
region
);
return this.getResource(url).then((result: AzureMonitorMetricsMetadataResponse) => {
return ResponseParser.parseMetadata(result, this.templateSrv.replace(metricName));
Expand Down
Expand Up @@ -398,5 +398,23 @@ describe('AzureMonitorUrlBuilder', () => {
);
});
});
describe('when multiple resources are selected', () => {
it('passes metricNamespace as query param', () => {
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
'',
'2017-05-01-preview',
{
resourceUri: '/subscriptions/sub/resource-uri/resource',
metricNamespace: 'Microsoft.Sql/servers',
},
templateSrv,
true,
'region'
);
expect(url).toBe(
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers&region=region'
);
});
});
});
});
Expand Up @@ -3,14 +3,12 @@ import { TemplateSrv } from '@grafana/runtime';
import { AzureMonitorResource, GetMetricNamespacesQuery, GetMetricNamesQuery } from '../types';

export default class UrlBuilder {
static buildResourceUri(templateSrv: TemplateSrv, resource: AzureMonitorResource) {
static buildResourceUri(templateSrv: TemplateSrv, resource: AzureMonitorResource, multipleResources?: boolean) {
const urlArray = [];
const { subscription, resourceGroup, metricNamespace, resourceName } = resource;

if (subscription) {
urlArray.push('/subscriptions', subscription);

if (resourceGroup) {
if (resourceGroup && !multipleResources) {
urlArray.push('resourceGroups', resourceGroup);

if (metricNamespace && resourceName) {
Expand Down Expand Up @@ -78,26 +76,40 @@ export default class UrlBuilder {
baseUrl: string,
apiVersion: string,
query: GetMetricNamesQuery,
templateSrv: TemplateSrv
templateSrv: TemplateSrv,
multipleResources?: boolean,
region?: string
) {
let resourceUri: string;
const { customNamespace } = query;
const { customNamespace, metricNamespace } = query;
if ('resourceUri' in query) {
resourceUri = query.resourceUri;
} else {
const { subscription, resourceGroup, metricNamespace, resourceName } = query;
resourceUri = UrlBuilder.buildResourceUri(templateSrv, {
subscription,
resourceGroup,
metricNamespace,
resourceName,
});
resourceUri = UrlBuilder.buildResourceUri(
templateSrv,
{
subscription,
resourceGroup,
metricNamespace,
resourceName,
},
multipleResources
);
}
let url = `${baseUrl}${resourceUri}/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}`;
if (customNamespace) {
url += `&metricnamespace=${encodeURIComponent(customNamespace)}`;
}

if (multipleResources && !customNamespace && metricNamespace) {
url += `&metricnamespace=${encodeURIComponent(metricNamespace)}`;

if (region) {
url += `&region=${region}`;
}
}

return url;
}
}
Expand Up @@ -88,25 +88,31 @@ export const useMetricNames: DataHook = (query, datasource, onChange, setError)
const { subscription } = query;
const { metricNamespace, metricName, resources, customNamespace } = query.azureMonitor ?? {};
const { resourceGroup, resourceName } = getResourceGroupAndName(resources);
const multipleResources = (resources && resources.length > 1) ?? false;
const region = query.azureMonitor?.region ?? '';

return useAsyncState(
async () => {
if (!subscription || !metricNamespace || !resourceGroup || !resourceName) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNames({
subscription,
resourceGroup,
resourceName,
metricNamespace,
customNamespace,
});
const results = await datasource.azureMonitorDatasource.getMetricNames(
{
subscription,
resourceGroup,
resourceName,
metricNamespace,
customNamespace,
},
multipleResources,
region
);
const options = formatOptions(results, metricName);

return options;
},
setError,
[subscription, resourceGroup, resourceName, metricNamespace, customNamespace]
[subscription, resourceGroup, resourceName, metricNamespace, customNamespace, multipleResources]
);
};

Expand All @@ -124,6 +130,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
const { subscription } = query;
const { resources, metricNamespace, metricName, aggregation, timeGrain, customNamespace } = query.azureMonitor ?? {};
const { resourceGroup, resourceName } = getResourceGroupAndName(resources);
const multipleResources = (resources && resources.length > 1) ?? false;

// Fetch new metric metadata when the fields change
useEffect(() => {
Expand All @@ -133,7 +140,10 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
}

datasource.azureMonitorDatasource
.getMetricMetadata({ subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace })
.getMetricMetadata(
{ subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace },
multipleResources
)
.then((metadata) => {
// TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata`
const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({
Expand All @@ -150,7 +160,16 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
primaryAggType: metadata.primaryAggType,
});
});
}, [datasource, subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace]);
}, [
datasource,
subscription,
resourceGroup,
resourceName,
metricNamespace,
metricName,
customNamespace,
multipleResources,
]);

// Update the query state in response to the meta data changing
useEffect(() => {
Expand Down

0 comments on commit df3e56a

Please sign in to comment.