mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Automate location retrieval (#59602)
* Automate location retrieval - Add function to retrieve provider information - Add function to retrieve subscription locations - Add parsers for each function - Add types - Update resourcePickerData with new functions - Remove old location metadata file * Improve types - Add types for responses - Use correct name parameter - Update response parser (parseProvider unnecessary) * Fix tests - Add necessary mocks * Fix logic and ensure maps are consistent * Add tests * Review * Lint issue
This commit is contained in:
parent
020b9960c8
commit
c52d4e2a64
@ -24,6 +24,17 @@ export default function createMockDatasource(overrides?: DeepPartial<Datasource>
|
|||||||
supportedTimeGrains: [],
|
supportedTimeGrains: [],
|
||||||
dimensions: [],
|
dimensions: [],
|
||||||
}),
|
}),
|
||||||
|
getProvider: jest.fn().mockResolvedValueOnce({
|
||||||
|
namespace: 'Microsoft.Insights',
|
||||||
|
resourceTypes: [
|
||||||
|
{ resourceType: 'logs', locations: ['North Europe'], apiVersions: ['2022-11-11'], capabilities: '' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
getLocations: jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce(
|
||||||
|
new Map([['northeurope', { displayName: 'North Europe', name: 'northeurope', supportsLogs: false }]])
|
||||||
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
getAzureLogAnalyticsWorkspaces: jest.fn().mockResolvedValueOnce([]),
|
getAzureLogAnalyticsWorkspaces: jest.fn().mockResolvedValueOnce([]),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
|
import { ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
|
||||||
|
import { AzureMonitorLocations } from '../types';
|
||||||
|
|
||||||
export const createMockSubscriptions = (): ResourceRowGroup => [
|
export const createMockSubscriptions = (): ResourceRowGroup => [
|
||||||
{
|
{
|
||||||
@ -117,3 +118,6 @@ export const mockSearchResults = (): ResourceRowGroup => [
|
|||||||
location: 'northeurope',
|
location: 'northeurope',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const mockGetValidLocations = (): Map<string, AzureMonitorLocations> =>
|
||||||
|
new Map([['northeurope', { displayName: 'North Europe', name: 'northeurope', supportsLogs: true }]]);
|
||||||
|
@ -1,3 +1,2 @@
|
|||||||
export * from './locations';
|
|
||||||
export * from './resourceTypes';
|
export * from './resourceTypes';
|
||||||
export * from './logsResourceTypes';
|
export * from './logsResourceTypes';
|
||||||
|
@ -1,359 +0,0 @@
|
|||||||
// Unfortunately this list is manually maintained as there's no (nice) automated way to get
|
|
||||||
// data from Azure.
|
|
||||||
|
|
||||||
export const locationMetadata = [
|
|
||||||
{
|
|
||||||
location: 'eastus',
|
|
||||||
displayName: 'East US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'eastus2',
|
|
||||||
displayName: 'East US 2',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southcentralus',
|
|
||||||
displayName: 'South Central US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westus2',
|
|
||||||
displayName: 'West US 2',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westus3',
|
|
||||||
displayName: 'West US 3',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'australiaeast',
|
|
||||||
displayName: 'Australia East',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southeastasia',
|
|
||||||
displayName: 'Southeast Asia',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'northeurope',
|
|
||||||
displayName: 'North Europe',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'uksouth',
|
|
||||||
displayName: 'UK South',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westeurope',
|
|
||||||
displayName: 'West Europe',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'centralus',
|
|
||||||
displayName: 'Central US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'northcentralus',
|
|
||||||
displayName: 'North Central US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westus',
|
|
||||||
displayName: 'West US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southafricanorth',
|
|
||||||
displayName: 'South Africa North',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'centralindia',
|
|
||||||
displayName: 'Central India',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'eastasia',
|
|
||||||
displayName: 'East Asia',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'japaneast',
|
|
||||||
displayName: 'Japan East',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'jioindiawest',
|
|
||||||
displayName: 'Jio India West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'koreacentral',
|
|
||||||
displayName: 'Korea Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'canadacentral',
|
|
||||||
displayName: 'Canada Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'francecentral',
|
|
||||||
displayName: 'France Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'germanywestcentral',
|
|
||||||
displayName: 'Germany West Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'norwayeast',
|
|
||||||
displayName: 'Norway East',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'switzerlandnorth',
|
|
||||||
displayName: 'Switzerland North',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'uaenorth',
|
|
||||||
displayName: 'UAE North',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'brazilsouth',
|
|
||||||
displayName: 'Brazil South',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'centralusstage',
|
|
||||||
displayName: 'Central US (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'eastusstage',
|
|
||||||
displayName: 'East US (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'eastus2stage',
|
|
||||||
displayName: 'East US 2 (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'northcentralusstage',
|
|
||||||
displayName: 'North Central US (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southcentralusstage',
|
|
||||||
displayName: 'South Central US (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westusstage',
|
|
||||||
displayName: 'West US (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westus2stage',
|
|
||||||
displayName: 'West US 2 (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'asia',
|
|
||||||
displayName: 'Asia',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'asiapacific',
|
|
||||||
displayName: 'Asia Pacific',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'australia',
|
|
||||||
displayName: 'Australia',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'brazil',
|
|
||||||
displayName: 'Brazil',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'canada',
|
|
||||||
displayName: 'Canada',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'europe',
|
|
||||||
displayName: 'Europe',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'global',
|
|
||||||
displayName: 'Global',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'india',
|
|
||||||
displayName: 'India',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'japan',
|
|
||||||
displayName: 'Japan',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'uk',
|
|
||||||
displayName: 'United Kingdom',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'unitedstates',
|
|
||||||
displayName: 'United States',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'eastasiastage',
|
|
||||||
displayName: 'East Asia (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southeastasiastage',
|
|
||||||
displayName: 'Southeast Asia (Stage)',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westcentralus',
|
|
||||||
displayName: 'West Central US',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southafricawest',
|
|
||||||
displayName: 'South Africa West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'australiacentral',
|
|
||||||
displayName: 'Australia Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'australiacentral2',
|
|
||||||
displayName: 'Australia Central 2',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'australiasoutheast',
|
|
||||||
displayName: 'Australia Southeast',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'japanwest',
|
|
||||||
displayName: 'Japan West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'koreasouth',
|
|
||||||
displayName: 'Korea South',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'southindia',
|
|
||||||
displayName: 'South India',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'westindia',
|
|
||||||
displayName: 'West India',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'canadaeast',
|
|
||||||
displayName: 'Canada East',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'francesouth',
|
|
||||||
displayName: 'France South',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'germanynorth',
|
|
||||||
displayName: 'Germany North',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'norwaywest',
|
|
||||||
displayName: 'Norway West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'switzerlandwest',
|
|
||||||
displayName: 'Switzerland West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'ukwest',
|
|
||||||
displayName: 'UK West',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'uaecentral',
|
|
||||||
displayName: 'UAE Central',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'brazilsoutheast',
|
|
||||||
displayName: 'Brazil Southeast',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'chinaeast',
|
|
||||||
displayName: 'China Eest',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'chinaeast2',
|
|
||||||
displayName: 'China Eest 2',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'chinanorth',
|
|
||||||
displayName: 'China North',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'chinanorth2',
|
|
||||||
displayName: 'China North 2',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
location: 'chinanorth3',
|
|
||||||
displayName: 'China North 3',
|
|
||||||
supportsLogs: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const logsSupportedLocationsKusto = locationMetadata
|
|
||||||
.filter((v) => v.supportsLogs)
|
|
||||||
.map((v) => `"${v.location}"`)
|
|
||||||
.join(',');
|
|
||||||
|
|
||||||
// Object, keyed by location ID
|
|
||||||
export const locationDisplayNames: Record<string, string> = locationMetadata.reduce(
|
|
||||||
(acc, location) => ({
|
|
||||||
...acc,
|
|
||||||
[location.location]: location.displayName,
|
|
||||||
}),
|
|
||||||
{}
|
|
||||||
);
|
|
@ -7,7 +7,7 @@ import createMockQuery from '../__mocks__/query';
|
|||||||
import { createTemplateVariables } from '../__mocks__/utils';
|
import { createTemplateVariables } from '../__mocks__/utils';
|
||||||
import { singleVariable, subscriptionsVariable } from '../__mocks__/variables';
|
import { singleVariable, subscriptionsVariable } from '../__mocks__/variables';
|
||||||
import AzureMonitorDatasource from '../datasource';
|
import AzureMonitorDatasource from '../datasource';
|
||||||
import { AzureDataSourceJsonData, AzureQueryType } from '../types';
|
import { AzureDataSourceJsonData, AzureMonitorLocationsResponse, AzureQueryType } from '../types';
|
||||||
|
|
||||||
const templateSrv = new TemplateSrv();
|
const templateSrv = new TemplateSrv();
|
||||||
|
|
||||||
@ -351,6 +351,89 @@ describe('AzureMonitorDatasource', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('When performing getLocations', () => {
|
||||||
|
const sub1Response: AzureMonitorLocationsResponse = {
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
id: '/subscriptions/mock-subscription-id-1/locations/northeurope',
|
||||||
|
name: 'northeurope',
|
||||||
|
displayName: 'North Europe',
|
||||||
|
regionalDisplayName: '(Europe) North Europe',
|
||||||
|
metadata: {
|
||||||
|
regionType: 'Physical',
|
||||||
|
regionCategory: 'Recommended',
|
||||||
|
geographyGroup: 'EU',
|
||||||
|
longitude: '-0',
|
||||||
|
latitude: '0',
|
||||||
|
physicalLocation: 'Europe',
|
||||||
|
pairedRegion: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const sub2Response: AzureMonitorLocationsResponse = {
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
id: '/subscriptions/mock-subscription-id-2/locations/eastus2',
|
||||||
|
name: 'eastus2',
|
||||||
|
displayName: 'East US 2',
|
||||||
|
regionalDisplayName: '(US) East US 2',
|
||||||
|
metadata: {
|
||||||
|
regionType: 'Physical',
|
||||||
|
regionCategory: 'Recommended',
|
||||||
|
geographyGroup: 'US',
|
||||||
|
longitude: '-0',
|
||||||
|
latitude: '0',
|
||||||
|
physicalLocation: 'US',
|
||||||
|
pairedRegion: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
|
||||||
|
const expectedPaths = [1, 2].map(
|
||||||
|
(sub) => `azuremonitor/subscriptions/mock-subscription-id-${sub}/locations?api-version=2020-01-01`
|
||||||
|
);
|
||||||
|
expect(expectedPaths).toContain(path);
|
||||||
|
if (path.includes('mock-subscription-id-1')) {
|
||||||
|
return Promise.resolve(sub1Response);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(sub2Response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a locations map', async () => {
|
||||||
|
const result = await ctx.ds.azureMonitorDatasource.getLocations(['mock-subscription-id-1']);
|
||||||
|
|
||||||
|
expect(result.size).toBe(1);
|
||||||
|
expect(result.has('northeurope')).toBe(true);
|
||||||
|
expect(result.get('northeurope')?.name).toBe('northeurope');
|
||||||
|
expect(result.get('northeurope')?.displayName).toBe('North Europe');
|
||||||
|
expect(result.get('northeurope')?.supportsLogs).toBe(undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a locations map with locations deduped', async () => {
|
||||||
|
const result = await ctx.ds.azureMonitorDatasource.getLocations([
|
||||||
|
'mock-subscription-id-1',
|
||||||
|
'mock-subscription-id-2',
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(result.size).toBe(2);
|
||||||
|
expect(result.has('northeurope')).toBe(true);
|
||||||
|
expect(result.get('northeurope')?.name).toBe('northeurope');
|
||||||
|
expect(result.get('northeurope')?.displayName).toBe('North Europe');
|
||||||
|
expect(result.get('northeurope')?.supportsLogs).toBe(undefined);
|
||||||
|
expect(result.has('eastus2')).toBe(true);
|
||||||
|
expect(result.get('eastus2')?.name).toBe('eastus2');
|
||||||
|
expect(result.get('eastus2')?.displayName).toBe('East US 2');
|
||||||
|
expect(result.get('eastus2')?.supportsLogs).toBe(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Legacy Azure Monitor Query Object data fetchers', () => {
|
describe('Legacy Azure Monitor Query Object data fetchers', () => {
|
||||||
describe('When performing getSubscriptions', () => {
|
describe('When performing getSubscriptions', () => {
|
||||||
const response = {
|
const response = {
|
||||||
|
@ -19,6 +19,9 @@ import {
|
|||||||
GetMetricNamesQuery,
|
GetMetricNamesQuery,
|
||||||
GetMetricMetadataQuery,
|
GetMetricMetadataQuery,
|
||||||
AzureMetricQuery,
|
AzureMetricQuery,
|
||||||
|
AzureMonitorLocations,
|
||||||
|
AzureMonitorProvidersResponse,
|
||||||
|
AzureMonitorLocationsResponse,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { routeNames } from '../utils/common';
|
import { routeNames } from '../utils/common';
|
||||||
import migrateQuery from '../utils/migrateQuery';
|
import migrateQuery from '../utils/migrateQuery';
|
||||||
@ -36,6 +39,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
|||||||
apiVersion = '2018-01-01';
|
apiVersion = '2018-01-01';
|
||||||
apiPreviewVersion = '2017-12-01-preview';
|
apiPreviewVersion = '2017-12-01-preview';
|
||||||
listByResourceGroupApiVersion = '2021-04-01';
|
listByResourceGroupApiVersion = '2021-04-01';
|
||||||
|
providerApiVersion = '2021-04-01';
|
||||||
|
locationsApiVersion = '2020-01-01';
|
||||||
defaultSubscriptionId?: string;
|
defaultSubscriptionId?: string;
|
||||||
resourcePath: string;
|
resourcePath: string;
|
||||||
azurePortalUrl: string;
|
azurePortalUrl: string;
|
||||||
@ -296,4 +301,26 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
|||||||
|
|
||||||
return workingQuery;
|
return workingQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getProvider(providerName: string) {
|
||||||
|
return await this.getResource<AzureMonitorProvidersResponse>(
|
||||||
|
`${routeNames.azureMonitor}/providers/${providerName}?api-version=${this.providerApiVersion}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getLocations(subscriptions: string[]) {
|
||||||
|
const locationMap = new Map<string, AzureMonitorLocations>();
|
||||||
|
for (const subscription of subscriptions) {
|
||||||
|
const subLocations = ResponseParser.parseLocations(
|
||||||
|
await this.getResource<AzureMonitorLocationsResponse>(
|
||||||
|
`${routeNames.azureMonitor}/subscriptions/${subscription}/locations?api-version=${this.locationsApiVersion}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for (const location of subLocations) {
|
||||||
|
locationMap.set(location.name, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return locationMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,11 @@ import { find, get } from 'lodash';
|
|||||||
import TimeGrainConverter from '../time_grain_converter';
|
import TimeGrainConverter from '../time_grain_converter';
|
||||||
import {
|
import {
|
||||||
AzureMonitorLocalizedValue,
|
AzureMonitorLocalizedValue,
|
||||||
|
AzureMonitorLocations,
|
||||||
AzureMonitorMetricAvailabilityMetadata,
|
AzureMonitorMetricAvailabilityMetadata,
|
||||||
AzureMonitorMetricsMetadataResponse,
|
AzureMonitorMetricsMetadataResponse,
|
||||||
AzureMonitorOption,
|
AzureMonitorOption,
|
||||||
|
AzureMonitorLocationsResponse,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
export default class ResponseParser {
|
export default class ResponseParser {
|
||||||
static parseResponseValues(
|
static parseResponseValues(
|
||||||
@ -170,4 +172,18 @@ export default class ResponseParser {
|
|||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static parseLocations(result: AzureMonitorLocationsResponse) {
|
||||||
|
const locations: AzureMonitorLocations[] = [];
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const location of result.value) {
|
||||||
|
locations.push({ name: location.name, displayName: location.displayName, supportsLogs: undefined });
|
||||||
|
}
|
||||||
|
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ import createMockQuery from '../../__mocks__/query';
|
|||||||
import {
|
import {
|
||||||
createMockResourceGroupsBySubscription,
|
createMockResourceGroupsBySubscription,
|
||||||
createMockSubscriptions,
|
createMockSubscriptions,
|
||||||
|
mockGetValidLocations,
|
||||||
mockResourcesByResourceGroup,
|
mockResourcesByResourceGroup,
|
||||||
} from '../../__mocks__/resourcePickerRows';
|
} from '../../__mocks__/resourcePickerRows';
|
||||||
import ResourcePickerData from '../../resourcePicker/resourcePickerData';
|
import ResourcePickerData from '../../resourcePicker/resourcePickerData';
|
||||||
@ -44,7 +45,7 @@ export function createMockResourcePickerData() {
|
|||||||
mockResourcePicker.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup());
|
mockResourcePicker.getResourcesForResourceGroup = jest.fn().mockResolvedValue(mockResourcesByResourceGroup());
|
||||||
mockResourcePicker.getResourceURIFromWorkspace = jest.fn().mockReturnValue('');
|
mockResourcePicker.getResourceURIFromWorkspace = jest.fn().mockReturnValue('');
|
||||||
mockResourcePicker.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({});
|
mockResourcePicker.getResourceURIDisplayProperties = jest.fn().mockResolvedValue({});
|
||||||
|
mockResourcePicker.getLogsLocations = jest.fn().mockResolvedValue(mockGetValidLocations());
|
||||||
return mockResourcePicker;
|
return mockResourcePicker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
} from '../__mocks__/argResourcePickerResponse';
|
} from '../__mocks__/argResourcePickerResponse';
|
||||||
import createMockDatasource from '../__mocks__/datasource';
|
import createMockDatasource from '../__mocks__/datasource';
|
||||||
import { createMockInstanceSetttings } from '../__mocks__/instanceSettings';
|
import { createMockInstanceSetttings } from '../__mocks__/instanceSettings';
|
||||||
|
import { mockGetValidLocations } from '../__mocks__/resourcePickerRows';
|
||||||
import { AzureGraphResponse } from '../types';
|
import { AzureGraphResponse } from '../types';
|
||||||
|
|
||||||
import ResourcePickerData from './resourcePickerData';
|
import ResourcePickerData from './resourcePickerData';
|
||||||
@ -21,9 +22,13 @@ const createResourcePickerData = (responses: AzureGraphResponse[]) => {
|
|||||||
postResource.mockResolvedValueOnce(res);
|
postResource.mockResolvedValueOnce(res);
|
||||||
});
|
});
|
||||||
resourcePickerData.postResource = postResource;
|
resourcePickerData.postResource = postResource;
|
||||||
|
const logLocationsMap = mockGetValidLocations();
|
||||||
return { resourcePickerData, postResource };
|
const getLogsLocations = jest.spyOn(resourcePickerData, 'getLogsLocations').mockResolvedValue(logLocationsMap);
|
||||||
|
resourcePickerData.logLocationsMap = logLocationsMap;
|
||||||
|
resourcePickerData.logLocations = Array.from(logLocationsMap.values()).map((location) => `"${location.name}"`);
|
||||||
|
return { resourcePickerData, postResource, mockDatasource, getValidLocations: getLogsLocations };
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('AzureMonitor resourcePickerData', () => {
|
describe('AzureMonitor resourcePickerData', () => {
|
||||||
describe('getSubscriptions', () => {
|
describe('getSubscriptions', () => {
|
||||||
it('makes 1 call to ARG with the correct path and query arguments', async () => {
|
it('makes 1 call to ARG with the correct path and query arguments', async () => {
|
||||||
@ -375,4 +380,35 @@ describe('AzureMonitor resourcePickerData', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getValidLocations', () => {
|
||||||
|
it('returns a locations map', async () => {
|
||||||
|
const { resourcePickerData, getValidLocations } = createResourcePickerData([createMockARGSubscriptionResponse()]);
|
||||||
|
getValidLocations.mockRestore();
|
||||||
|
const subscriptions = await resourcePickerData.getSubscriptions();
|
||||||
|
const locations = await resourcePickerData.getLogsLocations(subscriptions);
|
||||||
|
|
||||||
|
expect(locations.size).toBe(1);
|
||||||
|
expect(locations.has('northeurope')).toBe(true);
|
||||||
|
expect(locations.get('northeurope')?.name).toBe('northeurope');
|
||||||
|
expect(locations.get('northeurope')?.displayName).toBe('North Europe');
|
||||||
|
expect(locations.get('northeurope')?.supportsLogs).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the raw locations map if provider is undefined', async () => {
|
||||||
|
const { resourcePickerData, mockDatasource, getValidLocations } = createResourcePickerData([
|
||||||
|
createMockARGSubscriptionResponse(),
|
||||||
|
]);
|
||||||
|
getValidLocations.mockRestore();
|
||||||
|
mockDatasource.azureMonitorDatasource.getProvider = jest.fn().mockResolvedValue(undefined);
|
||||||
|
const subscriptions = await resourcePickerData.getSubscriptions();
|
||||||
|
const locations = await resourcePickerData.getLogsLocations(subscriptions);
|
||||||
|
|
||||||
|
expect(locations.size).toBe(1);
|
||||||
|
expect(locations.has('northeurope')).toBe(true);
|
||||||
|
expect(locations.get('northeurope')?.name).toBe('northeurope');
|
||||||
|
expect(locations.get('northeurope')?.displayName).toBe('North Europe');
|
||||||
|
expect(locations.get('northeurope')?.supportsLogs).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -3,12 +3,7 @@ import { uniq } from 'lodash';
|
|||||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
import { DataSourceWithBackend } from '@grafana/runtime';
|
||||||
|
|
||||||
import {
|
import { logsResourceTypes, resourceTypeDisplayNames } from '../azureMetadata';
|
||||||
locationDisplayNames,
|
|
||||||
logsSupportedLocationsKusto,
|
|
||||||
logsResourceTypes,
|
|
||||||
resourceTypeDisplayNames,
|
|
||||||
} from '../azureMetadata';
|
|
||||||
import AzureMonitorDatasource from '../azure_monitor/azure_monitor_datasource';
|
import AzureMonitorDatasource from '../azure_monitor/azure_monitor_datasource';
|
||||||
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
|
import { ResourceRow, ResourceRowGroup, ResourceRowType } from '../components/ResourcePicker/types';
|
||||||
import { addResources, parseResourceDetails, parseResourceURI } from '../components/ResourcePicker/utils';
|
import { addResources, parseResourceDetails, parseResourceURI } from '../components/ResourcePicker/utils';
|
||||||
@ -16,6 +11,7 @@ import {
|
|||||||
AzureDataSourceJsonData,
|
AzureDataSourceJsonData,
|
||||||
AzureGraphResponse,
|
AzureGraphResponse,
|
||||||
AzureMetricResource,
|
AzureMetricResource,
|
||||||
|
AzureMonitorLocations,
|
||||||
AzureMonitorQuery,
|
AzureMonitorQuery,
|
||||||
AzureResourceGraphOptions,
|
AzureResourceGraphOptions,
|
||||||
AzureResourceSummaryItem,
|
AzureResourceSummaryItem,
|
||||||
@ -36,6 +32,8 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
resultLimit = 200;
|
resultLimit = 200;
|
||||||
azureMonitorDatasource;
|
azureMonitorDatasource;
|
||||||
supportedMetricNamespaces = '';
|
supportedMetricNamespaces = '';
|
||||||
|
logLocationsMap: Map<string, AzureMonitorLocations> = new Map();
|
||||||
|
logLocations: string[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
|
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
|
||||||
@ -51,6 +49,12 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
currentSelection?: AzureMetricResource
|
currentSelection?: AzureMetricResource
|
||||||
): Promise<ResourceRowGroup> {
|
): Promise<ResourceRowGroup> {
|
||||||
const subscriptions = await this.getSubscriptions();
|
const subscriptions = await this.getSubscriptions();
|
||||||
|
|
||||||
|
if (this.logLocationsMap.size === 0) {
|
||||||
|
this.logLocationsMap = await this.getLogsLocations(subscriptions);
|
||||||
|
this.logLocations = Array.from(this.logLocationsMap.values()).map((location) => `"${location.name}"`);
|
||||||
|
}
|
||||||
|
|
||||||
if (!currentSelection) {
|
if (!currentSelection) {
|
||||||
return subscriptions;
|
return subscriptions;
|
||||||
}
|
}
|
||||||
@ -119,7 +123,7 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
resourceGroupName: item.resourceGroup,
|
resourceGroupName: item.resourceGroup,
|
||||||
type,
|
type,
|
||||||
typeLabel: resourceTypeDisplayNames[item.type] || item.type,
|
typeLabel: resourceTypeDisplayNames[item.type] || item.type,
|
||||||
location: locationDisplayNames[item.location] || item.location,
|
location: this.logLocationsMap.get(item.location)?.displayName || item.location,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -222,10 +226,13 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
resourceGroupId: string,
|
resourceGroupId: string,
|
||||||
type: ResourcePickerQueryType
|
type: ResourcePickerQueryType
|
||||||
): Promise<ResourceRowGroup> {
|
): Promise<ResourceRowGroup> {
|
||||||
|
if (!this.logLocations) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
const { data: response } = await this.makeResourceGraphRequest<RawAzureResourceItem[]>(`
|
const { data: response } = await this.makeResourceGraphRequest<RawAzureResourceItem[]>(`
|
||||||
resources
|
resources
|
||||||
| where id hasprefix "${resourceGroupId}"
|
| where id hasprefix "${resourceGroupId}"
|
||||||
${await this.filterByType(type)} and location in (${logsSupportedLocationsKusto})
|
${await this.filterByType(type)} and location in (${this.logLocations})
|
||||||
`);
|
`);
|
||||||
|
|
||||||
return response.map((item) => {
|
return response.map((item) => {
|
||||||
@ -240,7 +247,7 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
resourceGroupName: item.resourceGroup,
|
resourceGroupName: item.resourceGroup,
|
||||||
type: ResourceRowType.Resource,
|
type: ResourceRowType.Resource,
|
||||||
typeLabel: resourceTypeDisplayNames[item.type] || item.type,
|
typeLabel: resourceTypeDisplayNames[item.type] || item.type,
|
||||||
location: locationDisplayNames[item.location] || item.location,
|
location: this.logLocationsMap.get(item.location)?.displayName || item.location,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -359,4 +366,34 @@ export default class ResourcePickerData extends DataSourceWithBackend<AzureMonit
|
|||||||
}
|
}
|
||||||
this.supportedMetricNamespaces = uniq(supportedMetricNamespaces).join(',');
|
this.supportedMetricNamespaces = uniq(supportedMetricNamespaces).join(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getLogsLocations(subscriptions: ResourceRowGroup): Promise<Map<string, AzureMonitorLocations>> {
|
||||||
|
const subscriptionIds = subscriptions.map((sub) => sub.id);
|
||||||
|
const locations = await this.azureMonitorDatasource.getLocations(subscriptionIds);
|
||||||
|
const insightsProvider = await this.azureMonitorDatasource.getProvider('Microsoft.Insights');
|
||||||
|
const logsProvider = insightsProvider?.resourceTypes.find((provider) => provider.resourceType === 'logs');
|
||||||
|
|
||||||
|
if (!logsProvider) {
|
||||||
|
return locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
const logsLocations = logsProvider.locations.map((location) => ({
|
||||||
|
displayName: location,
|
||||||
|
name: '',
|
||||||
|
supportsLogs: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const logLocationsMap = new Map<string, AzureMonitorLocations>();
|
||||||
|
|
||||||
|
for (const logLocation of logsLocations) {
|
||||||
|
const name =
|
||||||
|
Array.from(locations.values()).find((location) => logLocation.displayName === location.displayName)?.name || '';
|
||||||
|
|
||||||
|
if (name !== '') {
|
||||||
|
logLocationsMap.set(name, { ...logLocation, name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return logLocationsMap;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,3 +258,48 @@ export interface LegacyAzureGetMetricMetadataQuery {
|
|||||||
customNamespace?: string;
|
customNamespace?: string;
|
||||||
metricName: string;
|
metricName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AzureMonitorLocations {
|
||||||
|
displayName: string;
|
||||||
|
name: string;
|
||||||
|
supportsLogs?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureMonitorProvidersResponse {
|
||||||
|
namespace: string;
|
||||||
|
resourceTypes: ProviderResourceType[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProviderResourceType {
|
||||||
|
resourceType: string;
|
||||||
|
locations: string[];
|
||||||
|
apiVersions: string[];
|
||||||
|
capabilities: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AzureMonitorLocationsResponse {
|
||||||
|
value: Location[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Location {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
regionalDisplayName: string;
|
||||||
|
metadata: LocationMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationMetadata {
|
||||||
|
regionType: string;
|
||||||
|
regionCategory: string;
|
||||||
|
geographyGroup: string;
|
||||||
|
longitude: string;
|
||||||
|
latitude: string;
|
||||||
|
physicalLocation: string;
|
||||||
|
pairedRegion: LocationPairedRegion[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface LocationPairedRegion {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user