Azure Monitor: Migrate template variables to new format (#52584)

This commit is contained in:
Andres Martinez Gotor 2022-07-21 13:21:07 +02:00 committed by GitHub
parent cf42365fdd
commit 5644555833
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 362 additions and 44 deletions

View File

@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event';
import React from 'react';
import { select, openMenu } from 'react-select-event';
import * as grafanaRuntime from '@grafana/runtime';
import * as ui from '@grafana/ui';
import createMockDatasource from '../../__mocks__/datasource';
@ -41,16 +40,11 @@ const defaultProps = {
}),
};
const originalConfigValue = grafanaRuntime.config.featureToggles.azTemplateVars;
beforeEach(() => {
// reset config
grafanaRuntime.config.featureToggles.azTemplateVars = originalConfigValue;
});
describe('VariableEditor:', () => {
it('can select a query type', async () => {
it('can view a legacy Grafana query function', async () => {
const onChange = jest.fn();
const { rerender } = render(<VariableEditor {...defaultProps} onChange={onChange} />);
const legacyQuery = { ...defaultProps.query, queryType: AzureQueryType.GrafanaTemplateVariableFn };
render(<VariableEditor {...defaultProps} onChange={onChange} query={legacyQuery} />);
await waitFor(() => screen.getByLabelText('select query type'));
expect(screen.getByLabelText('select query type')).toBeInTheDocument();
screen.getByLabelText('select query type').click();
@ -62,10 +56,6 @@ describe('VariableEditor:', () => {
queryType: AzureQueryType.GrafanaTemplateVariableFn,
})
);
const newQuery = onChange.mock.calls.at(-1)[0];
rerender(<VariableEditor {...defaultProps} query={newQuery} />);
expect(screen.queryByText('Logs')).not.toBeInTheDocument();
expect(screen.queryByText('Grafana Query Function')).toBeInTheDocument();
});
describe('log queries:', () => {
it('should render', async () => {
@ -157,22 +147,6 @@ describe('VariableEditor:', () => {
rerender(<VariableEditor {...defaultProps} query={newQuery} onChange={onChange} />);
await waitFor(() => expect(screen.getByText(text)).toBeInTheDocument());
};
beforeEach(() => {
grafanaRuntime.config.featureToggles.azTemplateVars = true;
});
it('should show the new query types if feature gate is enabled', async () => {
render(<VariableEditor {...defaultProps} />);
openMenu(screen.getByLabelText('select query type'));
await waitFor(() => expect(screen.getByText('Subscriptions')).toBeInTheDocument());
});
it('should not show the new query types if feature gate is disabled', async () => {
grafanaRuntime.config.featureToggles.azTemplateVars = false;
render(<VariableEditor {...defaultProps} />);
openMenu(screen.getByLabelText('select query type'));
await waitFor(() => expect(screen.queryByText('Subscriptions')).not.toBeInTheDocument());
});
it('should run the query if requesting subscriptions', async () => {
const onChange = jest.fn();

View File

@ -3,11 +3,10 @@ import React, { useEffect, useState } from 'react';
import { useEffectOnce } from 'react-use';
import { SelectableValue } from '@grafana/data';
import { config } from '@grafana/runtime';
import { Alert, InlineField, Select } from '@grafana/ui';
import DataSource from '../../datasource';
import { migrateStringQueriesToObjectQueries } from '../../grafanaTemplateVariableFns';
import { migrateQuery } from '../../grafanaTemplateVariableFns';
import { AzureMonitorOption, AzureMonitorQuery, AzureQueryType } from '../../types';
import useLastError from '../../utils/useLastError';
import LogsQueryEditor from '../LogsQueryEditor';
@ -26,16 +25,20 @@ const removeOption: SelectableValue = { label: '-', value: '' };
const VariableEditor = (props: Props) => {
const { query, onChange, datasource } = props;
const AZURE_QUERY_VARIABLE_TYPE_OPTIONS = [
{ label: 'Grafana Query Function', value: AzureQueryType.GrafanaTemplateVariableFn },
{ label: 'Subscriptions', value: AzureQueryType.SubscriptionsQuery },
{ label: 'Resource Groups', value: AzureQueryType.ResourceGroupsQuery },
{ label: 'Namespaces', value: AzureQueryType.NamespacesQuery },
{ label: 'Resource Names', value: AzureQueryType.ResourceNamesQuery },
{ label: 'Metric Names', value: AzureQueryType.MetricNamesQuery },
{ label: 'Workspaces', value: AzureQueryType.WorkspacesQuery },
{ label: 'Logs', value: AzureQueryType.LogAnalytics },
];
if (config.featureToggles.azTemplateVars) {
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Subscriptions', value: AzureQueryType.SubscriptionsQuery });
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Groups', value: AzureQueryType.ResourceGroupsQuery });
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Namespaces', value: AzureQueryType.NamespacesQuery });
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Resource Names', value: AzureQueryType.ResourceNamesQuery });
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Metric Names', value: AzureQueryType.MetricNamesQuery });
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({ label: 'Workspaces', value: AzureQueryType.WorkspacesQuery });
if (typeof props.query === 'object' && props.query.queryType === AzureQueryType.GrafanaTemplateVariableFn) {
// Add the option for the GrafanaTemplateVariableFn only if it's already in use
AZURE_QUERY_VARIABLE_TYPE_OPTIONS.push({
label: 'Grafana Query Function',
value: AzureQueryType.GrafanaTemplateVariableFn,
});
}
const [variableOptionGroup, setVariableOptionGroup] = useState<{ label: string; options: AzureMonitorOption[] }>({
label: 'Template Variables',
@ -55,7 +58,7 @@ const VariableEditor = (props: Props) => {
const queryType = typeof query === 'string' ? '' : query.queryType;
useEffect(() => {
migrateStringQueriesToObjectQueries(query, { datasource: datasource }).then((migratedQuery) => {
migrateQuery(query, { datasource: datasource }).then((migratedQuery) => {
onChange(migratedQuery);
});
}, [query, datasource, onChange]);

View File

@ -1,3 +1,5 @@
import { config } from '@grafana/runtime';
import { isGUIDish } from './components/ResourcePicker/utils';
import DataSource from './datasource';
import { AzureMonitorQuery, AzureQueryType } from './types';
@ -253,6 +255,58 @@ const createLogAnalyticsTemplateVariableQuery = async (
};
};
const migrateGrafanaTemplateVariableFn = (query: AzureMonitorQuery) => {
const { queryType, grafanaTemplateVariableFn } = query;
if (queryType !== AzureQueryType.GrafanaTemplateVariableFn || !grafanaTemplateVariableFn) {
return query;
}
const migratedQuery: AzureMonitorQuery = {
...query,
};
if ('subscription' in grafanaTemplateVariableFn) {
migratedQuery.subscription = grafanaTemplateVariableFn.subscription;
}
if ('resourceGroup' in grafanaTemplateVariableFn) {
migratedQuery.resourceGroup = grafanaTemplateVariableFn.resourceGroup;
}
if ('metricDefinition' in grafanaTemplateVariableFn) {
migratedQuery.namespace = grafanaTemplateVariableFn.metricDefinition;
}
if ('metricNamespace' in grafanaTemplateVariableFn) {
migratedQuery.namespace = grafanaTemplateVariableFn.metricNamespace;
}
if ('resourceName' in grafanaTemplateVariableFn) {
migratedQuery.resource = grafanaTemplateVariableFn.resourceName;
}
switch (grafanaTemplateVariableFn.kind) {
case 'SubscriptionsQuery':
migratedQuery.queryType = AzureQueryType.SubscriptionsQuery;
break;
case 'ResourceGroupsQuery':
migratedQuery.queryType = AzureQueryType.ResourceGroupsQuery;
break;
case 'MetricDefinitionsQuery':
migratedQuery.queryType = AzureQueryType.NamespacesQuery;
break;
case 'ResourceNamesQuery':
migratedQuery.queryType = AzureQueryType.ResourceNamesQuery;
break;
case 'MetricNamespaceQuery':
migratedQuery.queryType = AzureQueryType.NamespacesQuery;
break;
case 'MetricNamesQuery':
migratedQuery.queryType = AzureQueryType.MetricNamesQuery;
break;
case 'WorkspacesQuery':
migratedQuery.queryType = AzureQueryType.WorkspacesQuery;
break;
}
return migratedQuery;
};
export const migrateStringQueriesToObjectQueries = async (
rawQuery: string | AzureMonitorQuery,
options: { datasource: DataSource }
@ -266,3 +320,19 @@ export const migrateStringQueriesToObjectQueries = async (
? createGrafanaTemplateVariableQuery(rawQuery, options.datasource)
: createLogAnalyticsTemplateVariableQuery(rawQuery, options.datasource);
};
export const migrateQuery = async (
rawQuery: string | AzureMonitorQuery,
options: { datasource: DataSource }
): Promise<AzureMonitorQuery> => {
let query = await migrateStringQueriesToObjectQueries(rawQuery, options);
if (
!config.featureToggles.azLegacyTemplateVariables &&
query.queryType === AzureQueryType.GrafanaTemplateVariableFn
) {
query = migrateGrafanaTemplateVariableFn(query);
}
return query;
};

View File

@ -1,5 +1,5 @@
import createMockDatasource from './__mocks__/datasource';
import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns';
import { migrateQuery, migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns';
import { AzureMonitorQuery, AzureQueryType } from './types';
describe('migrateStringQueriesToObjectQueries', () => {
@ -239,3 +239,273 @@ describe('migrateStringQueriesToObjectQueries', () => {
});
});
});
describe('migrateStringQueriesToObjectQueries', () => {
const expectedMigrations: Array<{ input: AzureMonitorQuery; output: AzureMonitorQuery }> = [
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: { kind: 'SubscriptionsQuery', rawQuery: 'Subscriptions()' },
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.SubscriptionsQuery,
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'ResourceGroupsQuery',
rawQuery: 'ResourceGroups()',
subscription: 'defaultSubscriptionId',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.ResourceGroupsQuery,
subscription: 'defaultSubscriptionId',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'ResourceGroupsQuery',
rawQuery: 'ResourceGroups(subId)',
subscription: 'subId',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.ResourceGroupsQuery,
subscription: 'subId',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricDefinitionsQuery',
rawQuery: 'Namespaces(rg)',
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.NamespacesQuery,
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricDefinitionsQuery',
rawQuery: 'Namespaces(subId, rg)',
subscription: 'subId',
resourceGroup: 'rg',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.NamespacesQuery,
subscription: 'subId',
resourceGroup: 'rg',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'ResourceNamesQuery',
rawQuery: 'ResourceNames(rg, md)',
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
metricDefinition: 'md',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.ResourceNamesQuery,
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
namespace: 'md',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'ResourceNamesQuery',
rawQuery: 'ResourceNames(subId, rg, md)',
subscription: 'subId',
resourceGroup: 'rg',
metricDefinition: 'md',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.ResourceNamesQuery,
subscription: 'subId',
resourceGroup: 'rg',
namespace: 'md',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricNamespaceQuery',
rawQuery: 'MetricNamespace(rg, md, rn)',
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
metricDefinition: 'md',
resourceName: 'rn',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.NamespacesQuery,
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
namespace: 'md',
resource: 'rn',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricNamespaceQuery',
rawQuery: 'MetricNamespace(subId, rg, md, rn)',
subscription: 'subId',
resourceGroup: 'rg',
metricDefinition: 'md',
resourceName: 'rn',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.NamespacesQuery,
subscription: 'subId',
resourceGroup: 'rg',
namespace: 'md',
resource: 'rn',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricNamesQuery',
rawQuery: 'MetricNames(rg, md, rn, mn)',
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
metricDefinition: 'md',
resourceName: 'rn',
metricNamespace: 'mn',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.MetricNamesQuery,
subscription: 'defaultSubscriptionId',
resourceGroup: 'rg',
namespace: 'mn',
resource: 'rn',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'MetricNamesQuery',
rawQuery: 'MetricNames(subId, rg, md, rn, mn)',
subscription: 'subId',
resourceGroup: 'rg',
metricDefinition: 'md',
resourceName: 'rn',
metricNamespace: 'mn',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.MetricNamesQuery,
subscription: 'subId',
resourceGroup: 'rg',
namespace: 'mn',
resource: 'rn',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'WorkspacesQuery',
rawQuery: 'workspaces()',
subscription: 'defaultSubscriptionId',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.WorkspacesQuery,
subscription: 'defaultSubscriptionId',
},
},
{
input: {
refId: 'A',
queryType: AzureQueryType.GrafanaTemplateVariableFn,
grafanaTemplateVariableFn: {
kind: 'WorkspacesQuery',
rawQuery: 'workspaces(subId)',
subscription: 'subId',
},
subscription: 'defaultSubscriptionId',
},
output: {
refId: 'A',
queryType: AzureQueryType.WorkspacesQuery,
subscription: 'subId',
},
},
];
it('successfully converts all old variable functions into formatted predefined queries', async () => {
return expectedMigrations.map(async ({ input, output }) => {
const datasource = createMockDatasource({
azureMonitorDatasource: {
defaultSubscriptionId: 'defaultSubscriptionId',
},
});
const actual = await migrateQuery(input, { datasource });
expect(actual).toMatchObject(output);
});
});
});

View File

@ -389,7 +389,7 @@ describe('VariableSupport', () => {
azureMonitorDatasource: {
defaultSubscriptionId: 'defaultSubscriptionId',
},
getMetricDefinitions: jest.fn((sub: string, rg: string) => {
getMetricNamespaces: jest.fn((sub: string, rg: string) => {
if (sub === 'subscriptionId' && rg === 'resourceGroup') {
return Promise.resolve(expectedResults);
}

View File

@ -11,7 +11,7 @@ import { getTemplateSrv } from '@grafana/runtime';
import VariableEditor from './components/VariableEditor/VariableEditor';
import DataSource from './datasource';
import { migrateStringQueriesToObjectQueries } from './grafanaTemplateVariableFns';
import { migrateQuery } from './grafanaTemplateVariableFns';
import { AzureMonitorQuery, AzureQueryType } from './types';
import { GrafanaTemplateVariableQuery } from './types/templateVariables';
import messageFromError from './utils/messageFromError';
@ -27,7 +27,7 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
query(request: DataQueryRequest<AzureMonitorQuery>): Observable<DataQueryResponse> {
const promisedResults = async () => {
const queryObj = await migrateStringQueriesToObjectQueries(request.targets[0], { datasource: this.datasource });
const queryObj = await migrateQuery(request.targets[0], { datasource: this.datasource });
try {
switch (queryObj.queryType) {
@ -101,6 +101,7 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
return from(promisedResults());
}
// Deprecated
callGrafanaTemplateVariableFn(query: GrafanaTemplateVariableQuery): Promise<MetricFindValue[]> | null {
if (query.kind === 'SubscriptionsQuery') {
return this.datasource.getSubscriptions();