mirror of
https://github.com/grafana/grafana.git
synced 2025-01-11 00:22:06 -06:00
AzureMonitor: Improve resource picker efficiency (#93127)
* Parameterise region building metric namespace URL - Add parameter for region (this parameter takes precedence over if global is set) - Update tests - Support this parameter on the data source method * Refactor fetchAllNamespaces - Use Set rather than an array for greater performance - Request namespaces across WestEurope, EastUS, and JapanEast concurrently - Update test * Maintain existing behaviour
This commit is contained in:
parent
72bfa624ce
commit
6a3dbe7d41
@ -231,14 +231,15 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
return (await Promise.all(promises)).flat();
|
||||
}
|
||||
|
||||
getMetricNamespaces(query: GetMetricNamespacesQuery, globalRegion: boolean) {
|
||||
getMetricNamespaces(query: GetMetricNamespacesQuery, globalRegion: boolean, region?: string) {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
this.resourcePath,
|
||||
this.apiPreviewVersion,
|
||||
// Only use the first query, as the metric namespaces should be the same for all queries
|
||||
this.replaceSingleTemplateVariables(query),
|
||||
globalRegion,
|
||||
this.templateSrv
|
||||
this.templateSrv,
|
||||
region
|
||||
);
|
||||
return this.getResource(url)
|
||||
.then((result: AzureAPIResponse<Namespace>) => {
|
||||
|
@ -142,6 +142,38 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview®ion=global'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a getMetricNamesnamespace url with a specific region', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
},
|
||||
false,
|
||||
templateSrv,
|
||||
'testregion'
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview®ion=testregion'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a getMetricNamesnamespace url with a specific region (overriding global)', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
},
|
||||
true,
|
||||
templateSrv,
|
||||
'testregion'
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview®ion=testregion'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a resource uri and metric namespace is provided', () => {
|
||||
|
@ -51,7 +51,8 @@ export default class UrlBuilder {
|
||||
apiVersion: string,
|
||||
query: GetMetricNamespacesQuery,
|
||||
globalRegion: boolean,
|
||||
templateSrv: TemplateSrv
|
||||
templateSrv: TemplateSrv,
|
||||
region?: string
|
||||
) {
|
||||
let resourceUri: string;
|
||||
|
||||
@ -68,7 +69,7 @@ export default class UrlBuilder {
|
||||
}
|
||||
|
||||
return `${baseUrl}${resourceUri}/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}${
|
||||
globalRegion ? '®ion=global' : ''
|
||||
region ? `®ion=${region}` : globalRegion ? '®ion=global' : ''
|
||||
}`;
|
||||
}
|
||||
|
||||
|
@ -312,9 +312,33 @@ describe('AzureMonitor resourcePickerData', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
const { resourcePickerData, postResource } = createResourcePickerData([mockSubscriptionsResponse, mockResponse]);
|
||||
const { resourcePickerData, postResource, mockDatasource } = createResourcePickerData([
|
||||
mockSubscriptionsResponse,
|
||||
mockResponse,
|
||||
]);
|
||||
const formattedResults = await resourcePickerData.search('vmname', 'metrics');
|
||||
expect(postResource).toBeCalledTimes(2);
|
||||
expect(postResource).toHaveBeenCalledTimes(2);
|
||||
expect(mockDatasource.azureMonitorDatasource.getMetricNamespaces).toHaveBeenCalledWith(
|
||||
{
|
||||
resourceUri: '/subscriptions/1',
|
||||
},
|
||||
false,
|
||||
'westeurope'
|
||||
);
|
||||
expect(mockDatasource.azureMonitorDatasource.getMetricNamespaces).toHaveBeenCalledWith(
|
||||
{
|
||||
resourceUri: '/subscriptions/1',
|
||||
},
|
||||
false,
|
||||
'eastus'
|
||||
);
|
||||
expect(mockDatasource.azureMonitorDatasource.getMetricNamespaces).toHaveBeenCalledWith(
|
||||
{
|
||||
resourceUri: '/subscriptions/1',
|
||||
},
|
||||
false,
|
||||
'japaneast'
|
||||
);
|
||||
const secondCall = postResource.mock.calls[1];
|
||||
const [_, postBody] = secondCall;
|
||||
expect(postBody.query).not.toContain('union resourcecontainers');
|
||||
|
@ -1,5 +1,3 @@
|
||||
import { uniq } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { DataSourceWithBackend, reportInteraction } from '@grafana/runtime';
|
||||
|
||||
@ -359,28 +357,40 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
||||
private async fetchAllNamespaces() {
|
||||
const subscriptions = await this.getSubscriptions();
|
||||
reportInteraction('grafana_ds_azuremonitor_subscriptions_loaded', { subscriptions: subscriptions.length });
|
||||
let supportedMetricNamespaces: string[] = [];
|
||||
for await (const subscription of subscriptions) {
|
||||
|
||||
let supportedMetricNamespaces: Set<string> = new Set();
|
||||
// We make use of these three regions as they *should* contain every possible namespace
|
||||
const regions = ['westeurope', 'eastus', 'japaneast'];
|
||||
const getNamespacesForRegion = async (region: string) => {
|
||||
const namespaces = await this.azureMonitorDatasource.getMetricNamespaces(
|
||||
{
|
||||
resourceUri: `/subscriptions/${subscription.id}`,
|
||||
// We only need to run this request against the first available subscription
|
||||
resourceUri: `/subscriptions/${subscriptions[0].id}`,
|
||||
},
|
||||
true
|
||||
false,
|
||||
region
|
||||
);
|
||||
if (namespaces) {
|
||||
const namespaceVals = namespaces.map((namespace) => `"${namespace.value.toLocaleLowerCase()}"`);
|
||||
supportedMetricNamespaces = supportedMetricNamespaces.concat(namespaceVals);
|
||||
for (const namespace of namespaces) {
|
||||
supportedMetricNamespaces.add(`"${namespace.value.toLocaleLowerCase()}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (supportedMetricNamespaces.length === 0) {
|
||||
const promises = regions.map((region) => getNamespacesForRegion(region));
|
||||
await Promise.all(promises);
|
||||
|
||||
if (supportedMetricNamespaces.size === 0) {
|
||||
throw new Error(
|
||||
'Unable to resolve a list of valid metric namespaces. Validate the datasource configuration is correct and required permissions have been granted for all subscriptions. Grafana requires at least the Reader role to be assigned.'
|
||||
);
|
||||
}
|
||||
this.supportedMetricNamespaces = uniq(
|
||||
supportedMetricNamespaces.concat(resourceTypes.map((namespace) => `"${namespace}"`))
|
||||
).join(',');
|
||||
|
||||
resourceTypes.forEach((namespace) => {
|
||||
supportedMetricNamespaces.add(`"${namespace}"`);
|
||||
});
|
||||
|
||||
this.supportedMetricNamespaces = Array.from(supportedMetricNamespaces).join(',');
|
||||
}
|
||||
|
||||
parseRows(resources: Array<string | AzureMonitorResource>): ResourceRow[] {
|
||||
|
Loading…
Reference in New Issue
Block a user