AzureMonitor: Fix custom namespaces (#54937)

This commit is contained in:
Adam Simpson 2022-09-12 06:10:51 -04:00 committed by GitHub
parent b5c67fe0dd
commit 879ee82b83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 100 additions and 35 deletions

View File

@ -113,8 +113,13 @@ func (e *AzureMonitorDatasource) buildQueries(queries []backend.DataQuery, dsInf
params.Add("timespan", fmt.Sprintf("%v/%v", query.TimeRange.From.UTC().Format(time.RFC3339), query.TimeRange.To.UTC().Format(time.RFC3339)))
params.Add("interval", timeGrain)
params.Add("aggregation", azJSONModel.Aggregation)
params.Add("metricnames", azJSONModel.MetricName) // MetricName or MetricNames ?
params.Add("metricnamespace", azJSONModel.MetricNamespace)
params.Add("metricnames", azJSONModel.MetricName)
if azJSONModel.CustomNamespace != "" {
params.Add("metricnamespace", azJSONModel.CustomNamespace)
} else {
params.Add("metricnamespace", azJSONModel.MetricNamespace)
}
// old model
dimension := strings.TrimSpace(azJSONModel.Dimension)

View File

@ -250,6 +250,27 @@ func TestAzureMonitorBuildQueries(t *testing.T) {
}
}
func TestCustomNamespace(t *testing.T) {
datasource := &AzureMonitorDatasource{}
t.Run("it should set the metricNamespace to a customNamespace value if customNamespace is present as a parameter", func(t *testing.T) {
q := []backend.DataQuery{
{
JSON: []byte(`{
"azureMonitor": {
"customNamespace": "custom/namespace"
}
}`),
},
}
result, err := datasource.buildQueries(q, types.DatasourceInfo{})
require.NoError(t, err)
expected := "custom/namespace"
require.Equal(t, expected, result[0].Params.Get("metricnamespace"))
})
}
func makeDates(startDate time.Time, count int, interval time.Duration) (times []time.Time) {
for i := 0; i < count; i++ {
times = append(times, startDate.Add(interval*time.Duration(i)))

View File

@ -115,6 +115,7 @@ type AzureMonitorJSONQuery struct {
ResourceURI string `json:"resourceUri"`
// These are used to reconstruct a resource URI
MetricNamespace string `json:"metricNamespace"`
CustomNamespace string `json:"customNamespace"`
MetricName string `json:"metricName"`
ResourceGroup string `json:"resourceGroup"`
ResourceName string `json:"resourceName"`

View File

@ -35,6 +35,7 @@ export default function createMockQuery(overrides?: Partial<AzureMonitorQuery>):
dimensionFilters: [],
metricName: 'Metric A',
metricNamespace: 'Microsoft.Compute/virtualMachines',
customNamespace: '',
resourceGroup: 'grafanastaging',
resourceName: 'grafana',
timeGrain: 'auto',

View File

@ -192,7 +192,7 @@ describe('AzureMonitorDatasource', () => {
const expected =
basePath +
'/providers/microsoft.insights/components/resource1' +
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=microsoft.insights%2Fcomponents';
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
expect(path).toBe(expected);
return Promise.resolve(response);
});
@ -257,7 +257,7 @@ describe('AzureMonitorDatasource', () => {
const expected =
basePath +
'/providers/microsoft.insights/components/resource1' +
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=microsoft.insights%2Fcomponents';
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
expect(path).toBe(expected);
return Promise.resolve(response);
});
@ -594,7 +594,7 @@ describe('AzureMonitorDatasource', () => {
const expected =
basePath +
'/providers/microsoft.insights/components/resource1' +
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=microsoft.insights%2Fcomponents';
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
expect(path).toBe(expected);
return Promise.resolve(response);
});
@ -660,7 +660,7 @@ describe('AzureMonitorDatasource', () => {
const expected =
basePath +
'/providers/microsoft.insights/components/resource1' +
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=microsoft.insights%2Fcomponents';
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
expect(path).toBe(expected);
return Promise.resolve(response);
});
@ -728,7 +728,7 @@ describe('AzureMonitorDatasource', () => {
const expected =
basePath +
'/providers/microsoft.insights/components/resource1' +
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01&metricnamespace=microsoft.insights%2Fcomponents';
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
expect(path).toBe(expected);
return Promise.resolve(response);
});

View File

@ -94,6 +94,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
const resourceGroup = templateSrv.replace(item.resourceGroup, scopedVars);
const resourceName = templateSrv.replace(item.resourceName, scopedVars);
const metricNamespace = templateSrv.replace(item.metricNamespace, scopedVars);
const customNamespace = templateSrv.replace(item.customNamespace, scopedVars);
const timeGrain = templateSrv.replace((item.timeGrain || '').toString(), scopedVars);
const aggregation = templateSrv.replace(item.aggregation, scopedVars);
const top = templateSrv.replace(item.top || '', scopedVars);
@ -112,6 +113,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
const azMonitorQuery: AzureMetricQuery = {
resourceGroup,
metricNamespace,
customNamespace,
resourceName,
timeGrain,
allowedTimeGrainsMs: item.allowedTimeGrainsMs,

View File

@ -156,7 +156,22 @@ describe('AzureMonitorUrlBuilder', () => {
templateSrv
);
expect(url).toBe(
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers'
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
it('handles a custom namespace', () => {
const url = UrlBuilder.buildAzureMonitorGetMetricNamesUrl(
'',
'2017-05-01-preview',
{
resourceUri: '/subscriptions/sub/resource-uri/resource',
customNamespace: 'custom/namespace',
},
templateSrv
);
expect(url).toBe(
'/subscriptions/sub/resource-uri/resource/providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=custom%2Fnamespace'
);
});
});
@ -259,7 +274,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.NetApp%2FnetAppAccounts%2FcapacityPools%2Fvolumes'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -279,7 +294,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.Sql%2Fservers%2Fdatabases'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -299,7 +314,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
expect(url).toBe(
'/subscriptions/sub1/resourceGroups/rg/providers/Microsoft.Sql/servers/rn/' +
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview&metricnamespace=Microsoft.Sql%2Fservers'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -319,7 +334,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.Storage%2FstorageAccounts%2FblobServices'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -339,7 +354,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.Storage%2FstorageAccounts%2FfileServices'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -359,7 +374,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.Storage%2FstorageAccounts%2FtableServices'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});
@ -379,7 +394,7 @@ describe('AzureMonitorUrlBuilder', () => {
);
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=Microsoft.Storage%2FstorageAccounts%2FqueueServices'
'providers/microsoft.insights/metricdefinitions?api-version=2017-05-01-preview'
);
});
});

