mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
AzureMonitor: Use list of resources for metrics (#61485)
This commit is contained in:
@@ -36,8 +36,7 @@ export default function createMockQuery(overrides?: Partial<AzureMonitorQuery>):
|
||||
metricName: 'Metric A',
|
||||
metricNamespace: 'Microsoft.Compute/virtualMachines',
|
||||
customNamespace: '',
|
||||
resourceGroup: 'grafanastaging',
|
||||
resourceName: 'grafana',
|
||||
resources: [{ resourceGroup: 'grafanastaging', resourceName: 'grafana' }],
|
||||
timeGrain: 'auto',
|
||||
alias: '',
|
||||
// timeGrains: [],
|
||||
|
||||
+5
-6
@@ -43,12 +43,12 @@ describe('AzureMonitorDatasource', () => {
|
||||
},
|
||||
{
|
||||
description: 'filter query with no resourceGroup',
|
||||
query: createMockQuery({ azureMonitor: { resourceGroup: undefined } }),
|
||||
query: createMockQuery({ azureMonitor: { resources: [{ resourceGroup: undefined }] } }),
|
||||
filtered: false,
|
||||
},
|
||||
{
|
||||
description: 'filter query with no resourceName',
|
||||
query: createMockQuery({ azureMonitor: { resourceName: undefined } }),
|
||||
query: createMockQuery({ azureMonitor: { resources: [{ resourceName: undefined }] } }),
|
||||
filtered: false,
|
||||
},
|
||||
{
|
||||
@@ -117,9 +117,8 @@ describe('AzureMonitorDatasource', () => {
|
||||
expect(templatedQuery).toMatchObject({
|
||||
subscription,
|
||||
azureMonitor: {
|
||||
resourceGroup,
|
||||
metricNamespace,
|
||||
resourceName,
|
||||
resources: [{ resourceGroup, resourceName }],
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -346,8 +345,8 @@ describe('AzureMonitorDatasource', () => {
|
||||
|
||||
it('should return a query with any template variables replaced', () => {
|
||||
const templateableProps = [
|
||||
'resourceGroup',
|
||||
'resourceName',
|
||||
'resources[0].resourceGroup',
|
||||
'resources[0].resourceName',
|
||||
'metricNamespace',
|
||||
'timeGrain',
|
||||
'aggregation',
|
||||
|
||||
+9
-6
@@ -68,8 +68,9 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
|
||||
filterQuery(item: AzureMonitorQuery): boolean {
|
||||
const hasResource =
|
||||
hasValue(item?.azureMonitor?.resourceGroup) &&
|
||||
hasValue(item?.azureMonitor?.resourceName) &&
|
||||
item?.azureMonitor?.resources &&
|
||||
item.azureMonitor.resources.length > 0 &&
|
||||
item.azureMonitor.resources.every((r) => hasValue(r.resourceGroup) && hasValue(r.resourceName)) &&
|
||||
hasValue(item?.azureMonitor?.metricDefinition || item?.azureMonitor?.metricNamespace);
|
||||
const hasResourceUri = hasValue(item.azureMonitor?.resourceUri);
|
||||
|
||||
@@ -97,8 +98,10 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
const templateSrv = getTemplateSrv();
|
||||
|
||||
const subscriptionId = templateSrv.replace(target.subscription || this.defaultSubscriptionId, scopedVars);
|
||||
const resourceGroup = templateSrv.replace(item.resourceGroup, scopedVars);
|
||||
const resourceName = templateSrv.replace(item.resourceName, scopedVars);
|
||||
const resources = item.resources?.map((r) => ({
|
||||
resourceGroup: templateSrv.replace(r.resourceGroup, scopedVars),
|
||||
resourceName: templateSrv.replace(r.resourceName, scopedVars),
|
||||
}));
|
||||
const metricNamespace = templateSrv.replace(item.metricNamespace, scopedVars);
|
||||
const customNamespace = templateSrv.replace(item.customNamespace, scopedVars);
|
||||
const timeGrain = templateSrv.replace((item.timeGrain || '').toString(), scopedVars);
|
||||
@@ -117,10 +120,10 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
});
|
||||
|
||||
const azMonitorQuery: AzureMetricQuery = {
|
||||
resourceGroup,
|
||||
...item,
|
||||
resources,
|
||||
metricNamespace,
|
||||
customNamespace,
|
||||
resourceName,
|
||||
timeGrain,
|
||||
allowedTimeGrainsMs: item.allowedTimeGrainsMs,
|
||||
metricName: templateSrv.replace(item.metricName, scopedVars),
|
||||
|
||||
+7
-4
@@ -81,8 +81,7 @@ describe('MetricsQueryEditor', () => {
|
||||
const mockDatasource = createMockDatasource({ resourcePickerData: createMockResourcePickerData() });
|
||||
const query = createMockQuery();
|
||||
delete query?.subscription;
|
||||
delete query?.azureMonitor?.resourceGroup;
|
||||
delete query?.azureMonitor?.resourceName;
|
||||
delete query?.azureMonitor?.resources;
|
||||
delete query?.azureMonitor?.metricNamespace;
|
||||
const onChange = jest.fn();
|
||||
|
||||
@@ -125,8 +124,12 @@ describe('MetricsQueryEditor', () => {
|
||||
subscription: 'def-456',
|
||||
azureMonitor: expect.objectContaining({
|
||||
metricNamespace: 'microsoft.compute/virtualmachines',
|
||||
resourceGroup: 'dev-3',
|
||||
resourceName: 'web-server',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'dev-3',
|
||||
resourceName: 'web-server',
|
||||
},
|
||||
],
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
+2
-2
@@ -39,9 +39,9 @@ const MetricsQueryEditor: React.FC<MetricsQueryEditorProps> = ({
|
||||
const metricNames = useMetricNames(query, datasource, onChange, setError);
|
||||
const resource: AzureMetricResource = {
|
||||
subscription: query.subscription,
|
||||
resourceGroup: query.azureMonitor?.resourceGroup,
|
||||
resourceGroup: query.azureMonitor?.resources?.[0]?.resourceGroup,
|
||||
metricNamespace: query.azureMonitor?.metricNamespace,
|
||||
resourceName: query.azureMonitor?.resourceName,
|
||||
resourceName: query.azureMonitor?.resources?.[0]?.resourceName,
|
||||
};
|
||||
return (
|
||||
<span data-testid="azure-monitor-metrics-query-editor-with-experimental-ui">
|
||||
|
||||
+41
-13
@@ -45,13 +45,21 @@ describe('AzureMonitor: metrics dataHooks', () => {
|
||||
hook: useMetricNames,
|
||||
emptyQueryPartial: {
|
||||
metricNamespace: 'azure/vm',
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
},
|
||||
customProperties: {
|
||||
metricNamespace: 'azure/vm',
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
metricName: 'metric-$ENVIRONMENT',
|
||||
},
|
||||
expectedOptions: [
|
||||
@@ -75,13 +83,21 @@ describe('AzureMonitor: metrics dataHooks', () => {
|
||||
hook: useMetricNamespaces,
|
||||
emptyQueryPartial: {
|
||||
metricNamespace: 'azure/vm',
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
},
|
||||
customProperties: {
|
||||
metricNamespace: 'azure/vm-$ENVIRONMENT',
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
metricName: 'metric-name',
|
||||
},
|
||||
expectedOptions: [
|
||||
@@ -188,8 +204,12 @@ describe('AzureMonitor: metrics dataHooks', () => {
|
||||
name: 'useMetricMetadata',
|
||||
hook: useMetricMetadata,
|
||||
emptyQueryPartial: {
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
metricNamespace: 'azure/vm',
|
||||
metricName: 'Average CPU',
|
||||
},
|
||||
@@ -240,8 +260,12 @@ describe('AzureMonitor: metrics dataHooks', () => {
|
||||
name: 'useMetricNamespaces',
|
||||
hook: useMetricNamespaces,
|
||||
emptyQueryPartial: {
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
},
|
||||
],
|
||||
metricNamespace: 'azure/vm',
|
||||
},
|
||||
customProperties: {},
|
||||
@@ -264,7 +288,11 @@ describe('AzureMonitor: metrics dataHooks', () => {
|
||||
|
||||
expect(result.current).toEqual(metricNamespacesConfig.expectedOptions);
|
||||
expect(datasource.azureMonitorDatasource.getMetricNamespaces).toHaveBeenCalledWith(
|
||||
expect.objectContaining(metricNamespacesConfig.emptyQueryPartial),
|
||||
expect.objectContaining({
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'rn',
|
||||
metricNamespace: 'azure/vm',
|
||||
}),
|
||||
// Here, "global" should be false
|
||||
false
|
||||
);
|
||||
|
||||
+17
-5
@@ -4,7 +4,7 @@ import { rangeUtil } from '@grafana/data';
|
||||
|
||||
import Datasource from '../../datasource';
|
||||
import TimegrainConverter from '../../time_grain_converter';
|
||||
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../../types';
|
||||
import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery, AzureMonitorResource } from '../../types';
|
||||
import { toOption } from '../../utils/common';
|
||||
import { useAsyncState } from '../../utils/useAsyncState';
|
||||
|
||||
@@ -38,9 +38,20 @@ export interface MetricMetadata {
|
||||
|
||||
type OnChangeFn = (newQuery: AzureMonitorQuery) => void;
|
||||
|
||||
const getResourceGroupAndName = (resources?: AzureMonitorResource[]) => {
|
||||
if (!resources || !resources.length) {
|
||||
return { resourceGroup: '', resourceName: '' };
|
||||
}
|
||||
return {
|
||||
resourceGroup: resources[0].resourceGroup ?? '',
|
||||
resourceName: resources[0].resourceName ?? '',
|
||||
};
|
||||
};
|
||||
|
||||
export const useMetricNamespaces: DataHook = (query, datasource, onChange, setError) => {
|
||||
const { subscription } = query;
|
||||
const { metricNamespace, resourceGroup, resourceName } = query.azureMonitor ?? {};
|
||||
const { metricNamespace, resources } = query.azureMonitor ?? {};
|
||||
const { resourceGroup, resourceName } = getResourceGroupAndName(resources);
|
||||
|
||||
const metricNamespaces = useAsyncState(
|
||||
async () => {
|
||||
@@ -75,7 +86,8 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr
|
||||
|
||||
export const useMetricNames: DataHook = (query, datasource, onChange, setError) => {
|
||||
const { subscription } = query;
|
||||
const { metricNamespace, metricName, resourceGroup, resourceName, customNamespace } = query.azureMonitor ?? {};
|
||||
const { metricNamespace, metricName, resources, customNamespace } = query.azureMonitor ?? {};
|
||||
const { resourceGroup, resourceName } = getResourceGroupAndName(resources);
|
||||
|
||||
return useAsyncState(
|
||||
async () => {
|
||||
@@ -110,8 +122,8 @@ const defaultMetricMetadata: MetricMetadata = {
|
||||
export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasource, onChange: OnChangeFn) => {
|
||||
const [metricMetadata, setMetricMetadata] = useState<MetricMetadata>(defaultMetricMetadata);
|
||||
const { subscription } = query;
|
||||
const { resourceGroup, resourceName, metricNamespace, metricName, aggregation, timeGrain, customNamespace } =
|
||||
query.azureMonitor ?? {};
|
||||
const { resources, metricNamespace, metricName, aggregation, timeGrain, customNamespace } = query.azureMonitor ?? {};
|
||||
const { resourceGroup, resourceName } = getResourceGroupAndName(resources);
|
||||
|
||||
// Fetch new metric metadata when the fields change
|
||||
useEffect(() => {
|
||||
|
||||
+6
-2
@@ -184,8 +184,12 @@ describe('AzureMonitor ResourcePicker utils', () => {
|
||||
aggregation: undefined,
|
||||
metricName: undefined,
|
||||
metricNamespace: 'microsoft.storage/storageaccounts',
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'testacct',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'rg',
|
||||
resourceName: 'testacct',
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
+1
-2
@@ -145,9 +145,8 @@ export function setResource(query: AzureMonitorQuery, resource?: string | AzureM
|
||||
subscription: resource?.subscription,
|
||||
azureMonitor: {
|
||||
...query.azureMonitor,
|
||||
resourceGroup: resource?.resourceGroup,
|
||||
metricNamespace: resource?.metricNamespace?.toLocaleLowerCase(),
|
||||
resourceName: resource?.resourceName,
|
||||
resources: [{ resourceGroup: resource?.resourceGroup, resourceName: resource?.resourceName }],
|
||||
metricName: undefined,
|
||||
aggregation: undefined,
|
||||
timeGrain: '',
|
||||
|
||||
+1
-2
@@ -36,9 +36,8 @@ const SubscriptionField: React.FC<SubscriptionFieldProps> = ({
|
||||
if (query.queryType === AzureQueryType.AzureMonitor) {
|
||||
newQuery.azureMonitor = {
|
||||
...newQuery.azureMonitor,
|
||||
resourceGroup: undefined,
|
||||
resources: undefined,
|
||||
metricNamespace: undefined,
|
||||
resourceName: undefined,
|
||||
metricName: undefined,
|
||||
aggregation: undefined,
|
||||
timeGrain: '',
|
||||
|
||||
@@ -6,7 +6,7 @@ describe('Azure Monitor Datasource', () => {
|
||||
describe('interpolateVariablesInQueries()', () => {
|
||||
it('should interpolate variables in the queries', () => {
|
||||
const ds = new Datasource(createMockInstanceSetttings());
|
||||
const queries = [createMockQuery({ azureMonitor: { resourceGroup: '$resourceGroup' } })];
|
||||
const queries = [createMockQuery({ azureMonitor: { resources: [{ resourceGroup: '$resourceGroup' }] } })];
|
||||
|
||||
const interpolatedQueries = ds.interpolateVariablesInQueries(queries, {
|
||||
resourceGroup: { text: 'the-resource-group', value: 'the-resource-group' },
|
||||
@@ -14,7 +14,9 @@ describe('Azure Monitor Datasource', () => {
|
||||
|
||||
expect(interpolatedQueries).toContainEqual(
|
||||
expect.objectContaining({
|
||||
azureMonitor: expect.objectContaining({ resourceGroup: 'the-resource-group' }),
|
||||
azureMonitor: expect.objectContaining({
|
||||
resources: [expect.objectContaining({ resourceGroup: 'the-resource-group' })],
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
@@ -39,12 +39,16 @@ export interface AzureMonitorQuery extends DataQuery {
|
||||
resource?: string;
|
||||
}
|
||||
|
||||
export interface AzureMonitorResource {
|
||||
resourceGroup?: string;
|
||||
resourceName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Azure Monitor Metrics sub-query properties
|
||||
*/
|
||||
export interface AzureMetricQuery {
|
||||
resourceGroup?: string;
|
||||
resourceName?: string;
|
||||
resources?: AzureMetricResource[];
|
||||
/** metricNamespace is used as the resource type (or resource namespace).
|
||||
* It's usually equal to the target metric namespace.
|
||||
* Kept the name of the variable as metricNamespace to avoid backward incompatibility issues.
|
||||
@@ -74,6 +78,11 @@ export interface AzureMetricQuery {
|
||||
|
||||
/** @deprecated Use resourceGroup, resourceName and metricNamespace instead */
|
||||
resourceUri?: string;
|
||||
|
||||
/** @deprecated Use resources instead */
|
||||
resourceGroup?: string;
|
||||
/** @deprecated Use resources instead */
|
||||
resourceName?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
-4
@@ -53,8 +53,12 @@ const modernMetricsQuery: AzureMonitorQuery = {
|
||||
dimensionFilters: [{ dimension: 'dependency/success', filters: ['*'], operator: 'eq' }],
|
||||
metricName: 'dependencies/duration',
|
||||
metricNamespace: 'microsoft.insights/components',
|
||||
resourceGroup: 'cloud-datasources',
|
||||
resourceName: 'AppInsightsTestData',
|
||||
resources: [
|
||||
{
|
||||
resourceGroup: 'cloud-datasources',
|
||||
resourceName: 'AppInsightsTestData',
|
||||
},
|
||||
],
|
||||
timeGrain: 'PT5M',
|
||||
top: '10',
|
||||
},
|
||||
@@ -173,8 +177,7 @@ describe('AzureMonitor: migrateQuery', () => {
|
||||
subscription: modernMetricsQuery.subscription,
|
||||
azureMonitor: expect.objectContaining({
|
||||
metricNamespace: modernMetricsQuery.azureMonitor!.metricNamespace,
|
||||
resourceGroup: modernMetricsQuery.azureMonitor!.resourceGroup,
|
||||
resourceName: modernMetricsQuery.azureMonitor!.resourceName,
|
||||
resources: modernMetricsQuery.azureMonitor!.resources,
|
||||
resourceUri: undefined,
|
||||
}),
|
||||
})
|
||||
|
||||
+20
-2
@@ -28,6 +28,10 @@ export default function migrateQuery(query: AzureMonitorQuery): AzureMonitorQuer
|
||||
workingQuery = migrateDimensionToResourceObj(workingQuery);
|
||||
}
|
||||
|
||||
if (workingQuery.azureMonitor?.resourceGroup || workingQuery.azureMonitor?.resourceName) {
|
||||
workingQuery = migrateResourceGroupAndName(workingQuery);
|
||||
}
|
||||
|
||||
return workingQuery;
|
||||
}
|
||||
|
||||
@@ -144,9 +148,8 @@ function migrateDimensionToResourceObj(query: AzureMonitorQuery): AzureMonitorQu
|
||||
subscription: details?.subscription,
|
||||
azureMonitor: {
|
||||
...query.azureMonitor,
|
||||
resourceGroup: details?.resourceGroup,
|
||||
resources: [{ resourceGroup: details?.resourceGroup, resourceName: details?.resourceName }],
|
||||
metricNamespace: details?.metricNamespace,
|
||||
resourceName: details?.resourceName,
|
||||
resourceUri: isWellFormedUri ? undefined : query.azureMonitor.resourceUri,
|
||||
},
|
||||
};
|
||||
@@ -154,3 +157,18 @@ function migrateDimensionToResourceObj(query: AzureMonitorQuery): AzureMonitorQu
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
function migrateResourceGroupAndName(query: AzureMonitorQuery): AzureMonitorQuery {
|
||||
let workingQuery = query;
|
||||
|
||||
if (workingQuery.azureMonitor) {
|
||||
workingQuery.azureMonitor.resources = [
|
||||
{ resourceGroup: workingQuery.azureMonitor.resourceGroup, resourceName: workingQuery.azureMonitor.resourceName },
|
||||
];
|
||||
|
||||
delete workingQuery.azureMonitor.resourceGroup;
|
||||
delete workingQuery.azureMonitor.resourceName;
|
||||
}
|
||||
|
||||
return workingQuery;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user