mirror of
https://github.com/grafana/grafana.git
synced 2025-02-11 08:05:43 -06:00
Azure Monitor: Add support to migrate some queries with template variables (#51881)
This commit is contained in:
parent
f6f017edc2
commit
438c76252a
@ -1,7 +1,7 @@
|
||||
import { find, startsWith } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data';
|
||||
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
|
||||
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
|
||||
import { resourceTypeDisplayNames, supportedMetricNamespaces } from '../azureMetadata';
|
||||
@ -42,11 +42,13 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
declare resourceGroup: string;
|
||||
declare resourceName: string;
|
||||
timeSrv: TimeSrv;
|
||||
templateSrv: TemplateSrv;
|
||||
|
||||
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.timeSrv = getTimeSrv();
|
||||
this.templateSrv = getTemplateSrv();
|
||||
this.defaultSubscriptionId = instanceSettings.jsonData.subscriptionId;
|
||||
|
||||
const cloud = getAzureCloud(instanceSettings);
|
||||
@ -244,7 +246,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
this.resourcePath,
|
||||
this.apiPreviewVersion,
|
||||
this.replaceTemplateVariables(query)
|
||||
this.replaceTemplateVariables(query),
|
||||
this.templateSrv
|
||||
);
|
||||
return this.getResource(url)
|
||||
.then((result: AzureMonitorMetricNamespacesResponse) => {
|
||||
@ -273,7 +276,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
this.resourcePath,
|
||||
this.apiVersion,
|
||||
this.replaceTemplateVariables(query)
|
||||
this.replaceTemplateVariables(query),
|
||||
this.templateSrv
|
||||
);
|
||||
return this.getResource(url).then((result: AzureMonitorMetricNamesResponse) => {
|
||||
return ResponseParser.parseResponseValues(result, 'name.localizedValue', 'name.value');
|
||||
@ -285,7 +289,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
this.resourcePath,
|
||||
this.apiVersion,
|
||||
this.replaceTemplateVariables(query)
|
||||
this.replaceTemplateVariables(query),
|
||||
this.templateSrv
|
||||
);
|
||||
return this.getResource(url).then((result: AzureMonitorMetricsMetadataResponse) => {
|
||||
return ResponseParser.parseMetadata(result, metricName);
|
||||
|
@ -1,33 +1,96 @@
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import UrlBuilder from './url_builder';
|
||||
|
||||
let replaceMock = jest.fn().mockImplementation((s: string) => s);
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
const original = jest.requireActual('@grafana/runtime');
|
||||
return {
|
||||
...original,
|
||||
getTemplateSrv: () => ({
|
||||
replace: replaceMock,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('AzureMonitorUrlBuilder', () => {
|
||||
let templateSrv = getTemplateSrv();
|
||||
describe('buildResourceUri', () => {
|
||||
it('builds a resource uri when the required properties are provided', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', 'Microsoft.NetApp/netAppAccounts', 'name')).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/Microsoft.NetApp/netAppAccounts/name'
|
||||
);
|
||||
expect(
|
||||
UrlBuilder.buildResourceUri('sub', 'group', 'Microsoft.NetApp/netAppAccounts', 'name', templateSrv)
|
||||
).toEqual('/subscriptions/sub/resourceGroups/group/providers/Microsoft.NetApp/netAppAccounts/name');
|
||||
});
|
||||
|
||||
it('builds a resource uri correctly when a template variable is used as namespace', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns', 'name')).toEqual(
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns', 'name', templateSrv)).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/$ns/name'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a resource uri correctly when the namespace includes a storage sub-resource', () => {
|
||||
expect(
|
||||
UrlBuilder.buildResourceUri('sub', 'group', 'Microsoft.Storage/storageAccounts/tableServices', 'name')
|
||||
UrlBuilder.buildResourceUri(
|
||||
'sub',
|
||||
'group',
|
||||
'Microsoft.Storage/storageAccounts/tableServices',
|
||||
'name',
|
||||
templateSrv
|
||||
)
|
||||
).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/Microsoft.Storage/storageAccounts/name/tableServices/default'
|
||||
);
|
||||
});
|
||||
|
||||
describe('when using template variables', () => {
|
||||
replaceMock = jest
|
||||
.fn()
|
||||
.mockImplementation((s: string) =>
|
||||
s
|
||||
.replace('$ns', 'Microsoft.Storage/storageAccounts')
|
||||
.replace('$ns2', 'tableServices')
|
||||
.replace('$rs', 'name')
|
||||
.replace('$rs2', 'default')
|
||||
);
|
||||
templateSrv = getTemplateSrv();
|
||||
|
||||
it('builds a resource uri without specifying a subresource (default)', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns/tableServices', 'name', templateSrv)).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/$ns/name/tableServices/default'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a resource uri specifying a subresource (default)', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns/tableServices', 'name/default', templateSrv)).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/$ns/name/tableServices/default'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a resource uri specifying a resource template variable', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns/tableServices', '$rs/default', templateSrv)).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/$ns/$rs/tableServices/default'
|
||||
);
|
||||
});
|
||||
|
||||
it('builds a resource uri specifying multiple template variables', () => {
|
||||
expect(UrlBuilder.buildResourceUri('sub', 'group', '$ns/$ns2', '$rs/$rs2', templateSrv)).toEqual(
|
||||
'/subscriptions/sub/resourceGroups/group/providers/$ns/$rs/$ns2/$rs2'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a resource uri is provided', () => {
|
||||
it('builds a getMetricNamesnamespace url', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', {
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
);
|
||||
@ -36,10 +99,15 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when a resource uri and metric namespace is provided', () => {
|
||||
it('builds a getMetricNames url', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
metricNamespace: 'Microsoft.Sql/servers',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
resourceUri: '/subscriptions/sub/resource-uri/resource',
|
||||
metricNamespace: 'Microsoft.Sql/servers',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers'
|
||||
);
|
||||
@ -49,12 +117,17 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
describe('Legacy query object', () => {
|
||||
describe('when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes', () => {
|
||||
it('should build the getMetricNamespaces url in the even longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes',
|
||||
resourceName: 'rn1/rn2/rn3',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes',
|
||||
resourceName: 'rn1/rn2/rn3',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
@ -64,12 +137,17 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Sql/servers/databases', () => {
|
||||
it('should build the getMetricNamespaces url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers/databases',
|
||||
resourceName: 'rn1/rn2',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers/databases',
|
||||
resourceName: 'rn1/rn2',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
@ -79,12 +157,17 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Sql/servers', () => {
|
||||
it('should build the getMetricNamespaces url in the shorter format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers',
|
||||
resourceName: 'rn',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamespacesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' +
|
||||
'providers/microsoft.insights/metricNamespaces?api-version=2017-05-01-preview'
|
||||
@ -94,13 +177,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.NetApp/netAppAccounts/capacityPools/volumes and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the even longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes',
|
||||
resourceName: 'rn1/rn2/rn3',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.NetApp/netAppAccounts/capacityPools/volumes',
|
||||
resourceName: 'rn1/rn2/rn3',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.NetApp/netAppAccounts/rn1/capacityPools/rn2/volumes/rn3/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -110,13 +198,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Sql/servers/databases and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers/databases',
|
||||
resourceName: 'rn1/rn2',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers/databases',
|
||||
resourceName: 'rn1/rn2',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn1/databases/rn2/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -126,13 +219,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Sql/servers and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the shorter format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers',
|
||||
resourceName: 'rn',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Sql/servers',
|
||||
resourceName: 'rn',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -142,13 +240,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Storage/storageAccounts/blobServices and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/blobServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/blobServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/blobServices/default/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -158,13 +261,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Storage/storageAccounts/fileServices and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/fileServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/fileServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/fileServices/default/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -174,13 +282,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Storage/storageAccounts/tableServices and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/tableServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/tableServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/tableServices/default/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
@ -190,13 +303,18 @@ describe('AzureMonitorUrlBuilder', () => {
|
||||
|
||||
describe('when metric definition is Microsoft.Storage/storageAccounts/queueServices and the metricNamespace is default', () => {
|
||||
it('should build the getMetricNames url in the longer format', () => {
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl('', '2017-05-01-preview', {
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/queueServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
});
|
||||
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
|
||||
'',
|
||||
'2017-05-01-preview',
|
||||
{
|
||||
subscription: 'sub1',
|
||||
resourceGroup: 'rg',
|
||||
metricDefinition: 'Microsoft.Storage/storageAccounts/queueServices',
|
||||
resourceName: 'rn1/default',
|
||||
metricNamespace: 'default',
|
||||
},
|
||||
templateSrv
|
||||
);
|
||||
expect(url).toBe(
|
||||
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Storage/storageAccounts/rn1/queueServices/default/' +
|
||||
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=default'
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { GetMetricNamespacesQuery, GetMetricNamesQuery } from '../types';
|
||||
|
||||
export default class UrlBuilder {
|
||||
@ -5,40 +7,65 @@ export default class UrlBuilder {
|
||||
subscriptionId: string,
|
||||
resourceGroup: string,
|
||||
metricDefinition: string,
|
||||
resourceName: string
|
||||
resourceName: string,
|
||||
templateSrv: TemplateSrv
|
||||
) {
|
||||
const metricDefinitionProcessed = templateSrv.replace(metricDefinition);
|
||||
const metricDefinitionArray = metricDefinition.split('/');
|
||||
const resourceNameProcessed = templateSrv.replace(resourceName);
|
||||
const resourceNameArray = resourceName.split('/');
|
||||
const provider = metricDefinitionArray.shift();
|
||||
const urlArray = ['/subscriptions', subscriptionId, 'resourceGroups', resourceGroup, 'providers', provider];
|
||||
if (metricDefinition.startsWith('Microsoft.Storage/storageAccounts/') && resourceNameArray.at(-1) !== 'default') {
|
||||
|
||||
if (
|
||||
metricDefinitionProcessed.startsWith('Microsoft.Storage/storageAccounts/') &&
|
||||
!resourceNameProcessed.endsWith('default')
|
||||
) {
|
||||
resourceNameArray.push('default');
|
||||
}
|
||||
if (metricDefinitionArray.length > 0) {
|
||||
for (const i in metricDefinitionArray) {
|
||||
urlArray.push(metricDefinitionArray[i]);
|
||||
urlArray.push(resourceNameArray[i]);
|
||||
}
|
||||
} else {
|
||||
urlArray.push(resourceNameArray[0]);
|
||||
|
||||
if (resourceNameArray.length > metricDefinitionArray.length) {
|
||||
const parentResource = resourceNameArray.shift();
|
||||
urlArray.push(parentResource);
|
||||
}
|
||||
|
||||
for (const i in metricDefinitionArray) {
|
||||
urlArray.push(metricDefinitionArray[i]);
|
||||
urlArray.push(resourceNameArray[i]);
|
||||
}
|
||||
return urlArray.join('/');
|
||||
}
|
||||
|
||||
static buildAzureMonitorGetMetricNamespacesUrl(baseUrl: string, apiVersion: string, query: GetMetricNamespacesQuery) {
|
||||
static buildAzureMonitorGetMetricNamespacesUrl(
|
||||
baseUrl: string,
|
||||
apiVersion: string,
|
||||
query: GetMetricNamespacesQuery,
|
||||
templateSrv: TemplateSrv
|
||||
) {
|
||||
let resourceUri: string;
|
||||
|
||||
if ('resourceUri' in query) {
|
||||
resourceUri = query.resourceUri;
|
||||
} else {
|
||||
const { subscription, resourceGroup, metricDefinition, resourceName } = query;
|
||||
resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName);
|
||||
resourceUri = UrlBuilder.buildResourceUri(
|
||||
subscription,
|
||||
resourceGroup,
|
||||
metricDefinition,
|
||||
resourceName,
|
||||
templateSrv
|
||||
);
|
||||
}
|
||||
|
||||
return `${baseUrl}${resourceUri}/providers/microsoft.insights/metricNamespaces?api-version=${apiVersion}`;
|
||||
}
|
||||
|
||||
static buildAzureMonitorGetMetricNamesUrl(baseUrl: string, apiVersion: string, query: GetMetricNamesQuery) {
|
||||
static buildAzureMonitorGetMetricNamesUrl(
|
||||
baseUrl: string,
|
||||
apiVersion: string,
|
||||
query: GetMetricNamesQuery,
|
||||
templateSrv: TemplateSrv
|
||||
) {
|
||||
let resourceUri: string;
|
||||
const { metricNamespace } = query;
|
||||
|
||||
@ -46,7 +73,13 @@ export default class UrlBuilder {
|
||||
resourceUri = query.resourceUri;
|
||||
} else {
|
||||
const { subscription, resourceGroup, metricDefinition, resourceName } = query;
|
||||
resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName);
|
||||
resourceUri = UrlBuilder.buildResourceUri(
|
||||
subscription,
|
||||
resourceGroup,
|
||||
metricDefinition,
|
||||
resourceName,
|
||||
templateSrv
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -2,6 +2,8 @@ import deepEqual from 'fast-deep-equal';
|
||||
import { defaults } from 'lodash';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { AzureMonitorQuery, AzureQueryType } from '../../types';
|
||||
import migrateQuery from '../../utils/migrateQuery';
|
||||
|
||||
@ -12,7 +14,7 @@ const DEFAULT_QUERY = {
|
||||
const prepareQuery = (query: AzureMonitorQuery) => {
|
||||
// Note: _.defaults does not apply default values deeply.
|
||||
const withDefaults = defaults({}, query, DEFAULT_QUERY);
|
||||
const migratedQuery = migrateQuery(withDefaults);
|
||||
const migratedQuery = migrateQuery(withDefaults, getTemplateSrv());
|
||||
|
||||
// If we didn't make any changes to the object, then return the original object to keep the
|
||||
// identity the same, and not trigger any other useEffects or anything.
|
||||
|
@ -70,7 +70,7 @@ export default class Datasource extends DataSourceWithBackend<AzureMonitorQuery,
|
||||
|
||||
for (const baseTarget of options.targets) {
|
||||
// Migrate old query structures
|
||||
const target = datasourceMigrations(baseTarget);
|
||||
const target = datasourceMigrations(baseTarget, this.templateSrv);
|
||||
|
||||
// Skip hidden or invalid queries or ones without properties
|
||||
if (!target.queryType || target.hide || !hasQueryForType(target)) {
|
||||
|
@ -1,7 +1,22 @@
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { AzureMetricDimension, AzureMonitorQuery, AzureQueryType } from '../types';
|
||||
|
||||
import migrateQuery from './migrateQuery';
|
||||
|
||||
let replaceMock = jest.fn().mockImplementation((s: string) => s);
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
const original = jest.requireActual('@grafana/runtime');
|
||||
return {
|
||||
...original,
|
||||
getTemplateSrv: () => ({
|
||||
replace: replaceMock,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
let templateSrv = getTemplateSrv();
|
||||
|
||||
const azureMonitorQueryV7 = {
|
||||
appInsights: { dimension: [], metricName: 'select', timeGrain: 'auto' },
|
||||
azureLogAnalytics: {
|
||||
@ -82,7 +97,7 @@ const modernMetricsQuery: AzureMonitorQuery = {
|
||||
|
||||
describe('AzureMonitor: migrateQuery', () => {
|
||||
it('modern queries should not change', () => {
|
||||
const result = migrateQuery(modernMetricsQuery);
|
||||
const result = migrateQuery(modernMetricsQuery, templateSrv);
|
||||
|
||||
// MUST use .toBe because we want to assert that the identity of unmigrated queries remains the same
|
||||
expect(modernMetricsQuery).toBe(result);
|
||||
@ -90,7 +105,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
|
||||
describe('migrating from a v7 query to the latest query version', () => {
|
||||
it('should build a resource uri', () => {
|
||||
const result = migrateQuery(azureMonitorQueryV7);
|
||||
const result = migrateQuery(azureMonitorQueryV7, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -104,7 +119,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
|
||||
describe('migrating from a v8 query to the latest query version', () => {
|
||||
it('should build a resource uri', () => {
|
||||
const result = migrateQuery(azureMonitorQueryV8);
|
||||
const result = migrateQuery(azureMonitorQueryV8, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -114,6 +129,20 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should not build a resource uri with an unsupported template variable', () => {
|
||||
replaceMock = jest.fn().mockImplementation((s: string) => s.replace('$ns', 'Microsoft.Storage/storageAccounts'));
|
||||
templateSrv = getTemplateSrv();
|
||||
const query = {
|
||||
...azureMonitorQueryV8,
|
||||
azureMonitor: {
|
||||
...azureMonitorQueryV8,
|
||||
metricDefinition: '$ns',
|
||||
},
|
||||
};
|
||||
const result = migrateQuery(query, templateSrv);
|
||||
expect(result.azureMonitor?.resourceUri).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('migrating from a v9 query to the latest query version', () => {
|
||||
@ -121,7 +150,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
const dimensionFilters: AzureMetricDimension[] = [
|
||||
{ dimension: 'TestDimension', operator: 'eq', filters: ['testFilter'] },
|
||||
];
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } }, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -132,7 +161,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
});
|
||||
it('correctly updates old filter containing wildcard', () => {
|
||||
const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: '*' }];
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } }, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -145,7 +174,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
});
|
||||
it('correctly updates old filter containing value', () => {
|
||||
const dimensionFilters: AzureMetricDimension[] = [{ dimension: 'TestDimension', operator: 'eq', filter: 'test' }];
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } }, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -160,7 +189,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
const dimensionFilters: AzureMetricDimension[] = [
|
||||
{ dimension: 'TestDimension', operator: 'eq', filter: '*', filters: ['testFilter'] },
|
||||
];
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } }, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
@ -179,7 +208,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
const dimensionFilters: AzureMetricDimension[] = [
|
||||
{ dimension: 'TestDimension', operator: 'eq', filter: 'testFilter', filters: ['testFilter'] },
|
||||
];
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } });
|
||||
const result = migrateQuery({ ...azureMonitorQueryV8, azureMonitor: { dimensionFilters } }, templateSrv);
|
||||
expect(result).toMatchObject(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import UrlBuilder from '../azure_monitor/url_builder';
|
||||
import { setKustoQuery } from '../components/LogsQueryEditor/setQueryValue';
|
||||
import {
|
||||
@ -9,7 +11,7 @@ import { AzureMetricDimension, AzureMonitorQuery, AzureQueryType } from '../type
|
||||
|
||||
const OLD_DEFAULT_DROPDOWN_VALUE = 'select';
|
||||
|
||||
export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuery {
|
||||
export default function migrateQuery(query: AzureMonitorQuery, templateSrv: TemplateSrv): AzureMonitorQuery {
|
||||
let workingQuery = query;
|
||||
|
||||
// The old angular controller also had a `migrateApplicationInsightsKeys` migraiton that
|
||||
@ -21,7 +23,7 @@ export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuer
|
||||
workingQuery = migrateLogAnalyticsToFromTimes(workingQuery);
|
||||
workingQuery = migrateToDefaultNamespace(workingQuery);
|
||||
workingQuery = migrateDimensionToDimensionFilter(workingQuery);
|
||||
workingQuery = migrateResourceUri(workingQuery);
|
||||
workingQuery = migrateResourceUri(workingQuery, templateSrv);
|
||||
workingQuery = migrateDimensionFilterToArray(workingQuery);
|
||||
|
||||
return workingQuery;
|
||||
@ -96,7 +98,7 @@ function migrateDimensionToDimensionFilter(query: AzureMonitorQuery): AzureMonit
|
||||
// Azure Monitor metric queries prior to Grafana version 9 did not include a `resourceUri`.
|
||||
// The resourceUri was previously constructed with the subscription id, resource group,
|
||||
// metric definition (a.k.a. resource type), and the resource name.
|
||||
function migrateResourceUri(query: AzureMonitorQuery): AzureMonitorQuery {
|
||||
function migrateResourceUri(query: AzureMonitorQuery, templateSrv: TemplateSrv): AzureMonitorQuery {
|
||||
const azureMonitorQuery = query.azureMonitor;
|
||||
|
||||
if (!azureMonitorQuery || azureMonitorQuery.resourceUri) {
|
||||
@ -109,11 +111,28 @@ function migrateResourceUri(query: AzureMonitorQuery): AzureMonitorQuery {
|
||||
return query;
|
||||
}
|
||||
|
||||
if (metricDefinition.includes('$') || resourceName.includes('$')) {
|
||||
const metricDefinitionArray = metricDefinition.split('/');
|
||||
if (metricDefinitionArray.some((p) => templateSrv.replace(p).split('/').length > 2)) {
|
||||
// If a metric definition includes template variable with a subresource e.g.
|
||||
// Microsoft.Storage/storageAccounts/libraries, it's not possible to generate a valid
|
||||
// resource URI
|
||||
return query;
|
||||
}
|
||||
|
||||
const resourceUri = UrlBuilder.buildResourceUri(subscription, resourceGroup, metricDefinition, resourceName);
|
||||
const resourceNameArray = resourceName.split('/');
|
||||
if (resourceNameArray.some((p) => templateSrv.replace(p).split('/').length > 1)) {
|
||||
// If a resource name includes template variable with a subresource e.g.
|
||||
// abc123/def456, it's not possible to generate a valid resource URI
|
||||
return query;
|
||||
}
|
||||
|
||||
const resourceUri = UrlBuilder.buildResourceUri(
|
||||
subscription,
|
||||
resourceGroup,
|
||||
metricDefinition,
|
||||
resourceName,
|
||||
templateSrv
|
||||
);
|
||||
|
||||
return {
|
||||
...query,
|
||||
@ -163,7 +182,7 @@ function migrateDimensionFilterToArray(query: AzureMonitorQuery): AzureMonitorQu
|
||||
|
||||
// datasource.ts also contains some migrations, which have been moved to here. Unsure whether
|
||||
// they should also do all the other migrations...
|
||||
export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuery {
|
||||
export function datasourceMigrations(query: AzureMonitorQuery, templateSrv: TemplateSrv): AzureMonitorQuery {
|
||||
let workingQuery = query;
|
||||
|
||||
if (!workingQuery.queryType) {
|
||||
@ -175,7 +194,7 @@ export function datasourceMigrations(query: AzureMonitorQuery): AzureMonitorQuer
|
||||
|
||||
if (workingQuery.queryType === AzureQueryType.AzureMonitor && workingQuery.azureMonitor) {
|
||||
workingQuery = migrateDimensionToDimensionFilter(workingQuery);
|
||||
workingQuery = migrateResourceUri(workingQuery);
|
||||
workingQuery = migrateResourceUri(workingQuery, templateSrv);
|
||||
workingQuery = migrateDimensionFilterToArray(workingQuery);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user