View File

@ -81,8 +81,7 @@ export default class UrlBuilder {
templateSrv: TemplateSrv
) {
let resourceUri: string;
const { metricNamespace } = query;
const { customNamespace } = query;
if ('resourceUri' in query) {
resourceUri = query.resourceUri;
} else {
@ -94,11 +93,11 @@ export default class UrlBuilder {
resourceName,
});
}
let url = `${baseUrl}${resourceUri}/providers/microsoft.insights/metricdefinitions?api-version=${apiVersion}`;
if (metricNamespace) {
url += `&metricnamespace=${encodeURIComponent(metricNamespace)}`;
if (customNamespace) {
url += `&metricnamespace=${encodeURIComponent(customNamespace)}`;
}
return url;
}
}

View File

@ -6,7 +6,7 @@ import { Select } from '@grafana/ui';
import { AzureQueryEditorFieldProps, AzureMonitorOption } from '../../types';
import { Field } from '../Field';
import { setMetricNamespace } from './setQueryValue';
import { setCustomNamespace } from './setQueryValue';
interface MetricNamespaceFieldProps extends AzureQueryEditorFieldProps {
metricNamespaces: AzureMonitorOption[];
@ -24,7 +24,7 @@ const MetricNamespaceField: React.FC<MetricNamespaceFieldProps> = ({
return;
}
const newQuery = setMetricNamespace(query, change.value);
const newQuery = setCustomNamespace(query, change.value);
onQueryChange(newQuery);
},
[onQueryChange, query]
@ -34,7 +34,7 @@ const MetricNamespaceField: React.FC<MetricNamespaceFieldProps> = ({
const optionValues = metricNamespaces
.map((m) => m.value.toLowerCase())
.concat(variableOptionGroup.options.map((p) => p.value));
const value = query.azureMonitor?.metricNamespace;
const value = query.azureMonitor?.customNamespace || query.azureMonitor?.metricNamespace;
if (value && !optionValues.includes(value.toLowerCase())) {
options.push({ label: value, value });
}
@ -43,7 +43,7 @@ const MetricNamespaceField: React.FC<MetricNamespaceFieldProps> = ({
<Field label="Metric namespace">
<Select
inputId="azure-monitor-metrics-metric-namespace-field"
value={query.azureMonitor?.metricNamespace || null}
value={value || null}
onChange={handleChange}
options={options}
allowCustomValue

View File

@ -8,7 +8,7 @@ import { AzureMonitorErrorish, AzureMonitorOption, AzureMonitorQuery } from '../
import { toOption } from '../../utils/common';
import { useAsyncState } from '../../utils/useAsyncState';
import { setMetricNamespace } from './setQueryValue';
import { setCustomNamespace } from './setQueryValue';
type SetErrorFn = (source: string, error: AzureMonitorErrorish | undefined) => void;
@ -61,7 +61,7 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr
// Do some cleanup of the query state if need be
if (!metricNamespace && options.length) {
onChange(setMetricNamespace(query, options[0].value));
onChange(setCustomNamespace(query, options[0].value));
}
return options;
@ -75,26 +75,26 @@ export const useMetricNamespaces: DataHook = (query, datasource, onChange, setEr
export const useMetricNames: DataHook = (query, datasource, onChange, setError) => {
const { subscription } = query;
const { metricNamespace, metricName, resourceGroup, resourceName } = query.azureMonitor ?? {};
const { metricNamespace, metricName, resourceGroup, resourceName, customNamespace } = query.azureMonitor ?? {};
return useAsyncState(
async () => {
if (!subscription || !metricNamespace || !resourceGroup || !resourceName) {
return;
}
const results = await datasource.azureMonitorDatasource.getMetricNames({
subscription,
resourceGroup,
resourceName,
metricNamespace,
customNamespace,
});
const options = formatOptions(results, metricName);
return options;
},
setError,
[subscription, resourceGroup, resourceName, metricNamespace]
[subscription, resourceGroup, resourceName, metricNamespace, customNamespace]
);
};
@ -110,7 +110,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 } = query.azureMonitor ?? {};
const { resourceGroup, resourceName, metricNamespace, metricName, aggregation, timeGrain, customNamespace } =
query.azureMonitor ?? {};
// Fetch new metric metadata when the fields change
useEffect(() => {
@ -120,7 +121,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
}
datasource.azureMonitorDatasource
.getMetricMetadata({ subscription, resourceGroup, resourceName, metricNamespace, metricName })
.getMetricMetadata({ subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace })
.then((metadata) => {
// TODO: Move the aggregationTypes and timeGrain defaults into `getMetricMetadata`
const aggregations = (metadata.supportedAggTypes || [metadata.primaryAggType]).map((v) => ({
@ -137,7 +138,7 @@ export const useMetricMetadata = (query: AzureMonitorQuery, datasource: Datasour
primaryAggType: metadata.primaryAggType,
});
});
}, [datasource, subscription, resourceGroup, resourceName, metricNamespace, metricName]);
}, [datasource, subscription, resourceGroup, resourceName, metricNamespace, metricName, customNamespace]);
// Update the query state in response to the meta data changing
useEffect(() => {

View File

@ -0,0 +1,14 @@
import { setCustomNamespace } from './setQueryValue';
describe('setQueryValue', () => {
describe('setCustomNamespace', () => {
it('The metricnamespace must be: microsoft.storage/storageaccounts for storage accounts.', () => {
const result = setCustomNamespace({ refId: 'A' }, 'microsoft.storage/storageaccounts/fileservices');
expect(result.azureMonitor?.customNamespace).toEqual('');
});
it('Set a custom namespace for non storage accounts.', () => {
const result = setCustomNamespace({ refId: 'A' }, 'foo/bar');
expect(result.azureMonitor?.customNamespace).toEqual('foo/bar');
});
});
});

View File

@ -1,15 +1,16 @@
import { AzureMetricDimension, AzureMonitorQuery } from '../../types';
export function setMetricNamespace(query: AzureMonitorQuery, metricNamespace: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.metricNamespace === metricNamespace) {
export function setCustomNamespace(query: AzureMonitorQuery, selection: string | undefined): AzureMonitorQuery {
if (query.azureMonitor?.customNamespace === selection) {
return query;
}
const customNamespace = selection?.toLowerCase().startsWith('microsoft.storage/storageaccounts/') ? '' : selection;
return {
...query,
azureMonitor: {
...query.azureMonitor,
metricNamespace: metricNamespace,
customNamespace,
metricName: undefined,
aggregation: undefined,
timeGrain: '',

View File

@ -47,6 +47,7 @@ export interface AzureMetricQuery {
resourceName?: string;
/** Resource type */
metricNamespace?: string;
customNamespace?: string;
metricName?: string;
timeGrain?: string;
aggregation?: string;

View File

@ -232,6 +232,7 @@ export interface LegacyAzureGetMetricNamespacesQuery {
export interface AzureGetMetricNamesQuery {
resourceUri: string;
metricNamespace?: string;
customNamespace?: string;
}
export interface LegacyAzureGetMetricNamesQuery {
@ -239,11 +240,13 @@ export interface LegacyAzureGetMetricNamesQuery {
resourceGroup: string;
resourceName: string;
metricNamespace: string;
customNamespace?: string;
}
export interface AzureGetMetricMetadataQuery {
resourceUri: string;
metricNamespace: string;
customNamespace?: string;
metricName: string;
}
@ -252,5 +255,6 @@ export interface LegacyAzureGetMetricMetadataQuery {
resourceGroup: string;
resourceName: string;
metricNamespace: string;
customNamespace?: string;
metricName: string;
}