mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AzureMonitor: Fix custom namespaces (#54937)
This commit is contained in:
parent
b5c67fe0dd
commit
879ee82b83
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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"`
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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(() => {
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
@ -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: '',
|
||||
|
@ -47,6 +47,7 @@ export interface AzureMetricQuery {
|
||||
resourceName?: string;
|
||||
/** Resource type */
|
||||
metricNamespace?: string;
|
||||
customNamespace?: string;
|
||||
metricName?: string;
|
||||
timeGrain?: string;
|
||||
aggregation?: string;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user